@agentsfleet/orly 0.10.5 → 0.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,193 @@
1
+ import { existsSync } from "node:fs";
2
+ import { isAbsolute, join, normalize, resolve } from "node:path";
3
+
4
+ type MarkdownElement = { type?: unknown; props: { children?: MarkdownNode[] } };
5
+ type MarkdownNode = string | MarkdownElement;
6
+ type Section = { title: string; nodes: MarkdownNode[] };
7
+ type Commands = Record<string, string[][]>;
8
+
9
+ const CONFIG_PATH = ".oracle/orly.json";
10
+ const SPEC_LINE_LIMIT = 320;
11
+ const BACKTICK = "`";
12
+ const EMPTY = "";
13
+ const PIPE_OUTPUT = "pipe";
14
+ const TABLE_ROW = "tr";
15
+ const TEST_HEADING = /^Test Specification/;
16
+ const RUBRIC_HEADING = /^Acceptance (Rubric|Criteria)/;
17
+ const REQUIRED = [
18
+ /^Overview/, /^PR Intent/, /^Implementing agent/, /^Files Changed/, /^Applicable Rules/,
19
+ /^Applicable Gates/, /^(Prior-Art|Reference Implementation)/, /^Sections/,
20
+ /^Interfaces/, /^Failure Modes/, /^Invariants/, /^Metrics.*Observability/,
21
+ TEST_HEADING, RUBRIC_HEADING, /^Dead Code Sweep/, /^Out of Scope/,
22
+ /^Product Clarity/, /^(Decomposition|Alternatives)/, /^Discovery/,
23
+ ];
24
+ const RESIDUE = /<!--\s*tpl:|\{\{fill:|\{unresolved_[^}]+\}|path\/to\/file\.ext|path\/to\/spec_or_doc\.md|test_<short_name>|\{one-line reason\}|\{Slice title\}|\{smallest verifiable behaviour\}|\{one-line behavioural claim\}/;
25
+ const STAGED_MODES = new Set(["--staged", "staged"]);
26
+ const EXPECTED_COLUMN = "Expected";
27
+
28
+ function sectionsOf(text: string): Section[] {
29
+ const sections: Section[] = [];
30
+ const document = Bun.markdown.react(text) as MarkdownElement;
31
+ for (const node of children(document)) {
32
+ if (!isString(node) && node.type === "h2") sections.push({ title: textOf(node), nodes: [] });
33
+ else if (sections.length) sections[sections.length - 1]!.nodes.push(node);
34
+ }
35
+ return sections;
36
+ }
37
+
38
+ function section(sections: Section[], heading: RegExp): MarkdownNode[] {
39
+ return sections.find((entry) => heading.test(entry.title))?.nodes ?? [];
40
+ }
41
+
42
+ function children(node: MarkdownNode): MarkdownNode[] {
43
+ return isString(node) ? [] : node.props.children ?? [];
44
+ }
45
+
46
+ function elements(nodes: MarkdownNode[], type: string): MarkdownElement[] {
47
+ return nodes.filter((node): node is MarkdownElement => !isString(node) && node.type === type);
48
+ }
49
+
50
+ function textOf(node: MarkdownNode): string {
51
+ if (isString(node)) return node;
52
+ const text = children(node).map(textOf).join(EMPTY);
53
+ return node.type === "code" ? `${BACKTICK}${text}${BACKTICK}` : text;
54
+ }
55
+
56
+ function table(nodes: MarkdownNode[]): { columns: string[]; rows: string[][] } {
57
+ const groups = elements(nodes, "table").flatMap(children);
58
+ const headers = elements(groups, "thead").flatMap(children);
59
+ const bodies = elements(groups, "tbody").flatMap(children);
60
+ return {
61
+ columns: elements(headers, TABLE_ROW).flatMap(children).map((node) => textOf(node).trim()),
62
+ rows: elements(bodies, TABLE_ROW).map((row) => children(row).map((node) => textOf(node).trim())),
63
+ };
64
+ }
65
+
66
+ function testMappings(sections: Section[]): string[] {
67
+ const errors: string[] = [];
68
+ const items = elements(section(sections, /^Sections/), "ul").flatMap(children).map(textOf);
69
+ const declared = items.filter((item) => item.startsWith("Dimension "));
70
+ const dimensions = declared.flatMap((item) => [...item.matchAll(/^Dimension (\d+\.\d+)\b[^\n]*?→ Test `([^`]+)`/g)]);
71
+ if (!dimensions.length || dimensions.length !== declared.length) errors.push("every Dimension must name its proof with → Test `name`");
72
+ const ids = dimensions.map((match) => match[1]);
73
+ if (new Set(ids).size !== ids.length) errors.push("Dimension identifiers must be unique");
74
+ const tests = table(section(sections, TEST_HEADING));
75
+ const tier = tests.columns.indexOf("Tier");
76
+ const name = tests.columns.indexOf("Test");
77
+ const assertion = tests.columns.findIndex((column) => column.startsWith("Asserts"));
78
+ const id = tests.columns.indexOf("Dimension");
79
+ if ([tier, name, assertion, id].some((index) => index < 0)) return [...errors, "Test Specification needs Dimension, Tier, Test, and Asserts columns"];
80
+ for (const dimension of dimensions) {
81
+ if (!tests.rows.some((row) => row[id] === dimension[1] && row[name]?.replaceAll(BACKTICK, EMPTY) === dimension[2] && row[tier] && row[assertion])) {
82
+ errors.push(`Dimension ${dimension[1]} has no matching tiered test and assertion`);
83
+ }
84
+ }
85
+ for (const row of tests.rows) {
86
+ if (row[id] && !ids.includes(row[id])) errors.push(`test row references unknown Dimension ${row[id]}`);
87
+ }
88
+ return errors;
89
+ }
90
+
91
+ function readFirst(sections: Section[], exists: (path: string) => boolean): string[] {
92
+ const items = elements(section(sections, /^Implementing agent/), "ol").flatMap(children).map(textOf);
93
+ const references = items.flatMap((item) => [...item.matchAll(/^(?:`([^`]+)`|(https?:\/\/\S+))/g)]);
94
+ if (!references.length) return ["read-first section needs existing repository paths or URLs"];
95
+ const errors: string[] = [];
96
+ for (const reference of references) {
97
+ const path = reference[1] ?? reference[2] ?? EMPTY;
98
+ if (/^https?:\/\//.test(path)) continue;
99
+ const normalized = normalize(path.split("#")[0] ?? EMPTY);
100
+ if (isAbsolute(normalized) || normalized.startsWith("..") || normalized.includes("{")) {
101
+ errors.push(`read-first pointer must name a repository file: ${path}`);
102
+ } else if (!exists(normalized)) {
103
+ errors.push(`read-first pointer does not resolve: ${path}`);
104
+ }
105
+ }
106
+ return errors;
107
+ }
108
+
109
+ function git(argv: string[], root: string): { ok: boolean; text: string } {
110
+ const result = Bun.spawnSync(["git", ...argv], { cwd: root, stdout: PIPE_OUTPUT, stderr: PIPE_OUTPUT });
111
+ return { ok: result.exitCode === 0, text: result.stdout.toString() };
112
+ }
113
+
114
+ async function commands(staged: boolean, root: string): Promise<Commands> {
115
+ const text = staged ? git(["show", `:${CONFIG_PATH}`], root) : { ok: true, text: await Bun.file(join(root, CONFIG_PATH)).text() };
116
+ if (!text.ok) throw new Error(`cannot read ${CONFIG_PATH} from the index`);
117
+ const config: unknown = JSON.parse(text.text);
118
+ if (!object(config) || !object(config.commands)) throw new Error(`${CONFIG_PATH} needs declared commands`);
119
+ const result: Commands = {};
120
+ for (const [name, invocations] of Object.entries(config.commands)) {
121
+ if (!Array.isArray(invocations) || !invocations.length) throw new Error(`empty command group: ${name}`);
122
+ result[name] = invocations.map((argv: unknown) => {
123
+ if (!Array.isArray(argv) || !argv.length || !argv.every(isString)) throw new Error(`invalid arguments for ${name}`);
124
+ return argv;
125
+ });
126
+ }
127
+ if (!Object.keys(result).length) throw new Error(`${CONFIG_PATH} needs declared commands`);
128
+ return result;
129
+ }
130
+
131
+ function object(value: unknown): value is Record<string, unknown> {
132
+ return typeof value === "object" && value !== null && !Array.isArray(value);
133
+ }
134
+
135
+ function isString(value: unknown): value is string {
136
+ return typeof value === "string";
137
+ }
138
+
139
+ function rubricCommands(sections: Section[], declared: Commands): string[] {
140
+ const rubric = table(section(sections, RUBRIC_HEADING));
141
+ const verify = rubric.columns.findIndex((column) => column.startsWith("Verify"));
142
+ const expected = rubric.columns.indexOf(EXPECTED_COLUMN);
143
+ if (verify < 0 || expected < 0) return ["Acceptance Rubric needs Verify and Expected columns"];
144
+ const errors: string[] = [];
145
+ for (const [name, invocations] of Object.entries(declared)) {
146
+ if (name !== "conform" && !name.startsWith("verify.")) continue;
147
+ for (const argv of invocations) {
148
+ const command = argv.join(" ");
149
+ if (!rubric.rows.some((row) => row[verify] === `${BACKTICK}${command}${BACKTICK}` && row[expected])) {
150
+ errors.push(`rubric misses declared command verbatim: ${command}`);
151
+ }
152
+ }
153
+ }
154
+ return errors;
155
+ }
156
+
157
+ export function validateSpec(text: string, declared: Commands, exists: (path: string) => boolean): string[] {
158
+ const sections = sectionsOf(text);
159
+ const errors: string[] = [];
160
+ const count = text.trimEnd().split(/\r?\n/).length;
161
+ if (count > SPEC_LINE_LIMIT) errors.push(`spec has ${count} lines; cap ${SPEC_LINE_LIMIT}`);
162
+ for (const heading of REQUIRED) {
163
+ if (!section(sections, heading).map(textOf).join(EMPTY).trim()) errors.push(`missing or empty required section: ${heading.source}`);
164
+ }
165
+ if (RESIDUE.test(text)) errors.push("unfilled template placeholder or authoring guidance");
166
+ errors.push(...readFirst(sections, exists), ...testMappings(sections), ...rubricCommands(sections, declared));
167
+ return errors;
168
+ }
169
+
170
+ export async function auditSpec(args: string[], root: string): Promise<string[]> {
171
+ const [spec, path, mode] = args;
172
+ if (!spec || !path || !mode) throw new Error("expected spec path, content path, and audit mode");
173
+ const staged = STAGED_MODES.has(mode);
174
+ const exists = (reference: string) => staged ? git(["cat-file", "-e", `:${reference}`], root).ok : existsSync(join(root, reference));
175
+ return validateSpec(await Bun.file(resolve(root, path)).text(), await commands(staged, root), exists);
176
+ }
177
+
178
+ if (import.meta.main) {
179
+ try {
180
+ const errors = await auditSpec(Bun.argv.slice(2), process.cwd());
181
+ for (const error of errors) {
182
+ // logging: audit findings are the command-line interface.
183
+ console.error(`FAIL: ${Bun.argv[2]} — ${error}`);
184
+ }
185
+ if (errors.length) process.exitCode = 1;
186
+ // logging: report structural checks without claiming semantic readiness.
187
+ else console.log(`OK: ${Bun.argv[2]} — sections, references, test mappings, and declared commands checked`);
188
+ } catch (error) {
189
+ // logging: unavailable audit inputs must fail, never silently skip checks.
190
+ console.error(`FAIL: spec readiness check — ${error instanceof Error ? error.message : String(error)}`);
191
+ process.exitCode = 1;
192
+ }
193
+ }
@@ -173,7 +173,7 @@ Guards fire pre-hoc regardless of lifecycle stage. Override: `<GATE>: SKIPPED pe
173
173
  | Begin implementation OR branch carries spec changes in `pending/` | CHORE(open): `pending/`→`active/`, `Status: IN_PROGRESS` + `Branch:`, create worktree, commit on feature branch. **No code until 4 steps committed.** |
174
174
  | Every commit during implementation | Mark completed Dimensions/Sections `DONE` same commit as the code. |
175
175
  | All work complete, before PR | CHORE(close). |
176
- | Branch with spec in `active/` after any COMMIT | CHORE(close) is mandatory next action. |
176
+ | Final implementation commit, or explicit request to park a spec | CHORE(close) is mandatory next action; Section commits continue the stream. |
177
177
  <!-- oracle-packs:end -->
178
178
 
179
179
  ---
@@ -188,14 +188,14 @@ Non-trivial (full lifecycle) if it: touches >1 file · new abstraction · data m
188
188
 
189
189
  **Anchor invariant — `orly gate` proves the boundary.** No PR opens unless every `orly gate pr` criterion is green or carries an `Orly-Override` trailer the user recorded with a reason.
190
190
 
191
- **One gate per cadence, each tier run once.** `work` = the declared `conform` command, no git state (a commit hook's tree is dirty by construction). `verify` = spec dimensions, docs language, the fast `verify.*` set. `pr` = branch shape, clean tree, pushed branch, every spec criterion, the slow suites. `orly gate pr` is the command CHORE(close) runs; `git.pushed` proves HEAD is exactly what the pre-push `orly gate verify` already graded. A bare `orly gate` chains all three, for a branch whose earlier gates never ran. Stage map: `dispatch/lifecycle.md`. Both stop at the first red group and only read; green unlocks CHORE(close) but never performs it. No spec → spec criteria skip, quality gates still run. Slow suites (`verify.integration`, `verify.memory`) run only when the branch carries code. A user-surface change without a docs change is red until docs land or an override says why not.
191
+ **Gate ownership.** `work` runs the declared `conform` command at commit. `verify` runs documentation checks and non-test `verify.*` commands at push; it permits unfinished Sections. `pr` checks branch shape, exact upstream revision, spec completeness, and every declared `verify.*` command itself. `orly gate pr` is the command CHORE(close) runs; a push is never proof that tests ran. Unit and integration baselines are measured before the Pull Request against the recorded comparison revision. Full unit and integration suites run at the PR boundary, not at opening or every push. Integration and memory suites skip only on branches with no code. The authoritative sequence is `dispatch/lifecycle.md`; no spec means only spec criteria skip. A user-surface change without a docs change is red until docs land or an override says why not.
192
192
 
193
193
  **LAND (after merge, or when the user confirms it merged):** pull the default branch, prune the merged worktree + branch, `make down` where defined.
194
194
 
195
195
  <!-- oracle-packs:start workflow.specifications -->
196
196
  ### CHORE (open)
197
197
 
198
- Spec `pending/`→`active/`; `Status: IN_PROGRESS`; `Branch:` set; **`Test Baseline:` recorded** (declared `verify.*` counts → `**Test Baseline:** unit=<N> integration=<M>`; VERIFY's Test Delta compares against it); committed. Worktree created, CWD inside (verify `pwd` + `git worktree list`). No code until the 4 steps are committed.
198
+ Spec `pending/`→`active/`; `Status: IN_PROGRESS`; `Branch:` set; **`Test Baseline:` declared pending; `Baseline revision:` records the full comparison commit** (unit and integration counts plus evidence are due before the Pull Request); committed. Worktree created, CWD inside (verify `pwd` + `git worktree list`), carrying the base checkout's uncommitted work in with it. No code until the 4 steps are committed.
199
199
  <!-- oracle-packs:end -->
200
200
 
201
201
  ### PLAN
@@ -206,7 +206,7 @@ Required: one-paragraph goal · explicit assumptions · file/task impact list ·
206
206
 
207
207
  Read what the selected dispatch names — **sectioned, not whole-file**: scan headers (`grep -nE "^##+ "`), read the sections the diff shape touches, cite them.
208
208
  <!-- oracle-packs:start product.agentsfleet -->
209
- The full `agentsfleet` trigger map lives in `docs/EXECUTE_DOC_READS.md`. At EXECUTE start read the **Rule-code gloss legend** of `docs/greptile-learnings/RULES.md` plus the sections the spec's "Applicable Rules" names — never the full file. On sub-task shape change, section-scan (`grep -n "^## "`) and re-read only the sections the new shape touches. DOC READ GATE emits one `📖 DOC READ: <path>` proof-line **per triggered document per turn** before the turn's first triggering edit, citing the applied section or a cited skip; auto-mode and prior-turn reads never excuse it. **Record it, don't just claim it:** `bash audits/doc-read.sh log <path>` — any runtime; Claude Code's Read hook does it for you; pre-commit compares the record to the staged diff.
209
+ The full `agentsfleet` trigger map lives in `docs/EXECUTE_DOC_READS.md`. At EXECUTE start read the **Rule-code gloss legend** of `docs/greptile-learnings/RULES.md` plus the sections the spec's "Applicable Rules" names — never the full file. On sub-task shape change, section-scan (`grep -n "^## "`) and re-read only the sections the new shape touches. DOC READ GATE emits one `📖 DOC READ: <path>` proof-line **per triggered document per turn** before the turn's first triggering edit, citing the applied section or a cited skip; auto-mode and prior-turn reads never excuse it. **Record it, don't just claim it:** `bash audits/doc-read.sh log <path> "<section applied>"` — any runtime; Claude Code's Read hook does it for you; pre-commit compares the record to the staged diff. **Log each page as you read it, and cite the section**: a loop writes the same rows, and once did — nine façades in one second, `check` green, violations from a section nobody opened. `check` counts citations and names any bulk second.
210
210
  <!-- oracle-packs:end -->
211
211
 
212
212
  Edit only approved scope; no opportunistic refactors. Stay in active worktree. Cross-repo writes to `~/Projects/docs/` need explicit per-session ask.
@@ -222,9 +222,9 @@ In `agentsfleet` this stage is `make harness-verify`; its output block and end-o
222
222
 
223
223
  ### VERIFY
224
224
 
225
- **Two cadences, one boundary.** Inside a Section, run CONFORM plus the declared lane covering the surface the Section touched — enough to prove a Section, reported as a Section claim. The repository's full declared `verify.*` set runs ONCE, at the milestone boundary, before the PR. A package-scoped command never replaces a listed one at either cadence, and a Section lane never satisfies a "tests pass" claim about the repository.
225
+ **Section proof and final verification.** Follow `dispatch/lifecycle.md` for command timing. Section checks prove the changed Dimensions; the final gate runs the full declared verification set. A package-scoped command never replaces a declared boundary command, and a Section result never satisfies a repository-wide "tests pass" claim.
226
226
  <!-- oracle-packs:start product.agentsfleet -->
227
- The `agentsfleet` output block and both cadences live in `docs/VERIFY_TIERS.md`. **FIRST: `/orly-write-unit-test`** audits diff coverage. **LAST: the Test Delta row** compares against the CHORE(open) baseline; zero or negative unit growth on a code-adding diff needs justification or a return to EXECUTE. Paste memory-leak evidence into PR Session Notes or cite its Continuous Integration (CI) URL. After refactors, list newly dead code before removing it.
227
+ The `agentsfleet` output block and both cadences live in `docs/VERIFY_TIERS.md`. **FIRST: `/orly-write-unit-test`** audits diff coverage. **LAST: the Test Delta row** compares against the recorded comparison revision, measured before the Pull Request; zero or negative unit growth on a code-adding diff needs justification or a return to EXECUTE. Paste memory-leak evidence into PR Session Notes or cite its Continuous Integration (CI) URL. After refactors, list newly dead code before removing it.
228
228
  <!-- oracle-packs:end -->
229
229
 
230
230
  ### REVIEW
@@ -12,55 +12,108 @@ are entering, not the whole file.
12
12
 
13
13
  ## What runs at each stage
14
14
 
15
- The mechanical half of the lifecycle, in one table. Everything in the "runs"
16
- column is a command, not a claim — `orly gate` reads the repository's declared
17
- `.oracle/orly.json` commands and never invents one.
15
+ This is the authoritative sequence. Commands come from the repository's
16
+ `.oracle/orly.json`; the engine contains no product-specific build recipes.
18
17
 
19
- | Stage | Runs | Fired by |
18
+ | Stage | Runs | Owner |
20
19
  |---|---|---|
21
- | CHORE(open) | the declared `verify.unit` once, to record `Test Baseline:` | the agent |
22
- | PLAN | nothing — no file mutations | — |
23
- | EXECUTE | the dispatch façade for each edited file type; DOC READ recorded via `audits/doc-read.sh log` | the agent, and the runtime's read hook where it has one |
24
- | CONFORM | the declared `conform` command | `orly gate work` — the generated pre-commit hook |
25
- | VERIFY (Section) | `conform` plus the declared lane covering the surface touched | the agent |
26
- | VERIFY (milestone) | the fast `verify.*` set: lint, unit, version | `orly gate verify` — the generated pre-push hook |
27
- | REVIEW | gstack `/review` over the diff | the agent |
28
- | DOCUMENT | nothing mechanical | — |
29
- | COMMIT | `conform` again, on the staged tree | the pre-commit hook |
30
- | CHORE(close) | every spec criterion, branch shape, clean tree, pushed branch, docs surface, and the slow `verify.*` suites | `orly gate pr`, by hand, before `gh pr create` |
31
- | LAND | nothing gated | — |
32
-
33
- Three properties hold this together. A gate only asks what it can answer
34
- honestly at that moment: `work` judges no git state, because a commit hook's
35
- tree is dirty by construction and a new spec is committed on the default branch.
36
- Each tier runs once per cadence — `conform` at commit, the fast set at push, the
37
- slow set at the close. And `orly gate pr` can skip the fast set because
38
- `git.pushed` proves HEAD is the commit pre-push already graded.
20
+ | CHORE(open) | Record the comparison commit and pending baseline; create the tree, carry owned changes, commit metadata | Agent; commit hook still runs |
21
+ | PLAN | Resolve references, scope, decisions, and proof mapping; no application build | Agent |
22
+ | EXECUTE | Read triggered rules; implement one Section and its tests | Agent |
23
+ | First Section verification | Hydrate only the touched surfaces; format, lint, and execute Section proofs | Agent |
24
+ | Later Section verification | Reuse the tree; repeat affected formatting, lint, and proof commands | Agent |
25
+ | COMMIT | Declared `conform` over staged changes | `orly gate work` in pre-commit |
26
+ | Push | Documentation check and declared non-test verification commands | `orly gate verify` in generated pre-push |
27
+ | Before PR | Measure the comparison revision's unit and integration lanes; review results and fix final-branch failures | Agent |
28
+ | CHORE(close) | Branch and spec criteria, exact upstream revision, every declared `verify.*` command | `orly gate pr` |
29
+ | LAND | Pull and clean up the merged stream | Agent after merge |
30
+
31
+ `verify.unit`, `verify.integration`, and `verify.memory` belong to the PR
32
+ boundary. Other `verify.*` commands also run at push and are checked again at
33
+ that independent boundary. Full suites run once per PR-gate invocation;
34
+ failures or later changes require a new invocation. No verification cache is
35
+ kept, and a custom hook cannot silently remove final verification.
36
+ `conform` remains the commit hook's responsibility because it may read the
37
+ index. A bare `orly gate` runs all groups and stops at the first red group.
38
+
39
+ Formatting and lint do not prove behavior. A Section is complete only when its
40
+ Dimensions have passing proofs. Use existing repository test lanes; if a lane
41
+ runs a whole workspace, describe it honestly and add a supported filter to that
42
+ recipe when narrower Section checks are needed. Boundary commands remain
43
+ unfiltered. Rust projects run `cargo fmt` in their Rust workspace, followed by
44
+ the declared lint wrapper covering Clippy and the shipping build configuration.
45
+ Other languages use their own declared formatter and lint commands.
39
46
 
40
47
  ## CHORE (open) — runbook
41
48
 
42
49
  1. Spec `docs/v*/pending/` → `active/`; `Status: IN_PROGRESS`; `Branch:` set.
43
- 2. **Test Baseline** — run the repository's declared `verify.unit` (and
44
- `verify.integration` where declared) from `.oracle/orly.json`; copy the
45
- reported counts into the spec header as `**Test Baseline:** unit=<N>
46
- integration=<M>` (VERIFY's Test Delta row compares against it; a product
47
- pack may name a dedicated counter — see the product block below).
48
- 3. Create the worktree; verify CWD is inside it (`pwd` + `git worktree list`).
50
+ 2. **Test Baseline** — record `**Baseline revision:** <full commit>` and
51
+ `**Test Baseline:** pending — measured before the Pull Request`. Run no
52
+ application suite at opening. Before the PR, obtain both declared baseline
53
+ lanes (`verify.unit` and `verify.integration`) for that exact revision from
54
+ matching successful run evidence or an isolated checkout of that revision.
55
+ Never assume the default checkout stayed at the comparison commit. Keep
56
+ baseline and final integration environments separate; neither may reset a
57
+ shared deployment. Record passed, failed, and skipped counts per lane in a
58
+ report, with commands, revision, and environment; put totals in the header
59
+ and the report path or run URL in `**Baseline evidence:**`.
60
+ A missing or failed baseline run is recorded honestly; fix failures in the
61
+ implementation branch and keep the historical result unchanged. Baseline
62
+ failures never permit final failures. Compare like-for-like lanes and report
63
+ added, removed, or changed test selections rather than inventing a delta.
64
+ `n/a — <reason>` is valid only when there is no code or no baseline lane.
65
+ The gate validates counts, revision ancestry, and the evidence reference;
66
+ the agent reviews the report's contents. Performance baselines required by
67
+ a spec remain due before the changes they measure.
68
+ 3. Create the worktree and carry the base checkout's uncommitted work into it;
69
+ verify CWD is inside it (`pwd` + `git worktree list`).
49
70
  4. Commit the four steps on the feature branch. No code until the commit lands.
50
71
 
72
+ **The base checkout's uncommitted work moves with the stream.** A worktree cut
73
+ from a dirty base strands that work on the default branch, where it is neither
74
+ committed, nor reviewed, nor in the tree the stream is about to gate — and the
75
+ next `git status` there reads as drift nobody owns. So step 3 moves it, before
76
+ any spec edit:
77
+
78
+ ```bash
79
+ git -C <base> status --porcelain -uall # nothing? the step is a no-op
80
+ git -C <base> stash push --include-untracked -m "chore(open): <branch>"
81
+ git -C <worktree> stash pop
82
+ ```
83
+
84
+ `git stash` is per-repository, not per-worktree, so the entry the base pushes is
85
+ the entry the worktree pops. **Move, never copy** — work left in both places is
86
+ committed twice and conflicts on the merge. Name what came across in the
87
+ CHORE(open) report; a carry-over nobody announced is indistinguishable from
88
+ scope that wandered in. A pop that conflicts KEEPS the stash: stop there and
89
+ resolve before step 4, because a half-applied carry-over is worse than a dirty
90
+ base. Work that does not belong to this stream is the one exception — leave it,
91
+ and say so.
92
+
51
93
  <!-- oracle-packs:start product.agentsfleet -->
52
94
  ## Worktree recipe (agentsfleet)
53
95
 
54
- `git checkout main && git branch feat/mNN-name && git worktree add ../agentsfleet-mNN-name feat/mNN-name && cd ../agentsfleet-mNN-name && bun install && (cd cli && bun install && bun run build)`.
55
- The root `bun install` hydrates the workspace (`ui/packages/*`); `cli/` is its
56
- own Bun project needing install + build. `git worktree add` fires
57
- `.githooks/post-checkout` → symlinks `~/.config/agentsfleet/{ui,runner}.env.local`
58
- into the tree; on 🟠 run `provision-env-1password`, re-link. Post-merge:
59
- `git worktree remove ../agentsfleet-mNN-name`.
60
-
61
- **Test Baseline counter.** A repository whose declared `verify.unit` does not
62
- print a total names its own counter here. Without one, record the count the
63
- declared command reports.
96
+ ```bash
97
+ cd ~/Projects/agentsfleet && git checkout main
98
+ git status --porcelain -uall # dirty? carry it over — the two stash lines below
99
+ git stash push --include-untracked -m "chore(open): feat/mNN-name"
100
+ git branch feat/mNN-name
101
+ git worktree add ../agentsfleet-mNN-name feat/mNN-name
102
+ cd ../agentsfleet-mNN-name && git stash pop
103
+ ```
104
+
105
+ Omit stash operations when the base is clean. Identify the saved stash entry
106
+ explicitly when other streams exist, restore its staged state, and remove only
107
+ that entry after successful restoration. Leave changes owned by another stream
108
+ in place and report them. No application installation or build runs here.
109
+ `git worktree add` may run the repository's cheap environment-link hook.
110
+
111
+ At first Section verification, install dependencies for the surfaces being
112
+ verified: root `bun install` for the JavaScript workspace; `cli/` has its own
113
+ install and build. Rust checks run from `rustd/` using `make lint-rustd` after
114
+ `cargo fmt`. The first compilation resolves Rust dependencies; later Sections
115
+ reuse the build artifacts. Missing environment links follow the repository's
116
+ provisioning instructions before a check that needs them.
64
117
 
65
118
  **agentsfleet CHORE(close) paths:** the `<Update>` lands in
66
119
  `~/Projects/docs/changelog.mdx` (template + version-bump matrix:
@@ -99,8 +152,9 @@ into a separate tree.
99
152
  guesses `surfaces`.
100
153
  First session in a repository whose config is still that seed:
101
154
  1. Read the build files properly. Fill every `verify.*` the repository really
102
- has — the tiering is fixed (`conform` + `verify.unit` are the fast tier,
103
- every other `verify.*` is slow and skips on prose-only branches).
155
+ has — the gate ownership is fixed: `conform` at commit, non-test checks at
156
+ push, all `verify.*` at the PR boundary; only integration and memory
157
+ skip on branches with no code.
104
158
  2. Set `surfaces.user` and `surfaces.docs` to real path prefixes, or the docs
105
159
  gate can never fire and a user-visible change ships undocumented.
106
160
  3. Add any opt-in pack the repository's own sources cannot imply
@@ -108,8 +162,10 @@ into a separate tree.
108
162
  `orly update` so they materialise.
109
163
  4. Commit it. Every teammate and every later session reads this file, and
110
164
  `orly` never rewrites it — so an edit here is permanent.
111
- A seeded-but-uncompleted config is why `orly gate` reports a repository with
112
- no declared commands: the fix is this list, not another `orly init`.
165
+ Setup requires `conform` and at least one named `verify.*` command.
166
+ A documentation repository can declare `verify.docs` for its site and link
167
+ checks without application unit or integration suites. `init` reports
168
+ missing commands; `doctor` and the gates reject incomplete setup.
113
169
  - **Priming:** (1) Human runs `playbooks/founding/01_bootstrap/001_playbook.md`.
114
170
  (2) Agent runs `./playbooks/founding/02_preflight/00_gate.sh` (green before
115
171
  next). (3) Agent runs `playbooks/founding/03_priming_infra/001_playbook.md`.
@@ -25,58 +25,40 @@ Only when a target is genuinely unrunnable (e.g. Docker missing for integration
25
25
  tests). Surface the limitation in the done message — never dress a skip as "tests
26
26
  pass".
27
27
 
28
- ## Why `make` is canonical
28
+ ## Repository commands are canonical
29
29
 
30
- Package-scoped runners (`bun run test`, `vitest <file>`, `cargo test -p <crate>`)
31
- are **not** verification — they skip every other package's lint and tests and the
32
- cross-language gates. The declared `make` targets are the canonical gates.
30
+ The commands in `.oracle/orly.json` define full verification. Use repository
31
+ Make targets where declared; a repository may also declare its native tools
32
+ directly. A narrower package run never substitutes for a declared full check.
33
+ Documentation repositories may use `verify.docs` for site and link checks;
34
+ application test suites are required only when the repository declares them.
33
35
 
34
36
  ## Two cadences, one boundary
35
37
 
36
- "Always" is not a cadence. A Section closing and a milestone closing are
37
- different claims, and running the whole declared set at both spends the
38
- wall-clock of the comprehensive tier to answer a question the scoped lane
39
- already answered.
40
-
41
- The gates match those cadences: `orly gate work` runs the declared `conform`
42
- alone (every commit, seconds), `orly gate verify` runs the fast `verify.*` set
43
- (every push), and `orly gate pr` runs the whole-branch criteria and the slow
44
- suites (the close). Each tier runs once per cadence.
45
-
46
- **Section lane (per Section, and per done-claim inside one).** `make
47
- harness-verify` — the declared `conform`, seconds — plus the lane that covers
48
- the surface the Section touched (`make test-unit-rustd`, `make test-unit-app`,
49
- …). This proves a Section. It is not a repository claim and never satisfies a
50
- VERIFY row on its own.
51
-
52
- **Milestone boundary (once, at CHORE(close), before the PR).** Every command
53
- `.oracle/orly.json` declares, in one pass: `conform`, `verify.lint`,
54
- `verify.unit`, `verify.version`, and the slow tier (`verify.integration`,
55
- `verify.memory`). This is the repository claim, and it is what the done-message
56
- below reports.
57
-
58
- Run `orly gate pr` at the close. It does not re-run the fast tier, and it does
59
- not need to: its own `git.pushed` criterion proves HEAD is exactly the commit
60
- the pre-push `orly gate verify` already graded. A bare `orly gate` chains all
61
- three gates — use it on a branch whose earlier gates never ran.
62
-
63
- | Target | When |
64
- |---|---|
65
- | `make harness-verify` | Section lane and boundary. The declared `conform` — the deterministic gate audit. |
66
- | `make lint-all` | Boundary. The declared `verify.lint`. |
67
- | `make test-unit-all` | Boundary. The declared `verify.unit` — the cargo workspace plus every package coverage gate. |
68
- | `make check-version` | Boundary. The declared `verify.version`. |
69
- | `make test-integration-rustd` | Boundary. The declared `verify.integration` — live Postgres and Redis; `orly gate pr` skips it on a branch carrying no code. |
70
- | `make wire-fixtures` | The `/v1/runners` wire types changed. Regenerate, then review the diff — a changed fixture IS the wire change. |
71
- | `make bench` (local) | Diff touches request-path code, allocator wiring, or startup/shutdown sequencing. |
72
- | `API_BENCH_URL=https://api-dev.agentsfleet.net/healthz make bench` | After branch deploys to dev. |
73
- | `/orly-write-integration-test` (skill) | With `/orly-write-unit-test` at VERIFY when the diff crosses module boundaries with real I/O; otherwise record `N/A — <reason>`. |
74
- | Acceptance e2e (live tier) | Diff touches a surface the live/acceptance tier covers — relevant suites green, or their opt-in skip matrix recorded. |
75
-
76
- The declared set in `.oracle/orly.json` is the source of truth for what exists,
77
- and a lane listed there is a lane that runs at the boundary — including the slow
78
- tier. A package-scoped runner (`cargo test -p afd_wire`, `bun run test` inside a
79
- package) proves that package and never the repository, at either cadence.
38
+ The command ownership and chronological sequence live in
39
+ `dispatch/lifecycle.md` §What runs at each stage. Read that table before
40
+ verification; this page defines what counts as evidence.
41
+
42
+ - **Section proof:** format, lint, and run the tests covering the changed
43
+ Dimensions. Hydrate the required application dependencies at the first
44
+ Section verification, then reuse that worktree. Passing lint alone never
45
+ marks a behavior DONE.
46
+ - **Baseline:** before the Pull Request, measure the recorded comparison
47
+ revision's declared unit and integration lanes. Keep commands, counts, and
48
+ output in an evidence report. Compare equivalent selections; explain removed
49
+ tests. A historical failure never exempts the final branch.
50
+ - **Final verification:** `orly gate pr` runs every declared `verify.*` command
51
+ itself. It does not infer success from a pushed branch or an installed hook.
52
+ Capture full command output and the decisive counts, not only exit codes.
53
+ Custom hooks may run additional checks but cannot replace the final gate.
54
+ - **Repeat only for cause:** a failure or a subsequent change invalidating the
55
+ result requires a rerun. Run the final gate once per candidate; do not also
56
+ launch its full suites manually as an unrecorded duplicate.
57
+
58
+ Use repository-owned Make targets where declared. A narrower package run is
59
+ Section evidence only, never a substitute for the full declared boundary.
60
+ When a diff crosses a module boundary with real input/output, apply
61
+ `orly-write-integration-test`; otherwise record why it does not apply.
80
62
 
81
63
  ## Wire-fixture evidence rule
82
64