2026-09-15 08:41:51 +08:00
> 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` .
2026-09-25 00:09:37 +08:00
This document is the **foundation** (MVP slice, [TT-1968]). It defines the
2026-09-15 08:41:51 +08:00
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
2026-09-25 00:09:37 +08:00
transient output here — the follow-up iteration ([TT-1988]) has landed: `review-artifact` /
2026-09-15 08:41:51 +08:00
`audit-process` raw findings and producer working drafts are now Tier 1.
2026-09-25 00:09:37 +08:00
([TT-3072] phase 3, 2026-08-21: the legacy producer stages whose drafts are listed
2026-09-15 08:41:51 +08:00
below were archived — their rows and artifact dirs are kept for reading
historical run bundles.) See the migrated stages table below.
2026-09-25 00:09:37 +08:00
## Kind/MVP mode ([TT-3061])
2026-09-15 08:41:51 +08:00
`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)
2026-09-25 00:09:37 +08:00
├── working-notes.md # cross-stage narrative memory ([TT-2600], schema-less md)
├── workers/ # worker final-return reports ([TT-2847], schema-less md —
2026-09-15 08:41:51 +08:00
│ └── ... # 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
2026-09-25 00:09:37 +08:00
--slug {slug} --ticket N [--branch …] [--worktree …]` ([TT-3642]) — the
2026-09-15 08:41:51 +08:00
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.
2026-09-25 00:09:37 +08:00
### ` working-notes.md` — cross-stage narrative memory ([TT-2600])
2026-09-15 08:41:51 +08:00
` 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` 判据).
2026-09-25 00:09:37 +08:00
### ` workers/` — worker final-return reports ([TT-2847])
2026-09-15 08:41:51 +08:00
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`.
2026-09-25 00:09:37 +08:00
### ` browser/` — browser evidence packs ([TT-4497] N-03)
2026-09-15 08:41:51 +08:00
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.
2026-09-25 00:09:37 +08:00
**Migrated stages ([TT-1988]):**
2026-09-15 08:41:51 +08:00
| 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 |
2026-09-25 00:09:37 +08:00
| ` review-roadmap` | review | ` reviews/roadmap/round{N}/findings-{DIM}.json` | ` reviewer-output.schema.json` | legacy target archived ([TT-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 ([TT-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 ([TT-3072] phase 3) — row kept for historical bundles |
2026-09-15 08:41:51 +08:00
| ` 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 |
2026-09-25 00:09:37 +08:00
| ` requirements` (drafts) | requirements-elicitation | ` requirements/` (schema-less ` .md`/` .json`) | — | legacy skill archived ([TT-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/` [TT-3072] phase 3) | legacy skill archived ([TT-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/` [TT-3072] phase 3) | legacy skill archived ([TT-3072] phase 3) — historical bundles only |
| ` roadmap` (draft contracts) | roadmap | ` roadmap/contracts/{name}.json` | ` draft-contract.schema.json` (moved to ` <instance-root>/archive/schemas/` [TT-3072] phase 3) | legacy skill archived ([TT-3072] phase 3) — historical bundles only |
2026-09-15 08:41:51 +08:00
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
2026-09-25 00:09:37 +08:00
([TT-2600]): when ` working-notes.md` exists, its final content is inlined as
2026-09-15 08:41:51 +08:00
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
2026-09-25 00:09:37 +08:00
([TT-2601]). The bundle is validated by ` runs-bundle.schema.json`.
Offline review verifiability (TD/TRC-F006, [TT-2688]): for each review stage
2026-09-15 08:41:51 +08:00
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
2026-09-25 00:09:37 +08:00
removed workspace or artifact-path reconstruction ([TT-2591]).
2026-09-15 08:41:51 +08:00
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).
2026-09-25 00:09:37 +08:00
Steps 1– 4 collapse into ONE command ([TT-3642]): ` bash script/archive-run.sh
2026-09-15 08:41:51 +08:00
{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
2026-09-25 00:09:37 +08:00
- **` /tmp/octopus/` embryo** — ` review-code` ([TT-1988] MVP) now writes its
2026-09-15 08:41:51 +08:00
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).