Files

288 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
# Tier 1 Local Run Layout — `<runs-root>/{slug}/`
Normative layout for **Tier 1 local structured artifacts**. Authoritative
boundary rule: `core/rules/two-tier-artifacts.md`. Schemas that validate
the structured files below live in `core/schemas/runs-*.schema.json`.
This document is the **foundation** (MVP slice, [org-internal #1968]). It defines the
container, the manifest, and the metadata. Stage-specific artifact schemas
(design detail dumps, plan items, review dimension findings, synthesis
prompts, draft contracts) are added incrementally as each skill migrates its
transient output here — the follow-up iteration ([org-internal #1988]) has landed: `review-artifact` /
`audit-process` raw findings and producer working drafts are now Tier 1.
([org-internal #3072] phase 3, 2026-08-21: the legacy producer stages whose drafts are listed
below were archived — their rows and artifact dirs are kept for reading
historical run bundles.) See the migrated stages table below.
## Kind/MVP mode ([org-internal #3061])
`Kind/MVP` tickets write **nothing** to `<runs-root>/` by design — no stage
dirs, no `index.json`, no manifest. The ticket body is the doc (`## 决策日志`
+ `## Debt Register`). Optionally a single free-form
`<runs-root>/{slug}/working-notes.md` may capture scratch that does not
belong in the body; it has no schema and is not indexed. On graduation
(relabel `Kind/Feature`) the DAG route initializes the standard layout from
scratch; the MVP body remains the REQMAP baseline (analyze-dag backfill).
## Why a local tier
Agent-internal handoff (subagents sharing one worktree) currently pays the
Gitea tax (HTTP latency, wiki 409 conflicts, auth, Gitea-health dependency)
even though the consumer is a sibling subagent in the same process tree. The
embryo already proved local files viable (`/tmp/octopus/synthesis_task_*.md`
for `prompt_file`, see `core/skills/_shared/review-pipeline-phases.md` §Phase B step 1 — synthesis prompt).
This layout formalizes that embryo into a discoverable, schema-validated,
archive-at-close tier — without touching Gitea's role as the system of record
for human-visible decisions.
## Directory tree
```
<runs-root>/
├── .gitignore # ignores active-run workspaces; tracks archive/ + self
├── archive/ # TRACKED — closed-run bundles land here
│ ├── .gitkeep
│ └── {slug}.json # one bundle per closed run (meta + index digest)
└── {active-slug}/ # GITIGNORED — one subdir per active run
├── meta.json # run metadata (runs-meta.schema.json)
├── index.json # artifact manifest (runs-index.schema.json)
├── working-notes.md # cross-stage narrative memory ([org-internal #2600], schema-less md)
├── workers/ # worker final-return reports ([org-internal #2847], schema-less md —
│ └── ... # see _shared/worker-report-persistence.md)
└── {stage}/ # per-stage transient artifacts (added by skills)
└── ... # json (structured) / md (prompt files)
```
### `{active-slug}`
- One subdir per active pipeline run, named by the run slug (the same `{slug}`
used by the Gitea wiki namespace and the workflow branch tail).
- **Gitignored** while active — subagents share the worktree, not the git
history, so active-run content stays local (no repo bloat, no merge noise).
- **Initialized at run start** by `bun <harness-package>/script/runs-init.ts
--slug {slug} --ticket N [--branch …] [--worktree …]` ([org-internal #3642]) — the
producing role / Orchestrator runs the script (hand-writing the files is the
legacy path): it creates the `{active-slug}/` workspace, writes `meta.json`
with `state: "active"` and the run's slug/branch/worktree/ticket (branch
omitted / worktree null for ad-hoc runs without a worktree), and an empty
`index.json` (`artifacts: []`), schema-validated before write and idempotent
on re-run. Kind/MVP and Kind/Documentation tickets are skipped automatically
(label check, § Kind/MVP mode below). `script/claim-provision.sh` chains this
after claim + worktree in one command. Subsequent producing roles append to
`index.json` as they emit artifacts.
- Removed by the Verifier's archive step at close (its content is bundled into
`archive/{slug}.json` first).
### `meta.json` — run metadata
Schema: `core/schemas/runs-meta.schema.json`. Fields:
| field | type | notes |
|---|---|---|
| `schema_version` | integer | const `1` |
| `slug` | string | run slug; matches branch tail + wiki namespace |
| `ticket` | object\|null | `{ owner, repo, number }` of the source issue, or null for ad-hoc runs |
| `branch` | string | workflow branch, e.g. `workflow/enhancement/1968-two-tier-artifacts`; omitted for ad-hoc runs without a branch |
| `worktree` | string\|null | absolute path of the worktree, or null for ad-hoc runs without a worktree |
| `parent_epic` | object\|null | `{ number }` of the parent Epic, if any |
| `state` | enum | `active` → `archived` (set at close) |
| `created_at` | string | RFC 3339 timestamp |
| `updated_at` | string | RFC 3339 timestamp |
| `closed_at` | string\|null | RFC 3339; null while active |
| `close_commit_sha` | string\|null | git SHA of the archive commit; null while active |
### `index.json` — artifact manifest
Schema: `core/schemas/runs-index.schema.json`. This is the **compact
recovery entry point**: after `compact` (which never deletes local files), an
agent re-reads `index.json` to recover the run's artifact set without scanning
the tree. Each entry:
| field | type | notes |
|---|---|---|
| `schema_version` | integer | const `1` |
| `slug` | string | run slug |
| `artifacts` | array | one entry per Tier 1 artifact in this run |
| `artifacts[].path` | string | path relative to `{active-slug}/` |
| `artifacts[].type` | enum | `meta`, `index`, `design`, `plan`, `review-findings`, `review-status`, `review-synthesis`, `synthesis-prompt`, `precondition-gate`, `working-notes`, `analysis`, `browser-evidence`, `other` (authoritative enum: `core/schemas/runs-index.schema.json`) |
| `artifacts[].schema` | string\|null | `$id` of the validating schema, or null for schema-less (e.g. prompt `.md`) |
| `artifacts[].tier` | const `1` | local tier marker (Tier 2 is never listed here) |
| `artifacts[].stage` | enum\|null | producing pipeline stage (`requirements`/`design`/`plan-iterations`/`implement`/`review-code`/`verify`/…), or null for meta/index |
| `artifacts[].produced_by` | enum\|null | `Producer`/`Reviewer`/`Verifier`/`Tool`/`Coordinator` (canonical role names — the `name` field of `core/skills/_shared/roles/*.yaml`), plus fine-grained sub-roles mapped via `ROLE_ALIASES` in `<harness-package>/src/config/role.ts`: Producer sub-roles `Analyst`/`Architect`/`Planner`/`Developer`/`Synthesizer`/`Orchestrator`/`Remediator`, and the Reviewer sub-role `Auditor` |
| `artifacts[].sha` | string\|null | content digest (sha256), or null if not yet computed |
| `artifacts[].tier2_ref` | string\|null | cross-link to the Tier 2 mirror (e.g. issue comment URL / wiki page) when one exists |
`index.json` is **parallel, not replacement**, for the Tier 2 `## 工件索引`
issue comment: the former is the agent's compact-recovery entry; the latter is
the human-visible traceability hub. They are kept in sync by the producing
role but serve different consumers.
### `working-notes.md` — cross-stage narrative memory ([org-internal #2600])
`index.json` is a MANIFEST: it records WHAT artifacts exist. It does not
carry WHY code was written that way, which paths were tried and abandoned, or
what the reviewer's feedback actually changed — the tacit context every next
stage currently re-derives by re-reading formal artifacts. `working-notes.md`
is that narrative memory, in-file, append-only:
- **Format** (schema-less markdown): each entry is
`## [{stage}] {role} @ {RFC 3339 ts}` followed by **≤30 lines** covering:
decisions made (and why), dead ends (do NOT retry these), reviewer feedback
that changed the work, and one-line hints for the next stage.
- **Append cadence** — one entry per unit of ownership, never per subagent
dispatch: Producer sub-roles append at **stage exit**; the Reviewer side
appends **via the Synthesizer at review convergence** (one entry per
review, not per dimension — 9 dimension dispatches = 1 distilled entry);
the Verifier appends at **verify exit**; Tool agents append only when the
task produced a load-bearing finding (e.g. image evidence that changed a
decision).
- **Re-read rule**: the next stage reads `working-notes.md` FIRST — before
the formal artifacts — and may then skip full re-reads that the notes
already cover. Role yamls carry this line in `compact.preserve`
(`core/skills/_shared/roles/*.yaml`).
- **index.json row** (registered once by the first appending role):
`type: working-notes`, `schema: null` (markdown), `stage: null`
(cross-stage), `produced_by: null` (multi-role), `tier2_ref: null`.
- **Tier judgment**: consumed within one worktree/run ✓, never a human gate
decision ✓, never referenced cross-worktree ✓, archivable at close ✓ — all
four Tier 1 conditions hold (`core/rules/two-tier-artifacts.md` 判据).
### `workers/` — worker final-return reports ([org-internal #2847])
Every worker sub-agent dispatch (`agent: worker` roles — Producer
sub-roles, Verifier, Synthesizer) persists its **final return report**
verbatim to `workers/{chunk-id}-worker-{seq}.md` as its LAST action before
returning (lightweight fallback when no run workspace exists:
`/tmp/octopus/{chunk-id}-worker-{seq}.md`). The persisted copy is the report
of record — when the task completion notification is lost, the orchestrating
session recovers the worker's conclusion by reading the newest
`workers/{chunk-id}-worker-*.md` match. Skills that own a canonical stage
path for the dispatch (e.g. the review-code Synthesizer's
`reviews/{stage}/round{N}/synthesis-return.md`) use it instead of the
generic name. `index.json` row: `type: "other"`, `schema: null`, `stage` =
producing stage, `produced_by` = role. Full convention:
`core/skills/_shared/worker-report-persistence.md`.
### `browser/` — browser evidence packs ([org-internal #4497] N-03)
A browser-debug session writes one evidence pack per session under
`browser/{session-id}/` (contract `browser-evidence-4486/shared/pack-manifest-v1`,
frozen 2026-09-10 — `<harness-package>/src/browser/evidence-pack.ts` is the writer):
```
browser/{session-id}/
├── manifest.json # PackManifest: env probe (shared/env-probe-v1),
│ # replay metrics {attempted, succeeded, failures[]},
│ # artifact rows {kind, path, sha256_16}
├── trace/ # Playwright trace zips (recorded sessions)
├── screenshots/*.png # copies — the .playwright-mcp/ layer is additive-not-replaced
├── console-errors.jsonl # one sanitized entry per line
└── network-failures.json # sanitized failure summary
```
Write boundary invariants (the graph's single mandatory sanitize point,
`shared/sanitize-api-v1`): every textual capture passes `sanitizeForEvidence`
before hitting the disk; entries whose URL fails the navigation allowlist are
excluded from the pack; the write is non-blocking (the writer returns a handle
before the first byte lands). `index.json` row: `type: "browser-evidence"`,
`path: "browser/{session-id}/manifest.json"`, `stage` = producing session's
stage (`implement`/`verify`), `produced_by: "Tool"`.
### `{stage}/` — per-stage transient artifacts
Added incrementally by each skill as it migrates its transient output to Tier 1.
Conventions established by this foundation:
- Structured data → `.json` (schema-validated).
- Prompt files passed to subagents via `prompt_file` → `.md`.
- File naming mirrors the Gitea wiki namespace so a Tier 1 path and its (former
/ future) Tier 2 mirror are visually paired, e.g. on-disk
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-COR.json`. The
corresponding `index.json` `artifacts[].path` value strips the `{slug}/`
prefix (it is relative to `{active-slug}/`): `reviews/{stage}/round{N}/findings-COR.json`.
- A stage that has not yet migrated continues to write to Gitea (Tier 2) per its
existing skill body — the two tiers coexist during the migration.
**Migrated stages ([org-internal #1988]):**
| Stage | Skill | Tier 1 artifacts | Schema | Notes |
|---|---|---|---|---|
| `review-code` | review-code | `reviews/code/round{N}/findings-{DIM}.json` (raw dimension findings), `reviews/code/round{N}/task-synthesizer.md` (synthesis prompt) | `reviewer-output.schema.json` (findings) | synthesis comment + commit status stay Tier 2 |
| `review-roadmap` | review | `reviews/roadmap/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | legacy target archived ([org-internal #3072] phase 3) — row kept for historical bundles |
| `review-design-space` | review | `reviews/design-space/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | legacy target archived ([org-internal #3072] phase 3) — row kept for historical bundles |
| `review-iteration-plan` | review | `reviews/plan/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | legacy target archived ([org-internal #3072] phase 3) — row kept for historical bundles |
| `review-dag` | review | `reviews/review-dag/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | synthesis comment + commit status stay Tier 2 |
| `audit-process` | review | `reviews/audit-process/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | synthesis wiki page + commit status stay Tier 2 |
| `requirements` (drafts) | requirements-elicitation | `requirements/` (schema-less `.md`/`.json`) | — | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
| `design` (drafts) | design | `design/` (schema-less or `design-detail.schema.json`) | `design-detail.schema.json` (moved to `<instance-root>/archive/schemas/` [org-internal #3072] phase 3) | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
| `plan-iterations` (drafts) | plan-iterations | `plan-iterations/` (schema-less or `plan-items.schema.json`) | `plan-items.schema.json` (moved to `<instance-root>/archive/schemas/` [org-internal #3072] phase 3) | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
| `roadmap` (draft contracts) | roadmap | `roadmap/contracts/{name}.json` | `draft-contract.schema.json` (moved to `<instance-root>/archive/schemas/` [org-internal #3072] phase 3) | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
Every review/audit stage additionally maintains a per-stage lifecycle file
`reviews/{stage}/status.json` (schema `review-status.schema.json`) — live
stages: `review-code`, `review-dag`, `audit-process`; legacy bundle stages
(`review-roadmap`, `review-design-space`, `review-iteration-plan`) maintain
the same file in historical bundles only. It is initialized by the
Orchestrator before round 1, appended by the Synthesizer each round, and
finalized with `converged` at Phase E. The Orchestrator appends an
`index.json` row for it (`type` = `review-status`, `stage` = `{REVIEW_TYPE}`,
`produced_by` = `Orchestrator`).
Stages NOT yet migrated (none — all target stages have been migrated or have opt-in Tier 1 affordances): reviser task prompts (wiki pages) remain Tier 2 in this slice; synthesis prompts for `review-artifact` / `audit-process` targets follow the shared Phase B mechanics (Tier 1 for all `tier1-local` skills).
## Archive-at-close
Closed by the **Verifier** at verify PASS (or by the Orchestrator for flows
without verify). The archive step is the ONLY point at which active-run content
enters git history:
1. Set `meta.json.state = "archived"`, record `closed_at` and `close_commit_sha`.
2. Write `archive/{slug}.json` — a compact bundle carrying `meta` + the
`index.json` manifest (the durable trace; bulk artifacts are summarized by
digest, not copied wholesale, to keep the archive small). One exception
([org-internal #2600]): when `working-notes.md` exists, its final content is inlined as
the bundle's top-level `working_notes` field — the narrative memory is the
single artifact whose content (not just digest) rides in the bundle,
feeding later retro probes and the notes-injection degradation path
([org-internal #2601]). The bundle is validated by `runs-bundle.schema.json`.
Offline review verifiability (TD/TRC-F006, [org-internal #2688]): for each review stage
the run executed, also embed a top-level `review_history` entry in the
bundle — `{stage, rounds, final_verdict, per_round[]}` (per-round:
round/overall_verdict/blockers/majors/minors) — derived from that stage's
live `reviews/{stage}/status.json` `history[]`. Closed-run probes can then
reconstruct round counts and verdicts from the bundle alone, without the
removed workspace or artifact-path reconstruction ([org-internal #2591]).
3. `git add archive/{slug}.json` and commit on the workflow branch (rides into
main via the `--no-ff` merge).
4. Remove the `{active-slug}/` workspace from the worktree (its durable record
now lives in the archive bundle).
Steps 14 collapse into ONE command ([org-internal #3642]): `bash script/archive-run.sh
{slug} [--pr N]` — run from the session worktree, it resolves the PR by branch
head, generates + validates the bundle via `gen-run-bundle.ts` (recording
`closed_at` / `close_commit_sha` and the `--branch` workflow branch), commits
it with a hook-conforming `[{chunk}][iter-N] chore(runs): …` message, removes
the workspace, and tears the worktree down unless invoked from inside it. The
`## 工件索引` archived remark (issue-checklist-sync.md § 归档动作) remains an
agent action — content judgment, not scripted.
Tier 2 (Gitea) remains the **system of record** for decisions (verdicts, issue
status, PRs, frozen contracts). The Tier 1 git archive is the **reproducibility
trace** — enough to reconstruct what a run produced, without re-paying the
Gitea tax.
## Relationship to existing mechanisms
- **`/tmp/octopus/` embryo** — `review-code` ([org-internal #1988] MVP) now writes its
synthesis prompt under `<runs-root>/{slug}/reviews/code/` instead of
`/tmp/octopus/synthesis_task_*`. Other skills still using the `/tmp/octopus/`
embryo coexist until their own migration iteration.
- **`## 工件索引` issue comment (Tier 2)** — unchanged; `index.json` (Tier 1) is
its agent-side parallel, not a replacement.
- **compact** — `compact` never deletes local files, so `index.json` survives
compaction and is the designated re-read entry for a compacted run.
- **worktree discipline** — all subagents of one workflow share its worktree, so
they share `<runs-root>/{slug}/` directly (no cross-worktree coordination
needed for Tier 1; that coordination lives in Tier 2).