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 +20 -0
- package/README.md +4 -2
- package/package.json +1 -1
- package/templates/common/skills/ec-analysis/SKILL.md +9 -3
- package/templates/common/skills/ec-implementing/SKILL.md +3 -0
- package/templates/common/skills/ec-memory/SKILL.md +48 -24
- package/templates/common/skills/ec-quality/SKILL.md +11 -8
- package/templates/common/skills/ec-workflow/SKILL.md +3 -0
- package/templates/main-constraint/AGENTS.md.tpl +3 -2
- package/templates/main-constraint/CLAUDE.md.tpl +3 -2
- package/templates/runtime/memory/SHORT_MEMORY_TEMPLATE.md +21 -48
- package/templates/shared-hooks/easy_coding_inputs.py +55 -38
- package/templates/shared-hooks/easy_coding_operation.py +39 -0
- package/templates/shared-hooks/easy_coding_state.py +216 -1816
- package/templates/shared-hooks/easy_coding_status.py +412 -5
- package/templates/shared-hooks/easy_coding_store.py +1193 -0
- package/templates/shared-hooks/easy_dev_spec.py +5 -4
- package/templates/shared-hooks/inject-subagent-context.py +6 -1
- package/templates/shared-hooks/inject-workflow-state.py +6 -2
- package/templates/shared-hooks/session-start.py +6 -2
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`
|
|
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
|
@@ -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
|
-
|
|
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.
|
|
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 —
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
`execution.jsonl
|
|
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`:
|
|
21
|
-
|
|
22
|
-
- `
|
|
23
|
-
|
|
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`.
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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
|
-
--
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
129
|
-
and
|
|
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
|
|
127
|
-
and
|
|
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
|
-
- "{
|
|
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
|
-
#
|
|
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
|
|
30
|
-
>
|
|
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
|
-
##
|
|
33
|
+
## Knowledge Summary
|
|
37
34
|
|
|
38
|
-
|
|
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
|
-
##
|
|
37
|
+
## Reusable Knowledge
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
##
|
|
45
|
+
## Sources
|
|
54
46
|
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
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
|
-
|
|
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
|