19 KiB
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.mdmay capture scratch that does not belong in the body; it has no schema and is not indexed. On graduation (relabelKind/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, writesmeta.jsonwithstate: "active"and the run's slug/branch/worktree/ticket (branch omitted / worktree null for ad-hoc runs without a worktree), and an emptyindex.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.shchains this after claim + worktree in one command. Subsequent producing roles append toindex.jsonas they emit artifacts. - Removed by the Verifier's archive step at close (its content is bundled into
archive/{slug}.jsonfirst).
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.mdFIRST — before the formal artifacts — and may then skip full re-reads that the notes already cover. Role yamls carry this line incompact.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 correspondingindex.jsonartifacts[].pathvalue 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:
- Set
meta.json.state = "archived", recordclosed_atandclose_commit_sha. - Write
archive/{slug}.json— a compact bundle carryingmeta+ theindex.jsonmanifest (the durable trace; bulk artifacts are summarized by digest, not copied wholesale, to keep the archive small). One exception ([org-internal #2600]): whenworking-notes.mdexists, its final content is inlined as the bundle's top-levelworking_notesfield — 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 byruns-bundle.schema.json. Offline review verifiability (TD/TRC-F006, [org-internal #2688]): for each review stage the run executed, also embed a top-levelreview_historyentry in the bundle —{stage, rounds, final_verdict, per_round[]}(per-round: round/overall_verdict/blockers/majors/minors) — derived from that stage's livereviews/{stage}/status.jsonhistory[]. 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]). git add archive/{slug}.jsonand commit on the workflow branch (rides into main via the--no-ffmerge).- Remove the
{active-slug}/workspace from the worktree (its durable record now lives in the archive bundle).
Steps 1–4 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 —
compactnever deletes local files, soindex.jsonsurvives 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).