Merge authoritative main into the task branch

Why: include the current integration baseline before verification.

Evidence: fetched origin/main=9c9483ac54ff5355c86824ac94097e65e59dcb5d.

Impact: integrate upstream changes without rewriting task history.

Refs: task-sync
This commit is contained in:
2026-10-07 04:39:43 +00:00
13 changed files with 2144 additions and 15 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,207 @@
{
"source": "SELF",
"scope": "Closure metadata and receipt integrity only; independent read-only verification except this /tmp report",
"reviewer": "workflow_gate_audit",
"reviewed_at": "2026-10-07T03:25:21.422644+00:00",
"head": "8a8026338e3568943e02626f653524c8a7226941",
"base": "74e4382452a59fab598c09bb3ded96ff9495e153",
"result": "PASS",
"checks": [
{
"check": "closure_diff_scope",
"result": "PASS",
"detail": {
"head": "8a8026338e3568943e02626f653524c8a7226941",
"base": "74e4382452a59fab598c09bb3ded96ff9495e153",
"changed_paths": [
".task/archive/feature-iterative-delivery.md",
".task/feature-iterative-delivery.md"
],
"status": " M .task/archive/feature-iterative-delivery.md\n",
"stat": ".task/{ => archive}/feature-iterative-delivery.md | 413 +++++++++++++++++++++-\n 1 file changed, 401 insertions(+), 12 deletions(-)",
"uncommitted_only_appends_receipts": true,
"card_sha256": "7a898aa0d45a9ce75e862ea64a23fdd20e6fbf22510d0bbab3edc7e37dc6783d"
}
},
{
"check": "all_receipt_checksums",
"result": "PASS",
"detail": {
"count": 29,
"source_values": [
"SELF"
],
"failed_receipt_ids": []
}
},
{
"check": "available_originals_equal",
"result": "PASS",
"detail": {
"matched_count": 24,
"without_separate_tmp_original": [
{
"receipt_sha256": "298bd599936b05ed105cfd1dcc7072915393dea050bdfa2b3e40ffacd5ba9145",
"command": "python3 /tmp/iterative-delivery-check-docs-20261007.py && git diff --check && bash -n scripts/task.sh scripts/hooks/post-merge"
},
{
"receipt_sha256": "b43f740acdb11127cf4b9218fbb5000e20ac9516fabcaad2fa707b42269eb9bc",
"command": "make task-sync"
},
{
"receipt_sha256": "d6cc055aacf7973c4d1b098585f0ad44e0a600e92e95677896761945d7ec6efa",
"command": "make task-sync"
},
{
"receipt_sha256": "646d460f8597fde2486cfa7eaf87cd7c089d7800a729fd087dc183a221790d59",
"command": "make task-sync"
},
{
"receipt_sha256": "908b6b049717604136ad43f72545653591fa103534a3c22a1603e87d14084e68",
"command": "make task-sync"
}
]
}
},
{
"check": "three_red_results_restored_without_success_rewrite",
"result": "PASS",
"detail": [
{
"original": "iterative-delivery-red-20261007-tooling-01.json",
"receipt_sha256": "d1cefbefe5f188cb69c959b5707b384c8a2252b8835a4a727a9054488a1b0c00",
"equal": true,
"exit_code": 1,
"failed_output_retained": true
},
{
"original": "iterative-delivery-red-20261007-tooling-02.json",
"receipt_sha256": "80b8169cf23cb2e3c1cf625950561365f47c013d5e930ca194605aaf248ce0e8",
"equal": true,
"exit_code": 1,
"failed_output_retained": true
},
{
"original": "iterative-delivery-archive-link-red-20261007.json",
"receipt_sha256": "66fe51b5f69302232ff578266e6f4956e64a749cb77ae68512a1a87a1d1b95d2",
"equal": true,
"exit_code": 1,
"failed_output_retained": true
}
]
},
{
"check": "merged_receipts_preserved_except_documented_output_repair",
"result": "PASS",
"detail": {
"merged_receipt_count": 18,
"missing": [],
"changed": [
{
"receipt_sha256": "d1cefbefe5f188cb69c959b5707b384c8a2252b8835a4a727a9054488a1b0c00",
"changed_fields": [
"output"
]
},
{
"receipt_sha256": "80b8169cf23cb2e3c1cf625950561365f47c013d5e930ca194605aaf248ce0e8",
"changed_fields": [
"output"
]
},
{
"receipt_sha256": "66fe51b5f69302232ff578266e6f4956e64a749cb77ae68512a1a87a1d1b95d2",
"changed_fields": [
"output"
]
}
],
"added_count": 11
}
},
{
"check": "three_red_originals_match_pre_link_commit",
"result": "PASS",
"detail": {
"commit": "7c524d6cb6ff5d54d671edc5cea94eaaf5a10ceb",
"receipt_ids": [
"66fe51b5f69302232ff578266e6f4956e64a749cb77ae68512a1a87a1d1b95d2",
"80b8169cf23cb2e3c1cf625950561365f47c013d5e930ca194605aaf248ce0e8",
"d1cefbefe5f188cb69c959b5707b384c8a2252b8835a4a727a9054488a1b0c00"
]
}
},
{
"check": "saved_ci_record_matches_merged_candidate",
"result": "PASS",
"detail": {
"expected_head": "04a2e44fbaa00f6782be24b312fb11a271c68d71",
"run": 997,
"job": 1383,
"runner": "home-runner",
"url": "https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/actions/runs/997/jobs/1383",
"steps": [
{
"name": "make test (boundaries, pins, libs, core, backend, frontend, ui, agent-command)",
"conclusion": "success"
},
{
"name": "Build the single binary",
"conclusion": "success"
}
],
"record_source": "SELF captured API response; CI API not re-requested in this audit"
}
},
{
"check": "finish_receipt_matches_current_local_and_remote_state",
"result": "PASS",
"detail": {
"receipt_sha256": "af87912fcd3c02e0e3dedb27da397ecc5d90abbf11b27337ebfe81e9cba4727a",
"exit_code": 0,
"after": null,
"after_error": "Git object could not be recorded: rev-parse --show-toplevel",
"task_worktree_exists": false,
"local_refs": "",
"ls_remote_output": "74e4382452a59fab598c09bb3ded96ff9495e153\trefs/heads/main\n",
"registered_worktrees": "worktree /home/weichen/projects/Agent-System-Dev\nHEAD 8a8026338e3568943e02626f653524c8a7226941\nbranch refs/heads/feature/card-close-20261007-031829Z\n\nworktree /home/weichen/projects/Agent-System-Dev-wt-feature-agent-stack-n1\nHEAD 6af6d046ff413b31d38cad0def629345ba85b53b\nbranch refs/heads/feature/agent-stack-n1\n\n"
}
},
{
"check": "merged_tree_matches_tested_candidate",
"result": "PASS",
"detail": {
"candidate": "04a2e44fbaa00f6782be24b312fb11a271c68d71",
"merged": "74e4382452a59fab598c09bb3ded96ff9495e153",
"candidate_is_ancestor": true,
"tree_diff_paths": []
}
},
{
"check": "reviewed_snapshot_stable",
"result": "PASS",
"detail": {
"start_head": "8a8026338e3568943e02626f653524c8a7226941",
"end_head": "8a8026338e3568943e02626f653524c8a7226941",
"card_sha256": "7a898aa0d45a9ce75e862ea64a23fdd20e6fbf22510d0bbab3edc7e37dc6783d"
}
},
{
"check": "uncommitted_card_change_only_appends_validated_receipts",
"result": "PASS",
"detail": {
"base_head": "8a8026338e3568943e02626f653524c8a7226941",
"appended_receipt_count": 1,
"card_sha256": "7a898aa0d45a9ce75e862ea64a23fdd20e6fbf22510d0bbab3edc7e37dc6783d"
}
}
],
"knownlimit": [
"No full governance, make test or make build rerun; this audit does not replace the earlier code review.",
"CI run/job data checked against captured original JSON and Git commit identity, not a new authenticated CI API query.",
"Checksums and available originals establish consistency, not authorship or trusted executor identity; this report remains SELF.",
"Other task worktree currently remains registered; this audit cannot prove every historical action in another session.",
"Report applies to the fixed reviewed HEAD and card SHA; later added receipts require their own integrity check.",
"One valid make task-sync receipt is appended after reviewed HEAD 8a80263 and is uncommitted; archive card SHA identifies the audited worktree content."
]
}
+257
View File
@@ -0,0 +1,257 @@
{
"status": "complete",
"verdict": "Ready with fixes",
"verdict_reason": "R1-R10 are addressed. One confirmed P3 needs a localized template/reference fix; there are no P0-P2 findings. This source review does not establish platform merge readiness: no GATE was verified.",
"scope": {
"mode": "branch-remote",
"base": "2147d5cb6566c301eaea3c65448bdda873093aea",
"branch": "feature/iterative-delivery",
"head_sha": "125e159924d90954c3bafc8572cc260e1e12eadc",
"pr_url": null,
"files_changed": 9
},
"intent": "Make Dev delivery iterative with ten stages and a requirements/fit table; default to stage-by-stage human review while honoring explicit scoped continuous authorization. Align generated task cards, entrypoint docs, and capability matrix; remove implicit task closure writes from post-merge without weakening safe cleanup or claiming manual approvals are machine verified.",
"intent_confidence": "explicit",
"reviewers": [
"correctness",
"project-standards",
"testing",
"agent-native",
"adversarial"
],
"findings": [
{
"#": 1,
"autofix_class": "gated_auto",
"confidence": 100,
"evidence": [
"scripts/task.sh:184 -- - 流程依据:[policy/delivery.md](../policy/delivery.md)",
"scripts/task.sh:322-340 -- card_backfill updates status and main record, then appends closure notes; it does not rewrite relative links.",
"scripts/task.sh:391 and 401 -- git mv \"$c\" \".task/archive/${c##*/}\"",
"git ls-tree 125e159924d90954c3bafc8572cc260e1e12eadc:.task contains no policy subtree; policy/delivery.md exists at repository root."
],
"file": "scripts/task.sh",
"first_evidence": "scripts/task.sh:184 -- - 流程依据:[policy/delivery.md](../policy/delivery.md)",
"independent_reviewers": [],
"line": 184,
"owner": "downstream-resolver",
"pre_existing": false,
"requires_verification": true,
"reviewers": [
"scope-audit"
],
"severity": "P3",
"suggested_fix": "Use a plain-text repository-root reference, such as 流程依据:仓库根目录 policy/delivery.md, in the generated card and update its template assertion. This keeps the reference meaningful in both active and archived cards without modifying the generic archive mechanism.",
"title": "Keep the delivery reference valid after task archival",
"why_it_matters": "Every newly generated card has a working link while stored in .task/, but normal task-close moves it into .task/archive/ and leaves the link unchanged. The link then resolves to the nonexistent .task/policy/delivery.md, so historical task cards lose their clickable policy reference. A plain-text repository-root reference avoids dependence on the card directory.",
"validation_status": "confirmed",
"validation_reason": "The diff newly adds scripts/task.sh:184's ../policy/delivery.md link, while scripts/task.sh:391 and :401 move every closed card from .task/ to .task/archive/ without rewriting it, so it resolves to the absent .task/policy/delivery.md at reviewed head (the root policy/delivery.md exists); this occurs whenever a new task card is closed, and tests/test_task.py:605 only checks the pre-archive form.",
"protected_subject": null
}
],
"actionable_findings": [
{
"#": 1,
"autofix_class": "gated_auto",
"confidence": 100,
"evidence": [
"scripts/task.sh:184 -- - 流程依据:[policy/delivery.md](../policy/delivery.md)",
"scripts/task.sh:322-340 -- card_backfill updates status and main record, then appends closure notes; it does not rewrite relative links.",
"scripts/task.sh:391 and 401 -- git mv \"$c\" \".task/archive/${c##*/}\"",
"git ls-tree 125e159924d90954c3bafc8572cc260e1e12eadc:.task contains no policy subtree; policy/delivery.md exists at repository root."
],
"file": "scripts/task.sh",
"first_evidence": "scripts/task.sh:184 -- - 流程依据:[policy/delivery.md](../policy/delivery.md)",
"independent_reviewers": [],
"line": 184,
"owner": "downstream-resolver",
"pre_existing": false,
"requires_verification": true,
"reviewers": [
"scope-audit"
],
"severity": "P3",
"suggested_fix": "Use a plain-text repository-root reference, such as 流程依据:仓库根目录 policy/delivery.md, in the generated card and update its template assertion. This keeps the reference meaningful in both active and archived cards without modifying the generic archive mechanism.",
"title": "Keep the delivery reference valid after task archival",
"why_it_matters": "Every newly generated card has a working link while stored in .task/, but normal task-close moves it into .task/archive/ and leaves the link unchanged. The link then resolves to the nonexistent .task/policy/delivery.md, so historical task cards lose their clickable policy reference. A plain-text repository-root reference avoids dependence on the card directory.",
"validation_status": "confirmed",
"validation_reason": "The diff newly adds scripts/task.sh:184's ../policy/delivery.md link, while scripts/task.sh:391 and :401 move every closed card from .task/ to .task/archive/ without rewriting it, so it resolves to the absent .task/policy/delivery.md at reviewed head (the root policy/delivery.md exists); this occurs whenever a new task card is closed, and tests/test_task.py:605 only checks the pre-archive form.",
"protected_subject": null
}
],
"triage_groups": [],
"pre_existing_findings": [],
"requirements_completeness": {
"plan_source": "explicit",
"plan": ".task/feature-iterative-delivery.md",
"requirements": [
{
"id": "R1",
"status": "met",
"assessment": "Ten stages name their artifacts and exits.",
"evidence": "policy/delivery.md: stages and exits"
},
{
"id": "R2",
"status": "met",
"assessment": "Generated cards include stable requirement IDs, fit evidence, acceptance criteria and disposition.",
"evidence": "scripts/task.sh: requirement table"
},
{
"id": "R3",
"status": "met",
"assessment": "Manual review defaults and scoped continuous authorization are explicit; templates start unreviewed.",
"evidence": "policy/delivery.md: authorization; scripts/task.sh: stage review table"
},
{
"id": "R4",
"status": "met",
"assessment": "Design, implementation and test plans retain versioned approval objects, assumptions and verification methods.",
"evidence": "policy/delivery.md: stages, exploration and feedback loops"
},
{
"id": "R5",
"status": "met",
"assessment": "Four impact classes determine continuation, revision and renewed review.",
"evidence": "policy/delivery.md: feedback-loop impact table"
},
{
"id": "R6",
"status": "met",
"assessment": "Post-merge defaults to reminders; governance tests exercise real merge/pull without closure writes.",
"evidence": "scripts/hooks/post-merge; tests/test_task.py; independent local replay"
},
{
"id": "R7",
"status": "met",
"assessment": "Merge and cleanup exits preserve registered-workspace, writer and unique-file checks, with receipts outside deleted workspaces.",
"evidence": "policy/delivery.md: merge, cleanup and recovery; policy/governance.md"
},
{
"id": "R8",
"status": "met",
"assessment": "Entrypoint, adoption records, generated cards and capability classifications align; governance replay passed. Finding #1 is a narrow archival-reference defect.",
"evidence": "AGENTS.md; policy/adoption.md; policy/tool-adaptation-matrix.md; scripts/task.sh"
},
{
"id": "R9",
"status": "met",
"assessment": "Bounded experiments can precede complete design, with explicit approval and separate acceptance.",
"evidence": "policy/delivery.md: design exploration loop"
},
{
"id": "R10",
"status": "met",
"assessment": "Incremental work records discoveries, adjustments, renewed review/tests, decisions, outcomes and stopping conditions.",
"evidence": "policy/delivery.md: feedback loops; scripts/task.sh: exploration and iteration table"
}
],
"implementation_units": []
},
"learnings": [],
"agent_native_gaps": [],
"deployment_notes": [],
"residual_risks": [],
"testing_gaps": [
"Full application suite and build were not rerun at the reviewed head; the reported earlier author run is not independently verified.",
"Remote PR CI and GATE identity/SHA were not verified.",
"The later uncommitted archival-reference fix and assertion were not reviewed or verified."
],
"coverage": {
"notes": [
"Reviewed only the nine-file diff between fixed base and head; main checkout source was not used for changed files. Full roster selected because instruction prose and changed tests rule out lite coverage.",
"Project standards used root AGENTS.md as instruction fallback; no CODING_STANDARDS.md applies. No matching solutions corpus or declared local pack was selected.",
"Independent replay by /root/delivery_review at fixed head 125e159924d90954c3bafc8572cc260e1e12eadc: make test-governance && git diff --check; exit 0, 104 tests passed. Receipt /tmp/iterative-delivery-review-replay-20261007.json remains source SELF; local evidence does not authenticate CI or reviewer identity.",
"Full application suite and build were not rerun by this review. Author reported they passed on 9454819 with only task metadata changed afterward; remote PR CI is not yet available and is not validated by this review.",
"Supplemental scope-audit is a non-independent source inspection; the five dispatched specialist reviewers returned no findings.",
"The root caller is preparing the minimal archival-reference fix outside the fixed review head. Review does not claim that uncommitted fix verified."
],
"reviewer_selection": [
{
"reviewer": "correctness",
"tier": "session",
"reason": "always-on"
},
{
"reviewer": "project-standards",
"tier": "mid",
"reason": "AGENTS.md governs all changed files"
},
{
"reviewer": "testing",
"tier": "mid",
"reason": "real-Git hook tests and generated task-card assertions changed"
},
{
"reviewer": "agent-native",
"tier": "mid",
"reason": "agent instructions, commands and task-card context changed"
},
{
"reviewer": "adversarial",
"tier": "session",
"reason": "post-merge persistence side effects and command composition changed"
}
],
"lite": false,
"cross_model": {
"outcome": "in-process-fallback",
"note": "Independent external cross-model pass not run: branch-remote scope uses the local adversarial reviewer against fixed refs. No code sent externally."
},
"validator": {
"outcome": "verdicts",
"batches": 1,
"confirmed": 1,
"rejected": 0,
"unresolved": 0,
"malformed": 0,
"failed": 0,
"shortcut_skipped": 0,
"shortcut_basis": "No verified independent cross-model corroboration.",
"bound_exceeded": false,
"results": [
{
"#": 1,
"status": "confirmed",
"protected_subject": null,
"reason": "The diff newly adds scripts/task.sh:184's ../policy/delivery.md link, while scripts/task.sh:391 and :401 move every closed card from .task/ to .task/archive/ without rewriting it, so it resolves to the absent .task/policy/delivery.md at reviewed head (the root policy/delivery.md exists); this occurs whenever a new task card is closed, and tests/test_task.py:605 only checks the pre-archive form."
}
],
"protected_subject_reclassifications": 0,
"degraded_blockers": []
},
"suppressed_by_confidence": {},
"malformed_findings": 0,
"malformed_returns": 0,
"mode_aware_demotions": 0,
"missing_first_evidence_demotions": 0,
"first_evidence_backfilled": 0,
"settlement_evaluated": true,
"settlement_suppressed": 0,
"non_independent_attributed_sources": [
"scope-audit"
],
"confidence_basis": "Direct fixed-head source evidence; no confidence promotion from scope-audit attribution or validator agreement.",
"independent_replay": {
"source": "SELF",
"reviewer": "/root/delivery_review",
"reviewer_identity_authenticated": false,
"head_sha": "125e159924d90954c3bafc8572cc260e1e12eadc",
"command": "make test-governance && git diff --check",
"exit_code": 0,
"tests_passed": 104,
"receipt": "/tmp/iterative-delivery-review-replay-20261007.json"
},
"gate_verified": false,
"failed_reviewers": [],
"bound_exceeded": [],
"compound_packs": {
"roots": [],
"errors": [],
"warnings": []
},
"apply": "Skipped: agent report-only; later author edits are outside the fixed review head."
},
"artifact_path": "/tmp/compound-engineering-1000/ce-code-review/20261007-025812-7b611869",
"run_id": "20261007-025812-7b611869"
}
+6 -5
View File
@@ -1,7 +1,9 @@
# Agent 工作说明(Agent-System-Dev)
本仓开发遵循 Development-Guidelines(见 `deps/development-guidelines`,gitlink 固定版本):
核心规则与术语见其 `core/`,按任务触发阅读 `standards/` 专题;日常交付走 `workflows/delivery.md`。
核心规则与术语见其 `core/`,按任务触发阅读 `standards/` 专题;通用交付依据为 `workflows/delivery.md`。
本项目的需求、设计试验、实施调整及交付**必读 [policy/delivery.md](policy/delivery.md)**:
默认逐阶段人工评审,阶段内迭代、按影响回退;仅在用户明确授权范围内连续推进。
## UI 设计门禁(强制)
@@ -37,9 +39,8 @@
`make task-finish ABANDON="--abandon <原因>"` 并记录原因;放弃不豁免现场保护。
- **任务卡闭合(防欠账)**:分支合入 main 后,其任务卡须回填"已完成"+主记录 PR 链接并
归档至 `.task/archive/`。`task-finish` 回收后提示所有者运行 `make task-close`,不借回收修改
共享主检出或推送另一任务。自动路径保留主检出 pull 触发 post-merge
钩子(实测 ff-only pull 也触发);钩子内 close 只扫主检出本地 `.task/`,远端刚合入的卡片
要等 pull 完成才能被看到,扫不到时由手动 `make task-close` 兜底。手动闭合在主检出运行→建
共享主检出或推送另一任务。主检出 pull/merge 的 post-merge 钩子仅提示待办,不自动写入或推送。
`make task-close` 会提交、推送并可能创建 PR,须先核对闭合范围和对应授权。闭合在主检出运行→建
`feature/card-close-*` 分支+PR,任务分支内加 `FOLD=--fold` 随当前 PR 合入。
precheck 的 `[cards]` 行报告欠账,验收前应清零;闭合 PR 由所有者合入。
@@ -102,5 +103,5 @@
- **SSH key**:`~/.ssh/home_gitea`(ED25519,公钥指纹
`SHA256:GYXaOXIMeloQ1fKSfdJ/ndgPQzs/Ix7Hk62ekfkfQ20`,Gitea 侧密钥名 GMKtec);
`~/.ssh/config` 已有 `git-ssh.u1680832.nyat.app` 主机条目,克隆/推送直接可用。
- 任务卡闭合 PR(`make task-close` / `task-finish` 自动闭合)经 API 建 PR 时以
- 任务卡闭合 PR(获授权后运行 `make task-close`)经 API 建 PR 时以
`GITEA_TOKEN` 环境变量传入同上的 token。
+2
View File
@@ -16,6 +16,7 @@
| standards/verification.md | 验收 | 本地 make test-governance + 组合测试;当前证据与历史 scratch 记录分开 |
| standards/multi-repo.md | 跨仓/gitlink | manual;受控历史迁移通道已于 2026-09-30 行使一次(documents/records/2026-09-30-history-migration.md) |
| 工作区回收 | 交付终点 | advisory(task-finish);负例:合入前 finish 拒绝输出 |
| policy/delivery.md(项目收紧) | 需求、试验、实施迭代及交付 | manual:人工评审和有范围的连续推进授权;任务卡提供记录,post-merge 默认只提示,行为回归见 tests/test_task.py |
| standards/tool-adaptation.md(draft.8) | 为工具创建规则/钩子实例 | 本组合 CLI/git 级实例已登记:policy/tool-adaptation-matrix.md(A1–A7 逐行定级+正负例证据) |
| 同上(zcode 实例) | zcode 客户端写路径 | policy/tool-adaptation-zcode.md:PreToolUse 写守卫;脚本负例与客户端接线分开,客户端 pending-live |
| core/baseline.md「规范层级」+ documentation.md「记忆分层/机制化」(draft.9) | 层级效力、记忆权威、经验机制化 | manual:本仓 policy/ 仅收紧不放宽;记忆四层按条文执行(任务卡=任务记忆,documents/records=项目记忆,Dev 仓 AGENTS.md 指针已就位) |
@@ -25,6 +26,7 @@
| 原规则/章节 | 项目采用方式 | 原因与风险 | 负责人/复查条件 |
|---|---|---|---|
| baseline 硬边界 1(授权范围) | 高后果双钥匙:改写历史/解除分支保护/删归档/发布——Agent 只准备,用户执行(AGENTS.md 条文) | 单人+Agent 场景下消除单点;风险=用户不在场时高后果操作排队 | weichen;首次触发时复查 |
| delivery.md §2、design-verification.md 的轻量流程默认做法 | 按 [policy/delivery.md](delivery.md) 默认逐阶段人工评审;小任务简化篇幅,阶段内可迭代;明确授权时在指定范围连续推进 | 保留需求、方案和实施决策权;按变化影响重审,避免每条命令审批及全流程重复返工 | weichen;阶段等待或试验边界影响交付时复查 |
- 历史记录(2026-09-30)报告 11 个组合仓 main 禁直推、拒绝陈旧候选;当前平台能力须独立重验,本地脚本测试不替代远端证据。
- 当前规范内容以 gitlink 及其 VERSION 为准。治理可靠性修复的命令与证据约定见 [开发门禁](governance.md),本地与客户端/平台能力分别登记;本轮不改变分支保护、发布授权或通用库版本。
+110
View File
@@ -0,0 +1,110 @@
# 需求、试验与迭代交付
适用:本项目的需求、修复、规范修改和其他交付任务。依据固定版本的
[通用交付流程](../deps/development-guidelines/workflows/delivery.md),本项目收紧为
**默认逐阶段人工评审;阶段内迭代,按影响回退,按增量交付**。
本文件是阶段与评审的权威表述;启动、同步、命令证据和回收操作见
[开发门禁](governance.md)。规则的执行能力见 [能力矩阵](tool-adaptation-matrix.md)。
## 授权与阶段评审
任务卡默认记为 `manual`。完成当前阶段产物并取得人工评审结论后,才进入下一阶段。
评审记录包含阶段、对象版本、评审人、时间、结论、意见及可追溯依据;结论使用
未评审、通过、需修改、拒绝、不适用。人工结论与命令执行证据分开记录;Agent 自审、
测试通过、工具写入或用户沉默均不能代替人工批准,也不能由作者代签独立审核。
只有用户明确要求自动或连续推进时,才将对应范围记为 `authorized-continuous`,记录
授权来源、覆盖阶段、允许动作、交付终点、资源约束和停止条件。未覆盖阶段仍逐阶段评审。
授权连续推进不免除需求分析、试验判定、测试、代码 Review、证据和平台准入;
既有独立审核要求及高后果操作双钥匙仍有效。单独的合入授权不补齐尚不存在的前序产物。
阶段内已授权的调查、编辑、自检、缺陷修复和等价实现可以持续进行,无需逐命令批准。
需求拆解、评估、需求表可以共用一份任务卡,但分别保留阶段结论。小任务缩短篇幅;
确实不适用的内容说明原因,经人工确认或在明确授权的裁剪范围内处理,不能静默跳步。
任务启动检查、独立工作区和收据用于准备现场,不表示任何阶段评审已通过。
## 阶段与出口
| 阶段 | 必需产物 | 进入下一阶段的条件 |
|---|---|---|
| 1 需求拆解 | 目标、用户场景、子需求、边界、非目标及待澄清问题 | 人工确认拆解与原始意图一致 |
| 2 需求评估 | 价值、可行性、成本、风险、依赖、现有能力及在途任务重叠;与项目定位、模块职责和公开契约的符合度 | 人工确认取舍;重大未知转入下述受控试验 |
| 3 需求表确认 | 稳定 ID、描述、适配依据、优先级、可判定验收条件、依赖和处理结论 | 人工确认本增量范围;影响实施的关键待核实项已处理 |
| 4 方案设计 | 推荐方案与主要备选、职责和接口、数据与权限影响、失败恢复、待验证假设及试验结论 | 人工批准当前方案版本及剩余风险处理方式 |
| 5 实施计划 | 关联需求的工作项、依赖顺序、责任人、文件或模块范围、测试方法与环境、完成条件及停止条件 | 人工批准当前实施和测试计划;远期工作可保留粗粒度 |
| 6 实施与自检 | 可审查变更、局部测试、计划偏差与迭代记录 | 人工确认当前增量实现产物,必要设计调整已重新获批 |
| 7 正式测试与验收 | 需求到证据映射;正常、异常、边界、回归结果及缺项 | 人工审阅当前候选的验证结果;正式验收前同步并固定被测对象 |
| 8 代码 Review | 需求符合性、正确性、维护性、测试充分性及问题闭合记录 | 完成必要重放、整改、补测和复审,由有权评审者放行当前版本 |
| 9 合入 | 最新 base/head、必要检查及评审、冲突处置、合入授权 | 合入前再次同步核验;经 PR 合入后取得真实合入结果 |
| 10 合入核验与收口 | 实际合入内容及组件版本、验证依据、成果保存位置、写者状态、清理清单与任务归档安排 | 清理前取得人工批准或对应连续推进授权;安全回收后核对结果、闭合任务卡 |
表中的人工节点仅在明确的连续推进授权范围内按授权执行;阶段产物及验证条件保留。
正式测试和最终 Review 的顺序不限制设计阶段试验、实施期测试或提前审阅。
文本合并无冲突不证明组合兼容;合入后仍核验真实内容与被测候选是否一致。
## 需求表与追溯
需求表以任务卡为主记录。项目符合度使用“符合 / 需调整后符合 / 不符合 / 待核实”,
附当前源码、项目约束或实测依据。处理结论使用“纳入本增量 / 后续处理 / 拒绝 / 待定”,
与符合度分开;关键依据缺失时保留待核实,不凭推测判定通过。
每个纳入实施的需求必须能追溯到:
`需求 ID 与验收条件 → 方案及工作项 → 测试或审阅方法 → 当前版本结果与证据`。
没有可判定验收方法的需求先澄清;待定或不符合的需求未经重新评审不得进入正式实施。
跨模块、权限、持久化、异步和恢复需求按实际风险补充真实链路及故障场景。
规范和文档任务采用差异、链接、规则一致性及相关工具行为检查,不制造无关业务测试。
## 设计探索循环
需求分析和设计阶段允许先试验再完善方案,无须先批准尚未能确定的完整设计。
执行试验前提交最小试验计划:待回答的问题或假设、对象和隔离环境、方法、预期判据、
允许副作用、时间或成本边界、停止条件及产物保存或处置方式,并取得人工批准;
已有连续推进授权明确覆盖该试验范围时,引用该授权。
在已批准范围内可以反复尝试:
`初步方案 → 批准试验范围 → 原型或测试 → 分析证据 → 修订方案与计划 → 结果评审`。
记录成功、失败及不确定结果,以及下一步继续探索、采用、换方案或暂停的理由。
超出范围、触及未授权共享环境、达到预算或持续无有效进展时,暂停受影响试验并提交决策。
无需强求全部未知一次消除;影响下一步的关键假设必须有结论,其余风险有责任人与处理安排。
原型标明用途和限制,不自动成为正式实现。决定复用时纳入方案、计划、代码 Review 与正式验证。
试验成功仅证明相应假设,不能替代验收;失败结果用于调整方案,不能静默降低验收标准。
## 实施、测试与 Review 的反馈循环
按可独立验证的增量推进;近期工作细化到可执行,后续工作根据证据逐步细化。
每轮形成“发现与证据 → 原假设或困难 → 调整及影响 → 重审和补测范围 → 决策 → 结果”。
记录放在同一任务卡,较长材料只引用文件或 PR,避免多份状态。
| 变化 | 执行动作与评审边界 |
|---|---|
| 已批准范围内的内部组织、等价实现、局部缺陷修正 | 当前阶段继续调整、自测并记录;阶段出口统一人工评审 |
| 技术路线、模块职责、公开契约、数据或权限设计、外部影响,或计划的关键依赖、交付顺序和资源约束变化 | 暂停受影响工作,修订方案或计划,经人工重审后继续;已有明确授权覆盖该类调整时按其边界执行 |
| 目标、范围、优先级或验收条件变化 | 返回需求评估和需求表,取得对应批准后更新下游方案、计划和验证 |
| 影响不明 | 先调查;仍无法判定时提交人工决定,不自行归入细节调整 |
未受影响且已有授权的工作可以继续。测试或 Review 发现实现缺陷,返回实施修复;
发现设计缺陷返回方案;发现需求理解错误返回需求阶段。只重走受影响环节,并说明
未重走部分为何仍有效。新版本不能沿用已受影响的批准、测试或 Review 结论。
批准对象使用文档修订或提交标识;正式验证固定源码、组件组合与相关配置。
对 head/base、依赖、需求或契约变化重新判断证据及评审的有效性,按影响补测和复审。
阶段状态、测试状态、代码审核状态及合入状态分别记录,不以一个“完成”互相替代。
## 合入、清理与恢复
合入与清理分别核对授权及条件。仅回收本任务登记工作区,按开发门禁核对当前权威目标、
父子仓成果、未跟踪和 ignored 文件、独有提交与引用、活动 Git 操作以及所有写者退出。
无法证明成果已保存时保留现场;本地删除与远端分支删除分别报告。
清理前保存任务主记录与证据。执行回收时将命令输出用 evidence 的 `RECEIPT` 模式保存到
待删目录之外;回收后的结果写入后续闭合记录,不能向已删除的任务卡补写收据。
任务卡须回填真实主 PR 和完成状态并归档;闭合 PR 的创建、合入均依当前授权执行。
post-merge 默认只提示,不因 pull 或 merge 自动提交、推送或创建闭合 PR。
中断或交接时记录当前有效版本、批准范围、未决假设、最新试验结论、剩余事项及下一动作。
恢复后核对现场和授权,从最早未满足的环节续办;不重做仍有效的工作,不省略失效环节。
+5
View File
@@ -2,6 +2,8 @@
本文件细化根 AGENTS.md 的启动、验证和回收规则。通用语义以固定的
`deps/development-guidelines` 为准;门禁实现及回归用例随本仓版本管理。
需求表、设计试验、迭代调整、阶段产物与人工评审按 [迭代交付](delivery.md) 执行。
任务卡记录批准来源与对象版本;本地 evidence 仅记录命令,不能证明人工批准。
## 启动与恢复
@@ -64,6 +66,9 @@ Git 对含子模块的工作区需要显式删除选项时,只有全部现场
owner 须先独立确认其保存与归属,工具不把缓存的远端引用当作唯一数据已经备份的证据。
远端分支删除与本地回收分别报告,失败不能声明两者全部完成。
回收后由所有者执行 `make task-close` 闭合卡片;回收命令不顺带改写共享主检出。
post-merge 仅提示,不自动调用闭合。`make task-close` 会提交、推送并可能创建 PR,
执行者先按迭代交付规则核对该动作的授权及所选任务,保留未授权的其他任务现场。
回收命令使用待删目录之外的 `RECEIPT` 保存输出,清理结果由后续闭合记录引用。
## CI 凭据边界
+1
View File
@@ -14,6 +14,7 @@
| A5 回收 | 本地命令检查;写者退出由 owner 确认 | 登记根目录、锁/操作状态、父子仓与唯一文件、合入核验 | `tests/test_task.py` |
| A6 秘密 | 本地脱敏及 CI 获取边界;权限与宿主隔离另验 | 命令/输出/摘要脱敏,凭据不持久写 Git 配置 | `tests/test_evidence.py`、`tests/test_ci_credentials.py` |
| A7 证据 | 本地 SELF 收据;外部来源独立核验 | 首次捕获、完整脱敏输出/摘要、对象记录、原样导入 | `tests/test_evidence.py`;CI/审阅链接另核 |
| A8 阶段评审与迭代 | manual;模板和提示不构成批准身份校验或全流程阻断 | policy/delivery.md;任务卡记录需求、方案/计划、评审与试验迭代;post-merge 默认仅提示 | `tests/test_task.py` 核对模板及钩子不自动写入;人工核对批准范围和版本 |
## 失败处理
+4 -7
View File
@@ -1,15 +1,12 @@
#!/usr/bin/env bash
# 本地钩子(post-merge):pull/merge 落地后尝试闭合已合入任务卡;ff-only 也会触发。
# precheck [cards] 报告欠账,所有者可用 make task-close 手动兜底。
# 本地钩子(post-merge):pull/merge 落地后提示 owner 按授权闭合任务卡;ff-only 也会触发。
# precheck [cards] 报告欠账;此钩子只提示,不执行闭合或其他写入。
# 触发条件收紧到"主检出 + 集成分支 main"——任务 worktree 里 task-sync 的 merge 不触发,
# 防止把卡片闭合混进任务分支现场。失败只警告,绝不阻断 pull。
# 注意:task.sh close 在主检出会另建 feature/card-close-* 分支提交(集成分支不直推),
# 结束后切回 main;PR 由所有者合入。
# 防止把卡片闭合提示混进任务分支现场。
common=$(git rev-parse --git-common-dir) && common=$(cd "$common" 2>/dev/null && pwd) || exit 0
mainrepo=$(dirname "$common")
[ "$mainrepo" = "$PWD" ] || exit 0
[ "$(git rev-parse --abbrev-ref HEAD)" = "main" ] || exit 0
[ -f "$mainrepo/scripts/task.sh" ] || exit 0
bash "$mainrepo/scripts/task.sh" close \
|| echo "[warn] post-merge 卡片自动闭合失败——在主检出手动运行 make task-close" >&2
echo "[next] owner 按已有授权在主检出运行 make task-close 闭合已合入任务卡;未授权时先取得授权。"
exit 0
+43 -1
View File
@@ -99,7 +99,7 @@ cmd_precheck() {
else
echo "[facts] WARN policy/facts.md 不存在——外部事实登记表缺失(AGENTS.md 证据与验证门禁)"
fi
echo "[required-reading] core/baseline.md, workflows/delivery.md §1$([ "$dirty" -gt 0 ] && echo ", standards/collaboration.md(树脏=他人或未收尾现场,不得写入)")"
echo "[required-reading] core/baseline.md, workflows/delivery.md §1$([ -f policy/delivery.md ] && echo ", policy/delivery.md")$([ "$dirty" -gt 0 ] && echo ", standards/collaboration.md(树脏=他人或未收尾现场,不得写入)")"
echo "[R0] $(echo "$rmain" | cut -c1-10)(完整:$rmain)"
if [ "$dirty" -gt 0 ] || [ "${sub_drift:-0}" -gt 0 ]; then
echo "[verdict] SHARED_WORKSPACE_BUSY — 工作树不干净,不得写入;先收尾或经 owner 确认"
@@ -178,11 +178,48 @@ cmd_start() {
- 工作区:${wt}(worktree);分支:${name}
${parent_note}
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
- 交付模式:manual(逐阶段人工评审);明确授权后可记为 authorized-continuous
- 自动推进授权:无;授权范围:无;授权来源:无;授权终点:无;停止条件:无(未授权)
- 当前阶段:需求拆解;当前增量:I1(待定义)
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
## 目标与非目标
<预期结果、受影响入口/模块/契约;明确不做什么。>
## 需求分析
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|---|---|---|---|---|---|---|
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
## 方案与计划
- 方案引用:待填写(含对象版本)
- 实施计划引用:待填写(含增量与需求 ID)
- 测试计划引用:待填写(含需求 ID 与验收条件)
## 阶段评审
| 阶段 | 对象版本 | 结论 | 评审人 | 时间 | 依据 |
|---|---|---|---|---|---|
| 需求拆解 | 待定 | 未评审 | — | — | — |
| 需求评估 | 待定 | 未评审 | — | — | — |
| 需求表确认 | 待定 | 未评审 | — | — | — |
| 方案设计 | 待定 | 未评审 | — | — | — |
| 实施计划 | 待定 | 未评审 | — | — | — |
| 实施与自检 | 待定 | 未评审 | — | — | — |
| 正式测试与验收 | 待定 | 未评审 | — | — | — |
| 代码 Review | 待定 | 未评审 | — | — | — |
| 合入 | 待定 | 未评审 | — | — | — |
| 合入核验与收口 | 待定 | 未评审 | — | — | — |
## 探索试验与迭代
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 批准依据 | 结果 | 变更影响 | 需重审补测 |
|---|---|---|---|---|---|---|---|
| 待定义 | — | — | — | 未批准 | NOT_RUN | 待评估 | 待评估 |
## 同步记录
| 节点 | sync_base_sha | tested_head_sha | checked_at |
@@ -200,6 +237,11 @@ ${parent_note}
- 结果:
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
- 影响与剩余项:
## 合入与清理
- 合入记录:NOT_RUN(授权依据、PR、合入 SHA、验证来源)
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
EOF
(cd "$wt" && IMPORT_RECEIPT="$PRECHECK_RECEIPT" bash "${SCRIPT_DIR}/evidence.sh")
git -C "$wt" add .task
+101 -1
View File
@@ -89,6 +89,18 @@ class TaskLifecycleTests(unittest.TestCase):
self.assertNotEqual(result.returncode, 0, result.stdout)
self.assertIn("SYNC_REQUIRED", result.stdout)
def test_precheck_routes_existing_project_delivery_policy(self):
result = self.task(self.repo, "precheck")
self.assertNotIn("policy/delivery.md", result.stdout)
policy = self.repo / "policy/delivery.md"
policy.parent.mkdir()
policy.write_text("# Project delivery policy\n")
self.git(self.repo, "add", "policy")
self.git(self.repo, "commit", "-m", "Adopt project delivery policy")
result = self.task(self.repo, "precheck")
reading = next(line for line in result.stdout.splitlines() if line.startswith("[required-reading]"))
self.assertIn("policy/delivery.md", reading)
def test_sync_materializes_updated_component(self):
child = self.add_submodule()
wt = self.worktree()
@@ -586,11 +598,99 @@ class TaskLifecycleTests(unittest.TestCase):
self.task(self.repo, "start", "feature/new", env=self.env | {"PRECHECK_RECEIPT": str(receipt)})
wt = self.root / "repo-wt-feature-new"
self.assertTrue((wt / ".task/feature-new.md").is_file())
self.assertIn("make precheck", (wt / ".task/feature-new.md").read_text())
card = (wt / ".task/feature-new.md").read_text()
self.assertIn("make precheck", card)
self.assertIn("交付模式:manual(逐阶段人工评审)", card)
self.assertIn("authorized-continuous", card)
self.assertIn("流程依据:仓库根目录 policy/delivery.md", card)
for field in ("授权范围", "授权来源", "授权终点", "停止条件"):
self.assertIn(f"{field}:无", card)
for field in ("当前阶段", "当前增量", "方案引用", "实施计划引用", "测试计划引用"):
self.assertIn(field + ":", card)
self.assertIn("| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |", card)
reviews = card.split("## 阶段评审\n", 1)[1].split("\n## ", 1)[0]
self.assertIn("| 阶段 | 对象版本 | 结论 | 评审人 | 时间 | 依据 |", reviews)
for stage in ("需求拆解", "需求评估", "需求表确认", "方案设计", "实施计划", "实施与自检",
"正式测试与验收", "代码 Review", "合入", "合入核验与收口"):
row = next(line for line in reviews.splitlines() if line.startswith(f"| {stage} |"))
self.assertEqual(row.split("|")[3].strip(), "未评审")
self.assertNotIn("通过", reviews)
self.assertIn("## 探索试验与迭代", card)
for field in ("假设", "范围", "判断和停止条件", "批准依据", "结果", "变更影响", "需重审补测"):
self.assertIn(field, card.split("## 探索试验与迭代\n", 1)[1].split("\n## ", 1)[0])
self.assertIn("## 合入与清理", card)
for field in ("合入记录", "清理记录"):
self.assertIn(field + ":NOT_RUN", card)
body = self.git(wt, "log", "-1", "--format=%B").stdout
for field in ("Why:", "Evidence:", "Impact:", "Refs:"):
self.assertIn(field, body)
def install_post_merge_hook(self):
hook = self.repo / ".git/hooks/post-merge"
shutil.copyfile(SOURCE / "scripts/hooks/post-merge", hook)
hook.chmod(0o755)
def publish_merged_task(self):
publisher = self.root / "publisher"
self.git(self.root, "clone", str(self.remote), str(publisher))
self.git(publisher, "switch", "-c", "feature/merged")
card = publisher / ".task/feature-merged.md"
card.parent.mkdir()
card.write_text("# Merged task\n任务主记录:待回填;状态:进行中;owner:test\n"
"- 工作区:fixture;分支:feature/merged\n")
self.git(publisher, "add", ".task")
self.git(publisher, "commit", "-m", "Record merged task")
self.git(publisher, "switch", "main")
self.git(publisher, "merge", "--no-ff", "feature/merged", "-m",
"Merge pull request 'Task' (#7) from feature/merged into main")
self.git(publisher, "push", "origin", "main")
return self.git(publisher, "rev-parse", "HEAD").stdout.strip(), card.read_text()
def assert_post_merge_only_reminds_owner(self, operation):
expected_head, expected_card = self.publish_merged_task()
self.git(self.repo, "fetch", "origin")
old_head = self.git(self.repo, "rev-parse", "HEAD").stdout.strip()
local_heads = self.git(self.repo, "show-ref", "--heads").stdout
worktrees = self.git(self.repo, "worktree", "list", "--porcelain").stdout
remote_refs = self.git(self.remote, "show-ref").stdout
self.install_post_merge_hook()
result = self.git(self.repo, *operation)
self.assertIn("make task-close", result.stdout)
self.assertIn("授权", result.stdout)
self.assertNotIn("[close]", result.stdout)
self.assertEqual(self.git(self.repo, "branch", "--show-current").stdout.strip(), "main")
self.assertEqual(self.git(self.repo, "rev-parse", "HEAD").stdout.strip(), expected_head)
self.assertEqual(self.git(self.repo, "show-ref", "--heads").stdout,
local_heads.replace(old_head, expected_head))
self.assertEqual(self.git(self.repo, "worktree", "list", "--porcelain").stdout,
worktrees.replace(old_head, expected_head))
self.assertEqual(self.git(self.remote, "show-ref").stdout, remote_refs)
self.assertEqual(self.git(self.repo, "status", "--porcelain").stdout, "")
self.assertEqual((self.repo / ".task/feature-merged.md").read_text(), expected_card)
self.assertFalse((self.repo / ".task/archive").exists())
def test_post_merge_main_merge_only_reminds_owner(self):
self.assert_post_merge_only_reminds_owner(("merge", "--ff-only", "origin/main"))
def test_post_merge_main_pull_only_reminds_owner(self):
self.assert_post_merge_only_reminds_owner(("pull", "--ff-only"))
def test_post_merge_is_silent_outside_main_checkout(self):
wt = self.worktree()
self.install_post_merge_hook()
self.advance_remote()
result = self.git(wt, "merge", "--ff-only", "origin/main")
self.assertNotIn("make task-close", result.stdout)
self.assertNotIn("[close]", result.stdout)
self.git(self.repo, "switch", "-c", "feature/primary")
self.git(wt, "switch", "-c", "feature/advance")
(wt / "feature-work").write_text("another task\n")
self.git(wt, "add", "feature-work")
self.git(wt, "commit", "-m", "Advance feature")
result = self.git(self.repo, "merge", "--ff-only", "feature/advance")
self.assertNotIn("make task-close", result.stdout)
self.assertNotIn("[close]", result.stdout)
def test_start_stacked_branch_records_parent_and_main_baseline(self):
base = self.git(self.repo, "rev-parse", "origin/main").stdout.strip()
parent = self.worktree("feature/parent")