@agentsfleet/orly 0.7.0 → 0.8.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/SOUL.md +4 -1
- package/audits/agents-md.md +1 -0
- package/audits/logging.sh +90 -16
- 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_any.md +5 -4
- 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/LOGGING_STANDARD.md +14 -4
- package/docs/RULE_ENFORCEMENT.md +3 -3
- 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 +6 -0
- 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 +6 -1
- package/src/install_packs.test.ts +56 -0
- package/src/render.test.ts +59 -1
- package/src/verify.test.ts +72 -2
- package/src/verify.ts +44 -3
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):**
|
package/dispatch/write_any.md
CHANGED
|
@@ -127,7 +127,7 @@ shebang and is the authoritative check.
|
|
|
127
127
|
**Triggers** — every `Edit`/`Write` that adds, removes, or changes a log emit:
|
|
128
128
|
|
|
129
129
|
- `*.zig` outside `vendor/`/`third_party/`/`.zig-cache/`/`*_test.zig` — `std.log.*`, `std.debug.print`, raw stderr writes, calls into the `log` named module (source: `src/logging/mod.zig`).
|
|
130
|
-
- `*.rs`
|
|
130
|
+
- Every tracked or unignored `*.rs` outside `build.rs`, `examples/`, `tests/`, `benches/`, and `#[cfg(test)]` items — `tracing::*`, `println!`, `eprintln!`, and `dbg!`.
|
|
131
131
|
- `*.ts`/`*.tsx`/`*.js`/`*.jsx` outside `vendor/`/`node_modules/`/`*.test.*`/`*.spec.*` — `console.*`, custom logger calls.
|
|
132
132
|
- `*.sh` outside generated dirs — `echo`/`printf` to `&2`.
|
|
133
133
|
|
|
@@ -150,7 +150,8 @@ shebang and is the authoritative check.
|
|
|
150
150
|
| New `err`/`warn` log mapping to a domain failure | registry-scheme `error_code=` field required (agentsfleet: `UZ-XXX-NNN`). Registry entry must land in same commit. |
|
|
151
151
|
| Per-iteration / hot-loop log | Use `debug` (hidden by default), not `info`. |
|
|
152
152
|
| `info` level | No allow-list — `info` is open (§4 rule 2). Two checks instead: a boundary-crossing operation needs its `_started`/`_completed`\|`_failed` pair on every exit path (rule 1), and a per-iteration path is `debug` (rule 3). Hot-poll boundary pairs remain complete but use `debug`. |
|
|
153
|
-
| `console.log
|
|
153
|
+
| `console.log` in non-test source | Forbidden. Convert to the structured logger or delete before commit. |
|
|
154
|
+
| `std.debug.print` or a Rust print macro in runtime source | Convert to structured logging. When the stream output is the program interface, add `// logging: <reason>` on or immediately above the emit. The reason is mandatory. |
|
|
154
155
|
| `std.log.scoped` outside `src/logging/` | Migration target. The `log` named module's `scoped` API is the only non-test entry point; flip the file's alias and migrate every call site in the same commit. |
|
|
155
156
|
| `msg=` field | ≤ 300 chars. Stack traces emit as separate `event=stack_trace` debug record. |
|
|
156
157
|
| Multi-line values | Newlines must be `\n` literal (two chars), not raw newline byte. |
|
|
@@ -190,13 +191,13 @@ LOGGING GATE: <file>
|
|
|
190
191
|
|
|
191
192
|
> [DETERMINISTIC → LOG]
|
|
192
193
|
|
|
193
|
-
`logging.sh` walks
|
|
194
|
+
`logging.sh` walks `src/**/*.zig`, every tracked or unignored `*.rs`, and `agentsfleet/src/**/*.{js,jsx,ts,tsx}`. Rust repositories do not need to use a fixed source-root name. The staged mode reads the index, so a fix staged in pre-commit satisfies the check on the same hook run. `--staged` is preserved as an opt-in narrowing mode for iterative development.
|
|
194
195
|
|
|
195
196
|
#### End-of-turn audit
|
|
196
197
|
|
|
197
198
|
> [DETERMINISTIC → LOG]
|
|
198
199
|
|
|
199
|
-
`audits/logging.sh` runs as part of `make lint`. It greps `std.log.*`, Rust `tracing` and direct-print macros, `console.*`, and raw stderr writes. It does NOT see calls through the Zig `log` named module's `scoped()`, so a clean run is not evidence that §4/§5 hold everywhere. Mechanical today: direct Zig/Rust/TypeScript diagnostics, Rust's required `event` field and positional-format ban, `std.log.scoped` outside `src/lib/logging/`, and legacy Zig error-code hints. Reviewer-side: severity choice, entry/exit pairing, registry applicability, inline Rust expression hoisting, and Personally Identifiable Information (PII) spot-checks.
|
|
200
|
+
`audits/logging.sh` runs as part of `make lint`. It greps `std.log.*`, Rust `tracing` and direct-print macros, `console.*`, and raw stderr writes. It accepts a direct Zig or Rust stream write only when `// logging: <reason>` appears on or immediately above the emit with a non-empty reason. It does NOT see calls through the Zig `log` named module's `scoped()`, so a clean run is not evidence that §4/§5 hold everywhere. Mechanical today: direct Zig/Rust/TypeScript diagnostics, Rust's required `event` field and positional-format ban, `std.log.scoped` outside `src/lib/logging/`, and legacy Zig error-code hints. Reviewer-side: reason validity, severity choice, entry/exit pairing, registry applicability, inline Rust expression hoisting, and Personally Identifiable Information (PII) spot-checks.
|
|
200
201
|
|
|
201
202
|
#### Family
|
|
202
203
|
|
package/dispatch/write_go.md
CHANGED
|
@@ -22,5 +22,13 @@ a working value.
|
|
|
22
22
|
Table-driven tests are the default shape. Concurrency work needs a `-race` run,
|
|
23
23
|
and a deterministic contention test rather than a sleep.
|
|
24
24
|
|
|
25
|
+
A string literal repeated in one file becomes a named constant — `audits/ufs.sh`
|
|
26
|
+
reads `*.go` and enforces it. Two Go shapes are held out, because no rename can
|
|
27
|
+
fix them: members of a `const ( … )` group, which state the binding keyword once
|
|
28
|
+
on the opening line rather than per member, and backtick struct tags, whose
|
|
29
|
+
quoted halves (`json:"id"`) are addressed by reflection and cannot hold a const.
|
|
30
|
+
Both are carve-outs for what the *language* makes unfixable, not permission —
|
|
31
|
+
a flag name or a label spelled inline twice is still the rule.
|
|
32
|
+
|
|
25
33
|
The repository owns formatting, vet, lint, build, and test commands — it
|
|
26
34
|
declares them in `.oracle/orly.json`.
|
package/dispatch/write_rust.md
CHANGED
|
@@ -33,6 +33,172 @@ Local convention wins on conflict, but the divergence is named in the review
|
|
|
33
33
|
output. Refresh the copy when the user asks or a cited rule looks stale
|
|
34
34
|
(re-run the fetch; upstream updates continuously).
|
|
35
35
|
|
|
36
|
+
## Error discipline (RULE ERR-RS)
|
|
37
|
+
|
|
38
|
+
> [DETERMINISTIC → ERR-RS]
|
|
39
|
+
|
|
40
|
+
Two of the clauses below are decided by `audits/rust-error.sh`, which
|
|
41
|
+
`dispatch/write_rust.sh` runs over staged Rust: an error type with no `Result`
|
|
42
|
+
alias in its crate, and a `map_err` that stringifies its own cause. The other
|
|
43
|
+
two — whether a `source()` implementation returns its own kind, and whether a
|
|
44
|
+
context addition is warranted — need the call site read beside the diff and stay
|
|
45
|
+
the agent's call at write time.
|
|
46
|
+
|
|
47
|
+
One error type per crate, and a `Result` alias beside it:
|
|
48
|
+
|
|
49
|
+
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
|
50
|
+
|
|
51
|
+
Every fallible function in the crate returns `Result<T>`. The default type
|
|
52
|
+
parameter is what lets the few functions answering with a foreign error keep
|
|
53
|
+
the same spelling — `Result<T, VerifyError>` — instead of reaching for
|
|
54
|
+
`std::result::Result`. A reader must never have to check WHICH error a
|
|
55
|
+
signature returns to know it is this crate's.
|
|
56
|
+
|
|
57
|
+
**Compose with `From`; `?` does the lifting.** This is `M-FROM-ERROR` and is
|
|
58
|
+
read there, carve-out included; what the guideline leaves open is how the
|
|
59
|
+
variant is spelled, which locally is `#[from]`:
|
|
60
|
+
|
|
61
|
+
#[derive(Debug, thiserror::Error)]
|
|
62
|
+
pub enum BootFailure {
|
|
63
|
+
#[error(transparent)]
|
|
64
|
+
Environment(#[from] Refusal),
|
|
65
|
+
#[error("cannot boot: the database would not answer")]
|
|
66
|
+
Database(#[from] db::Error),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
**`map_err` only to ADD context the call site alone knows** — a role tag, a
|
|
70
|
+
budget, an operation name; the same carve-out `M-FROM-ERROR` names. And never
|
|
71
|
+
this, which no guideline covers:
|
|
72
|
+
|
|
73
|
+
// WRONG: a lossy conversion wearing a conversion's clothes.
|
|
74
|
+
.map_err(|error| Mine::Database(error.to_string()))?
|
|
75
|
+
|
|
76
|
+
`to_string()` on the way INTO an error type destroys the `source()` chain. It
|
|
77
|
+
compiles, it reads fine, and it silently defeats every chain walker downstream.
|
|
78
|
+
|
|
79
|
+
**`source()` returns what caused you, never yourself.** An error whose
|
|
80
|
+
`Display` already renders its kind must not also return that kind as its
|
|
81
|
+
source, or a chain walker prints the same sentence twice before reaching
|
|
82
|
+
anything new:
|
|
83
|
+
|
|
84
|
+
// WRONG — Display is "[code] {kind}", so the kind is already printed.
|
|
85
|
+
fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&self.kind) }
|
|
86
|
+
|
|
87
|
+
// RIGHT — skip ourselves, hand back what the kind wraps.
|
|
88
|
+
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
89
|
+
std::error::Error::source(&self.kind)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
**Not every error has a cause, and that is not a defect.** A variant holding
|
|
93
|
+
another error has one; a variant holding only data (`MissingUrl { knob }`) does
|
|
94
|
+
not — nothing *caused* an unset variable. A test asserting `source().is_some()`
|
|
95
|
+
for every variant is WRONG and forces authors to invent causes. Assert the real
|
|
96
|
+
invariant: where there IS a source, it is not a repeat of our own message.
|
|
97
|
+
|
|
98
|
+
**Divergence from the reference guideline, named as that section requires.**
|
|
99
|
+
`M-ERRORS-CANONICAL-STRUCTS` prescribes a situation-specific `struct` carrying
|
|
100
|
+
a `Backtrace` and lets a complex crate expose several error types;
|
|
101
|
+
`M-APP-ERROR` further lets an application crate skip its own type for
|
|
102
|
+
`anyhow`/`eyre`/`ohno::AppError`. This rule diverges on all three for one
|
|
103
|
+
reason: a `#[from]`-composed enum behind a single per-crate alias is what lets
|
|
104
|
+
`?` lift across a crate boundary with no call-site conversion, and an
|
|
105
|
+
application-level catch-all erases the variant the caller would have matched
|
|
106
|
+
on. A `Backtrace` inside a variant is welcome. A crate with two genuinely
|
|
107
|
+
unrelated fallible surfaces may carry a second type — with its own alias, under
|
|
108
|
+
these same rules. Cite this divergence in review rather than re-deriving it.
|
|
109
|
+
|
|
110
|
+
Prior art, not invention: `core_api` (multi-crate workspace, per-crate
|
|
111
|
+
`error.rs` with `pub type Result<T>` + flat `Error`, in production for years),
|
|
112
|
+
bun (`thiserror`, `#[from]`, `pub type Result<T, E = Error>`), habitat (one
|
|
113
|
+
payload-carrying `enum Error`, one alias).
|
|
114
|
+
|
|
115
|
+
## Functional design (RULE FN-RS)
|
|
116
|
+
|
|
117
|
+
Design the fallible path as a Result PIPELINE, never as a control-flow tree.
|
|
118
|
+
|
|
119
|
+
**Fallibility lives in the type, never in a sentinel.** A function that can
|
|
120
|
+
fail returns `Result<T>`; a value that can be absent returns `Option<T>`.
|
|
121
|
+
Never a bool-plus-out-parameter, never a magic value (-1, empty string, null
|
|
122
|
+
UUID), never a logged-and-swallowed error. If the caller cannot see the
|
|
123
|
+
failure in the signature, the design is wrong before the body is written.
|
|
124
|
+
|
|
125
|
+
**Compose with combinators; spend `match` only where arms genuinely differ.**
|
|
126
|
+
`?`, `map`, `and_then`, `ok_or_else`, `unwrap_or_else` say "transform on
|
|
127
|
+
success, pass failure through" in one line, where a nested match spends five
|
|
128
|
+
and gives every arm a chance to swallow the `Err`:
|
|
129
|
+
|
|
130
|
+
// WRONG: the failure path is hand-plumbed, twice.
|
|
131
|
+
let config = match load(path) {
|
|
132
|
+
Ok(raw) => match parse(&raw) {
|
|
133
|
+
Ok(c) => c,
|
|
134
|
+
Err(e) => return Err(e.into()),
|
|
135
|
+
},
|
|
136
|
+
Err(e) => return Err(e.into()),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// RIGHT: one pipeline; `?` and `From` (RULE ERR-RS) do the lifting.
|
|
140
|
+
let config = parse(&load(path)?)?;
|
|
141
|
+
|
|
142
|
+
A `match` earns its place when the arms carry different LOGIC — not when one
|
|
143
|
+
arm is a hand-written `?`.
|
|
144
|
+
|
|
145
|
+
**Make illegal states unrepresentable; parse, don't validate.** Convert raw
|
|
146
|
+
input into a type that can only hold valid states ONCE, at the boundary, and
|
|
147
|
+
pass that type inward. Two `Option` fields where exactly one is ever set is an
|
|
148
|
+
enum. A `state: String` compared against literals is an enum. A checked-then-
|
|
149
|
+
used raw value is a newtype whose constructor returns `Result` — that
|
|
150
|
+
constructor is `M-STRONG-TYPES-GUARD`, and picking the type family is
|
|
151
|
+
`M-STRONG-TYPES`. What those do not say is the consequence: interior code that
|
|
152
|
+
trusts its types needs no defensive re-checks — delete them.
|
|
153
|
+
|
|
154
|
+
**Expression-orientation: bind once.** `let x = if ... { a } else { b };` and
|
|
155
|
+
`let x = match ...` — never a mutable default that later branches overwrite.
|
|
156
|
+
A `mut` that exists only to simulate an expression is a bug surface with no
|
|
157
|
+
compensating power.
|
|
158
|
+
|
|
159
|
+
**`unwrap()` / `expect()` are for tests and for invariants already proved.**
|
|
160
|
+
`M-PANIC-ON-BUG` settles when a panic is right and `M-PANIC-MESSAGE` that it
|
|
161
|
+
must be helpful; the addition here is WHAT the message carries — the proof
|
|
162
|
+
("mutex never poisoned: no thread panics while holding it"). In production
|
|
163
|
+
code, an `unwrap()` without a written invariant is a `Result` that was never
|
|
164
|
+
designed.
|
|
165
|
+
|
|
166
|
+
Prior art, not invention: `core_api` — a multi-crate production workspace
|
|
167
|
+
where every crate with a fallible surface carries its own `error.rs` +
|
|
168
|
+
`Result` alias, and the fallible path is written as pipelines throughout
|
|
169
|
+
(573 `.map`, 463 `.and_then`, 164 `.ok_or_else` across the workspace). It has
|
|
170
|
+
run in production for years; its author attributes the absence of error-path
|
|
171
|
+
defects to exactly this design, and the maintenance record bears that out.
|
|
172
|
+
|
|
173
|
+
## Constant discipline (RULE UFS)
|
|
174
|
+
|
|
175
|
+
`audits/ufs.sh` reads `*.rs`. It did not until this pack said so, and the cost
|
|
176
|
+
was concrete: a crate spelled a wire verb inline twice — `pong == "PONG" ||
|
|
177
|
+
pong.contains("PONG")` — while `write_any.sh` printed a green UFS row over the
|
|
178
|
+
file, because the façade had always declared `*.rs` and the leaf had never read
|
|
179
|
+
it. Bind the repeated literal to a `const` or `static` and use the name.
|
|
180
|
+
|
|
181
|
+
Four Rust shapes are held out of the count, because no rename can fix them:
|
|
182
|
+
|
|
183
|
+
- **`#[cfg(test)]` and `#[test]` blocks.** Rust keeps unit tests inside the file
|
|
184
|
+
they cover, so fixture keys would otherwise read as that file's worst debt.
|
|
185
|
+
- **Feature-gated test seams** — `#[cfg(feature = "test-util")]`. A helper that
|
|
186
|
+
hands a sibling integration test one of each error kind has to compile into
|
|
187
|
+
the crate, which is the only reason it is not behind `cfg(test)`; its contents
|
|
188
|
+
are fixture data all the same. The match is narrow on purpose: `test` as a
|
|
189
|
+
whole word or a `-`/`_` segment, so `#[cfg(feature = "redis")]` still counts.
|
|
190
|
+
- **Attribute literals** — `#[serde(rename = "…")]`, `#[cfg(feature = "…")]`,
|
|
191
|
+
`#[doc = "…"]`. An attribute takes a literal token by language rule; a const
|
|
192
|
+
is not accepted in that position.
|
|
193
|
+
- **`tests/` and `benches/`** at the crate root, which are fixture trees.
|
|
194
|
+
|
|
195
|
+
Everything else counts, including `examples/` — not because it compiles, but
|
|
196
|
+
because what repeats there is coupling, not demonstration. A cache namespace
|
|
197
|
+
declared at the top of a walkthrough and matched ninety lines below it, or a
|
|
198
|
+
fixture id that is both the fake store's match arm and the key five call sites
|
|
199
|
+
pass, breaks silently when one end moves. That is the drift RULE UFS exists to
|
|
200
|
+
catch, and example code is where a reader copies it from.
|
|
201
|
+
|
|
36
202
|
## Evolution
|
|
37
203
|
|
|
38
204
|
This pack is young next to the Zig façade and grows the same way: a Rust
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# dispatch/write_rust.sh — the Rust dispatch, DETERMINISTIC façade.
|
|
3
|
+
#
|
|
4
|
+
# Pairs with dispatch/write_rust.md (the LATENT façade — prose the agent reads).
|
|
5
|
+
# This .sh runs the mechanically-checkable subset of the Rust discipline and
|
|
6
|
+
# emits ONE verdict block. The latent trigger ("about to write a *.rs file")
|
|
7
|
+
# dispatches here; everything below is deterministic and re-runnable.
|
|
8
|
+
#
|
|
9
|
+
# dispatch/write_rust.sh <src/file.rs> [...] # explicit targets (EXECUTE)
|
|
10
|
+
# dispatch/write_rust.sh --staged # staged *.rs (CONFORM, pre-commit)
|
|
11
|
+
#
|
|
12
|
+
# Layering: AGENTS.md → write_rust.md (latent) → write_rust.sh (this) → audits/*.sh
|
|
13
|
+
# Signals: 🟢 pass · 🔴 fail (blocks) · 🤔 DECIDE (judgment; blocks the TURN, not the script) · 🟣 delegated
|
|
14
|
+
# Exit: 0 = mechanical gates pass · 1 = ≥1 failed · 2 = usage error.
|
|
15
|
+
#
|
|
16
|
+
# Note: Rust had no deterministic façade at all until this file, which is why
|
|
17
|
+
# the error standard the operating model calls mandatory was enforced by review
|
|
18
|
+
# alone. ERR-RS carries the two clauses a machine can decide from the text — a
|
|
19
|
+
# map_err that stringifies its own cause, and an error type with no Result alias
|
|
20
|
+
# beside it. The rest of write_rust.md stays judgment on purpose: ownership,
|
|
21
|
+
# justified `unsafe`, feature combinations, and whether a source() implementation
|
|
22
|
+
# returns its own kind all need a call site read beside the diff, and a gate that
|
|
23
|
+
# guesses at those is a gate people learn to ignore.
|
|
24
|
+
#
|
|
25
|
+
# UFS over *.rs is NOT re-run here. It is wired once in its home façade
|
|
26
|
+
# (write_any), and the universal-code carve-out makes that row satisfy the tag
|
|
27
|
+
# wherever it appears — running it twice would report one violation as two.
|
|
28
|
+
|
|
29
|
+
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
30
|
+
|
|
31
|
+
dispatch_init "RUST" '*.rs'
|
|
32
|
+
dispatch_resolve_files "$@"
|
|
33
|
+
dispatch_header
|
|
34
|
+
|
|
35
|
+
# ── deterministic gates ────────────────────────────────────────────
|
|
36
|
+
dispatch_run_helper "ERR-RS" rust-error.sh --staged
|
|
37
|
+
|
|
38
|
+
dispatch_verdict
|
|
@@ -15,7 +15,7 @@ Every triggered document requires one `📖 DOC READ: <path>` proof-line **per t
|
|
|
15
15
|
| `*.zig` | `dispatch/write_zig.md`. ZIG GATE per edit. |
|
|
16
16
|
| `*.ts`/`*.tsx`/`*.js`/`*.jsx` | `dispatch/write_ts_adhere_bun.md` — TS FILE SHAPE DECISION (§1) at PLAN, const/import/Bun-primitive discipline, anti-patterns. |
|
|
17
17
|
| `*.rs` | `dispatch/write_rust.md` — ownership, error variants, feature combinations, and contention tests. | <!-- oracle-packs:language.rust -->
|
|
18
|
-
| Rust error type, `Result` alias, or any fallible signature | `dispatch/write_rust.md` §Error discipline — one error per crate, one `Result` alias, `#[from]` composition, `source()` never returns self. | <!-- oracle-packs:language.rust -->
|
|
18
|
+
| Rust error type, `Result` alias, or any fallible signature | `dispatch/write_rust.md` §Error discipline — one error per crate, one `Result` alias, `#[from]` composition, `source()` never returns self. ERR-RS fires per edit: `audits/rust-error.sh` (via `dispatch/write_rust.sh`) blocks a `map_err` that stringifies its own cause and an error type with no alias in its crate; the remaining clauses stay judgment. | <!-- oracle-packs:language.rust -->
|
|
19
19
|
| Designing a Rust function, type, or module; writing `match` on a `Result`/`Option`; any production `unwrap()` | `dispatch/write_rust.md` §Functional design — Result pipelines over control-flow trees, illegal states unrepresentable, bind once, `unwrap` needs a proved invariant. | <!-- oracle-packs:language.rust -->
|
|
20
20
|
| `*.py` | `dispatch/write_python.md` — parsing boundaries, resource ownership, and specific exceptions. |
|
|
21
21
|
| `*.sh` | `dispatch/write_shell.md` — quoting, array arguments, cleanup, input safety, and shell compatibility. |
|
package/docs/LOGGING_STANDARD.md
CHANGED
|
@@ -250,7 +250,7 @@ The `comptime if ... return` makes the entire emit path disappear when the scope
|
|
|
250
250
|
| Pattern | Why banned | Fix |
|
|
251
251
|
|---|---|---|
|
|
252
252
|
| `std.log.info("..."` outside `logging.scoped(.tag)` | Bypasses scope/event/field discipline. | Convert to `logging.scoped(.tag).info("event", .{...})`. |
|
|
253
|
-
| `std.debug.print(` in non-test source | No level, no scope, no field structure. Dev-only debugging that escaped to main. | Convert or delete
|
|
253
|
+
| `std.debug.print(` in non-test source | No level, no scope, no field structure. Dev-only debugging that escaped to main. | Convert or delete. If stdout or stderr is the program interface, add `// logging: <reason>` on or immediately above the emit. |
|
|
254
254
|
| `std.log.err` without `error_code=` field | Drops registry traceability. | Add the registry code, or add a registry entry if missing. |
|
|
255
255
|
| `std.log.scoped(...)` outside `src/lib/logging/` | Free-form printf-style emit. | Switch the file's alias to `const log = logging.scoped(.tag);` and migrate every call site to the event+fields form. |
|
|
256
256
|
| Positional `{s} {d}` placeholders | Not logfmt; not greppable. | Convert to struct-of-fields. |
|
|
@@ -295,7 +295,7 @@ class AgentError extends Error {
|
|
|
295
295
|
|
|
296
296
|
Throw-style modules `throw new AgentError({...})`. Result-style modules return `{ ok: false, error: new AgentError({...}) }`. Render path picks human or JSON based on the runtime mode.
|
|
297
297
|
|
|
298
|
-
## §8A · Per-language binding — Rust
|
|
298
|
+
## §8A · Per-language binding — Rust
|
|
299
299
|
|
|
300
300
|
> [DETERMINISTIC → LOG]
|
|
301
301
|
|
|
@@ -333,10 +333,20 @@ match. Renaming is a separate, intentional observability migration.
|
|
|
333
333
|
|
|
334
334
|
| Pattern | Why banned | Fix |
|
|
335
335
|
|---|---|---|
|
|
336
|
-
| `println!`, `eprintln!`, or `dbg!` in
|
|
336
|
+
| `println!`, `eprintln!`, or `dbg!` in runtime source | No level, event, or structured fields. | Convert to a `tracing` emit or delete. If the stream is the program interface, add `// logging: <reason>` on or immediately above the emit. |
|
|
337
337
|
| `tracing::<level>!` without `event = ...` | Drops the stable §3 event key. | Add the byte-stable snake_case event field. |
|
|
338
338
|
| Positional formatting in a `tracing` message | Hides values inside text and defeats field queries. | Hoist values and emit named fields. |
|
|
339
339
|
|
|
340
|
+
The annotation reason is mandatory and must be non-empty. A bare `// logging:`
|
|
341
|
+
marker still fails. The annotation is for direct program output, such as a
|
|
342
|
+
command result or a fatal renderer that must work before tracing starts. It does
|
|
343
|
+
not excuse an incomplete `tracing` emit.
|
|
344
|
+
|
|
345
|
+
```rust
|
|
346
|
+
// logging: stdout is this command's answer, not a log record
|
|
347
|
+
println!("{}", rendered_summary);
|
|
348
|
+
```
|
|
349
|
+
|
|
340
350
|
## §9 · Pretty-printer (dev-only render variant)
|
|
341
351
|
|
|
342
352
|
In production: every record on the wire is logfmt. No exceptions.
|
|
@@ -379,7 +389,7 @@ Failure modes the audit script and reviewer must close. These are **not aspirati
|
|
|
379
389
|
|
|
380
390
|
| # | Rationalization | Closure |
|
|
381
391
|
|---|---|---|
|
|
382
|
-
| L1 | "Temporary debug print, I'll remove later" | `logging.sh`
|
|
392
|
+
| L1 | "Temporary debug print, I'll remove later" | `logging.sh` rejects `std.debug.print`, Rust `println!` / `eprintln!` / `dbg!`, and `console.log` / `console.debug` / `console.info` in runtime source. Direct Zig or Rust stream output needs `// logging: <reason>` on or immediately above the emit. The reason must be non-empty and explain why the stream is the program interface. |
|
|
383
393
|
| L2 | "`std.log.scoped` is fine, `obs.scoped` is just a wrapper" | `std.log.scoped` is **forbidden** in `src/**/*.zig` outside `src/lib/logging/`. Only `obs.scoped` is callable. Audit flags every `std.log.` call site. |
|
|
384
394
|
| L3 | "I added `error_code=UZ-NEW-001` — registry entry coming next commit" | The registry entry **must land in the same commit** as the first reference. `error-codes.sh` runs against the staged diff; missing entry = blocking. |
|
|
385
395
|
| L4 | "This per-iteration event matters for debugging — `info`-level" | Per-iteration and per-row paths are `debug`, including mandatory boundary pairs on hot polls. `debug` is the level with a volume switch. This closes the ONLY `info` prohibition — there is no allow-list of event names and none may be reintroduced (§4). The inverse rationalization is closed too: "this operation is obviously fine, no need to log it" does not survive §4 rule 1, which requires a `_started`/`_completed`\|`_failed` pair on every boundary-crossing operation. Reviewer checks the pair and the loop, never the spelling. |
|
package/docs/RULE_ENFORCEMENT.md
CHANGED
|
@@ -6,15 +6,15 @@ regenerates this file and fails on any difference. To update it, run
|
|
|
6
6
|
|
|
7
7
|
| Rule document | enforced by | judged | acknowledged | classified | trigger |
|
|
8
8
|
|---|---|--:|--:|--:|---|
|
|
9
|
-
| `docs/LOGGING_STANDARD.md` | `LOG` → `logging.sh` | 11 | 13 |
|
|
9
|
+
| `docs/LOGGING_STANDARD.md` | `LOG` → `logging.sh` | 11 | 13 | 30/30 | mechanical |
|
|
10
10
|
| `docs/REST_API_DESIGN_GUIDELINES.md` | — | 0 | 0 | 0/74 | mechanical |
|
|
11
11
|
| `docs/SCHEMA_CONVENTIONS.md` | — | 0 | 0 | 0/16 | mechanical |
|
|
12
12
|
| `docs/DOCUMENTATION_RULES.md` | — | 0 | 0 | 0/22 | latent |
|
|
13
13
|
| `docs/LIFECYCLE_PATTERNS.md` | — | 0 | 0 | 0/22 | mechanical |
|
|
14
14
|
| `docs/CHANGELOG_VOICE.md` | — | 0 | 0 | 0/3 | latent |
|
|
15
|
-
| `docs/VERIFY_TIERS.md` | — | 0 | 0 | 0/
|
|
15
|
+
| `docs/VERIFY_TIERS.md` | — | 0 | 0 | 0/7 | uncited |
|
|
16
16
|
| `docs/greptile-learnings/RULES.md` | — | 0 | 0 | 0/114 | mechanical |
|
|
17
|
-
| **corpus** | | | | **
|
|
17
|
+
| **corpus** | | | | **30/288** | |
|
|
18
18
|
|
|
19
19
|
## How to read this
|
|
20
20
|
|
package/docs/VERIFY_TIERS.md
CHANGED
|
@@ -8,16 +8,33 @@ page carries only what is specific to this repository.
|
|
|
8
8
|
|
|
9
9
|
| Tier | Command | When |
|
|
10
10
|
|---|---|---|
|
|
11
|
-
| conform | `make harness-verify` |
|
|
12
|
-
| lint | `make lint-all` |
|
|
13
|
-
| unit | `make test-unit-all` |
|
|
14
|
-
| version | `make check-version` |
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
| conform | `make harness-verify` | Section lane and boundary, after EXECUTE and before the rest. Any 🔴 returns to EXECUTE. |
|
|
12
|
+
| lint | `make lint-all` | Boundary. Rust rides `lint-rustd` (rustfmt + Clippy, warnings are errors); script self-tests ride `lint-scripts`. |
|
|
13
|
+
| unit | `make test-unit-all` | Boundary. The cargo workspace plus every TypeScript package coverage gate. |
|
|
14
|
+
| version | `make check-version` | Boundary. `VERSION` against `build.zig.zon`, `cli/package.json` and both `rustd/Cargo.toml` sites. |
|
|
15
|
+
| integration | `make test-integration-rustd` | Boundary. Live Postgres and Redis via docker compose, schemas reset per run. `orly gate pr` skips it on a branch carrying no code. |
|
|
16
|
+
|
|
17
|
+
These five are exactly what `.oracle/orly.json` declares and exactly what
|
|
18
|
+
`orly gate pr` runs, so the rubric and the mechanical gate grade one boundary.
|
|
19
|
+
|
|
20
|
+
**Two cadences.** A Section closing is not a milestone closing. Inside a
|
|
21
|
+
Section, run `make harness-verify` plus the lane covering the surface the
|
|
22
|
+
Section touched (`make test-unit-rustd`, `make test-unit-app`, …) — enough to
|
|
23
|
+
prove the Section, never a repository claim. The full table above runs ONCE, at
|
|
24
|
+
CHORE(close).
|
|
25
|
+
|
|
26
|
+
**Which gate runs which row.** `orly gate work` runs the `conform` row alone and
|
|
27
|
+
is what the pre-commit hook fires, so it costs seconds. `orly gate verify` runs
|
|
28
|
+
the lint, unit, and version rows and is what pre-push fires. `orly gate pr` runs
|
|
29
|
+
the integration row plus the whole-branch and spec criteria, and is what
|
|
30
|
+
CHORE(close) fires — it skips the fast rows because `git.pushed` proves HEAD is
|
|
31
|
+
the commit pre-push already graded.
|
|
32
|
+
|
|
33
|
+
One lane needs live datastores, and only one. `make/test-infra.mk` brings up
|
|
34
|
+
docker compose Postgres and Redis for `test-integration-rustd`; `make
|
|
35
|
+
test-unit-all` stays datastore-free, because every Rust test needing one is
|
|
36
|
+
`#[ignore]`d and runs only in that lane. `KEEP_TEST_STATE=1` skips the reset for
|
|
37
|
+
the inner loop; Continuous Integration (CI) never sets it.
|
|
21
38
|
|
|
22
39
|
## Test Baseline
|
|
23
40
|
|
|
@@ -61,7 +78,14 @@ Paste the deciding line, not the exit code. A gate that reports a number — a s
|
|
|
61
78
|
against a cap, a count against a budget — is owed that number in the done message.
|
|
62
79
|
|
|
63
80
|
```
|
|
64
|
-
✅ Verified: 🧪 lint-all ✓ · 🧪 test-unit-all ✓ <N>p/<M>s · 🔆 harness-verify ✓ · 🔆 check-version ✓
|
|
81
|
+
✅ Verified: 🧪 lint-all ✓ · 🧪 test-unit-all ✓ <N>p/<M>s · 🧩 test-integration-rustd ✓ · 🔆 harness-verify ✓ · 🔆 check-version ✓
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A Section lane reports what it ran and says so, never borrowing the repository's
|
|
85
|
+
verdict:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
✅ Section verified: 🔆 harness-verify ✓ · 🧪 test-unit-<surface> ✓ <N>p/<M>s
|
|
65
89
|
```
|
|
66
90
|
|
|
67
91
|
A skipped target is surfaced as 🟠 with its reason, never dressed as a pass.
|
|
@@ -51,6 +51,7 @@ every code a dispatch `.sh` emits resolves to exactly one row here.
|
|
|
51
51
|
| LOG | LOGging discipline (scoped event, error_code, severity, redaction) |
|
|
52
52
|
| MSID | Milestone-ID ban in source (M{N}_{NNN} / §x.y / T{N} / dim) |
|
|
53
53
|
| ERR | ERror Registry (UZ-XXX-NNN declared + referenced) |
|
|
54
|
+
| ERR-RS | Rust ERror discipline (one type per crate, Result alias, no stringified cause) |
|
|
54
55
|
| GRP | GREptile rule audit (diff vs greptile-learnings/RULES.md codes) |
|
|
55
56
|
| LDC | Legacy-Design Consult (A remove / B patch / C keep) |
|
|
56
57
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentsfleet/orly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Repository-scoped engineering harness: renders agent rules, materialises the gates that enforce them, and proves the PR boundary \u2014 for any coding agent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,15 @@ output. Refresh the copy when the user asks or a cited rule looks stale
|
|
|
35
35
|
|
|
36
36
|
## Error discipline (RULE ERR-RS)
|
|
37
37
|
|
|
38
|
+
> [DETERMINISTIC → ERR-RS]
|
|
39
|
+
|
|
40
|
+
Two of the clauses below are decided by `audits/rust-error.sh`, which
|
|
41
|
+
`dispatch/write_rust.sh` runs over staged Rust: an error type with no `Result`
|
|
42
|
+
alias in its crate, and a `map_err` that stringifies its own cause. The other
|
|
43
|
+
two — whether a `source()` implementation returns its own kind, and whether a
|
|
44
|
+
context addition is warranted — need the call site read beside the diff and stay
|
|
45
|
+
the agent's call at write time.
|
|
46
|
+
|
|
38
47
|
One error type per crate, and a `Result` alias beside it:
|
|
39
48
|
|
|
40
49
|
pub type Result<T, E = Error> = core::result::Result<T, E>;
|