easy-coding-harness 1.1.0-beta.2 → 1.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,26 @@
6
6
  - `y`:常规功能升级
7
7
  - `z`:日常 bug 修复
8
8
 
9
+ ## 1.1.0-beta.4
10
+
11
+ - Hook 使用独立的会话存储与状态展示模块,避免加载完整工作流和 Canonical Spec 校验器;
12
+ 同次调用复用配置、任务与执行日志读取,保留会话迁移及状态提示。
13
+ - 定向检查区分生产依赖、选中测试与公共辅助输入,保留构建所需依赖,减少无关测试变化导致的重跑。
14
+ - 验收快照复用未变 Git 对象,只读取变化内容;自动验收共享本次漂移结果,保留全仓变化检测。
15
+ - Canonical 写回复用同次前置检查与写后确认,保留设计新鲜度、CAS 和幂等语义;批量准备、
16
+ 登记检查作为默认调用方式,减少重复状态查询。
17
+
18
+ ## 1.1.0-beta.3
19
+
20
+ - MEMORY 以可复用开发知识为正文,提炼业务规则、设计原因、修改入口、适用边界与真实踩坑经验;
21
+ 短期记忆写出后即可用于后续开发,没有新增知识时明确记录无新增,不拼凑验收报告。
22
+ - 精简短期记忆模板,移除执行流水、审批、指纹、单次测试统计与无价值内容清单;验收证据保留在
23
+ 原任务日志,通过来源引用追溯,不再要求向记忆全文复制验收字段。
24
+ - 普通与派发协作复用已有方案、实施和 Review 结论完成沉淀,不增加报告、仓库扫描或验证轮次;
25
+ 分析优先读取命中的知识摘要,长期蒸馏过滤历史过程噪声。
26
+ - 升级保留历史记忆与用户模板,新 MEMORY 指令优先于旧模板中的过程栏目;schema 2、归属与
27
+ 完整性校验、滑动窗口及验收流程保持兼容。
28
+
9
29
  ## 1.1.0-beta.2
10
30
 
11
31
  - 新增 `cooperate_mode: default | dispatch`,支持用户手动派发实现和 QUALITY 内修复,主 Agent
package/README.md CHANGED
@@ -102,7 +102,9 @@ any stage --[user abort via ec-task-close]--> CLOSED
102
102
  `diff_sha256`。用户确认后不重跑 Review Gate:纯非执行差异可沿用
103
103
  原验证,可执行差异补定向验证,显式风险豁免单独记录。配置、方案或 Canonical 设计漂移
104
104
  不能走这条例外。
105
- - `MEMORY` 先写入本次任务短期记忆,再执行长期记忆阈值门禁;未超过阈值时长期沉淀为 no-op。
105
+ - `MEMORY` 提炼可直接用于后续开发的知识:业务规则、设计原因、修改入口、适用边界与踩坑经验。
106
+ 复用已有方案、实施和 Review 结论,验收及过程证据留在任务日志,记忆只引用来源;没有新增
107
+ 可复用知识时简短注明,不拼凑报告。短期记忆超过阈值才归并长期知识,否则长期沉淀为 no-op。
106
108
  - `ec-lite` 仅由用户显式启停,不是 Fast 的别名。它只保留“紧凑方案 → 用户确认 → 最小实现”,
107
109
  不创建任务、Dev-Spec、QUALITY 或 MEMORY;存在活动任务时由用户选择取消启动、关闭任务后
108
110
  启动,或只清除当前任务指针后启动。活动任务决策使用 session 级原子锁;每次方案生成一次性
@@ -174,7 +176,7 @@ Dev-Spec 继续走原有整文分析流程。
174
176
  | `ec-analysis` | 生成 dev-spec、执行计划和测试策略 |
175
177
  | `ec-implementing` | 按确认后的计划执行代码实现;非 TDD 不运行质量命令 |
176
178
  | `ec-quality` | 编排 Review/Verification 双门、证据复用和一次性 Repair Bundle |
177
- | `ec-memory` | 写短期记忆,并在超过阈值时沉淀长期记忆 |
179
+ | `ec-memory` | 提炼可复用开发知识,超过阈值时归并长期记忆 |
178
180
  | `ec-task-management` | 任务面板:查看、创建、选择、恢复、交接任务 |
179
181
  | `ec-config` | 只读查看或显式修改项目/session 的 Approval、Workflow、单测策略与阈值 |
180
182
  | `ec-tdd-init` | 为 UT/TDD 初始化/刷新 Java changed-line coverage 基础设施,不补存量单测 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-coding-harness",
3
- "version": "1.1.0-beta.2",
3
+ "version": "1.1.0-beta.4",
4
4
  "description": "CLI scaffold for installing Easy Coding harness files into agent-native directories.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,9 +15,11 @@ Communicate with the user in the user's language.
15
15
 
16
16
  1. Read task.json, SOUL, RULES index/headings, ABSTRACT index/headings, and the dev-spec
17
17
  skeleton.
18
- 2. Search short-memory frontmatter and summaries first. Open only memories whose domains,
18
+ 2. Search short-memory frontmatter and knowledge summaries first. Open only memories whose domains,
19
19
  tags, related files, or predecessor links match this task. Do not load the newest five
20
- memories unconditionally.
20
+ memories unconditionally. Skip `memory_value: none` during knowledge retrieval. For legacy
21
+ reports, read the matching reusable decisions or pitfalls; consult acceptance/process records
22
+ only when the current task needs that history, not as default development context.
21
23
  3. Read full RULES/ABSTRACT sections only for affected modules.
22
24
  4. Inspect concrete code paths and tests. Expand context only when evidence reveals another
23
25
  dependency or risk.
@@ -206,7 +208,11 @@ Acceptance: <observable outcome and minimum check>
206
208
  Record Unit `input_files` for the known additional direct inputs (an empty list means the
207
209
  Unit files are self-contained). Include shared helpers, fixtures, schemas and configuration
208
210
  actually consumed by its checks. Without a declared closure, checks cover the owning module.
209
- Build commands still include their module compilation inputs. Do not infer a whole-program call graph. Keep Unit contracts and test points in the existing execution plan. Do not duplicate them across
211
+ For targeted Vitest/Jest/Python/Node checks, name the selected test file in the command and declare
212
+ its helper/fixture inputs so unrelated test cases can retain their evidence. Module production
213
+ inputs and build configuration remain included. Maven/Gradle/tsc compilation still includes
214
+ neighboring sources/tests even with a test selector. Do not infer a whole-program call graph.
215
+ Keep Unit contracts and test points in the existing execution plan. Do not duplicate them across
210
216
  full template chapters or create a separate test strategy for this compact form. Canonical work
211
217
  consumes the selected source closure; it does not redesign unrelated selected tasks.
212
218
 
@@ -37,6 +37,9 @@ tracking metadata is handled once; do not reopen design or rewrite the complete
37
37
 
38
38
  When a lifecycle check is necessary, call `prepare-check` before running it and `record-check`
39
39
  afterward as documented in ec-quality. QUALITY reuses these input-bound results.
40
+ Batch independent checks at the same code state with the array forms of `--record` and `--result`.
41
+ Use each state operation's returned context and next action; query again only when required
42
+ information is absent or an intervening edit makes the returned context stale.
40
43
 
41
44
  ## Non-negotiable gates
42
45
 
@@ -1,27 +1,55 @@
1
1
  ---
2
2
  name: ec-memory
3
- description: MEMORY-stage skill. Creates a workflow-mode-aware schema-v2 checkpoint from existing task evidence and performs conditional long-memory distillation.
3
+ description: MEMORY-stage skill. Extracts reusable development knowledge from existing task evidence and performs conditional long-memory distillation.
4
4
  ---
5
5
 
6
- # ec-memory — evidence-derived checkpoint and knowledge governance
6
+ # ec-memory — reusable knowledge for future development
7
7
 
8
8
  MEMORY remains mandatory for code tasks. Daily task processing and architecture maintenance are
9
9
  separate responsibilities: every completed code task produces one immutable short-memory fact;
10
10
  only a long-memory distillation, or the explicit missing-ABSTRACT startup exception, may open an
11
11
  architecture assessment. Never update architecture merely because MEMORY was entered.
12
12
 
13
- The short-memory checkpoint must not re-analyze the repository or repeat the entire conversation.
14
- Generate it only from the verified evidence already stored in `task.json`, `dev-spec.md`, and
15
- `execution.jsonl`. The bounded repository reads described below belong only to a required
16
- `backfill` or `update` architecture assessment.
13
+ MEMORY must not re-analyze the repository or repeat the entire conversation. Reuse confirmed
14
+ decisions and verified findings already available in `dev-spec.md`, implementation results and
15
+ Review evidence in `execution.jsonl`; `task.json` supplies identity and frozen mode. The bounded
16
+ repository reads below belong only to a required `backfill` or `update` architecture assessment.
17
+
18
+ ## Knowledge value
19
+
20
+ A short memory is directly usable knowledge, not an acceptance report. Keep a fact when it helps
21
+ a later task understand behavior, choose the right change, diagnose a failure, or verify correctly.
22
+
23
+ - Lead with a knowledge topic and a retrieval summary, not a version release or task-completion
24
+ headline. Prefer business semantics, design reasons, relevant code entrypoints, compatibility
25
+ boundaries, and observed failure causes with their fixes.
26
+ - State the applicable situation and useful conclusion. Include reasons, limitations and exact
27
+ symbols or source references where they help the next developer; do not fill a fixed checklist
28
+ for every fact. Preserve only confirmed conclusions, and keep task-specific scope constraints
29
+ scoped to that task rather than turning them into permanent project rules.
30
+ - Leave acceptance records in `execution.jsonl`; cite the relevant source instead of copying it.
31
+ Omit approval JSON, fingerprints, execution timelines, test counts, per-run coverage numbers,
32
+ temporary log paths, file inventories and handoff history. Do not add a list of excluded noise.
33
+ A reusable verification command or environment constraint belongs here only when it guides
34
+ future work; a single run's pass/fail and UT/TDD lifecycle evidence stay in the task record.
35
+ - When no new reusable knowledge exists, set `memory_value: none` and `target_long: NONE`, write
36
+ a brief reason and retain the source reference. Do not manufacture lessons or duplicate existing
37
+ knowledge merely to populate sections. This still completes the mandatory short-memory step.
38
+
39
+ For example, preserve that a cache write may return a failure code without throwing, why proceeding
40
+ with an unpersisted local value breaks shared counting, and where to bypass that behavior. A count
41
+ of passing tests and the user's acceptance timestamp do not teach a future task how to handle it.
42
+
43
+ Both `default` and `dispatch` use this contract. The coordinator reuses the analysis, implementation
44
+ results and Review findings already available. Reuse any executor-only discovery from its existing
45
+ result; do not require another report, another handoff, or repeated checks for MEMORY.
17
46
 
18
47
  ## Depth by workflow mode
19
48
 
20
- - `fast`: mechanically produce a compact checkpoint: goal, scope, result, frozen mode,
21
- commands/results, and only clearly reusable decisions.
22
- - `standard`: add reusable contract, compatibility, and troubleshooting facts when present.
23
- - `strict`: preserve architecture, migration, risk, verification, and cross-module decisions
24
- needed for future high-risk work.
49
+ - `fast`: keep the directly reusable conclusions concise.
50
+ - `standard`: include relevant contract, compatibility, and troubleshooting reasons when present.
51
+ - `strict`: retain non-obvious architecture, migration and cross-module boundaries needed by
52
+ future changes. Greater depth never requires process logs, more checks, or invented knowledge.
25
53
 
26
54
  Every memory uses schema 2 and includes `workflow_mode` in frontmatter. Generate its UUIDv7 ID
27
55
  through:
@@ -33,22 +61,18 @@ through:
33
61
  Name it `{memory_id}_{YYYYMMDD}_{smart_name}.md` and set
34
62
  `source_task: {current task id, exact}`. Write one immutable short memory under
35
63
  `.easy-coding/memory/short/`, then register it with
36
- `memory-short-complete`. Never invent test results or commit hashes.
64
+ `memory-short-complete`. Task ownership and content integrity remain runtime checks; acceptance
65
+ and Canonical evidence remain in their existing records, not duplicated in the memory body.
37
66
 
38
- Copy the final `acceptance` record from `execution.jsonl` into the checkpoint as a concise
39
- decision fact: authorization source, decision summary, `diff_sha256`, review policy, verification
40
- policy, changed files, and any Canonical source tasks that required targeted verification.
41
- `memory-short-complete` rejects a checkpoint that omits any of those decision fields. This records
42
- the user's accepted exception without re-reviewing or re-analyzing the code. Canonical writeback
43
- already carries the same digest and authorization as shared `acceptance` evidence.
44
-
45
- For frozen UT/TDD, record the strategy, shared threshold, passed local unit-test result, and
46
- changed-line coverage result with its frozen baseline. Only TDD includes lifecycle evidence.
47
- Remote CI is not acceptance evidence. With `none`, omit coverage-specific fields.
67
+ Upgrades preserve existing project templates. These instructions take precedence over legacy
68
+ process sections in `SHORT_MEMORY_TEMPLATE.md`; omit those sections when writing a new memory.
69
+ Do not rewrite old memories or user templates to adopt this contract.
48
70
 
49
71
  Ask the state API for `memory-instruction`. Distill only when it returns `action:distill`;
50
- otherwise record `no-op`. Long memory receives reusable facts only, not file dumps, transient
51
- logs, routine command output, or speculation.
72
+ otherwise record `no-op`. Long memory merges reusable facts, deduplicates matching knowledge and
73
+ retires superseded conclusions. Extract useful knowledge from legacy acceptance reports without
74
+ carrying over process noise; `memory_value: none` contributes no long-memory topic. Work only
75
+ within the frozen candidates and matching topics, without a global history cleanup.
52
76
 
53
77
  ## Architecture assessment
54
78
 
@@ -22,24 +22,27 @@ Call `evidence-fingerprints` once to obtain the runtime-owned attempt and candid
22
22
  owns signatures and prior-evidence references. Never calculate historical fingerprints, import
23
23
  runtime internals to reconstruct old candidates, or ask a reviewer to audit workflow bookkeeping.
24
24
 
25
- For each distinct review or verification, prepare its actual inputs before executing it:
25
+ Prepare independent checks for the same unchanged candidate in one batch before executing them:
26
26
 
27
27
  ```bash
28
28
  {{PYTHON_CMD}} {{platform_config_dir}}/hooks/easy_coding_state.py prepare-check \
29
- --record '<review/verify JSON with unit_id, dimension or check/check_type/command>' \
29
+ --record '[<review/verify JSON with unit_id, dimension or check/check_type/command>, ...]' \
30
30
  --agent <agent-id> --session-file <P>
31
31
  ```
32
32
 
33
- When `reusable:true`, use the returned evidence index and skip that check. Otherwise run the
34
- specified check once, then register its real result:
33
+ For each returned item with `reusable:true`, use its evidence index and skip that check. Run the
34
+ remaining checks once, then register their real results in one batch:
35
35
 
36
36
  ```bash
37
37
  {{PYTHON_CMD}} {{platform_config_dir}}/hooks/easy_coding_state.py record-check \
38
- --prepared-id <returned-id> --result '<JSON with passed, exit_code for verification, findings/reviewer for review>' \
38
+ --result '[{"prepared_id":"<returned-id>","result":<JSON with passed, exit_code for verification, findings/reviewer for review>}, ...]' \
39
39
  --agent <agent-id> --session-file <P>
40
40
  ```
41
41
 
42
42
  Preparation binds code/test inputs, module dependencies, build files and the actual command.
43
+ Batching preserves each check's result and evidence identity. A single check may still use
44
+ `--prepared-id` with one result object. Start a new preparation batch after a code change;
45
+ never reuse a pre-edit input snapshot for post-edit checks.
43
46
  Use the analyzed Unit `input_files` closure for additional helpers/fixtures/configuration, and record intentional
44
47
  environment overrides in the check descriptor. Production-only reviews may declare
45
48
  `review_scope:"production"`; test review still covers changed test behavior. A result is accepted
@@ -185,9 +188,9 @@ This returns other-Agent repairs to the coordinator for delta review/verificatio
185
188
  may pass while the repair is pending. Scope/contract changes must be resolved rather than silently
186
189
  included in the accepted bundle. Repeated starts/completions reuse the existing repair ID.
187
190
 
188
- `prepare-check --record` also accepts an array. `record-check --result` accepts an array of
189
- `{prepared_id,result}` to register the results of those prepared checks. Each check keeps its own
190
- inputs and real result; batching shares runtime reads, not unrelated evidence.
191
+ Use the same batch prepare/record flow for repair checks. Consume returned state and next-action
192
+ fields directly; do not follow a successful state operation with an unchanged `snapshot` or
193
+ Spec inspection merely to retrieve fields already returned.
191
194
 
192
195
  After repair, choose the minimum honest evidence refresh:
193
196
 
@@ -57,6 +57,9 @@ or `qoder`. Never use a display or source-author attribution such as `Codex with
57
57
 
58
58
  1. Read the injected state breadcrumbs or call:
59
59
 
60
+ Prefer the injected or most recent operation's state. Call `snapshot` only when that context
61
+ is missing or stale; do not repeat it after a successful operation that already returns state.
62
+
60
63
  ```bash
61
64
  {{PYTHON_CMD}} {{platform_config_dir}}/hooks/easy_coding_state.py snapshot --agent <agent-id> --session-file <P>
62
65
  ```
@@ -125,8 +125,9 @@ First run `ec-init`; daily work goes through `ec-workflow`.
125
125
  ANALYSIS reads the selected consumption closure once and treats exact/scope-unchanged as a fast
126
126
  projection, while shared execution is the dependency fact source.
127
127
  - MEMORY combines short-memory creation and the conditional long-memory gate. Entry follows the
128
- effective confirmation mode; its checkpoint records any accepted post-quality diff digest
129
- and decision. Once memory processing completes, COMPLETE is automatic.
128
+ effective confirmation mode. Record reusable development knowledge and source references;
129
+ acceptance digests and process evidence stay in task records. Do not add reports or repeated
130
+ checks for memory. Once memory processing completes, COMPLETE is automatic.
130
131
  - NO CODE-TASK COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.
131
132
  - All cross-platform modules (skills, hooks, references) must use universal agent protocols.
132
133
  Do not rely on any specific agent's proprietary conventions unless the module is explicitly
@@ -123,8 +123,9 @@ First run `/ec-init`; daily work goes through `/ec-workflow`.
123
123
  ANALYSIS reads the selected consumption closure once and treats exact/scope-unchanged as a fast
124
124
  projection, while shared execution is the dependency fact source.
125
125
  - MEMORY combines short-memory creation and the conditional long-memory gate. Entry follows the
126
- effective confirmation mode; its checkpoint records any accepted post-quality diff digest
127
- and decision. Once memory processing completes, COMPLETE is automatic.
126
+ effective confirmation mode. Record reusable development knowledge and source references;
127
+ acceptance digests and process evidence stay in task records. Do not add reports or repeated
128
+ checks for memory. Once memory processing completes, COMPLETE is automatic.
128
129
  - NO CODE-TASK COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.
129
130
  - All cross-platform modules (skills, hooks, references) must use universal agent protocols.
130
131
  Do not rely on any specific agent's proprietary conventions unless the module is explicitly
@@ -11,14 +11,12 @@ domain:
11
11
  tags:
12
12
  - "{keyword}"
13
13
  related_files:
14
- - "{key file or module}"
15
- commit: none
16
- verification: passed | partial | not_run
14
+ - "{entrypoint or file useful for applying this knowledge}"
17
15
  memory_value: business | technical | both | none
18
16
  target_long: BUSINESS | TECHNICAL | BOTH | NONE
19
17
  ---
20
18
 
21
- # Short Memory Template
19
+ # {Reusable knowledge topic}
22
20
 
23
21
  > This template defines the format for files under `.easy-coding/memory/short/`.
24
22
  > File naming convention: `{memory_id}_{YYYYMMDD}_{smart_name}.md`
@@ -26,57 +24,32 @@ target_long: BUSINESS | TECHNICAL | BOTH | NONE
26
24
  > both the filename prefix and this frontmatter `id`. The UUIDv7 id is safe for concurrent agents.
27
25
  > Keep `smart_name` as the readable summary suffix.
28
26
  > `source_task` must exactly match the current workflow task id from `task.json`.
29
- > Short memories are immutable after creation they serve as a sliding window of recent
30
- > details and a buffer for long-term distillation candidates.
31
- > When short memories reach the threshold (default 10), the newest 5 are kept as recent
27
+ > Short memories are immutable and directly useful for later development before distillation.
28
+ > When short memories exceed the threshold (default 10), the newest 5 are kept as recent
32
29
  > context; older entries are distillation candidates for long-term memory.
33
30
  > Sorting: by frontmatter `date` ascending, then by frontmatter `id`, then by filename. Legacy
34
31
  > `SM-YYYYMMDD-NNN` ids sort before UUIDv7 ids on the same date for upgrade compatibility.
35
32
 
36
- ## Task Summary
33
+ ## Knowledge Summary
37
34
 
38
- - Goal: {the actual problem solved}
39
- - Scope: {modules, pages, interfaces, or files involved}
40
- - Result: {completed / partially completed / not completed, with reasons}
41
- - Key Constraints: {encoding, compatibility, interface, spec, or user-specified constraints; "none" if none}
42
- - Workflow Mode: {frozen mode and why it was selected or escalated}
35
+ {When this knowledge is useful and the main conclusion a future developer should find.}
43
36
 
44
- ## Execution Evidence
37
+ ## Reusable Knowledge
45
38
 
46
- | Type | Content |
47
- |---|---|
48
- | Key Files | {file or module list; "none" if none} |
49
- | Verification Commands | {test / build / lint commands and results; explain if not run} |
50
- | Manual Acceptance | {key behaviors checked; "none" if none} |
51
- | Commit Info | {commit hash; "none" if not committed} |
39
+ {Write only confirmed facts with future reuse value. Use natural paragraphs or bullets; omit
40
+ inapplicable categories rather than filling a checklist. Useful material includes business rules,
41
+ design reasons, change entrypoints, compatibility boundaries, and failure causes with their fixes.
42
+ Add applicable conditions, necessary reasons and exact symbols where they guide the next change.
43
+ Do not promote a task-specific restriction into a permanent project rule.}
52
44
 
53
- ## Business Memory Candidates
45
+ ## Sources
54
46
 
55
- > Only record business facts with future reuse value. Write "none" if none.
47
+ - {Relevant dev-spec section, implementation result or Review finding; reference instead of copying.}
48
+ - {Related memory or existing knowledge topic, only when useful.}
56
49
 
57
- - Concepts / Field Semantics: {concepts, fields, enums, state meanings}
58
- - Workflows / State Transitions: {chain steps, pre/post conditions, exception branches}
59
- - Business Rules / Compatibility: {admission criteria, decision basis, grayscale or legacy reasons}
60
- - Upstream/Downstream Contracts: {producers, consumers, interface or message fields}
61
- - Business Troubleshooting: {common misdiagnosis, priority check paths}
62
-
63
- ## Technical Memory Candidates
64
-
65
- > Only record engineering facts with future reuse value. Write "none" if none.
66
-
67
- - Architecture / Interface Decisions: {module boundaries, dependency direction, interface contracts}
68
- - Engineering Rules / Workflows: {coding, commit, release, installation, directory boundaries}
69
- - Implementation Patterns / Reusable Approaches: {recommended approaches, fallback strategies, compatibility patterns}
70
- - Pitfalls / Fix Strategies: {root cause, fix approach, verification method}
71
- - Verification Experience: {test commands, environment constraints, acceptance paths}
72
-
73
- ## Non-Distillation Content
74
-
75
- > Content that should NOT enter long-term memory, with reasons — prevents accidental absorption of noise.
76
-
77
- - {routine file lists / temp logs / one-time data / non-reusable implementation details; "none" if none}
78
-
79
- ## Related Memories
80
-
81
- - Predecessor: {related short memory id, long-term topic, or "none"}
82
- - Successor: {follow-up task; "none" if none}
50
+ > Authoring guidance, not memory content: remove these instructions and unused placeholders.
51
+ > If nothing new is reusable, set `memory_value: none` and `target_long: NONE`, give a brief reason
52
+ > in Knowledge Summary and retain Sources; omit Reusable Knowledge rather than inventing lessons.
53
+ > Leave task status, approvals, fingerprints, test statistics and temporary logs in task records.
54
+ > Keep a verification command only when it teaches a reusable method or environment requirement.
55
+ > Do not include an acceptance report or a list of excluded noise.
@@ -10,47 +10,16 @@ import stat
10
10
  import subprocess
11
11
  import sys
12
12
  import xml.etree.ElementTree as ET
13
- from contextlib import contextmanager
14
- from contextvars import ContextVar
13
+ from easy_coding_operation import evidence_operation, memo, cached_memo, invalidate_memo
15
14
  from pathlib import Path
16
15
 
17
16
 
18
- _operation = ContextVar("evidence_operation", default=None)
19
17
  BUILD_FILES = ("pom.xml", "package.json", "package-lock.json", "pnpm-lock.yaml",
20
18
  "yarn.lock", "tsconfig.json", "build.gradle", "build.gradle.kts",
21
19
  "settings.gradle", "gradle.properties", ".gitattributes", ".npmrc",
22
20
  "vitest.config.ts", "jest.config.js", "biome.json", "pytest.ini", "pyproject.toml")
23
21
 
24
22
 
25
- @contextmanager
26
- def evidence_operation():
27
- token = _operation.set({})
28
- try:
29
- yield
30
- finally:
31
- _operation.reset(token)
32
-
33
-
34
- def memo(key, compute):
35
- cache = _operation.get()
36
- if cache is None:
37
- return compute()
38
- if key not in cache:
39
- cache[key] = compute()
40
- return cache[key]
41
-
42
-
43
- def invalidate_memo(key):
44
- cache = _operation.get()
45
- if cache is not None:
46
- cache.pop(key, None)
47
-
48
-
49
- def cached_memo(key):
50
- cache = _operation.get()
51
- return cache.get(key) if cache is not None else None
52
-
53
-
54
23
  def command_identity(command):
55
24
  # Shell 展开和重定向受引号影响;只归一化普通 argv 命令,避免错误复用。
56
25
  if re.search(r"[\$`\\\n;|&<>*?\[\]{}~#]", command):
@@ -153,7 +122,7 @@ def is_test(path):
153
122
 
154
123
  def is_test_case(path):
155
124
  name = Path(path).name
156
- return bool(re.search(r"(?:Test|Tests|IT)\.java$|\.(?:test|spec)\.[cm]?[jt]sx?$|^test_.*\.py$", name))
125
+ return bool(re.search(r"(?:Test|Tests|IT)\.java$|\.(?:test|spec)\.[cm]?[jt]sx?$|^test_.*\.py$|_test\.py$", name))
157
126
 
158
127
 
159
128
  def command_tokens(command):
@@ -165,6 +134,36 @@ def command_tokens(command):
165
134
  return tokens
166
135
 
167
136
 
137
+ def targeted_test_files(command):
138
+ """只识别完整、无 shell 展开的文件选择调用;未知参数保持模块范围。"""
139
+ if not isinstance(command_identity(command), tuple):
140
+ return set()
141
+ tokens = command_tokens(command)
142
+ if not tokens:
143
+ return set()
144
+ if Path(tokens[0]).name == "npx":
145
+ tokens = tokens[1:]
146
+ elif tokens[:2] in (["pnpm", "exec"], ["npm", "exec"]):
147
+ tokens = tokens[2:]
148
+ if tokens[:1] == ["--"]:
149
+ tokens = tokens[1:]
150
+ if not tokens:
151
+ return set()
152
+ runner, arguments = Path(tokens[0]).name, tokens[1:]
153
+ if runner in {"python", "python3"} and arguments[:2] == ["-m", "pytest"]:
154
+ runner, arguments = "pytest", arguments[2:]
155
+ if runner == "vitest" and arguments[:1] == ["run"]:
156
+ arguments = arguments[1:]
157
+ elif runner == "node" and arguments[:1] == ["--test"]:
158
+ arguments = arguments[1:]
159
+ elif runner not in {"jest", "pytest", "python", "python3"}:
160
+ return set()
161
+ files = [value for value in arguments if value not in {"-q", "-v", "--runInBand", "--coverage"}]
162
+ if not files or any(value.startswith("-") or not is_test_case(value) for value in files):
163
+ return set()
164
+ return {str(Path(value)) for value in files}
165
+
166
+
168
167
  def toolchain_identity(command):
169
168
  tokens = command_tokens(command)
170
169
  executable = tokens[0] if tokens else ""
@@ -220,14 +219,25 @@ def input_spec(root, task, plan, check):
220
219
  tokens = command_tokens(command)
221
220
  executable = Path(tokens[0]).name if tokens else ""
222
221
  builds_module = check.get("type") == "verify" and executable in {
223
- "mvn", "mvnw", "gradle", "gradlew", "npm", "npx", "pnpm", "yarn", "tsc"
222
+ "mvn", "mvnw", "gradle", "gradlew", "npm", "npx", "pnpm", "yarn", "tsc",
223
+ "vitest", "jest", "pytest", "python", "python3", "node",
224
224
  }
225
+ selected_tests = targeted_test_files(command)
225
226
  repositories = {}
227
+ production_repositories = {}
226
228
  for unit in selected:
227
229
  repo_id = unit.get("repo_id") or "current"
228
230
  base = Path(task.get("repo_paths", {}).get(repo_id, root))
229
231
  base = (base if base.is_absolute() else root / base).resolve()
230
232
  paths = repositories.setdefault(str(base), set())
233
+ # 只有明确文件选择器和已声明依赖闭包的脚本测试才收窄测试输入。
234
+ # Maven/Gradle/tsc 的编译阶段仍消费模块内源文件,不能按测试名称裁剪。
235
+ test_files = {str(Path(name)) for name in [*unit.get("files", []), *unit.get("input_files", [])]
236
+ if is_test_case(name)}
237
+ targeted_script = (
238
+ check.get("type") == "verify" and "input_files" in unit
239
+ and bool(selected_tests) and selected_tests <= test_files
240
+ )
231
241
  for name in [*unit.get("files", []), *unit.get("input_files", [])]:
232
242
  absolute = Path(name) if Path(name).is_absolute() else base / name
233
243
  relative = absolute.relative_to(base).as_posix()
@@ -261,16 +271,18 @@ def input_spec(root, task, plan, check):
261
271
  pending.append(candidate)
262
272
  for module in involved:
263
273
  for folder in ("src/main", "src" if not (module / "pom.xml").exists() else "src/test"):
264
- if (builds_module or "input_files" not in unit) and (module / folder).is_dir():
265
- paths.add((module / folder).relative_to(base).as_posix())
274
+ if (builds_module or targeted_script or "input_files" not in unit) and (module / folder).is_dir():
275
+ destination = (production_repositories.setdefault(str(base), set())
276
+ if targeted_script else paths)
277
+ destination.add((module / folder).relative_to(base).as_posix())
266
278
  for parent in [module, *module.parents]:
267
279
  if not parent.is_relative_to(base):
268
280
  break
269
281
  for filename in BUILD_FILES:
270
282
  if (parent / filename).is_file():
271
283
  paths.add((parent / filename).relative_to(base).as_posix())
272
- for config in (".mvn", "gradle", "test" if builds_module else "test/fixtures",
273
- "tests" if builds_module else "tests/fixtures"):
284
+ for config in (".mvn", "gradle", "test" if builds_module and not targeted_script else "test/fixtures",
285
+ "tests" if builds_module and not targeted_script else "tests/fixtures"):
274
286
  if (base / config).is_dir():
275
287
  paths.add(config)
276
288
  if task.get("unit_test_mode") in {"ut", "tdd"}:
@@ -284,6 +296,8 @@ def input_spec(root, task, plan, check):
284
296
  return {
285
297
  "schema": 1,
286
298
  "repositories": {r: sorted(paths) for r, paths in sorted(repositories.items())},
299
+ **({"production_repositories": {r: sorted(paths) for r, paths in sorted(production_repositories.items())}}
300
+ if production_repositories else {}),
287
301
  "production_only": check.get("review_scope") == "production",
288
302
  "contract": [{"id": u["id"], "contracts": u.get("contracts", []),
289
303
  "acceptance_criteria": u.get("acceptance_criteria", [])} for u in owners]
@@ -299,6 +313,9 @@ def capture(spec):
299
313
  for root, scopes in spec["repositories"].items():
300
314
  repository = memo(("repository", root), lambda: RepositoryInputs(Path(root)))
301
315
  inputs[root] = repository.capture(scopes, spec["production_only"])
316
+ production_scopes = spec.get("production_repositories", {}).get(root, [])
317
+ if production_scopes:
318
+ inputs[root].update(repository.capture(production_scopes, production_only=True))
302
319
  return {"spec": spec, "files": inputs, "signature": digest([spec, inputs])}
303
320
 
304
321
 
@@ -0,0 +1,39 @@
1
+ """Operation-local caches shared by hooks and state commands."""
2
+
3
+ from contextlib import contextmanager
4
+ from contextvars import ContextVar
5
+
6
+
7
+ _operation = ContextVar("evidence_operation", default=None)
8
+
9
+
10
+ @contextmanager
11
+ def evidence_operation():
12
+ if _operation.get() is not None:
13
+ yield
14
+ return
15
+ token = _operation.set({})
16
+ try:
17
+ yield
18
+ finally:
19
+ _operation.reset(token)
20
+
21
+
22
+ def memo(key, compute):
23
+ cache = _operation.get()
24
+ if cache is None:
25
+ return compute()
26
+ if key not in cache:
27
+ cache[key] = compute()
28
+ return cache[key]
29
+
30
+
31
+ def invalidate_memo(key):
32
+ cache = _operation.get()
33
+ if cache is not None:
34
+ cache.pop(key, None)
35
+
36
+
37
+ def cached_memo(key):
38
+ cache = _operation.get()
39
+ return cache.get(key) if cache is not None else None