@agentsfleet/orly 0.7.1 → 0.9.0
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.
- package/AGENTS.md +16 -11
- package/README.md +8 -1
- package/SOUL.md +4 -1
- package/audits/agents-md.md +3 -0
- package/audits/rust-error.sh +228 -0
- package/core/operating-model.md +13 -11
- package/dispatch/lib.sh +28 -3
- package/dispatch/lifecycle.md +27 -0
- package/dispatch/verify.md +45 -11
- package/dispatch/write_go.md +8 -0
- package/dispatch/write_rust.md +166 -0
- package/dispatch/write_rust.sh +38 -0
- package/docs/EXECUTE_DOC_READS.md +1 -1
- package/docs/RULE_ENFORCEMENT.md +2 -2
- package/docs/VERIFY_TIERS.md +35 -11
- package/docs/greptile-learnings/RULES.md +1 -0
- package/package.json +1 -1
- package/packs/language/rust/rules.md +9 -0
- package/registry.json +16 -0
- package/skills/orly-babysit-prs/SKILL.md +4 -8
- package/skills/orly-spec-new/SKILL.md +4 -6
- package/skills/orly-write-integration-test/SKILL.md +4 -6
- package/skills/orly-write-unit-test/SKILL.md +4 -6
- package/src/cli.ts +11 -1
- package/src/criteria.test.ts +17 -6
- package/src/criteria.ts +34 -9
- package/src/gates.test.ts +33 -9
- package/src/gates_spec.test.ts +1 -1
- package/src/install.ts +18 -72
- package/src/install_packs.test.ts +56 -0
- package/src/loaders.test.ts +148 -0
- package/src/loaders.ts +240 -0
- package/src/render.test.ts +59 -1
- package/src/verify.test.ts +72 -2
- package/src/verify.ts +44 -3
package/AGENTS.md
CHANGED
|
@@ -61,7 +61,7 @@ Auto-memory is **disabled** (`autoMemoryEnabled: false` + `CLAUDE_CODE_DISABLE_A
|
|
|
61
61
|
- **Skip hooks/signing.** Never `--no-verify`/`--no-gpg-sign`/`-c commit.gpgsign=false`. Hook fails → fix cause.
|
|
62
62
|
- **Plaintext secrets in entity tables.** Store vault `key_name` ref, resolve via `crypto_store.load()`.
|
|
63
63
|
- **Static strings in SQL schema.** No `DEFAULT 'value'`/`CHECK (col IN ('a','b'))`. Enforce in app via named constants.
|
|
64
|
-
- **Resolving/printing credentials.** `op read 'op://...'` at runtime — never paste/log.
|
|
64
|
+
- **Resolving/printing credentials.** `op read 'op://...'` at runtime — never paste/log. Vault names come from the environment, never from a rules file.
|
|
65
65
|
- **Force-push default branch** (`main`/`master`).
|
|
66
66
|
- **Install-process launches in core paths.** Native SDKs. Exception: personal dev tools (`op`/`gh`/`glab`/`oracle`).
|
|
67
67
|
|
|
@@ -83,14 +83,14 @@ Auto-memory is **disabled** (`autoMemoryEnabled: false` + `CLAUDE_CODE_DISABLE_A
|
|
|
83
83
|
- **Symlinked edits land in the repository that owns the file.** A file whose `readlink` resolves outside this checkout — into a dotfiles or config repository — is an edit to *that* repository, not this one. Detect via `readlink` BEFORE editing. After: `cd` to the resolved repository, `git add <files> && git commit && git push` on its default branch. Never leave uncommitted.
|
|
84
84
|
- **Docs-repo edits on own branch.** `~/Projects/docs/` is shared across milestones: check `git status` first; from `main` (checkout or worktree off `main`) commit on `chore/m{N}-{slug}-changelog`; recovery = stash, re-apply on a fresh branch.
|
|
85
85
|
- Other dotfiles (`.zshrc`/`.gitconfig`/etc.): timestamped backup; minimal edits.
|
|
86
|
+
- **Check a sibling repository for an existing pattern in the same language before inventing one.** A shape shipping next door is prior art; a new one needs a reason.
|
|
87
|
+
- **Read the reference implementation before designing.** Where the language's dispatch page names a canonical external source, that read is mandatory in review and its guideline identifiers are cited — applied or diverged from. "Broken for us" means the delta was missed: diff our call site against theirs before blaming the principle.
|
|
86
88
|
- Before commit/push: `gitleaks` must pass.
|
|
87
89
|
- No new `make` targets without a distinct caller (CI job, spec-mandated gate, or a workflow existing targets can't express) — check `make/*.mk` first; extend over near-duplicate wrappers.
|
|
88
90
|
- `*.zig` → read `dispatch/write_zig.md`; ZIG GATE fires.
|
|
89
91
|
- Auth-flow (token-minting handlers, credential-typed spec dimensions, the repository's auth directories) → read the repository's `docs/AUTH.md` first, where it exists.
|
|
90
92
|
- `conn.query()` requires `.drain()` in same fn before `deinit()`. Verify `make lint-governance`. Use `conn.exec()` for no-rows.
|
|
91
|
-
- **
|
|
92
|
-
- Check a sibling repository under `$HOME/Projects/` for an existing pattern in the same language before inventing one.
|
|
93
|
-
- **Reference canon (read before designing):** TypeScript → `oss/supabase/apps/studio` + `oss/supabase/packages/{ui,ui-patterns}` + `oss/cli`; Zig → `oss/ghostty/src/`; Rust → `oss/bun/src/` + `oss/exonum` + `oss/core_api-develop` + Microsoft's Rust guidelines (`oss/rust-guidelines/all.txt`, fetch if absent; mandatory in review). Missing checkout → ask, then clone into `~/Projects/oss/`. "Broken for us" → call-site diff first.
|
|
93
|
+
- **Indy's reference checkouts** (the machine-local half of the rule above): TypeScript → `oss/supabase/apps/studio` + `oss/supabase/packages/{ui,ui-patterns}` + `oss/cli`; Zig → `oss/ghostty/src/`; Rust → `oss/bun/src/` + `oss/exonum` + `oss/core_api-develop`. Missing checkout → ask, then clone into `~/Projects/oss/`.
|
|
94
94
|
|
|
95
95
|
**Forge detection:** `github.com` → `gh`; `gitlab.com` → `glab`. Check `git remote -v`.
|
|
96
96
|
|
|
@@ -180,7 +180,9 @@ Non-trivial (full lifecycle) if it: touches >1 file · new abstraction · data m
|
|
|
180
180
|
|
|
181
181
|
**With spec:** `CHORE(open) → PLAN → EXECUTE → CONFORM → VERIFY → REVIEW → DOCUMENT → COMMIT → CHORE(close)`. **Without spec** (bug fix/config/refactor): `PLAN → EXECUTE → CONFORM → VERIFY → REVIEW → DOCUMENT → COMMIT`. CHORE bookends iff work creates/continues a spec under `docs/v*/{active,pending}/`. Stage runbooks (checklists, recipes, formats): `dispatch/lifecycle.md`.
|
|
182
182
|
|
|
183
|
-
**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.
|
|
183
|
+
**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.
|
|
184
|
+
|
|
185
|
+
**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.
|
|
184
186
|
|
|
185
187
|
**LAND (after merge, or when the user confirms it merged):** pull the default branch, prune the merged worktree + branch, `make down` where defined.
|
|
186
188
|
|
|
@@ -203,13 +205,13 @@ Edit only approved scope; no opportunistic refactors. Stay in active worktree. C
|
|
|
203
205
|
|
|
204
206
|
### CONFORM
|
|
205
207
|
|
|
206
|
-
Runs after EXECUTE, before VERIFY. Invokes the `conform` commands the repository declares in `.oracle/orly.json` and aggregates every gate verdict. Any 🔴 returns to EXECUTE; the lifecycle does not advance.
|
|
208
|
+
Runs after EXECUTE, before VERIFY. Invokes the `conform` commands the repository declares in `.oracle/orly.json` and aggregates every gate verdict. Any 🔴 returns to EXECUTE; the lifecycle does not advance. This is the `work` gate's tier and the pre-commit hook runs it at every commit: declare a `conform` costing seconds, not minutes.
|
|
207
209
|
In `agentsfleet` this stage is `make harness-verify`; its output block and end-of-turn audit detail live in `docs/HARNESS_VERIFY_OUTPUT.md`. Required rows: FILE SHAPE, PUB GATE, LENGTH GATE, MILESTONE-ID GATE, ZIG GATE, UI GATE, DESIGN TOKEN GATE, UFS GATE, SCHEMA GUARD, GREPTILE GATE, Architecture consult, Coverage, and `/orly-write-unit-test`.
|
|
208
210
|
|
|
209
211
|
### VERIFY
|
|
210
212
|
|
|
211
|
-
|
|
212
|
-
The `agentsfleet` output block and
|
|
213
|
+
**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.
|
|
214
|
+
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.
|
|
213
215
|
|
|
214
216
|
### REVIEW
|
|
215
217
|
|
|
@@ -235,7 +237,7 @@ Required when spec involved — after last COMMIT, before PR. Also runs when par
|
|
|
235
237
|
|
|
236
238
|
| # | When | Skill |
|
|
237
239
|
|---|---|---|
|
|
238
|
-
| 1 | VERIFY | `/orly-write-unit-test
|
|
240
|
+
| 1 | VERIFY | `/orly-write-unit-test` once per Section over that Section's diff, and again at the boundary — never skipped, never deferred. Then `/orly-write-integration-test` at the boundary **when the diff crosses a module boundary with real input/output**; otherwise record `N/A — <reason>` in Session Notes. |
|
|
239
241
|
| 2 | REVIEW | gstack `/review` — every runtime, same route; address findings or record a user-acked deferral; reviewer unavailable → Session Notes: *"skipped — <reason> <ts>; rerun before merge"* |
|
|
240
242
|
| 3 | After every push | `orly-babysit-prs` — CI check runs + greptile inline threads + PR-level summary; stops on two consecutive empty polls with CI green; never `gh pr checks --watch` for greptile |
|
|
241
243
|
|
|
@@ -294,7 +296,10 @@ Optimise for one thing: he never has to ask twice.
|
|
|
294
296
|
justification clauses, importance puffery, em-dash rhythm crutches, and
|
|
295
297
|
fake-profound kickers. End on the clearest concrete sentence. The banned-word
|
|
296
298
|
list lives in `docs/DOCUMENTATION_RULES.md` §DOC-05, §DOC-07, §DOC-14b, and
|
|
297
|
-
`orly gate verify`
|
|
299
|
+
`orly gate verify` REPORTS it — `docs.language` prints its findings and stays
|
|
300
|
+
green, because the corpus predates the rule and a red on day one gets switched
|
|
301
|
+
off by the end of the week. Nobody fails a build over this, so the prose is
|
|
302
|
+
mine to get right.
|
|
298
303
|
|
|
299
304
|
## Reading Indy
|
|
300
305
|
|
|
@@ -370,5 +375,5 @@ Optimise for one thing: he never has to ask twice.
|
|
|
370
375
|
|
|
371
376
|
| Responsibility | Commands |
|
|
372
377
|
|---|---|
|
|
373
|
-
| `conform` | `make
|
|
378
|
+
| `conform` | `make conform` |
|
|
374
379
|
| `verify.unit` | `bun test src` |
|
package/README.md
CHANGED
|
@@ -81,6 +81,9 @@ your-repo/
|
|
|
81
81
|
├── AGENTS.md ─────────────── yours. untouched, except one delimited pointer block
|
|
82
82
|
├── AGENTS.orly.md ────────── the generated rules: safety, dispatch router, lifecycle
|
|
83
83
|
│
|
|
84
|
+
├── CLAUDE.md ─────────────── one import line; the only file Claude Code loads itself
|
|
85
|
+
├── opencode.json ─────────── names both rule files; opencode loads nothing by default
|
|
86
|
+
│
|
|
84
87
|
├── dispatch/*.md ─────────── one rule page per kind of work
|
|
85
88
|
├── audits/*.sh ───────────── the deterministic gates
|
|
86
89
|
├── docs/*.md ─────────────── the standards those rules cite
|
|
@@ -191,8 +194,12 @@ Each edit activates the rule page for that file kind. A gate proves whether the
|
|
|
191
194
|
|---|---|---|
|
|
192
195
|
| `AGENTS.md` | **you** | untouched, except one delimited pointer block |
|
|
193
196
|
| `AGENTS.orly.md` | orly | rewritten |
|
|
197
|
+
| `CLAUDE.md` | **you** | written only when missing; never edited after |
|
|
198
|
+
| `opencode.json` | **you** | gains the two rule files in `instructions`; nothing else touched |
|
|
199
|
+
|
|
200
|
+
`AGENTS.md` stays yours — orly writes its rules beside it and points at them from one delimited block.
|
|
194
201
|
|
|
195
|
-
|
|
202
|
+
The last two files exist because installing a rules file is not the same as delivering it, and no runtime loads `AGENTS.orly.md` on its own. Codex and Amp auto-load `AGENTS.md`, which carries the pointer block onward. Claude Code loads `CLAUDE.md` and nothing else. opencode loads only what its `instructions` name. So orly writes the one import line each of those needs, and leaves any file you already wrote alone — a `CLAUDE.md` of your own, symlink included, is your answer to the question and orly does not touch it.
|
|
196
203
|
|
|
197
204
|
> [!WARNING]
|
|
198
205
|
> orly refuses to replace a hook or rule page it did not write. `--force` and `--no-hooks` are the ways through. A refused run changes nothing.
|
package/SOUL.md
CHANGED
|
@@ -44,7 +44,10 @@ Optimise for one thing: he never has to ask twice.
|
|
|
44
44
|
justification clauses, importance puffery, em-dash rhythm crutches, and
|
|
45
45
|
fake-profound kickers. End on the clearest concrete sentence. The banned-word
|
|
46
46
|
list lives in `docs/DOCUMENTATION_RULES.md` §DOC-05, §DOC-07, §DOC-14b, and
|
|
47
|
-
`orly gate verify`
|
|
47
|
+
`orly gate verify` REPORTS it — `docs.language` prints its findings and stays
|
|
48
|
+
green, because the corpus predates the rule and a red on day one gets switched
|
|
49
|
+
off by the end of the week. Nobody fails a build over this, so the prose is
|
|
50
|
+
mine to get right.
|
|
48
51
|
|
|
49
52
|
## Reading Indy
|
|
50
53
|
|
package/audits/agents-md.md
CHANGED
|
@@ -95,6 +95,7 @@ The questionnaire is organised by scenario. Each scenario corresponds to a momen
|
|
|
95
95
|
| 4.23 (Documentation) | Before published Markdown JSX (MDX), reusable snippets, customer readme, or public OpenAPI prose, must the agent read `dispatch/write_documentation.md` and `docs/DOCUMENTATION_RULES.md` before any narrower guide? | YES |
|
|
96
96
|
| 4.24 (Documentation) | Are page, snippet, generated API, and changelog scopes distinct, with repository-owned pre-commit checks enforcing the mechanical rules? | YES |
|
|
97
97
|
| 4.25 (Rust) | Do `*.rs` edits route to `dispatch/write_rust.md` — ownership, error variants, feature combinations, deterministic contention tests — with Microsoft's Pragmatic Rust Guidelines (`~/Projects/oss/rust-guidelines/all.txt`, fetched when absent) read sectioned and its `M-…` IDs cited in every Rust review? | YES | <!-- oracle-packs:language.rust -->
|
|
98
|
+
| 4.25b (Rust errors) | Is RULE ERR-RS deterministic where a machine can decide it — `dispatch/write_rust.sh` running `audits/rust-error.sh` over staged `*.rs` to block a `map_err` that stringifies its own cause and an error type with no `pub type Result<…>` in its crate — while `source()` shape and whether a context addition is warranted stay the agent's call? | YES | <!-- oracle-packs:language.rust -->
|
|
98
99
|
| 4.25a (Go) | Do `*.go` edits route to `dispatch/write_go.md`, with wrapped errors, goroutine exit paths, context-first signatures, defer-at-acquire, and `-race` verification? | YES | <!-- oracle-packs:language.go -->
|
|
99
100
|
| 4.26 (Python) | Do `*.py` edits route to `dispatch/write_python.md`, with parse-boundary validation and context-managed resources? | YES |
|
|
100
101
|
| 4.27 (Shell) | Do `*.sh` edits route to `dispatch/write_shell.md`, with quoted expansions, array arguments, cleanup, and compatibility requirements? | YES |
|
|
@@ -334,6 +335,8 @@ siblings.
|
|
|
334
335
|
| 26.5 | Does the `agentsfleet` profile map CONFORM to `make harness-verify` while VERIFY remains behavior proof? | YES |
|
|
335
336
|
| 26.6 | Does `README.md` document initialization, explicit synchronization, status, and doctor commands for new repositories? | YES |
|
|
336
337
|
| 26.7 | Does `orly gate pr` discover a spec closed to `done/` on the branch (its `Branch:` header names the branch) and run the spec criteria — including `spec.moved`, `spec.baseline`, `spec.ordering`, and `spec.deferrals` — instead of skip-passing as spec-less? | YES |
|
|
338
|
+
| 26.8 | Does the install write one loader per runtime that has a deterministic way into context — a bare, unbackticked `@AGENTS.orly.md` import line inside the pointer block, a `CLAUDE.md` carrying `@AGENTS.md` where the repository has none, and both rule files in `opencode.json`'s `instructions` — so delivery never depends on an agent noticing a markdown link? | YES |
|
|
339
|
+
| 26.9 | Are those loaders the repository's files rather than orly's — absent from `managed`, `CLAUDE.md` written only when missing (a symlink at the host counted as delivery, any other symlink left alone), `opencode.json` edited only by appending a missing `instructions` entry, and an unparseable one left byte for byte? | YES |
|
|
337
340
|
|
|
338
341
|
### Scenario 27 — Rule-path residence & reachability
|
|
339
342
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rust-error.sh — the mechanical half of RULE ERR-RS (Rust error discipline).
|
|
3
|
+
#
|
|
4
|
+
# Dispatch façade: dispatch/write_rust.md (Error discipline). Pairs with
|
|
5
|
+
# dispatch/write_rust.sh, which is what runs it.
|
|
6
|
+
#
|
|
7
|
+
# The rule has four clauses. Two of them a machine can decide from the text
|
|
8
|
+
# alone, and only those two live here:
|
|
9
|
+
#
|
|
10
|
+
# 1. LOSSY MAP_ERR — `map_err` whose closure converts its own cause through
|
|
11
|
+
# `to_string()` or `format!`. The rule allows `map_err` to ADD context the
|
|
12
|
+
# call site alone knows; it forbids stringifying the cause on the way into
|
|
13
|
+
# an error type, because `to_string()` destroys the `source()` chain. The
|
|
14
|
+
# closure's BINDING is what makes this decidable: `.map_err(|e| Mine::Db(
|
|
15
|
+
# e.to_string()))` stringifies the cause, while `.map_err(|e| Mine::Read {
|
|
16
|
+
# source: e, path: path.to_string() })` stringifies a path and is correct.
|
|
17
|
+
# Only a `to_string()` or `format!` applied to the closure's own parameter
|
|
18
|
+
# is a finding.
|
|
19
|
+
#
|
|
20
|
+
# 2. MISSING RESULT ALIAS — a crate declaring `pub enum Error` or
|
|
21
|
+
# `pub struct Error` with no `pub type Result<` anywhere beside it. The
|
|
22
|
+
# rule is "one error type per crate, and a `Result` alias beside it": a
|
|
23
|
+
# reader must never have to check WHICH error a signature returns.
|
|
24
|
+
#
|
|
25
|
+
# The other two clauses stay JUDGMENT and are not attempted here. Whether a
|
|
26
|
+
# `source()` implementation returns its own kind needs the Display body read
|
|
27
|
+
# beside it, and whether a context addition is warranted is a call-site
|
|
28
|
+
# question. A gate that guesses at those gets switched off within a week, and a
|
|
29
|
+
# switched-off gate protects nothing — partial mechanisation stated honestly is
|
|
30
|
+
# the whole design (the same call audits/doc-read.sh makes about its record).
|
|
31
|
+
#
|
|
32
|
+
# Carve-outs match audits/logging.sh exactly, so one Rust scope answers to both
|
|
33
|
+
# leaves: build scripts, `tests/`, `benches/`, `examples/`, and `#[cfg(test)]`
|
|
34
|
+
# modules are fixture code, where a stringified error in a test assertion is
|
|
35
|
+
# not a production error path.
|
|
36
|
+
#
|
|
37
|
+
# Modes:
|
|
38
|
+
# --staged diff-scope: only *.rs in `git diff --cached`
|
|
39
|
+
# --all (default) every tracked/unignored *.rs
|
|
40
|
+
#
|
|
41
|
+
# Exits 0 clean, 1 on findings.
|
|
42
|
+
|
|
43
|
+
set -euo pipefail
|
|
44
|
+
|
|
45
|
+
MODE_STAGED="--staged"
|
|
46
|
+
MODE_ALL="--all"
|
|
47
|
+
RULE="ERR-RS"
|
|
48
|
+
FACADE="dispatch/write_rust.md"
|
|
49
|
+
# The closure body window. A `map_err` closure that has not closed its
|
|
50
|
+
# parentheses within this many lines is a block long enough that its conversion
|
|
51
|
+
# is no longer a one-liner, and the rule's subject is the one-liner.
|
|
52
|
+
MAX_CLOSURE_LINES=12
|
|
53
|
+
CARGO_MANIFEST="Cargo.toml"
|
|
54
|
+
RESULT_ALIAS_PATTERN='^[[:space:]]*pub[[:space:]]+type[[:space:]]+Result[[:space:]]*<'
|
|
55
|
+
ERROR_TYPE_PATTERN='^[[:space:]]*pub[[:space:]]+(enum|struct)[[:space:]]+Error[[:space:]]*[<{(;]'
|
|
56
|
+
|
|
57
|
+
MODE="$MODE_ALL"
|
|
58
|
+
for arg in "$@"; do
|
|
59
|
+
case "$arg" in
|
|
60
|
+
--staged|staged) MODE="$MODE_STAGED" ;;
|
|
61
|
+
--all|all) MODE="$MODE_ALL" ;;
|
|
62
|
+
-h|--help)
|
|
63
|
+
printf 'usage: %s [--staged|--all]\n' "$0"
|
|
64
|
+
exit 0
|
|
65
|
+
;;
|
|
66
|
+
*) printf 'unknown arg: %s\n' "$arg" >&2; exit 64 ;;
|
|
67
|
+
esac
|
|
68
|
+
done
|
|
69
|
+
|
|
70
|
+
ROOT="$(git rev-parse --show-toplevel)"
|
|
71
|
+
cd "$ROOT"
|
|
72
|
+
|
|
73
|
+
FAIL=0
|
|
74
|
+
fail() { printf 'FAIL: %s\n' "$*" >&2; FAIL=1; }
|
|
75
|
+
ok() { printf 'OK: %s\n' "$*"; }
|
|
76
|
+
|
|
77
|
+
# Non-runtime Rust paths, identical to audits/logging.sh's carve-out list.
|
|
78
|
+
is_non_runtime_rust_path() {
|
|
79
|
+
case "$1" in
|
|
80
|
+
build.rs|*/build.rs|tests/*|*/tests/*|benches/*|*/benches/*|examples/*|*/examples/*) return 0 ;;
|
|
81
|
+
esac
|
|
82
|
+
return 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
gather_paths() {
|
|
86
|
+
case "$MODE" in
|
|
87
|
+
"$MODE_STAGED")
|
|
88
|
+
git diff --cached --name-only --diff-filter=ACMRT -- '*.rs' || true
|
|
89
|
+
;;
|
|
90
|
+
"$MODE_ALL")
|
|
91
|
+
git ls-files --cached --others --exclude-standard -- '*.rs' || true
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
FILES=()
|
|
97
|
+
while IFS= read -r path; do
|
|
98
|
+
[[ -n "$path" && -f "$path" ]] || continue
|
|
99
|
+
is_non_runtime_rust_path "$path" && continue
|
|
100
|
+
FILES+=("$path")
|
|
101
|
+
done < <(gather_paths)
|
|
102
|
+
|
|
103
|
+
if [[ ${#FILES[@]} -eq 0 ]]; then
|
|
104
|
+
ok "$RULE: no runtime Rust files in scope ($MODE)"
|
|
105
|
+
exit 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# 1. Scan every in-scope file once, emitting both signals.
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# One pass, one carve-out. Both halves need the same `#[cfg(test)]` skip, and
|
|
112
|
+
# implementing it twice is how the two halves would come to disagree about what
|
|
113
|
+
# a test module is — so the scanner emits `LOSSY:` and `ERRTYPE:` records from a
|
|
114
|
+
# single walk and the shell decides what to do with each.
|
|
115
|
+
#
|
|
116
|
+
# The lossy scanner accumulates a map_err closure from its opening parenthesis
|
|
117
|
+
# until the parentheses balance, so a conversion split over several lines reads
|
|
118
|
+
# the same as a one-liner.
|
|
119
|
+
signals="$(awk -v maxlines="$MAX_CLOSURE_LINES" -v errtype="$ERROR_TYPE_PATTERN" '
|
|
120
|
+
FNR == 1 { intest = 0; testdepth = 0; pending = 0; collecting = 0 }
|
|
121
|
+
|
|
122
|
+
# A #[cfg(test)] module is fixture code. The attribute precedes the item, so
|
|
123
|
+
# arm on the attribute and start counting braces at the item that follows.
|
|
124
|
+
!collecting && /#\[cfg\(test\)\]/ { pending = 1; next }
|
|
125
|
+
pending {
|
|
126
|
+
if (index($0, "{") > 0) { pending = 0; intest = 1; testdepth = 0 }
|
|
127
|
+
else next
|
|
128
|
+
}
|
|
129
|
+
intest {
|
|
130
|
+
n = gsub(/\{/, "{"); m = gsub(/\}/, "}")
|
|
131
|
+
testdepth += n - m
|
|
132
|
+
if (testdepth <= 0) intest = 0
|
|
133
|
+
next
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
{
|
|
137
|
+
line = $0
|
|
138
|
+
# Strip a trailing line comment so a commented example is not a finding.
|
|
139
|
+
sub(/\/\/.*$/, "", line)
|
|
140
|
+
|
|
141
|
+
# A public error type outside any test module. Emitted per file; the shell
|
|
142
|
+
# resolves it to a crate and asks that crate for its alias once.
|
|
143
|
+
if (!collecting && line ~ errtype) printf "ERRTYPE:%s\n", FILENAME
|
|
144
|
+
|
|
145
|
+
if (!collecting) {
|
|
146
|
+
where = match(line, /map_err[[:space:]]*\([[:space:]]*\|[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*\|/)
|
|
147
|
+
if (where == 0) next
|
|
148
|
+
rest = substr(line, where)
|
|
149
|
+
# The closure binding: the identifier between the pipes.
|
|
150
|
+
bind = rest
|
|
151
|
+
sub(/^map_err[[:space:]]*\([[:space:]]*\|[[:space:]]*/, "", bind)
|
|
152
|
+
sub(/[[:space:]]*\|.*$/, "", bind)
|
|
153
|
+
if (bind == "_") next
|
|
154
|
+
collecting = 1; buffer = rest; startline = FNR; spanned = 1
|
|
155
|
+
depth = gsub(/\(/, "(", rest) - gsub(/\)/, ")", rest)
|
|
156
|
+
} else {
|
|
157
|
+
buffer = buffer " " line
|
|
158
|
+
spanned++
|
|
159
|
+
depth += gsub(/\(/, "(", line) - gsub(/\)/, ")", line)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (depth > 0 && spanned < maxlines) next
|
|
163
|
+
|
|
164
|
+
# `bind.to_string()` — the cause converted to text on the way in.
|
|
165
|
+
stringified = 0
|
|
166
|
+
if (buffer ~ ("(^|[^A-Za-z0-9_])" bind "[[:space:]]*\\.[[:space:]]*to_string[[:space:]]*\\(")) stringified = 1
|
|
167
|
+
# `format!(... bind ...)` — the same loss wearing a formatter.
|
|
168
|
+
if (buffer ~ /format!/ && buffer ~ ("(^|[^A-Za-z0-9_])" bind "([^A-Za-z0-9_]|$)")) {
|
|
169
|
+
after = buffer; sub(/^.*format!/, "", after)
|
|
170
|
+
if (after ~ ("(^|[^A-Za-z0-9_])" bind "([^A-Za-z0-9_]|$)")) stringified = 1
|
|
171
|
+
}
|
|
172
|
+
if (stringified) printf "LOSSY:%s:%d\n", FILENAME, startline
|
|
173
|
+
collecting = 0
|
|
174
|
+
}
|
|
175
|
+
' "${FILES[@]}" || true)"
|
|
176
|
+
|
|
177
|
+
lossy_count=0
|
|
178
|
+
error_type_files=()
|
|
179
|
+
while IFS= read -r signal; do
|
|
180
|
+
case "$signal" in
|
|
181
|
+
LOSSY:*)
|
|
182
|
+
lossy_count=$((lossy_count + 1))
|
|
183
|
+
fail "${signal#LOSSY:} $RULE: map_err stringifies its own cause — to_string()/format! on the closure binding destroys the source() chain. Carry the cause with #[from] or a source field; map_err only ADDS context."
|
|
184
|
+
;;
|
|
185
|
+
ERRTYPE:*)
|
|
186
|
+
error_type_files+=("${signal#ERRTYPE:}")
|
|
187
|
+
;;
|
|
188
|
+
esac
|
|
189
|
+
done <<< "$signals"
|
|
190
|
+
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
# 2. An error type with no Result alias beside it.
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
# Crate-scoped: the alias may live in any file of the crate that declares the
|
|
195
|
+
# error, so the question is asked of the crate, once, not of the file.
|
|
196
|
+
crate_root_of() {
|
|
197
|
+
local dir; dir="$(dirname "$1")"
|
|
198
|
+
while [[ "$dir" != "." && "$dir" != "/" ]]; do
|
|
199
|
+
[[ -f "$dir/$CARGO_MANIFEST" ]] && { printf '%s' "$dir"; return 0; }
|
|
200
|
+
dir="$(dirname "$dir")"
|
|
201
|
+
done
|
|
202
|
+
[[ -f "$CARGO_MANIFEST" ]] && { printf '.'; return 0; }
|
|
203
|
+
return 1
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
alias_count=0
|
|
207
|
+
checked_crates=""
|
|
208
|
+
for path in ${error_type_files[@]+"${error_type_files[@]}"}; do
|
|
209
|
+
crate=""
|
|
210
|
+
crate_root_of "$path" > /dev/null 2>&1 && crate="$(crate_root_of "$path")"
|
|
211
|
+
# A .rs file outside any Cargo crate has no crate to carry the alias; the
|
|
212
|
+
# lossy-map_err half still judged it.
|
|
213
|
+
[[ -n "$crate" ]] || continue
|
|
214
|
+
case " $checked_crates " in *" $crate "*) continue ;; esac
|
|
215
|
+
checked_crates="$checked_crates $crate"
|
|
216
|
+
if ! git ls-files --cached --others --exclude-standard -- "$crate/*.rs" \
|
|
217
|
+
| xargs grep -lE "$RESULT_ALIAS_PATTERN" 2>/dev/null | grep -q .; then
|
|
218
|
+
alias_count=$((alias_count + 1))
|
|
219
|
+
fail "$path $RULE: crate '$crate' declares a public Error type with no \`pub type Result<T, E = Error>\` beside it — a reader must not have to check WHICH error a signature returns."
|
|
220
|
+
fi
|
|
221
|
+
done
|
|
222
|
+
|
|
223
|
+
ok "$RULE: scanned ${#FILES[@]} runtime Rust file(s); lossy-map_err=$lossy_count missing-result-alias=$alias_count"
|
|
224
|
+
if [[ $FAIL -ne 0 ]]; then
|
|
225
|
+
printf '\n🔴 %s: blocking violations. See %s (Error discipline).\n' "$RULE" "$FACADE" >&2
|
|
226
|
+
exit 1
|
|
227
|
+
fi
|
|
228
|
+
exit 0
|
package/core/operating-model.md
CHANGED
|
@@ -61,7 +61,7 @@ Auto-memory is **disabled** (`autoMemoryEnabled: false` + `CLAUDE_CODE_DISABLE_A
|
|
|
61
61
|
- **Skip hooks/signing.** Never `--no-verify`/`--no-gpg-sign`/`-c commit.gpgsign=false`. Hook fails → fix cause.
|
|
62
62
|
- **Plaintext secrets in entity tables.** Store vault `key_name` ref, resolve via `crypto_store.load()`.
|
|
63
63
|
- **Static strings in SQL schema.** No `DEFAULT 'value'`/`CHECK (col IN ('a','b'))`. Enforce in app via named constants.
|
|
64
|
-
- **Resolving/printing credentials.** `op read 'op://...'` at runtime — never paste/log.
|
|
64
|
+
- **Resolving/printing credentials.** `op read 'op://...'` at runtime — never paste/log. Vault names come from the environment, never from a rules file.
|
|
65
65
|
- **Force-push default branch** (`main`/`master`).
|
|
66
66
|
- **Install-process launches in core paths.** Native SDKs. Exception: personal dev tools (`op`/`gh`/`glab`/`oracle`).
|
|
67
67
|
|
|
@@ -80,18 +80,18 @@ Auto-memory is **disabled** (`autoMemoryEnabled: false` + `CLAUDE_CODE_DISABLE_A
|
|
|
80
80
|
### Operational defaults
|
|
81
81
|
|
|
82
82
|
- Workspace `~/Projects`. `gh`/`glab` not browsers. `trash` not `rm`. Conventional Commits. Process decisions → repo docs, not chat. "Make a note" → update `AGENTS.md`/repo docs.
|
|
83
|
-
- **Symlinked edits land in the repository that owns the file.** A file whose `readlink` resolves outside this checkout — into a dotfiles or config repository — is an edit to *that* repository, not this one. Detect via `readlink` BEFORE editing. After: `cd` to the resolved repository, `git add <files> && git commit && git push` on its default branch. Never leave uncommitted.
|
|
83
|
+
- **Symlinked edits land in the repository that owns the file.** A file whose `readlink` resolves outside this checkout — into a dotfiles or config repository — is an edit to *that* repository, not this one. Detect via `readlink` BEFORE editing. After: `cd` to the resolved repository, `git add <files> && git commit && git push` on its default branch. Never leave uncommitted.
|
|
84
84
|
- **Docs-repo edits on own branch.** `~/Projects/docs/` is shared across milestones: check `git status` first; from `main` (checkout or worktree off `main`) commit on `chore/m{N}-{slug}-changelog`; recovery = stash, re-apply on a fresh branch.
|
|
85
|
-
- Other dotfiles (`.zshrc`/`.gitconfig`/etc.): timestamped backup; minimal edits.
|
|
85
|
+
- Other dotfiles (`.zshrc`/`.gitconfig`/etc.): timestamped backup; minimal edits.
|
|
86
|
+
- **Check a sibling repository for an existing pattern in the same language before inventing one.** A shape shipping next door is prior art; a new one needs a reason.
|
|
87
|
+
- **Read the reference implementation before designing.** Where the language's dispatch page names a canonical external source, that read is mandatory in review and its guideline identifiers are cited — applied or diverged from. "Broken for us" means the delta was missed: diff our call site against theirs before blaming the principle.
|
|
86
88
|
- Before commit/push: `gitleaks` must pass.
|
|
87
89
|
- No new `make` targets without a distinct caller (CI job, spec-mandated gate, or a workflow existing targets can't express) — check `make/*.mk` first; extend over near-duplicate wrappers.
|
|
88
90
|
- `*.zig` → read `dispatch/write_zig.md`; ZIG GATE fires. <!-- oracle-packs:language.zig -->
|
|
89
91
|
- Auth-flow (token-minting handlers, credential-typed spec dimensions, the repository's auth directories) → read the repository's `docs/AUTH.md` first, where it exists. <!-- oracle-packs:domain.auth -->
|
|
90
92
|
- `conn.query()` requires `.drain()` in same fn before `deinit()`. Verify `make lint-governance`. Use `conn.exec()` for no-rows. <!-- oracle-packs:product.agentsfleet -->
|
|
91
93
|
<!-- oracle-packs:start persona.indy -->
|
|
92
|
-
- **
|
|
93
|
-
- Check a sibling repository under `$HOME/Projects/` for an existing pattern in the same language before inventing one.
|
|
94
|
-
- **Reference canon (read before designing):** TypeScript → `oss/supabase/apps/studio` + `oss/supabase/packages/{ui,ui-patterns}` + `oss/cli`; Zig → `oss/ghostty/src/`; Rust → `oss/bun/src/` + `oss/exonum` + `oss/core_api-develop` + Microsoft's Rust guidelines (`oss/rust-guidelines/all.txt`, fetch if absent; mandatory in review). Missing checkout → ask, then clone into `~/Projects/oss/`. "Broken for us" → call-site diff first.
|
|
94
|
+
- **Indy's reference checkouts** (the machine-local half of the rule above): TypeScript → `oss/supabase/apps/studio` + `oss/supabase/packages/{ui,ui-patterns}` + `oss/cli`; Zig → `oss/ghostty/src/`; Rust → `oss/bun/src/` + `oss/exonum` + `oss/core_api-develop`. Missing checkout → ask, then clone into `~/Projects/oss/`.
|
|
95
95
|
<!-- oracle-packs:end -->
|
|
96
96
|
|
|
97
97
|
**Forge detection:** `github.com` → `gh`; `gitlab.com` → `glab`. Check `git remote -v`.
|
|
@@ -186,7 +186,9 @@ Non-trivial (full lifecycle) if it: touches >1 file · new abstraction · data m
|
|
|
186
186
|
|
|
187
187
|
**With spec:** `CHORE(open) → PLAN → EXECUTE → CONFORM → VERIFY → REVIEW → DOCUMENT → COMMIT → CHORE(close)`. **Without spec** (bug fix/config/refactor): `PLAN → EXECUTE → CONFORM → VERIFY → REVIEW → DOCUMENT → COMMIT`. CHORE bookends iff work creates/continues a spec under `docs/v*/{active,pending}/`. Stage runbooks (checklists, recipes, formats): `dispatch/lifecycle.md`.
|
|
188
188
|
|
|
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.
|
|
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
|
+
|
|
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.
|
|
190
192
|
|
|
191
193
|
**LAND (after merge, or when the user confirms it merged):** pull the default branch, prune the merged worktree + branch, `make down` where defined.
|
|
192
194
|
|
|
@@ -213,16 +215,16 @@ Edit only approved scope; no opportunistic refactors. Stay in active worktree. C
|
|
|
213
215
|
|
|
214
216
|
### CONFORM
|
|
215
217
|
|
|
216
|
-
Runs after EXECUTE, before VERIFY. Invokes the `conform` commands the repository declares in `.oracle/orly.json` and aggregates every gate verdict. Any 🔴 returns to EXECUTE; the lifecycle does not advance.
|
|
218
|
+
Runs after EXECUTE, before VERIFY. Invokes the `conform` commands the repository declares in `.oracle/orly.json` and aggregates every gate verdict. Any 🔴 returns to EXECUTE; the lifecycle does not advance. This is the `work` gate's tier and the pre-commit hook runs it at every commit: declare a `conform` costing seconds, not minutes.
|
|
217
219
|
<!-- oracle-packs:start product.agentsfleet -->
|
|
218
220
|
In `agentsfleet` this stage is `make harness-verify`; its output block and end-of-turn audit detail live in `docs/HARNESS_VERIFY_OUTPUT.md`. Required rows: FILE SHAPE, PUB GATE, LENGTH GATE, MILESTONE-ID GATE, ZIG GATE, UI GATE, DESIGN TOKEN GATE, UFS GATE, SCHEMA GUARD, GREPTILE GATE, Architecture consult, Coverage, and `/orly-write-unit-test`.
|
|
219
221
|
<!-- oracle-packs:end -->
|
|
220
222
|
|
|
221
223
|
### VERIFY
|
|
222
224
|
|
|
223
|
-
|
|
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.
|
|
224
226
|
<!-- oracle-packs:start product.agentsfleet -->
|
|
225
|
-
The `agentsfleet` output block and
|
|
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.
|
|
226
228
|
<!-- oracle-packs:end -->
|
|
227
229
|
|
|
228
230
|
### REVIEW
|
|
@@ -250,7 +252,7 @@ Required when spec involved — after last COMMIT, before PR. Also runs when par
|
|
|
250
252
|
|
|
251
253
|
| # | When | Skill |
|
|
252
254
|
|---|---|---|
|
|
253
|
-
| 1 | VERIFY | `/orly-write-unit-test
|
|
255
|
+
| 1 | VERIFY | `/orly-write-unit-test` once per Section over that Section's diff, and again at the boundary — never skipped, never deferred. Then `/orly-write-integration-test` at the boundary **when the diff crosses a module boundary with real input/output**; otherwise record `N/A — <reason>` in Session Notes. |
|
|
254
256
|
| 2 | REVIEW | gstack `/review` — every runtime, same route; address findings or record a user-acked deferral; reviewer unavailable → Session Notes: *"skipped — <reason> <ts>; rerun before merge"* |
|
|
255
257
|
| 3 | After every push | `orly-babysit-prs` — CI check runs + greptile inline threads + PR-level summary; stops on two consecutive empty polls with CI green; never `gh pr checks --watch` for greptile |
|
|
256
258
|
|
package/dispatch/lib.sh
CHANGED
|
@@ -80,6 +80,7 @@ declare -A DISPATCH_GLOSS=(
|
|
|
80
80
|
[LOG]="LOGging discipline (scoped event, error_code, severity, redaction)"
|
|
81
81
|
[MSID]="Milestone-ID ban in source (M{N}_{NNN} / §x.y / T{N} / dim)"
|
|
82
82
|
[ERR]="ERror Registry (UZ-XXX-NNN declared + referenced)"
|
|
83
|
+
[ERR-RS]="Rust ERror discipline (one type per crate, Result alias, no stringified cause)"
|
|
83
84
|
[GRP]="GREptile rule audit (diff vs greptile-learnings/RULES.md codes)"
|
|
84
85
|
[LDC]="Legacy-Design Consult (A remove / B patch / C keep)"
|
|
85
86
|
)
|
|
@@ -92,13 +93,35 @@ dispatch_init() {
|
|
|
92
93
|
DISPATCH_EXTS=("$@")
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
# Paths this repository's engine wrote, one per line, from the install record.
|
|
97
|
+
#
|
|
98
|
+
# A managed file is orly's, not the repository's: the next `orly update`
|
|
99
|
+
# overwrites it, so a finding against one is a chore nobody can complete. Left
|
|
100
|
+
# unfiltered, staging a fresh install put orly's own leaf helpers in front of
|
|
101
|
+
# orly's own length gate — audits/logging.sh is 444 lines against a 350 cap —
|
|
102
|
+
# and the first commit of an adoption failed on the adoption itself.
|
|
103
|
+
dispatch_managed_paths() {
|
|
104
|
+
local config="$TARGET_ROOT/.oracle/orly.json"
|
|
105
|
+
[ -f "$config" ] || return 0
|
|
106
|
+
sed -n '/"managed"[[:space:]]*:[[:space:]]*\[/,/\]/p' "$config" \
|
|
107
|
+
| grep -oE '"[^"]+"' \
|
|
108
|
+
| sed -e 's/^"//' -e 's/"$//' \
|
|
109
|
+
| grep -vx 'managed' || true
|
|
110
|
+
}
|
|
111
|
+
|
|
95
112
|
# Resolve targets: explicit file args, or --staged self-discovery via git.
|
|
113
|
+
# Explicit arguments are never filtered — naming a file is asking about it.
|
|
96
114
|
dispatch_resolve_files() {
|
|
97
115
|
DISPATCH_FILES=()
|
|
98
116
|
if [ "${1:-}" = "--staged" ]; then
|
|
99
|
-
local pathspecs=() e
|
|
117
|
+
local pathspecs=() e managed
|
|
100
118
|
for e in "${DISPATCH_EXTS[@]}"; do pathspecs+=("$e"); done
|
|
101
|
-
|
|
119
|
+
managed="$(dispatch_managed_paths)"
|
|
120
|
+
while IFS= read -r f; do
|
|
121
|
+
[ -n "$f" ] || continue
|
|
122
|
+
[ -n "$managed" ] && grep -qxF "$f" <<<"$managed" && continue
|
|
123
|
+
DISPATCH_FILES+=("$f")
|
|
124
|
+
done \
|
|
102
125
|
< <(git -C "$TARGET_ROOT" diff --cached --name-only --diff-filter=ACMRT -- "${pathspecs[@]}" \
|
|
103
126
|
| grep -vE '(^|/)(vendor|third_party|node_modules|\.zig-cache|dist|build|\.next)/' || true)
|
|
104
127
|
elif [ "$#" -ge 1 ]; then
|
|
@@ -124,9 +147,11 @@ dispatch_resolve_files() {
|
|
|
124
147
|
# dispatch must never claim a file its gates cannot parse.
|
|
125
148
|
dispatch_add_shebang_files() {
|
|
126
149
|
[ "${1:-}" = "--staged" ] || return 0
|
|
127
|
-
local f base path firstline
|
|
150
|
+
local f base path firstline managed
|
|
151
|
+
managed="$(dispatch_managed_paths)"
|
|
128
152
|
while IFS= read -r f; do
|
|
129
153
|
[ -n "$f" ] || continue
|
|
154
|
+
[ -n "$managed" ] && grep -qxF "$f" <<<"$managed" && continue
|
|
130
155
|
base="${f##*/}"
|
|
131
156
|
case "$base" in *.*) continue ;; esac
|
|
132
157
|
path="$TARGET_ROOT/$f"; [ -f "$path" ] || path="$f"; [ -f "$path" ] || continue
|
package/dispatch/lifecycle.md
CHANGED
|
@@ -10,6 +10,33 @@ that moment. 🤔 judgment-only — no `.sh` pair; CONFORM's deterministic gates
|
|
|
10
10
|
Section-scan first (`grep -n "^## " dispatch/lifecycle.md`); read the stage you
|
|
11
11
|
are entering, not the whole file.
|
|
12
12
|
|
|
13
|
+
## What runs at each stage
|
|
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.
|
|
18
|
+
|
|
19
|
+
| Stage | Runs | Fired by |
|
|
20
|
+
|---|---|---|
|
|
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.
|
|
39
|
+
|
|
13
40
|
## CHORE (open) — runbook
|
|
14
41
|
|
|
15
42
|
1. Spec `docs/v*/pending/` → `active/`; `Status: IN_PROGRESS`; `Branch:` set.
|
package/dispatch/verify.md
CHANGED
|
@@ -31,24 +31,52 @@ Package-scoped runners (`bun run test`, `vitest <file>`, `cargo test -p <crate>`
|
|
|
31
31
|
are **not** verification — they skip every other package's lint and tests and the
|
|
32
32
|
cross-language gates. The declared `make` targets are the canonical gates.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Two cadences, one boundary
|
|
35
|
+
|
|
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.
|
|
35
62
|
|
|
36
63
|
| Target | When |
|
|
37
64
|
|---|---|
|
|
38
|
-
| `make
|
|
39
|
-
| `make
|
|
40
|
-
| `make
|
|
41
|
-
| `make check-version` |
|
|
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. |
|
|
42
70
|
| `make wire-fixtures` | The `/v1/runners` wire types changed. Regenerate, then review the diff — a changed fixture IS the wire change. |
|
|
43
71
|
| `make bench` (local) | Diff touches request-path code, allocator wiring, or startup/shutdown sequencing. |
|
|
44
72
|
| `API_BENCH_URL=https://api-dev.agentsfleet.net/healthz make bench` | After branch deploys to dev. |
|
|
45
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>`. |
|
|
46
74
|
| Acceptance e2e (live tier) | Diff touches a surface the live/acceptance tier covers — relevant suites green, or their opt-in skip matrix recorded. |
|
|
47
75
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
A package-scoped runner (`cargo test -p afd_wire`, `bun run test` inside a
|
|
51
|
-
package) proves that package and never the repository.
|
|
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.
|
|
52
80
|
|
|
53
81
|
## Wire-fixture evidence rule
|
|
54
82
|
|
|
@@ -70,10 +98,16 @@ generated output; a type change with no fixture diff means the emitter never ran
|
|
|
70
98
|
|
|
71
99
|
## Required output (done-message)
|
|
72
100
|
|
|
73
|
-
**Success:**
|
|
101
|
+
**Success (boundary):**
|
|
74
102
|
|
|
75
103
|
```
|
|
76
|
-
✅ Verified: 🧪 lint-all ✓ · 🧪 test-unit-all ✓ <N>p/<M>s · 🔆 harness-verify ✓ · 🔆 check-version ✓
|
|
104
|
+
✅ Verified: 🧪 lint-all ✓ · 🧪 test-unit-all ✓ <N>p/<M>s · 🧩 test-integration-rustd ✓ · 🔆 harness-verify ✓ · 🔆 check-version ✓
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Success (Section lane)** — names the lane it ran, never the repository:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
✅ Section verified: 🔆 harness-verify ✓ · 🧪 test-unit-<surface> ✓ <N>p/<M>s
|
|
77
111
|
```
|
|
78
112
|
|
|
79
113
|
**Failure (any required target failed):**
|