288 lines
19 KiB
Markdown
288 lines
19 KiB
Markdown
> 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 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** — `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).
|