v0.1.5: standalone-readability pass — delink --strip provenance mode ([TT-NNNN]), Tier-2 dangling refs neutralized, verify/review-code giant skills split into reference/ on-demand files; guards now runtime-neutral (bun|node)

This commit is contained in:
2026-09-25 00:09:37 +08:00
parent d6567b1842
commit 88204a90c8
74 changed files with 1417 additions and 1358 deletions
+26 -674
View File
@@ -60,10 +60,10 @@ digraph verify {
## Agent Role
> **Progressive disclosure ([org-internal #3722], [org-internal #3734], [org-internal #3745])**: when dispatching a Verifier
> **Progressive disclosure ([TT-3722], [TT-3734], [TT-3745])**: when dispatching a Verifier
> scoped to one phase, pass `skills: ["verify:phase-2"]` (preamble + that
> phase only) instead of the full body; multi-phase dispatches may union.
> Phase 5 is subdivided ([org-internal #3734]): `phase-5` covers the report template only;
> Phase 5 is subdivided ([TT-3734]): `phase-5` covers the report template only;
> each mandatory filing subsection carries its own marker (`phase-5.5` tech
> debt, `phase-5.55` baseline failures, `phase-5.56` flaky tests,
> `phase-5.6` checklist sync, `phase-5.7` CI sync). Dispatch recipes:
@@ -71,7 +71,7 @@ digraph verify {
> / `phase-5.56` when baseline failures / flaky tests are present); PASS
> close-out →
> `"verify:phase-5,phase-5.5,phase-5.55,phase-5.56,phase-5.6,phase-5.7,phase-6"`.
> The DAG branch is separately marked ([org-internal #3745]): `phase-6` narrows to
> The DAG branch is separately marked ([TT-3745]): `phase-6` narrows to
> Approval + Commit Gate, and the tail References rides `dag-branch` (same
> tail rule as review-code's phase-e). Non-DAG dispatches thereby drop the
> ~175-line DAG branch; DAG milestone/task verify → union `dag-branch` onto
@@ -155,7 +155,7 @@ Before starting verification, confirm:
node spec, its `acceptance_criteria` + `test_id` declarations, and the
DoD slice. (Legacy tier rule — `{slug}/04-plan-*` wiki pages / issue
comment, and the design-space + iteration-plan convergence checks that
used to follow here — was archived 2026-08-21, [org-internal #3072] phase 3; historical
used to follow here — was archived 2026-08-21, [TT-3072] phase 3; historical
plan/AC pages stay readable via `_shared/gitea-read-patterns.md`.)
- [ ] `core/checklists/verification.md` is accessible.
- [ ] `slug` matches the run's slug (DAG: `{epic-slug}`).
@@ -223,7 +223,7 @@ the DAG branch below for its read map):
(The legacy `Size/*`-tiered locations — `{slug}/04-plan-*`,
`{slug}/02-requirements-index`, `{slug}/02-03-req-design`,
`{slug}/03-design-06-non-functional-design` — were archived 2026-08-21,
[org-internal #3072] phase 3; historical pages stay readable for legacy-run verification
[TT-3072] phase 3; historical pages stay readable for legacy-run verification
via `_shared/gitea-read-patterns.md`.)
5. **Code review final report** — `{slug}/reviews/code/final/report`
@@ -277,7 +277,7 @@ before. This section saves redundant typecheck/lint/security re-runs; it
never weakens a gate.
**Second evidence source — the review-code precondition-gate cache
(eff-gate-cache [org-internal #2598] / [org-internal #2990])**. The mechanical checks this phase would re-run
(eff-gate-cache [TT-2598] / [TT-2990])**. The mechanical checks this phase would re-run
(oxlint / typecheck / `test:changed`) are exactly the precondition-gate's
check-set, and the gate caches GREEN results keyed on a tree fingerprint
(HEAD + tracked diff + untracked contents) in
@@ -394,8 +394,8 @@ For each DoD entry with verification method "Security scan":
every failing test, run it on the last known-green commit (or the base
branch) in isolation. **Never use `git stash` for this** — the stash stack
is shared across all worktrees of this repo and a parallel session can
overwrite your entry mid-flight (issue [org-internal #1655]). Instead, use the
lifecycle-managed scratch-worktree script ([org-internal #2790]) — it registers the
overwrite your entry mid-flight (issue [TT-1655]). Instead, use the
lifecycle-managed scratch-worktree script ([TT-2790]) — it registers the
worktree for the sweep backstop, symlinks `node_modules` from the source
checkout when `bun.lock` matches (dropping the per-instance cost from
~170k inodes to ~7k), and deregisters on removal:
@@ -435,7 +435,7 @@ For each DoD entry with verification method "Security scan":
### 2.7 — Browser Evidence Reference Validation (UI-touching PR)
For DoD entries of the「UI 主张 ↔ 可复现浏览器证据」type — contract
`browser-evidence-4486/shared/evidence-ref-v1` (frozen 2026-09-10, [org-internal #4498]).
`browser-evidence-4486/shared/evidence-ref-v1` (frozen 2026-09-10, [TT-4498]).
Applicable ONLY to UI-touching PRs (changes under `packages/app`,
`packages/ui`, or `packages/console/*` frontend surfaces):
@@ -493,7 +493,7 @@ Browser-evidence rows (UI-touching PRs only, Phase 2.7): ✅ PASS requires a
mechanically validated, replayable pack-ref; `unregistered` / `manifest-missing` /
`manifest-corrupt` / `not-replayable` / `over-limit` references are
⚠️ UNVERIFIABLE at best — the reference channel fails explicitly, never
silently green ([org-internal #4498] AC-4).
silently green ([TT-4498] AC-4).
### Iteration Verdict
@@ -619,501 +619,20 @@ issue close) is performed later in Phase 5.6 — see
`rules/issue-checklist-sync.md` § 归档动作.
<!-- section:phase-5.5 -->
### Phase 5.5 — Tech Debt Registry Filing (mandatory before PASS)
Tech debt is filed **registry-first** (`core/rules/ticket-lifecycle.md`,
[org-internal #3002] G1): new items become ROWS in the source ticket's `## TD 登记` comment;
an independent ticket is created only at **promotion** (claim/schedule). There
is no `tech-debt.md` file and no `{slug}/tech-debt` wiki page — both remain
retired. The registry preserves the three guarantees of the old
one-issue-per-item model: visible (the comment lives on a tracker ticket),
individually closeable (row-level checkbox), de-duplicated across sessions
(the TD index is stronger than title-keyword matching).
**Sprint-mode override ([org-internal #3002] G5/[org-internal #3007])**: read `filing.sprint-mode` from
`<instance-root>/workflow-routing.yaml` at the START of this phase (no caching). When
`true`, ALL severities file registry-only — registration rows are written as
normal (severity included, nothing lost) but promotion to standalone tickets
is FROZEN until the switch returns to `false`. Rationale: the 2026-08-12
sprint zeroed fix capacity while filing kept running (the 125-fragment
backlog's direct trigger); an explicit debt-side downshift beats hoping for
discipline. BF/FT phases 5.55/5.56 are NOT covered by this switch — failure
evidence must stay immediately visible. Flipping the switch is a human
decision recorded on the sprint tracker; semantics in
`rules/workflow-routing.md` §立案降档.
**Adjacency quota ([org-internal #3002] G3/[org-internal #3005])**: while collecting inputs, also check the
AREAS this iteration touched (packages/modules with commits this iteration).
If the source tickets for those areas carry applicable `## TD 登记` rows, at
least ONE row must be taken along and disposed this iteration (fixed, or
explicitly re-deferred with a reason — silence is not a disposition). Record
`consumed X / applicable Y` (or `0 applicable`) in the TD report section;
verification checklist §8.4 cross-checks it against the run's debt-quota
record (historically the roadmap's per-chunk debt-quota line). This gives debt digestion a steady capacity source tied to
touch, not to cleanup campaigns.
**Promotion quota + pull event ([org-internal #3061])**: promotion to a standalone ticket
requires (a) a **pull event** — the row's Reactivation Trigger fired (state
the observed event) OR a named claimer/schedule; "it seemed important" is not
a pull event — recorded in the promotion body's `## 拉动事件 / Pull Event`
section; and (b) the origin module's OPEN promoted-TD count is below the
quota (**default 10** — counted **per origin module**: resolve the row's
module from the ticket body's `Origin` field, then count that module's OPEN
`tech-debt` tickets; retro §2.10 reports breaches). At or above quota →
register-only until the count drops; before each Phase 5.5 promotion
re-check the owning module's count, and a row whose module is at or above
quota is NOT promoted — defer it and annotate the registry row with
`quota-held` (re-checked on the next verify run, retro 2026-08-21). A row
marked `[COLD]` by retrospective §2.10
(unclaimed >3 retro cycles) promotes only after the pull event clears the
mark.
**Collect inputs** — every tech-debt item to register this iteration:
1. Every UNVERIFIABLE row in the DoD matrix (Phase 3), with its Reactivation
Trigger.
2. Every ACCEPTED_RISK finding in the code-review final report
(`{slug}/reviews/code/final/report`), with its Recommended Reactivation
Trigger.
3. Every `[OPEN: <short-id>]` marker the Developer left in code this iteration
(per implementation checklist POST-6.2).
If the code-review final report does not exist, warn the user — ACCEPTED_RISK
items cannot be captured. This is itself a process gap that must be flagged.
**Step 1 — De-duplicate against the registry index and promoted issues.**
1. Read the source ticket's `## TD 登记` comment (create the comment if this
is its first row) and match each candidate against existing rows by
`TD-NNN` and reactivation trigger. Same-source re-reporting is the
anti-pattern this step exists to prevent (the visual-cluster incident: 18
fragments from one source under the old keyword matching).
2. Query OPEN promoted tech-debt issues for cross-source duplicates:
```
工单 API 检索(见 TERMINOLOGY)
(owner/repo 按实例绑定,labels="tech-debt", state="open")
```
**Step 2 — Assign `TD-NNN` via the ledger mutex ([org-internal #3322]).** One script call
per batch: `bash script/td-alloc.sh <count = new items> <source-slug>
[--ticket N]` returns a contiguous range from the TD allocation ledger
(append-only comments, earliest-comment-id wins; the seed comment pins the
historical floor). Split the returned range across the Step 3 rows in order.
**Any allocation — manual included — must land a `**td-alloc**` comment on
the ledger before the registry row is written; hand-computed `max + 1` is
forbidden** (the 2026-08-24 three-way collision). On exit 5 (retry
exhaustion) abort this phase's filing and retry — never hand-assign. The id is **reserved for life**: promotion reuses it, never
renumbers. Run `--check` (reconciliation) here if the last check predates
this batch.
**Step 3 — Append one registry row per new item.** Append to the source
ticket's `## TD 登记` comment (no source ticket → the owning Epic's tracker
ticket; no Epic → the standing `TD 冷存档` tracker):
```markdown
- [ ] TD-{NNN} · {UNVERIFIABLE | ACCEPTED_RISK | DEFERRED} · {HIGH | MEDIUM | LOW}
· {origin chunk-id/iter-N} — {one-line summary}
· trigger: {objectively verifiable reactivation condition — never "when ready";
if no trigger can be defined this is NOT tech debt and the iteration verdict
becomes FAIL}
```
**Step 4 — Cross-link (adapted).** The registry comment already lives on the
source issue — the source → debt navigation the old body-edit provided. When
filing went to an Epic tracker or the 冷存档 (no source ticket), reference the
originating slug in each row so the trail survives.
**Step 5 — Record the mapping in the verification report.** The report's
"Tech Debt" section records `TD-NNN → registry row` (source `#NNNN` +
`## TD 登记` comment); promoted items record `TD-NNN → #NNNN` as before.
**Promotion (认领升票) — when a row is scheduled or picked up.** Create the
independent ticket THEN, carrying the reserved `TD-NNN`:
```
POST <gitea-base-url>/api/v1/repos/Octopus/octopus/issues
body: {"title":"[{origin}] Tech debt: {short description} (TD-{NNN})",
"body":<promotion template below>,
"labels":[tech_debt_label_id, severity_label_id]}
```
- **Severity → label** (reuse the existing exclusive Priority set):
| Severity | Label |
| -------- | ----------------- |
| HIGH | `Priority/High` |
| MEDIUM | `Priority/Medium` |
| LOW | `Priority/Low` |
- **Promotion body template** (the pre-[org-internal #3002] issue template, now used only at
promotion):
```markdown
## Parent / 父级
{#source_issue carrying the ## TD 登记 row}
## Summary
{one-paragraph description of the debt}
| Field | Value |
| -------- | ------------------------------------------- |
| TD ID | TD-{NNN} (reserved at registration) |
| Type | {UNVERIFIABLE \| ACCEPTED_RISK \| DEFERRED} |
| Origin | {chunk-id/iter-N/phase or slug} |
| Severity | {HIGH \| MEDIUM \| LOW} |
## Reactivation Trigger
{from the registry row}
## 拉动事件 / Pull Event
{trigger fired: {observed event} | claimed by {who} for {when}}
## Acceptance Criteria (how to close this issue)
- [ ] {specific action that resolves the debt}
## Context
- Verification report: `{slug}/05-verify-iteration-{N}`
- Code review report: `{slug}/reviews/code/final/report` (if ACCEPTED_RISK)
```
- After creating the promoted ticket: tick the registry row (`- [x]`) and
append `→ #NNNN` to it.
If no new tech-debt items were found this iteration, the phase is a no-op —
record "0 tech-debt items" in the report and proceed.
<!-- section:phase-5.55 -->
### Phase 5.55 — Baseline-Failure Issue Registration (mandatory before PASS when pre-existing failures exist)
Pre-existing test failures (BF) are tracked as **root-cause family umbrella
tickets** (`core/rules/ticket-lifecycle.md`, [org-internal #3002] G1; precedents [org-internal #2585]
umbrella + [org-internal #2925] cluster) — one `BF-NNN` ticket per failure-signature family,
labeled `baseline-failure`, with each occurrence's evidence attached as
comments. There is no `baseline-failures.md` file and no
`{slug}/baseline-failures` wiki page. A pre-existing failure does not block
the iteration that _discovered_ it (it pre-dates that work), but it MUST be
filed against (or into) a family; an un-filed baseline failure is a process
gap and the iteration is NOT Done.
**Birth triage SLA ([org-internal #3061])**: a BF umbrella is born with `Kind/Bug` (it IS a
bug — of the baseline) and must be triaged the SAME DAY it is filed:
assigned, scheduled, or explicitly deferred with a reason on the ticket.
Untriaged-next-day BF umbrellas are named individually in retro §2.10
(compliance %), not averaged away.
**Collect inputs** — every pre-existing failure to register this iteration:
1. Every failure classified **pre-existing** in Phase 2.6 (fails on the base
branch / last-green commit too).
2. Any baseline-run failures noted in the brownfield pre-check (Greenfield vs.
Brownfield section).
**Step 1 — De-duplicate by failure SIGNATURE against existing families (open
AND closed), per `core/rules/testing.md`** (de-duplicate BEFORE filing,
retro [org-internal #2696]). Query `baseline-failure` issues in ALL states:
```
GET <gitea-base-url>/api/v1/repos/Octopus/octopus/issues?labels=baseline-failure&state=all&page=N&limit=50
```
Match each candidate by **failure signature** — the error/assertion signature
plus the affected surface, not just `file::test-name` (one root-cause family
may span several tests; several tests failing with the same assertion shape
are ONE family). On a signature hit, do NOT create a new ticket: attach the
new run evidence (run, commit, failing output, sibling tests observed) as a
comment on the FAMILY umbrella ticket. Filing a second ticket inside a known
root-cause family is the anti-pattern this phase exists to prevent. If a
tracked family is now FIXED by this iteration's work, close its umbrella with
a comment citing the fixing commit (a bonus, not a requirement). If the query
itself errors (API failure — not "no matches"), retry once; on the second
failure proceed to file with the literal marker `dedup-unverified` in the
umbrella's first evidence comment (query-failure ruling, [org-internal #3383] — fail-open,
visible; rationale in `core/rules/ticket-lifecycle.md`).
**Step 2 — Assign the next `BF-NNN`** per `core/rules/testing.md`
§ "Pre-existing test failures must be filed, not silently logged" (the L1 rule
owns the repo-wide sequential numbering invariant — `max + 1`, duplicate ids
forbidden incl. closed tickets). `BF-NNN` names the FAMILY; the Gitea issue
number `#NNNN` is the tracker handle.
**Step 3 — Create one umbrella ticket per NEW root-cause family.** Only when
no existing family matches the signature:
```
POST <gitea-base-url>/api/v1/repos/Octopus/octopus/issues
body: {"title":"[{origin}] Baseline failure: {test identifier} (BF-{NNN})",
"body":<issue body template below>,
"labels":[baseline_failure_label_id, severity_label_id, kind_bug_label_id]}
```
- **Title**: `[{origin}] Baseline failure: {signature summary} (BF-{NNN})`
where `{origin}` is `chunk-{id}/iter-{N}` or the bugfix slug, and
`{signature summary}` is the family's error/assertion shape (not a single
test name — sibling tests observed later join this umbrella as evidence
comments).
- **Severity → label** (reuse the existing exclusive Priority set; a failing
test defaults to at least MEDIUM, HIGH if on a critical path):
| Severity | Label |
| -------- | ----------------- |
| HIGH | `Priority/High` |
| MEDIUM | `Priority/Medium` |
| LOW | `Priority/Low` |
- Also attach `Kind/Bug` so the failure is discoverable in the bug tracker
alongside manually-reported bugs.
- **Issue body template**:
````markdown
## Parent / 父级
{#source_issue, or "no source issue — discovered during iteration baseline"}
## Summary
A test was already failing before this iteration's work began. It does not
block the discovering iteration but must be tracked to closure.
| Field | Value |
| -------------- | ---------------------------------------------- |
| BF ID | BF-{NNN} (family) |
| Signature | {error/assertion shape} |
| Tests affected | `{file::test-name}`, … (initial members) |
| Origin | {chunk-id/iter-N or slug} |
| Severity | {HIGH \| MEDIUM \| LOW} |
| First observed | {base commit / date the failure reproduces on} |
## Reproduction
```
git checkout {base commit}
(cd <harness-package> && bun test --timeout 120000 {test file}) # or project-equivalent
```
Failing output:
```
{paste the relevant assertion / error}
```
## Acceptance Criteria (how to close this issue)
- [ ] The named test passes on a clean checkout of the target branch.
- [ ] If the test is deleted/renamed, the replacement covers the same
behavior and passes.
## Context
- Verification report: `{slug}/05-verify-iteration-{N}`
````
**Step 4 — Cross-link to the source issue (mandatory).** Per the
issue-cross-linking rule, every baseline-failure issue MUST link back to its
source:
- The issue body already carries `## Parent` (above).
- Update the source/parent issue body to reference each new baseline-failure
issue by `#NNNN` in its task list (e.g.
`- [ ] #NNNN — BF-{NNN} {test identifier}`), so reviewers can navigate from
source → failure.
**Step 5 — Record the mapping in the verification report.** The
`BF-NNN → #NNNN` mapping is written ONLY in the Phase 5 verification report's
"Baseline Failures" section (and in the issue title itself). There is no local
file to update.
If no new baseline failures were found this iteration, the phase is a no-op —
record "0 baseline failures" in the report and proceed.
<!-- section:phase-5.56 -->
### Phase 5.56 — Flaky-Test Issue Registration (mandatory before PASS when flaky tests are unfixed)
Flaky tests (intermittent pass/fail) are tracked as **root-cause family
umbrella tickets** (`core/rules/ticket-lifecycle.md`, [org-internal #3002] G1) — one
`FT-NNN` ticket per failure-signature family, labeled `flaky-test`, with each
occurrence's evidence (run count, failure rate, commit) attached as comments.
A flaky test that is not fixed within this iteration MUST be filed against
(or into) a family; otherwise it evaporates at iteration end and the
"compare flaky count against baseline" check (TST-2.5) has no persistent
register to compare against. Filing does not block the verdict, but an unfixed
flaky test left un-filed is a process gap and the iteration is NOT Done.
**Expiry SLA ([org-internal #3061])**: an FT umbrella is born with `Kind/Testing` and a
**fix-or-isolate window** (default 7 days) recorded in its body (isolating =
quarantining/skipping the test with the family id in the skip reason, which
keeps the register comparable). Past the window without fix or isolation, the
umbrella is escalated on the ticket (comment naming the owner); retro §2.10
reports fix-or-isolate compliance per cycle.
**Collect inputs** — every flaky test to register this iteration:
1. Every flaky test surfaced in Phase 2.1 (intermittent pass/fail across
reruns) that was NOT fixed by the Developer this iteration.
2. Any flaky test carried over from a prior iteration's report that is still
intermittently failing.
**Step 1 — De-duplicate by failure SIGNATURE against existing families (open
AND closed), per `core/rules/testing.md`** (de-duplicate BEFORE filing,
retro [org-internal #2696]). Query `flaky-test` issues in ALL states:
```
GET <gitea-base-url>/api/v1/repos/Octopus/octopus/issues?labels=flaky-test&state=all&page=N&limit=50
```
Match each candidate by **failure signature** — the flakiness mode (timeout /
race / order-dependence / env-leak) plus the affected surface, not just
`file::test-name` (one root cause may flake several tests). On a signature
hit, do NOT create a new ticket: attach the new run evidence (run count,
failure rate, commit, sibling tests) as a comment on the FAMILY umbrella
ticket. Filing a second ticket inside a known root-cause family is the
anti-pattern this phase exists to prevent. If a tracked family now passes
deterministically, close its umbrella citing the fixing commit. If the query
itself errors (API failure — not "no matches"), retry once; on the second
failure proceed to file with the literal marker `dedup-unverified` in the
umbrella's first evidence comment (query-failure ruling, [org-internal #3383] — fail-open,
visible; rationale in `core/rules/ticket-lifecycle.md`).
**Step 2 — Assign the next `FT-NNN`** per `core/rules/testing.md`
§ "Flaky tests must be filed, not silently tolerated" (the L1 rule owns the
repo-wide sequential numbering invariant — `max + 1`, duplicate ids forbidden
incl. closed tickets). `FT-NNN` names the FAMILY.
**Step 3 — Create one umbrella ticket per NEW root-cause family.** Only when
no existing family matches the signature:
```
POST <gitea-base-url>/api/v1/repos/Octopus/octopus/issues
body: {"title":"[{origin}] Flaky test: {test identifier} (FT-{NNN})",
"body":<issue body template below>,
"labels":[flaky_test_label_id, severity_label_id, kind_testing_label_id]}
```
- **Title**: `[{origin}] Flaky test: {signature summary} (FT-{NNN})` — the
family's flakiness mode + surface (sibling tests join later as evidence
comments, not new tickets).
- **Severity** → a flaky test defaults to MEDIUM (HIGH if on a critical path
or blocking CI); reuse the exclusive Priority set.
- Also attach `Kind/Testing` so it is discoverable in the testing tracker.
- **Issue body template**:
````markdown
## Parent / 父级
{#source_issue, or "no source issue — discovered during iteration verification"}
## Summary
A test passes and fails intermittently across runs. It undermines the
reliability of the suite and must be stabilized or quarantined.
| Field | Value |
| ------------ | ------------------------- |
| FT ID | FT-{NNN} (family) |
| Signature | {flakiness mode + surface} |
| Tests affected | `{file::test-name}`, … (initial members) |
| Origin | {chunk-id/iter-N or slug} |
| Severity | {HIGH \| MEDIUM \| LOW} |
| Failure rate | {e.g. 3/10 runs} |
## Reproduction
```
for i in $(seq 1 10); do (cd <harness-package> && bun test --timeout 120000 {test file}) || echo "run $i FAILED"; done
```
## Acceptance Criteria (how to close this issue)
- [ ] The test passes deterministically across 10 consecutive runs.
- [ ] If it cannot be stabilized, it is quarantined (`test.skip` /
`test.todo` with a comment citing FT-{NNN}) and a stable replacement
is filed as tech-debt (TD-NNN).
## Context
- Verification report: `{slug}/05-verify-iteration-{N}`
````
**Step 4 — Cross-link to the source issue (mandatory).** Every flaky-test
issue links back to its source via `## Parent`, and the source/parent issue
task list references each by `#NNNN`
(`- [ ] #NNNN — FT-{NNN} {test identifier}`).
**Step 5 — Record the mapping in the verification report.** The
`FT-NNN → #NNNN` mapping is written ONLY in the Phase 5 report's "Flaky Tests"
section (and the issue title). No local file.
If no unfixed flaky tests exist this iteration, the phase is a no-op — record
"0 flaky tests" in the report and proceed.
<!-- section:phase-5.6 -->
### Phase 5.6 — Issue Checklist Sync (final sweep, mandatory before PASS)
This is the **final** checklist sync — the last of the progressive sync
points (requirements exit, design exit, plan exit, each iteration commit, PR
creation, review convergence, CI transitions, and here — per the sync-point
table in `core/rules/issue-checklist-sync.md`). Earlier syncs may have
already checked off completed items; this phase catches everything remaining
and ensures no `- [ ]` item is left without an annotation.
1. **Identify the source issue** per `core/rules/issue-checklist-sync.md`
§ "How to sync (each point)" step 1 (find `Closes #N` / `Fixes #N` in the
PR body or commit messages). If no source issue exists (e.g. standalone
bugfix with no tracking issue), skip this phase.
2. **Fetch the issue body** via `工单 API(见 TERMINOLOGY)get` (rule step 2).
3. **Map every remaining `- [ ]` checklist item** to one of three states:
- **Done** (missed by earlier syncs) → mark `- [x]` and append
`_(PR #NNN: <file/component>)_`.
- **Deferred** → keep `- [ ]` and append `_(Deferred: TD-NNN <reason>)_`.
- **Pending** → keep `- [ ]` and append `_(Pending: <blocker>)_`.
4. **Fold the `## 当前状态` live-status section** (if present). Rows that have
a checklist counterpart (review, process-ACs) are already reflected as
checklist annotations at their sync points. Rows with no checklist
counterpart in a non-Epic issue (PR opened/merged) are folded as follow-up
evidence on the issue narrative (e.g. `_(PR #NNN merged: <summary>)_` on the
item it delivered, or in the report). Once every row is accounted for,
remove the section from the issue body.
5. **archive-at-close: mark the `## 工件索引` comment archived** (归档动作 — [org-internal #1929]). Issue
closing IS the archive-at-close action; it is explicit, not implicit. Edit the index
comment in place per `_shared/gitea-write-patterns.md` Pattern 10 Archive-at-close
(mechanism — banner + every row's 重读 → ARCHIVE + invariants — is defined there,
do not restate; sequence/actor per `rules/issue-checklist-sync.md` § 归档动作).
- Skip if no source issue exists.
- **Tier 1 run archive** ([org-internal #1988] two-tier artifacts — only if a Tier 1 run
workspace exists): if `<runs-root>/{slug}/` exists (a stage in this run
produced Tier 1 local artifacts, e.g. review-code findings), archive the
run workspace per `core/rules/two-tier-artifacts.md` § Archive-at-close
(the L1 rule is authoritative — do NOT restate its steps here).
The archived bundle must pass
`core/schemas/runs-bundle.schema.json` validation (the generator
validates by default; a red exit blocks close).
Verify deltas only: skip in milestone/task DAG mode (the aggregator agent
performs it at Epic/task-ticket close — see the DAG branch below).
6. **Update the issue body** via `工单 API(见 TERMINOLOGY)update` with the annotated
checklist. Preserve all non-checklist content unchanged.
7. **Verify**: re-read the issue and confirm no `- [ ]` item lacks an
annotation. Every unchecked item MUST have a `(Deferred: ...)` or
`(Pending: ...)` note explaining why it is not done.
8. **Close the issue** (completes the archive-at-close loop): if the flow has
no PR (no `Closes #N` on merge), the Verifier closes the issue after PASS
approval via `工单 API(见 TERMINOLOGY)update(state="closed")` — the banner date is
corrected to the actual close date if it differs from today. If a PR carries
`Closes #N`, the merge closes it; no further action.
If a checklist item cannot be mapped to evidence, deferral, or pending
status, the iteration is NOT Done — the item represents untracked scope.
### Phase 5.5–5.6 — Issue Filing Protocols (TD registry / baseline / flaky / checklist sync)
> The four mandatory-before-PASS filing protocols — Phase 5.5 Tech-Debt
> registry filing (sprint-mode override), Phase 5.55 baseline-failure
> registration, Phase 5.56 flaky-test registration, Phase 5.6 issue
> checklist final sweep — live in `reference/issue-filing.md`. Read each at
> its boundary during Phase 5; PASS is blocked until all applicable filings
> report completion. Phase 5.7 (CI state sync) stays inline below.
<!-- section:phase-5.7 -->
### Phase 5.7 — CI State Transition Sync (progressive)
Process-type acceptance criteria (e.g. "ci.yml passes N consecutive runs",
AC#3 in [org-internal #1668]) have no single moment of completion — their progress lives on
AC#3 in [TT-1668]) have no single moment of completion — their progress lives on
the issue. The `CI` row in `## 当前状态` is written by the `status-sync` poller
(`.gitea/scripts/status-sync-poll.ts`), NOT this skill — do NOT manually
`工单 API(见 TERMINOLOGY)update` it. When verification observes a CI state transition
@@ -1121,7 +640,7 @@ the issue. The `CI` row in `## 当前状态` is written by the `status-sync` pol
process-AC's progress note in place via `工单 API(见 TERMINOLOGY)update` (per the
`issue-checklist-sync` L1 rule § Automated sync — the poller owns the CI row;
the checklist annotation remains manual). Do NOT wait for Phase 5.6: a
transition left unsynced for tens of minutes is exactly the staleness [org-internal #1689]
transition left unsynced for tens of minutes is exactly the staleness [TT-1689]
targets. Skip if no source issue exists.
---
@@ -1213,181 +732,14 @@ To notify workflow completion, call the `signal_stage_done` tool.
---
<!-- section:dag-branch -->
## DAG branch (milestone + task modes)
## DAG branch (milestone + task modes) — see `reference/dag-branch.md`
> This branch OVERRIDES the Preconditions table, Phase 1 read inputs, Phase 5
> report index, Phase 5.5 ACCEPTED_RISK source, Phase 5.6 final sweep, and
> Phase 6.5 commit gate for a DAG-ticket-pipeline Epic. It applies when the
> routed Epic is DAG-shaped: a frozen DAG copy exists at `{epic-slug}/dag` and
> the DAG's `task_route.keep_gates` includes `verify`. Legacy iteration verify
> (the sections above) remains authoritative for non-DAG iterations. Spec:
> `dag-pipeline/spec-05` §5. commit status stays
> `pipeline/verify` in both modes.
> This branch (milestone mode / task mode / no-milestone Epic gate) lives in
> `reference/dag-branch.md`, read it on-demand when the Epic is DAG-shaped:
> frozen DAG at `{epic-slug}/dag` + `task_route.keep_gates` includes `verify`.
> Legacy iteration verify (the sections above) remains authoritative for
> non-DAG iterations. Spec: `dag-pipeline/spec-05` §5.
### Milestone mode (verify runs on a milestone M when `all_incoming_done_and_green`)
A milestone node (`type: milestone`) has no implementation work and no ticket;
its verify is the integration verification welded at a DAG convergence point
(`cross_session_in ≥ 2`). Trigger: the aggregator agent (main
session/Orchestrator) detects `all_incoming_done_and_green` — every fan-in
source node at its terminal state (task source `done` / milestone source
`green`) — and initiates this verify run; the milestone never self-triggers.
**Precondition replacements** (vs legacy iteration verify):
| Legacy verify precondition | Milestone-mode replacement |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| design-space review converged (`review status --stage design-space` success) | review-dag converged (`review status --stage review-dag` success) |
| iteration-plan review converged | REMOVED (DAG has no plan review; the single review-dag gate replaces it) |
| legacy iteration plan at `{slug}/04-plan-index` | DAG artifact + milestone fan-in slice (no `04-plan-*` page) |
| legacy NFR targets from `{slug}/03-design-06-non-functional-design` | REMOVED (product NFRs live in node AC, handled by per-task review-code) |
| upstream roadmap chunk done+verified | all milestone fan-in source nodes at terminal state (task source `done` / milestone source `green`, i.e. `all_incoming_done_and_green`) |
| code review converged (`pipeline/review-code` success) | ALL fan-in node tickets review-code PASS + PR merged (implied by `done`) |
| code review final report exists | REMOVED (no Epic-level final report; per-node review-code reports live on task tickets) |
| carried items from previous iteration closed | REMOVED (vacuous — no iteration) |
| `core/checklists/verification.md` accessible | KEEP (unconditional) |
| Acceptance criteria exist | from DAG-slice-generated DoD matrix (fan-in node AC + integration/contract-conformance/regression/NFR-coverage items) |
| `slug` matches the plan slug | N/A (output naming frozen to `{epic-slug}`) |
**Phase 1 read inputs** → the DAG slice: read the milestone's fan-in subgraph
(fan-in nodes + their cross-session edges + contracts) from the frozen DAG copy
`{epic-slug}/dag`, and generate the DoD matrix from it (integration items,
contract-conformance items, regression items, NFR-coverage items) instead of
reading `04-plan-*` pages.
**Output naming**: `{epic-slug}/05-verify-milestone-{M-id}` (distinct from
iteration verify `05-verify-iteration-{N}`).
**Phase 5 report index**: append a `VERIFY-M-{M-id}` row to the `## 工件索引`
comment with 重读 = `ON-DEMAND` (NOT `ARCHIVE`). The milestone verify does NOT
edit the comment directly — it reports the row-payload to the aggregator agent
(main session/Orchestrator), which is the SINGLE WRITER appending in place
(`工单评论 API(见 TERMINOLOGY)`). `ARCHIVE` is never written by milestone verify
(Epic close is the sole archive point).
**Phase 5.5 ACCEPTED_RISK source**: fan-in node task-ticket review-code reports
(located in each task ticket's review annotations / `## 当前状态` review row),
not an Epic-level final report. A missing Epic-level final report does NOT
trigger the "code-review final report does not exist" warn (that warn is
legacy-iteration-only). tech-debt / baseline-failure / flaky-test registration
proceeds as usual, sourced from the milestone DoD matrix's regression /
baseline-classification results.
**Phase 5.6 milestone mode** (restricted semantics — skip archive-at-close and
issue close):
1. **Checklist final sweep** only — object = each fan-in task ticket's row 6
(`verify里程碑`) + the milestone DoD matrix, NOT the Epic body checklist.
Verify each fan-in node's row 6 is checked or annotated
`_(Pending: M-{M-id} 未 green)_`. Do NOT flip a still-Pending row 6 — that
flip belongs to the aggregator agent (main session/Orchestrator), which
performs the node-ticket close action. Write the milestone DoD matrix final
sweep to `{epic-slug}/05-verify-milestone-{M-id}`.
2. **Do NOT fold/remove any Epic-body `## 当前状态` / `## DAG 状态` section** —
milestone verify is read-only on Epic body state.
3. **SKIP archive-at-close** (the `## 工件索引` archive banner + row-level
`ARCHIVE`) and **SKIP issue close** — both belong to the aggregator agent at
Epic close.
4. **SKIP Tier 1 run archive** (Phase 5.6 step 5, Tier 1 run archive sub-bullet) — the aggregator agent
performs it at Epic close.
**Milestone verify execution context (pinned)**: run the
integration/regression/contract-conformance/NFR suite on a checkout CONTAINING
all fan-in nodes' merged code — the aggregator agent syncs main (containing the
merged fan-in PRs) into the execution worktree, or builds a temp verify worktree
from main HEAD, before triggering; the verify report
`{epic-slug}/05-verify-milestone-{M-id}` records the actual execution commit
SHA.
**M→green status transition**: NOT done by milestone verify (read-only on Epic
body). The aggregator agent (main session/Orchestrator) reads the verify report
and sets the aggregator table M row 状态 = `green`, 重读 = `ON-DEMAND`, and
syncs the frozen DAG copy `{epic-slug}/dag` node `status` to `green`.
**Phase 6.5 milestone mode**: vacuous skip — a milestone has no implementation
commit. Commit existence is evidenced by the fan-in node verify PASS + PR
merged (already implied by `all_incoming_done_and_green`).
**Milestone verify FAIL recovery**: FAIL → M `status` = `blocked`. Fix
responsibility = the fan-in node owning the failing integration/regression item
(the last-done fan-in node's session) or the Epic-level session (DAG author) if
un-attributable. Re-run is triggered ONLY by a "fix-landed event" (fix commit
merged + CI green, or an explicit re-done report from the owning session) —
NOT status polling (done nodes are never reset; `all_incoming_done_and_green`
stays true after FAIL). M stays `blocked` until a fix event, then returns to
`pending` and verify re-runs. No review-dag re-run unless the fix changes a
cross-session contract or the node/edge set.
### Task mode (verify on a task node ticket, because `task_route.keep_gates` includes `verify`)
Each task ticket closes only after its own verify run, but legacy verify
preconditions do not hold in DAG mode — this node-level branch reuses the
`verify` skill with the following node-scoped semantics (so the `verify` gate is
not silently downgraded to CI-only).
**Verification object**: the node's own `acceptance_criteria` → the `test_id`
suite (`file-path::test-name` / `MANUAL` / `BENCH:`) all passing fresh + that
node's review-code PASS recorded + CI green. NOT milestone fan-in
integration/contract/regression items (those are milestone verify's). Spec
authority = the node in the frozen DAG copy `{epic-slug}/dag`.
**Precondition replacements** (task mode — mirror the milestone table, scoped to
the node):
| Legacy verify precondition | Task-mode replacement |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| design-space review converged | review-dag converged (this Epic already passed the single gate; do NOT re-run it) |
| iteration-plan review converged | REMOVED (DAG has no plan review) |
| legacy iteration plan at `{slug}/04-plan-index` | the node in the frozen DAG copy `{epic-slug}/dag` (no `04-plan-*` page) |
| legacy NFR targets from `{slug}/03-design-06-non-functional-design` | REMOVED (product NFRs live in node AC, handled by per-task review-code) |
| upstream roadmap chunk done+verified | this node's cross-session in-edge source nodes at terminal state (task source `done` / milestone source `green`) |
| code review converged (`pipeline/review-code` success) | this node's ticket review-code PASS + PR merged |
| code review final report exists | REMOVED (this node's review-code report lives on the task ticket) |
| carried items from previous iteration closed | REMOVED (vacuous — no iteration) |
| `core/checklists/verification.md` accessible | KEEP |
| Acceptance criteria exist | from node AC + `test_id` suite |
| `slug` matches the plan slug | N/A (Tier 1 run slug = `{epic-slug}-task-{node-id}` — the same node-task run namespace review-code runs in and this mode's archive bundle uses below; the Phase 2 precondition-gate cache read MUST use this slug. Only the wiki output naming stays Epic-scoped: `{epic-slug}/05-verify-task-{node-id}`) |
**Phase 1 read inputs** → the node's AC + `test_id` suite from the frozen DAG
copy `{epic-slug}/dag`, generating the DoD matrix.
**Output naming**: `{epic-slug}/05-verify-task-{node-id}`.
**Phase 5 report index**: append a `VERIFY-TASK-{node-id}` row, 重读 =
`ON-DEMAND` (NOT `ARCHIVE`), via the aggregator agent as single writer (task
verify does not edit the comment directly).
**Phase 5.5 task mode**: ACCEPTED_RISK source = this node's task-ticket
review-code report (no Epic-level final report → no "does not exist" warn).
tech-debt / baseline-failure / flaky-test registration proceeds as usual.
**Phase 5.6 task mode**: only checklist final sweep — object = this ticket's
5-row checklist (NOT row 6 `verify里程碑` — row 6 is flipped ONLY by the
aggregator agent). SKIP archive-at-close (`## 工件索引` row-level `ARCHIVE`) and
issue close (both belong to the aggregator agent). SKIP Tier 1 run archive — the
aggregator agent collects it cross-worktree at task-ticket close (archive to
`<runs-root>/archive/{epic-slug}-task-{node-id}.json` and commit to the
workflow branch). Do NOT fold Epic body status sections.
**Phase 6.5 task mode**: object = this node ticket's commit; semantics = "node
implementation commit exists". Baseline = the task worktree's creation-commit
SHA (`git log {creation-SHA}..HEAD`), NOT a generic base-branch (post-merge
there are no new commits vs base). The task worktree is retained until ticket
close (the aggregator agent removes it after collecting task-level Tier 1
archive); the task session must NOT remove its own worktree.
**commit status**: still `pipeline/verify` in both modes.
### No-milestone DAG Epic-level verify gate
When the routed Epic's DAG has NO milestone (no `cross_session_in ≥ 2`
convergence point), the Epic-level `keep_gates: verify` is satisfied
AGGREGATELY by all task-ticket verify runs (each with commit status
`pipeline/verify` success) — NO independent Epic-level verify run, NO
`{epic-slug}/05-verify-milestone-{M-id}` artifact, NO `VERIFY-M-{M-id}` index
row. The Epic close condition degenerates to "all task nodes `done`".
---
## References