@agentsfleet/orly 0.5.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.
Files changed (112) hide show
  1. package/.githooks/pre-commit +42 -0
  2. package/.githooks/pre-push +67 -0
  3. package/AGENTS.md +348 -0
  4. package/LICENSE +21 -0
  5. package/README.md +249 -0
  6. package/SOUL.md +90 -0
  7. package/audits/agents-md.md +497 -0
  8. package/audits/agents-md.sh +353 -0
  9. package/audits/cross-tier-rates.sh +105 -0
  10. package/audits/data.sh +185 -0
  11. package/audits/deinit-pairs.sh +224 -0
  12. package/audits/design-tokens.sh +143 -0
  13. package/audits/doc-read.sh +183 -0
  14. package/audits/error-codes.sh +221 -0
  15. package/audits/fixtures/clean.diff +15 -0
  16. package/audits/fixtures/dirty.diff +16 -0
  17. package/audits/lifecycle-anchors.sh +22 -0
  18. package/audits/logging.sh +217 -0
  19. package/audits/msid-ui.sh +144 -0
  20. package/audits/parity-dispatch.sh +51 -0
  21. package/audits/rule-ledger-lib.sh +264 -0
  22. package/audits/rule-ledger.sh +225 -0
  23. package/audits/rule-paths.sh +108 -0
  24. package/audits/spec-template.sh +258 -0
  25. package/audits/sql-mod.sh +102 -0
  26. package/audits/ufs.sh +303 -0
  27. package/bin/orly +26 -0
  28. package/core/operating-model.md +256 -0
  29. package/dispatch/edit_rules.md +69 -0
  30. package/dispatch/lib.sh +213 -0
  31. package/dispatch/lifecycle.md +187 -0
  32. package/dispatch/name_architecture.md +93 -0
  33. package/dispatch/verify.md +111 -0
  34. package/dispatch/write_any.md +740 -0
  35. package/dispatch/write_any.sh +49 -0
  36. package/dispatch/write_auth.md +36 -0
  37. package/dispatch/write_changelog.md +64 -0
  38. package/dispatch/write_documentation.md +45 -0
  39. package/dispatch/write_go.md +24 -0
  40. package/dispatch/write_http.md +45 -0
  41. package/dispatch/write_mdx.md +7 -0
  42. package/dispatch/write_python.md +8 -0
  43. package/dispatch/write_rust.md +38 -0
  44. package/dispatch/write_shell.md +8 -0
  45. package/dispatch/write_spec.md +105 -0
  46. package/dispatch/write_sql.md +112 -0
  47. package/dispatch/write_sql.sh +36 -0
  48. package/dispatch/write_ts_adhere_bun.md +493 -0
  49. package/dispatch/write_ts_adhere_bun.sh +37 -0
  50. package/dispatch/write_zig.md +852 -0
  51. package/dispatch/write_zig.sh +40 -0
  52. package/docs/CHANGELOG_VOICE.md +18 -0
  53. package/docs/DISPATCH_ARCHITECTURE.md +664 -0
  54. package/docs/DISPATCH_REVIEW_DISPOSITION.md +65 -0
  55. package/docs/DOCUMENTATION_RULES.md +209 -0
  56. package/docs/EXECUTE_DOC_READS.md +29 -0
  57. package/docs/HARNESS_VERIFY_OUTPUT.md +40 -0
  58. package/docs/LIFECYCLE_PATTERNS.md +316 -0
  59. package/docs/LOGGING_STANDARD.md +397 -0
  60. package/docs/ORLY_ARCHITECTURE.md +129 -0
  61. package/docs/REST_API_DESIGN_GUIDELINES.md +783 -0
  62. package/docs/RULE_ENFORCEMENT.md +41 -0
  63. package/docs/SCHEMA_CONVENTIONS.md +93 -0
  64. package/docs/TEMPLATE.md +411 -0
  65. package/docs/VERIFY_TIERS.md +57 -0
  66. package/docs/greptile-learnings/README.md +51 -0
  67. package/docs/greptile-learnings/RULES.md +1045 -0
  68. package/fixtures/registry-invalid.json +1 -0
  69. package/fixtures/registry-valid.json +18 -0
  70. package/fixtures/unclosed-pack-block.md +8 -0
  71. package/package.json +63 -0
  72. package/packs/language/go/rules.md +24 -0
  73. package/packs/language/mdx/rules.md +7 -0
  74. package/packs/language/python/rules.md +8 -0
  75. package/packs/language/rust/rules.md +38 -0
  76. package/packs/language/shell/rules.md +8 -0
  77. package/registry.json +405 -0
  78. package/schemas/evidence.schema.json +43 -0
  79. package/schemas/profile.schema.json +12 -0
  80. package/schemas/registry.schema.json +12 -0
  81. package/skills/orly-babysit-prs/SKILL.md +326 -0
  82. package/skills/orly-spec-new/SKILL.md +157 -0
  83. package/skills/orly-write-integration-test/SKILL.md +470 -0
  84. package/skills/orly-write-unit-test/SKILL.md +520 -0
  85. package/src/cli.test.ts +70 -0
  86. package/src/cli.ts +287 -0
  87. package/src/config.test.ts +226 -0
  88. package/src/config.ts +288 -0
  89. package/src/criteria.test.ts +137 -0
  90. package/src/criteria.ts +166 -0
  91. package/src/criteria_spec.ts +127 -0
  92. package/src/criteria_support.ts +53 -0
  93. package/src/gates.test.ts +183 -0
  94. package/src/gates.ts +169 -0
  95. package/src/gates_spec.test.ts +98 -0
  96. package/src/gates_test_support.ts +194 -0
  97. package/src/git_env.ts +20 -0
  98. package/src/install.test.ts +514 -0
  99. package/src/install.ts +447 -0
  100. package/src/install_nested.test.ts +32 -0
  101. package/src/model.test.ts +31 -0
  102. package/src/model.ts +245 -0
  103. package/src/references.test.ts +47 -0
  104. package/src/references.ts +147 -0
  105. package/src/render.test.ts +60 -0
  106. package/src/render.ts +94 -0
  107. package/src/surfaces.test.ts +43 -0
  108. package/src/surfaces.ts +89 -0
  109. package/src/validation.test.ts +11 -0
  110. package/src/validation.ts +87 -0
  111. package/src/verify.test.ts +13 -0
  112. package/src/verify.ts +59 -0
@@ -0,0 +1,740 @@
1
+ # write_any.md — cross-cutting authoring latent façade
2
+
3
+ **Load protocol — sectioned, not whole-file.** Scan the section headers first
4
+ (`grep -nE "^##+ " dispatch/write_any.md` — two-plus hashes, so the gate cards
5
+ nested under container headings surface too), read §Scope plus the sections the
6
+ diff shape touches, and cite them in the proof-line; on sub-task shape change
7
+ re-scan and read only newly-touched sections — never re-read the full file
8
+ within a session.
9
+
10
+ This is the prose the AGENT consults before writing ANY source file (`*.zig` / `*.ts` / `*.tsx` / `*.js` / `*.jsx` / `*.py` / `*.rs` / `*.go` / `*.sh` / `*.sql`). These are the language-agnostic authoring invariants that apply identically to every surface — literal hygiene, length, observability, milestone-free naming, dead-code/legacy. They fire IN ADDITION to the language façade (`write_zig` / `write_ts_adhere_bun` / `write_sql`), not instead of it. It pairs with the deterministic façade `dispatch/write_any.sh`. Nine dissolving gate cards are merged verbatim below (headings demoted one level, each subsection tagged); the durable rule catalogue stays in `docs/greptile-learnings/RULES.md` (retained), which the Greptile Gate audits. Mechanical thresholds live once in the `.sh`; this file references rule codes, never restates the numbers.
11
+
12
+ **Signal legend** (printed by `write_any.sh`):
13
+
14
+ - 🟢 pass — deterministic check passed.
15
+ - 🔴 fail — deterministic check failed (or helper absent); STOP, fix, rerun.
16
+ - 🤔 DECIDE — judgment-only; no script can decide, the agent reads the section and makes the call (blocks the TURN, not the script).
17
+ - 🟣 delegated — the checker runs only in the product repo, not in dotfiles.
18
+
19
+ **Tag legend** — each section heading below carries one of:
20
+
21
+ - `> [DETERMINISTIC → <CODE>]` — a machine can pass/fail it; the `.sh` row for `<CODE>` (e.g. `FLL`, `UFS`, `LOG`, `MSID`, `ERR`) enforces it. `TODO-CHECK` marks a mechanizable rule with no helper wired yet (build-the-check). `NEW:<name>` marks a proposed-but-not-yet-existing code.
22
+ - `> [JUDGMENT → <CODE>]` — no script can decide; the agent decides at write time against the prose.
23
+ - `> [container]` — a non-enforcement wrapper heading (e.g. "Merged from dissolved gate cards"); its tagged subsections carry the real codes, and the coherence audit (§6.3) skips it.
24
+
25
+ The tag grammar and semantic-anchor model are defined by the tagged sections and
26
+ deterministic façade in this snapshot.
27
+
28
+ ---
29
+
30
+ # Cross-cutting authoring discipline
31
+
32
+ ## Scope
33
+
34
+ > [JUDGMENT → GRP]
35
+
36
+ Triggers on every `Edit`/`Write` to a source file in any language: `*.zig`, `*.ts`, `*.tsx`, `*.js`, `*.jsx`, `*.py`, `*.rs`, `*.go`, `*.sh`, `*.sql` (outside `vendor/`, `node_modules/`, `third_party/`, `.zig-cache/`, `dist/`, `build/`, `.next/`). Fires alongside — never instead of — the per-language façade. `.md` and everything under `docs/**` are exempt from the length sub-gate (see the card below).
37
+
38
+ **Per-file lens.** This is the EXECUTE/CONFORM early-warning: it dispatches over the source files actually touched or `--staged`, so a turn that stages no in-scope source short-circuits with nothing to check. The full-tree audits (`audit-ufs` etc.) still fire unconditionally via the product repo's conformance command — `make harness-verify` in `agentsfleet` — so the dispatch is the per-edit lens, not the codebase-wide backstop.
39
+
40
+ **"Green deferred" defers only the build gate.** When the user grants "free to break, green later" on a multi-slice cutover, the per-edit gates in this façade (UFS, LENGTH, and the language façade's PUB / LIFECYCLE / BUFFER) still run on every Write/Edit — with no compile until late convergence they are the *only* correctness signal, and a big-bang build will not retro-catch speculative `pub`s, re-spelled literals, or missing `errdefer`s (a multi-slice cutover drifted exactly this way).
41
+
42
+ ## Merged from dissolved gate cards
43
+
44
+ > [container]
45
+
46
+ Nine cross-cutting gate cards dissolve into this façade. Each is preserved verbatim below (title demoted to a tagged `###`, subsections demoted to `####` and tagged with the card's code — except the file-length card's fn/method sub-cap, which is a `TODO-CHECK` subsection per §13). Five carry deterministic codes: `FLL` (inline) and `UFS`/`LOG`/`MSID` run here via leaf helpers, while `ERR` is deterministic-but-**delegated** — its leaf needs the product-repo `error_registry.zig`, so `write_any.sh` emits `🟣 DELEGATED` and the product harness runs it. Four are judgment gates (`GRP`/`NLR`/`NLG`/`LDC`).
47
+
48
+ ### File & Function Length Gate (LENGTH GATE)
49
+
50
+ > [DETERMINISTIC → FLL]
51
+
52
+
53
+ **Family:** Universal length discipline. **Source:** `docs/greptile-learnings/RULES.md` RULE FLL (File & Function Length).
54
+
55
+ **Caps:** file ≤ 350 lines · function ≤ 50 lines · method ≤ 70 lines.
56
+
57
+ **350 is a ceiling, not a budget.** A file at 340 is not therefore correct. Split whenever your own rubric — unit-test coverage, simplicity, optimisation, performance, concurrency, adversarial review — says two files beat one; two files of ~150 lines is a good outcome, not an over-split. A file with no headroom left for the tests that would cover it is already too long, whatever `wc -l` reports.
58
+
59
+ **Triggers** — every Write/Edit that net-adds lines to a source file:
60
+ `.zig`, `.js`, `.ts`, `.tsx`, `.jsx`, `.py`, `.rs`, `.go`, `.sh`, `.sql`, `.yaml`/`.toml` (when carrying code). If the file extension is ambiguous, the gate FIRES by default — opt-out requires the user override below.
61
+
62
+ **Exempt:** `vendor/`, `node_modules/`, `third_party/` (upstream); `.md` files; **everything under `docs/**`** (documentation tree — any extension); published API artefacts under `public/` (loose ≤ 400-line advisory on path YAMLs); per-repo extensions in `docs/greptile-learnings/RULES.md`. **Test files are NOT exempt** — a test file over the cap splits by concern like any other; the self-audit below and `dispatch_length_gate` both count them, and an exclusion added to one surface but not the other is how a 418-line test file hid.
63
+
64
+ **Override:** `LENGTH GATE: SKIPPED per user override (reason: ...)` immediately preceding the edit.
65
+
66
+ #### Pre-edit check (mandatory)
67
+
68
+ > [DETERMINISTIC → FLL]
69
+
70
+ 1. `wc -l <file>` — current count (0 for new files).
71
+ 2. Net delta: `+added - removed`.
72
+ 3. Projected: `current + delta`.
73
+ 4. If projected > 350, **STOP**. Split first: extract a cohesive block to a sibling file using the repo's `<module>_<concern>.<ext>` convention (`agent_list.js` beside `agent.js`). Then apply the original edit.
74
+ 5. Projected ≤ 350 does not end the check. Ask the rubric above whether this should be two files — most often it should because there is no room left for the tests. Split now, before the edit, not on arrival at the cap.
75
+ 6. Function sub-gate: project post-edit line count for any touched function. If > 50 (function) or > 70 (method), split into named helpers **before** writing.
76
+
77
+ #### Splitting conventions
78
+
79
+ > [DETERMINISTIC → FLL]
80
+
81
+ - Files named after the concern extracted (`agent_list.js` not `agent2.js`).
82
+ - Helper function names describe the step (`normalizeCursor()` not `helperA()`).
83
+ - The first cut on an over-long source file is its inline tests and test support — fakes, stubs, harnesses — to a `_test` sibling. It frees the most lines for the least risk, and coverage instruments count test support written inline as product.
84
+
85
+ #### Required output
86
+
87
+ > [DETERMINISTIC → FLL]
88
+
89
+ Print only when projected ≥ 300 lines OR touched function within 10 of cap:
90
+
91
+ ```
92
+ LENGTH GATE: <file> N+Δ=<N+Δ> (cap 350, headroom <H>) | fn:<name> <F> lines (cap 50/70) | proceed|split.
93
+ ```
94
+
95
+ #### Self-audit (end-of-turn)
96
+
97
+ > [DETERMINISTIC → FLL]
98
+
99
+ ```bash
100
+ git diff --name-only origin/main \
101
+ | grep -E '\.(zig|js|jsx|ts|tsx|py|rs|go|sh|sql)$' \
102
+ | grep -v -E '^(docs|vendor|third_party)/|/node_modules/|/fixtures?/' \
103
+ | xargs -I{} sh -c 'wc -l "{}" 2>/dev/null' \
104
+ | awk '$1 > 350 { print "❌ " $2 ": " $1 " lines (limit 350)" }'
105
+ ```
106
+
107
+ Non-empty output = hard fail. The extension allowlist mirrors `dispatch_init`'s
108
+ globs so the two surfaces agree; gate fixtures are excluded because some exist
109
+ precisely to sit over the cap. Extensionless executables (`bin/*` scripts) carry
110
+ no extension to match here — `dispatch/write_any.sh --staged` picks those up via
111
+ shebang and is the authoritative check.
112
+
113
+ #### fn/method sub-cap is a separate check
114
+
115
+ > [DETERMINISTIC → TODO-CHECK]
116
+
117
+ `dispatch_length_gate` enforces only the **file** cap (FLL). The **fn ≤ 50 / method ≤ 70** sub-cap is mechanizable but has no leaf wired — flagged TODO-CHECK and named explicitly (not folded into the 350 file cap), per the dispatch acceptance criteria (§13).
118
+
119
+ <!-- oracle-packs:start product.agentsfleet -->
120
+ ### Logging Gate (LOGGING GATE)
121
+
122
+ > [DETERMINISTIC → LOG]
123
+
124
+
125
+ **Family:** Observability discipline. **Source:** `docs/LOGGING_STANDARD.md`.
126
+
127
+ **Triggers** — every `Edit`/`Write` that adds, removes, or changes a log emit:
128
+
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
+ - `*.ts`/`*.tsx`/`*.js`/`*.jsx` outside `vendor/`/`node_modules/`/`*.test.*`/`*.spec.*` — `console.*`, custom logger calls.
131
+ - `*.sh` outside generated dirs — `echo`/`printf` to `&2`.
132
+
133
+ **Override:** `LOGGING GATE: SKIPPED per user override (reason: ...)`. **User-invokable only.** Auto-mode does NOT cover this override.
134
+
135
+ #### What this gate covers
136
+
137
+ > [DETERMINISTIC → LOG]
138
+
139
+ `docs/LOGGING_STANDARD.md` codifies the wire format (logfmt), severity ladder, error-code embedding, scope discipline, and PII redaction. Drift is silent until incident response hits ungreppable logs at 3 AM.
140
+
141
+ #### Pre-edit check
142
+
143
+ > [DETERMINISTIC → LOG]
144
+
145
+ | Pattern | Rule |
146
+ |---|---|
147
+ | New `logging.scoped(.tag)` call | Scope is a Zig enum literal — adding a new tag is freeform. `event` must be snake_case `verb_noun`. |
148
+ | 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. |
149
+ | Per-iteration / hot-loop log | Use `debug` (hidden by default), not `info`. |
150
+ | `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). |
151
+ | `console.log`/`std.debug.print` in non-test source | Forbidden. Convert to logger or delete before commit. |
152
+ | `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. |
153
+ | `msg=` field | ≤ 300 chars. Stack traces emit as separate `event=stack_trace` debug record. |
154
+ | Multi-line values | Newlines must be `\n` literal (two chars), not raw newline byte. |
155
+
156
+ Verify each rule applies or is N/A for this edit.
157
+
158
+ #### Required output (default — one line)
159
+
160
+ > [DETERMINISTIC → LOG]
161
+
162
+ ```
163
+ LOGGING GATE: <file> | scope:<ok|new:.tag> event:<ok|new:.event> error_code:<ok|N/A> severity:<ok|escalation> redaction:<ok|N/A>
164
+ ```
165
+
166
+ Comment-only edit:
167
+
168
+ ```
169
+ LOGGING GATE: <file> | comment-only | N/A
170
+ ```
171
+
172
+ Full multi-line block fires when a sub-rule reports a violation:
173
+
174
+ ```
175
+ LOGGING GATE: <file>
176
+ docs/LOGGING_STANDARD.md sections consulted: §3 (wire format), §4 (severity), §5 (error codes), §6 (PII), §7 (zig binding) | §8 (TS binding), §10A (tightenings)
177
+ Wire format: <logfmt ✓ | violation: <where>>
178
+ Required keys: <ts_ms,level,scope,event present ✓ | violation: <missing>>
179
+ Severity choice: <within rules ✓ | violation: <e.g. info on per-iteration path>>
180
+ Error-code embedding: <UZ-XXX-NNN present and registered ✓ | orphan: <code> | missing on err/warn>
181
+ PII discipline: <no secret materials ✓ | violation: <where>>
182
+ Field caps: <≤15 fields, msg≤300 chars ✓ | violation: <count>>
183
+ Newline encoding: <\n literal ✓ | raw newline at <line>>
184
+ Audit script: <logging.sh on staged diff: 0 findings ✓ | N findings>
185
+ ```
186
+
187
+ #### Scope
188
+
189
+ > [DETERMINISTIC → LOG]
190
+
191
+ `logging.sh` walks the **full `src/` + `agentsfleet/src/` working tree** via `git ls-files`. The index includes staged-but-not-yet-committed content, 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 dev.
192
+
193
+ #### End-of-turn audit
194
+
195
+ > [DETERMINISTIC → LOG]
196
+
197
+ `audits/logging.sh` runs as part of `make lint`. It greps `std.log.*`, `console.*` and raw stderr writes — it does NOT see calls through the `log` named module's `scoped()`, which is where every structured emit in `agentsfleet` goes, so a clean run is not evidence that §4/§5 hold. Mechanical today: `std.debug.print`/`console.log` presence, `std.log.scoped` outside `src/lib/logging/`, msg-length. Reviewer-side: severity choice, entry/exit pairing, `error_code` on scoped-logger calls, and PII spot-checks.
198
+
199
+ #### Family
200
+
201
+ > [DETERMINISTIC → LOG]
202
+
203
+ - `docs/LOGGING_STANDARD.md` — full standard, including §10A tightenings.
204
+ - Selected language façades carry their language-specific logging discipline;
205
+ this gate supplies the shared checks.
206
+ - the Error Registry Gate section of this façade — pairs with this gate on `error_code=` audits.
207
+ <!-- oracle-packs:end -->
208
+
209
+ ### Milestone-ID Gate (MILESTONE ID)
210
+
211
+ > [DETERMINISTIC → MSID]
212
+
213
+
214
+ **Family:** Source-rot prevention. **Source:** `AGENTS.md` (project-side guard). Related: **RULE TST-NAM** (test identifiers are milestone-free) in `docs/greptile-learnings/RULES.md`.
215
+
216
+ **Triggers — before saving any file matching:**
217
+
218
+ - `**/*.zig` · `**/*.sql` · `**/*.ts` · `**/*.tsx` · `**/*.js` · `**/*.jsx` · `**/*.py` · `**/*.rs` · `**/*.go` · `**/*.sh`
219
+ - Any config file (`*.toml`, `*.yaml`, `*.json`) outside `docs/`
220
+ - Test files (the `_test.` / `.test.` / `.spec.` naming doesn't exempt — tests are code).
221
+
222
+ **Exempt paths** (IDs allowed): `docs/`, `**/*.md` outside `node_modules/`/`vendor/`, `CLAUDE.md`/`AGENTS.md`/`audits/agents-md.md`.
223
+
224
+ **Override:** `MILESTONE ID ALLOWED per user override (reason: ...)` in the immediately-preceding comment line.
225
+
226
+ #### Why
227
+
228
+ > [DETERMINISTIC → MSID]
229
+
230
+ Milestone IDs (`M{N}_{NNN}`), section refs (`§X.Y`), and dimension tokens (`T7`, `dim 5.8.15`) belong in specs, PR descriptions, and scratchpads — never in source code, since the codebase outlives any individual milestone and these references rot.
231
+
232
+ #### Pre-edit check
233
+
234
+ > [DETERMINISTIC → MSID]
235
+
236
+ Grep about-to-save content for:
237
+
238
+ ```
239
+ M[0-9]+_[0-9]+
240
+ §[0-9]+(\.[0-9]+)+
241
+ \bT[0-9]+\b
242
+ \bdim [0-9]+\.[0-9]+\b
243
+ ```
244
+
245
+ If any match, **strip the reference before saving.** Rewrite to describe the code's purpose, not its spec lineage.
246
+
247
+ #### Self-audit (end-of-turn)
248
+
249
+ > [DETERMINISTIC → MSID]
250
+
251
+ Covered by the combined awk audit in the AGENTS.md CONFORM section.
252
+
253
+ <!-- oracle-packs:start product.agentsfleet -->
254
+ ### Error Registry Gate (ERROR REGISTRY GATE)
255
+
256
+ > [DETERMINISTIC → ERR]
257
+
258
+
259
+ **Family:** Observability discipline. **Source:** `docs/LOGGING_STANDARD.md` §5; canonical registry per-project (agentsfleet: `src/errors/error_registry.zig`).
260
+
261
+ **Triggers** — every `Edit`/`Write` that:
262
+
263
+ - Adds or modifies a registry-scheme `error_code=` in any source file (agentsfleet: `UZ-XXX-NNN`).
264
+ - Edits the repository's error registry.
265
+ - Touches HTTP error responses, executor frames, or CLI error surfaces.
266
+
267
+ **Override:** `ERROR REGISTRY GATE: SKIPPED per user override (reason: ...)`. **User-invokable only.** Auto-mode does NOT cover this override.
268
+
269
+ #### What this gate covers
270
+
271
+ > [DETERMINISTIC → ERR]
272
+
273
+ Every `err`/`warn` log mapping to a domain failure must carry an `error_code=UZ-XXX-NNN` registered in `src/errors/error_registry.zig`. Every CLI / HTTP / executor error surface must emit codes from the same registry. Drift produces ungreppable error chains across layers — operator can't trace `UZ-EXEC-012` from the CLI back to the executor frame back to the docs.
274
+
275
+ #### Pre-edit check
276
+
277
+ > [DETERMINISTIC → ERR]
278
+
279
+ | Pattern | Rule |
280
+ |---|---|
281
+ | New `error_code=UZ-XXX-NNN` reference | Registry entry exists in `src/errors/error_registry.zig` in the same commit. **Blocking.** |
282
+ | Adding a registry entry | Code follows `UZ-<CATEGORY>-<NNN>` pattern (categories: AUTH, EXEC, HTTP, REDIS, DB, CLI, EXTERNAL, INTERNAL). Hint and docs URL fields populated. |
283
+ | Removing a registry entry | All references removed in same commit (RULE ORP — orphan sweep). |
284
+ | Registry entry with no references in `src/**` or `agentsfleet/**` | Informational — declared-but-unreferenced (dead code). |
285
+ | Reference in source with no registry entry | Blocking — used-but-undeclared (orphan). |
286
+ | HTTP error response | Body includes `code: "UZ-XXX-NNN"`. |
287
+ | `agentsfleet` error output | Human format: `error UZ-XXX-NNN: <message> (<context>)`. JSON format: `{ "code": "UZ-XXX-NNN", "message": ..., ... }` per `docs/LOGGING_STANDARD.md` §8. |
288
+
289
+ #### Required output (default — one line)
290
+
291
+ > [DETERMINISTIC → ERR]
292
+
293
+ ```
294
+ ERROR REGISTRY GATE: <file> | new-codes:<list|none> orphans:<0|N> dead-codes:<0|N informational> hint:<populated|missing>
295
+ ```
296
+
297
+ Comment-only edit:
298
+
299
+ ```
300
+ ERROR REGISTRY GATE: <file> | comment-only | N/A
301
+ ```
302
+
303
+ Full multi-line block fires on violation:
304
+
305
+ ```
306
+ ERROR REGISTRY GATE: <file>
307
+ Registry: src/errors/error_registry.zig
308
+ New codes added (this diff): <UZ-XXX-NNN, ...>
309
+ Code references added (this diff): <UZ-XXX-NNN at <file>:<line>, ...>
310
+ Orphan codes (used but not declared): <list — BLOCKING>
311
+ Dead codes (declared but unreferenced): <list — informational>
312
+ Hint/docs populated: <ok ✓ | missing for <code>>
313
+ Audit script: <error-codes.sh on staged diff: 0 findings ✓ | N findings>
314
+ ```
315
+
316
+ #### Scope
317
+
318
+ > [DETERMINISTIC → ERR]
319
+
320
+ `error-codes.sh` walks the **full `src/` + `agentsfleet/` working tree** via `git ls-files`. The index includes staged-but-not-yet-committed content, 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 dev.
321
+
322
+ #### End-of-turn audit
323
+
324
+ > [DETERMINISTIC → ERR]
325
+
326
+ `audits/error-codes.sh` runs as part of `make lint`. Greps `src/errors/error_registry.zig` for declared codes, then greps `src/**` and `agentsfleet/**` for any `UZ-[A-Z]+-[0-9]+` literal that isn't in the registry. Reports orphans (blocking) and dead codes (informational).
327
+
328
+ #### Family
329
+
330
+ > [DETERMINISTIC → ERR]
331
+
332
+ - `docs/LOGGING_STANDARD.md` §5 — error-code embedding rules in log records.
333
+ - the Logging Gate section of this façade — pairs with this gate on `error_code=` audits.
334
+ - `docs/REST_API_DESIGN_GUIDELINES.md` — HTTP error response shape includes `code`.
335
+ - `docs/greptile-learnings/RULES.md` RULE ORP — orphan sweep on rename/delete.
336
+ <!-- oracle-packs:end -->
337
+
338
+ ### UFS Gate (Unified Form for Symbols)
339
+
340
+ > [DETERMINISTIC → UFS]
341
+
342
+
343
+ **Family:** Constant discipline. **Source:** `AGENTS.md` conformance enforcement
344
+ + the selected language façade's Uniform Free Strings (UFS) clauses.
345
+
346
+ **Triggers** — every `Edit`/`Write` to source files under `src/`, `ui/packages/*/src/`, `ui/packages/*/app/`, `ui/packages/*/lib/`, `ui/packages/*/components/`, `ui/packages/*/pages/`, `ui/packages/*/tests/`, `agentsfleet/src/`, `agentsfleet/test/` matching `*.zig`, `*.ts`, `*.tsx`, `*.js`, `*.jsx`. Excluded: `vendor/`, `third_party/`, `.zig-cache/`, `node_modules/`, `*.tsbuildinfo`.
347
+
348
+ **Override:** `UFS GATE: SKIPPED per user override (reason: ...)` immediately preceding the edit. **User-only**; auto-mode does not cover.
349
+
350
+ **Carve-out:** **Pin tests** keep their literals if and only if the literal IS the contract under test (`expect(formatDollars(4_710_000_000)).toBe("$4.71")`). Each pin must carry an inline `// pin test: literal is the contract` comment on or above the line.
351
+
352
+ Deterministic negative fixtures under `evals/dispatch/fixtures/` are storage
353
+ inputs, not authored source. The full-tree audit excludes that directory; the
354
+ dispatch evaluator copies each fixture into `src/` before asserting its expected
355
+ pass or failure result.
356
+
357
+ #### What this gate enforces
358
+
359
+ > [DETERMINISTIC → UFS]
360
+
361
+ Three discipline points, all part of RULE UFS:
362
+
363
+ 1. **Repeat literals → named constant.** Any string literal appearing ≥2 times in the same file (or in the same module/package — see scope ladder below) becomes a named const. Wire-format enum values (`"platform"`, `"self_managed"`, `"receive"`, `"stage"`, mode strings, posture labels, channel names, queue names, error code categories) are *always* named — even at first use, even in test fixtures.
364
+
365
+ 2. **Semantic numeric literals → named constant.** Numeric literals carrying meaning beyond their digits become named consts even at first use. Includes: conversion factors (powers-of-ten ≥ 1e3 used as multipliers/divisors), thresholds, sub-cent rates, time-unit factors, byte-unit factors, retry caps, sentinel offsets. Bare `1_000_000_000` in `nanos / 1_000_000_000` is a smell — `nanos / NANOS_PER_USD` is the rule.
366
+
367
+ 3. **Cross-runtime parity.** Any constant that exists in more than one runtime (Zig, TS, JS) **must share its identifier verbatim**, modulo case where a language idiom forces it (Zig snake_case → TS/JS SCREAMING_SNAKE is a single-runtime style choice; the *semantic* name is identical). When a literal in runtime A would benefit from a name and a sibling runtime already has that name, reuse it. When introducing a new constant, define it in every runtime that uses the same value, same commit. There is no per-constant carve-out: this applies to every cross-runtime constant, not a curated subset.
368
+
369
+ The gate exists because RULE UFS is otherwise pure self-policing. Without an audit script, an inline literal in a 800-LOC editing session reliably slips past attention.
370
+
371
+ #### Pre-edit checks
372
+
373
+ > [DETERMINISTIC → UFS]
374
+
375
+ Before saving an edit that introduces or relocates a literal:
376
+
377
+ 1. **Same-file repeat?** Grep within the file for the literal value. If it appears ≥1 time already, define a const at the top of the file (or in the module's `constants.ts`/`constants.zig`) and replace all occurrences.
378
+
379
+ 2. **Same-module repeat?** Grep within the module/package (`ui/packages/app/`, `agentsfleet/src/`, `src/`). If repeated, define in the module's canonical types/constants file (`lib/types.ts`, `src/state/<topic>.zig`, `agentsfleet/src/constants/<topic>.js`).
380
+
381
+ 3. **Cross-runtime sibling?** When the literal is a wire-format string or unit-conversion numeric, grep all three runtimes for an existing matching name. If found, reuse the name. If not, define in every runtime that uses the same value, same commit.
382
+
383
+ 4. **Carve-out check?** If the literal IS the contract a pin test verifies, write the `// pin test: literal is the contract` comment on or above the line. Otherwise, name it.
384
+
385
+ #### Required output (per Edit/Write — one line by default)
386
+
387
+ > [DETERMINISTIC → UFS]
388
+
389
+ ```
390
+ UFS GATE: <file> | repeats:<count> | semantic-numerics:<count> | cross-runtime:<status> | pin-test-carve-outs:<count>
391
+ ```
392
+
393
+ Multi-line block fires when repeats > 0 OR semantic-numerics > 0 OR cross-runtime status ≠ ok:
394
+
395
+ ```
396
+ UFS GATE: <file>
397
+ Repeat string literals named: <list of value → CONSTANT_NAME pairs>
398
+ Repeat string literals KEPT (carve-out): <value> — <reason: pin test / single-use semantically irreducible / etc.>
399
+ Semantic numerics named: <list of value → CONSTANT_NAME pairs>
400
+ Semantic numerics KEPT: <value> — <reason>
401
+ Cross-runtime parity: <status — e.g. "NANOS_PER_USD added to Zig+TS+JS this commit" or "n/a — runtime-internal constant">
402
+ ```
403
+
404
+ #### Scope
405
+
406
+ > [DETERMINISTIC → UFS]
407
+
408
+ `ufs.sh` walks the **full working tree** via `git ls-files`. The index includes staged-but-not-yet-committed content, so a fix staged in pre-commit satisfies the check on the same hook run.
409
+
410
+ The previous `--diff` (`BASE...HEAD`) default was retired with M70. The forcing function was M68 commit `02c1f3cf`: pre-commit's `HEAD` is the prior commit, so `BASE...HEAD` was blind to nine cross-runtime mismatches the agent had staged but not yet committed. The full-codebase scope removes that blindspot.
411
+
412
+ `--all` is accepted as a back-compat alias for the default. `--diff` is rejected with exit 2 + a pointer to this section.
413
+
414
+ #### Self-audit (end-of-turn / CONFORM)
415
+
416
+ > [DETERMINISTIC → UFS]
417
+
418
+ `audits/ufs.sh` is invoked at CONFORM by the agent. Convention follows the sibling audit scripts (`logging.sh`, `error-codes.sh`, `deinit-pairs.sh`, `spec-template.sh`) — none of them wire into `make lint`; the agent runs each as part of the gate ceremony, with the result feeding the CONFORM table. The script is generic — no manifest of known literals — so it scales as the codebase grows:
419
+
420
+ ```bash
421
+ bash audits/ufs.sh # full-codebase scan (default and only mode)
422
+ bash audits/ufs.sh --all # alias for default
423
+ ```
424
+
425
+ The script emits a violations table. Empty table = pass. Each row is one of:
426
+
427
+ - `string-dup-file <file> "<literal>" <count>` — same string ≥2× in one file
428
+ - `string-dup-module <module> "<literal>" <count>` — same string ≥2× across a module/package
429
+ - `numeric-suspect <file>:<line> <literal>` — power-of-ten or known unit-factor numeric not bound to a const (excludes anything on a `// pin test: literal is the contract` line)
430
+ - `cross-runtime-orphan <const_name> <runtime>` — SCREAMING_SNAKE constant defined in one runtime, missing from a sibling runtime that the same diff touches
431
+
432
+ Violations resolve by either (1) extracting to a named const + replacing all sites, (2) adding the constant in the missing sibling runtime same-commit, or (3) adding the `// pin test: literal is the contract` comment.
433
+
434
+ #### CONFORM row
435
+
436
+ > [DETERMINISTIC → UFS]
437
+
438
+ ```
439
+ | UFS GATE | ✅ pass | 🟡 N violations addressed | 🔴 N unresolved |
440
+ ```
441
+
442
+ 🔴 unresolved → return to EXECUTE; the lifecycle does not advance.
443
+
444
+ #### Failure mode this gate exists to prevent
445
+
446
+ > [DETERMINISTIC → UFS]
447
+
448
+ A real session surfaced the failure clearly: `RULE UFS` lived as a single bullet in the former Bun rules §2 with no audit, no CONFORM row, and no per-edit ceremony. Across an 800-LOC session the agent introduced ~20 inline literals (`"platform"`, `"self_managed"`, `"receive"`, `"stage"`, `mode: "byok"`, `1e9`, `1_000_000_000`, etc.) that the rule already covered but no mechanism caught. The rule is fine; the enforcement was missing. This gate adds the enforcement.
449
+
450
+ ### Greptile Gate (GREPTILE GATE)
451
+
452
+ > [JUDGMENT → GRP]
453
+
454
+
455
+ **Family:** Universal coding-rules audit. **Source:** `docs/greptile-learnings/RULES.md`.
456
+
457
+ **Triggers:** fires twice per work unit — (1) per EXECUTE iteration when diff languages change (new layer/language enters the diff); (2) end-of-turn, before claiming complete.
458
+
459
+ **Override:** `GREPTILE GATE: SKIPPED per user override (reason: ...)`. Violations stay in the diff and the user's override gets recorded in the spec's Discovery section.
460
+
461
+ #### What this gate enforces
462
+
463
+ > [JUDGMENT → GRP]
464
+
465
+ `docs/greptile-learnings/RULES.md` is the universal coding-rules catalogue. The agent failure mode is grepping the spec verbatim instead of grepping the rules; this gate makes the rule audit a printable artefact.
466
+
467
+ #### Common rules referenced
468
+
469
+ > [JUDGMENT → GRP]
470
+
471
+ | Code | Rule | Applies to |
472
+ |---|---|---|
473
+ | UFS | String literals are always constants | all source |
474
+ | STS | No static strings in SQL schema | SQL |
475
+ | EMS | Error messages follow a standard structure | Zig handlers |
476
+ | NSQ | Named constants, schema-qualified SQL | SQL |
477
+ | TGU | Tagged unions over optional-field structs | Zig |
478
+ | VLT | Secrets belong in vault, not in entity tables | SQL/Zig |
479
+ | CTM | Constant-time comparison for secrets | Zig |
480
+ | CTC | Constant-time compare must not short-circuit on length | Zig |
481
+ | FLL | File / function length caps | all source |
482
+ | ORP | Cross-layer orphan sweep on rename/delete/format | all |
483
+ | WAUTH | Workspace-IDOR safety | handlers |
484
+ | TST-NAM | Test identifiers are milestone-free | tests |
485
+ | PRI | No prompt injection from user input | LLM I/O |
486
+
487
+ Read RULES.md for any code not in this short-list.
488
+
489
+ #### Pre-print check
490
+
491
+ > [JUDGMENT → GRP]
492
+
493
+ 1. Identify diff languages (`zig|ts|tsx|sql|sh|py|go|rs`).
494
+ 2. List rules whose `Applies to` overlaps. UFS applies to all source; STS to SQL; EMS to Zig handlers; etc.
495
+ 3. **String-literals (UFS) audit:**
496
+
497
+ ```bash
498
+ git diff -U0 origin/main | grep -oE '"[^"]{4,}"' | sort -u
499
+ ```
500
+
501
+ For each unique literal, grep `src/ ui/ agentsfleet/` for an existing `const` / `pub const` / `export const` / `as const` / `Final[str]` / `readonly` declaration. Found → import. Novel + ≥2 sites → declare a const.
502
+
503
+ #### Required output (one row per applicable rule, suppress non-applicable)
504
+
505
+ > [JUDGMENT → GRP]
506
+
507
+ ```
508
+ GREPTILE GATE: <iteration tag> Diff langs: <…>
509
+ | Code | Verdict |
510
+ | UFS | clean | N violations: <list> |
511
+ | STS | clean | N violations |
512
+ | … | … |
513
+ String-literals audit: <N literals scanned, M violations>
514
+ ```
515
+
516
+ #### Anti-rationalisation clause
517
+
518
+ > [JUDGMENT → GRP]
519
+
520
+ "It's just a label" / "I'll only use it once" are not exceptions to UFS. The clause applies to every rule with a behaviour modifier — when you find yourself reaching for a justification, the rule wins.
521
+
522
+ ### RULE NLR — No Legacy Retained
523
+
524
+ > [JUDGMENT → NLR]
525
+
526
+
527
+ **Family:** Legacy-control. Sibling rules: **RULE NDC** (no dead code at write time), **RULE NLG** (no new legacy framing pre-v2.0.0), **Legacy-Design Consult Guard** (judgment calls on whole subsystems). **Source:** `docs/greptile-learnings/RULES.md` RULE NLR.
528
+
529
+ **Triggers:** any Edit/Write to a file containing pre-existing legacy framing or dead code.
530
+
531
+ **Override:** `RULE NLR: SKIPPED per user override (reason: ...)` immediately preceding the edit. **User-invokable only.** Concrete external-impact constraint OR explicit NLR DECISION resolution required. Generic "scope creep"/"too big" not valid.
532
+
533
+ #### What this gate enforces
534
+
535
+ > [JUDGMENT → NLR]
536
+
537
+ Any edit to a file with pre-existing legacy framing or dead code MUST remove it in the same diff. Patterns covered:
538
+
539
+ - `?*T = null` fields whose only caller always sets a non-null value (dead defense for a phantom caller).
540
+ - `legacy_*` symbol names, `V2`-suffixed twin types, `if (legacy_caller)` branches.
541
+ - `// legacy` / `// pre-M*` / `// bootstrap` comments.
542
+ - Runtime warns saying `legacy path` / `deprecated` / `*_bootstrap_*`.
543
+ - `pub` fns/fields with no in-tree consumer (verify with `grep -rn`).
544
+ - `defer if (x) ... else null` patterns that compensate for an `?T` that should have been `T`.
545
+ - Unused parameters, captures, unreachable branches.
546
+
547
+ The carve-out "pre-existing violations are not the agent's responsibility" does **not** apply when the agent is already touching the file. You see it, you own it.
548
+
549
+ The inverse discipline on the same touch: **performance opportunities are surfaced, never silently bundled.** A behavior-preserving refactor that quietly folds in optimizations (pooling a per-call construction, collapsing allocations on a hot path) destroys bisectability of any regression. Keep a perf-opportunities list as you edit and surface it at the next checkpoint for the user's fold-in-or-file decision — same posture as security follow-ups. Dead-code removal on touch stays in (that is this rule); perf rewrites stay out until decided.
550
+
551
+ #### Pre-edit check
552
+
553
+ > [JUDGMENT → NLR]
554
+
555
+ 1. Before the first `Edit`/`Write` to a file, scan for the patterns above in the *whole file*, not just the lines you're touching.
556
+ 2. List violations in the gate output before the edit.
557
+ 3. Remove them in the same diff. Update every caller in the same commit.
558
+ 4. If cleanup is judged infeasible (large net-line delta, cross-package cascade, meaningfully different design path emerges), print the **NLR DECISION** block below and **wait**. The agent has no autonomous escape; the user is the only authority.
559
+
560
+ #### Decision block
561
+
562
+ > [JUDGMENT → NLR]
563
+
564
+ ```
565
+ NLR DECISION: <file>
566
+ Cleanup-in-place cost: +<N> net lines, <M> files touched.
567
+ Alternative approach: <one-line description> (avoids the dirty file).
568
+ If alternative chosen, legacy that survives:
569
+ - <symbol/pattern>: <file:line>
570
+ - ...
571
+ WAITING for user: clean / alternative.
572
+ ```
573
+
574
+ #### Required output (when violations found before edit)
575
+
576
+ > [JUDGMENT → NLR]
577
+
578
+ ```
579
+ NLR: <file> | violations: <list with file:line each> | action: clean-in-diff
580
+ ```
581
+
582
+ #### Anti-evasion clause
583
+
584
+ > [JUDGMENT → NLR]
585
+
586
+ Three failure modes the agent MUST NOT use to skip cleanup:
587
+
588
+ 1. **Route-around design.** Picking an architecture that avoids the dirty file specifically to dodge cleanup. If NLR avoidance was a motivation, surface it.
589
+ 2. **Silent rejection.** Quietly choosing not to touch a file because cleanup looked expensive, without disclosing the decision.
590
+ 3. **Shim-and-skip.** Introducing a wrapper/adapter to sidestep a dirty interface — the new code becomes its own legacy debt; the original rots in place.
591
+
592
+ If any of these patterns is in play, surface it via the NLR DECISION block. The user retains all discretion.
593
+
594
+ #### Family
595
+
596
+ > [JUDGMENT → NLR]
597
+
598
+ - **RULE NDC** — prevention at write time (don't author dead code).
599
+ - **RULE NLG** — ban new legacy framing pre-v2.0.0.
600
+ - **Legacy-Design Consult Guard** — harder judgment calls ("should this whole subsystem exist") that need user input.
601
+ - **RULE NLR** — mechanical cleanup on touch.
602
+
603
+ ### RULE NLG — No new Legacy framing
604
+
605
+ > [JUDGMENT → NLG]
606
+
607
+
608
+ **Family:** Legacy-control. Sibling rules: **RULE NDC**, **RULE NLR**, **Legacy-Design Consult Guard**. **Source:** `docs/greptile-learnings/RULES.md` RULE NLG.
609
+
610
+ **Triggers:** introducing any new `legacy_*` name, `V2`-twin type, `if (legacy_caller)` branch, backward-compat shim, command-line alias for an old verb or flag, "rejecting legacy X" prose, or violation tracking-list while `cat VERSION` < `2.0.0`.
611
+
612
+ **Override:** `RULE NLG: SKIPPED per user override (reason: ...)` immediately preceding the edit. **User-invokable only.** Requires a concrete external consumer that can't migrate same-commit (vanishingly rare pre-v2.0.0).
613
+
614
+ #### What this gate enforces
615
+
616
+ > [JUDGMENT → NLG]
617
+
618
+ While `cat VERSION` < `2.0.0`, the project has no external consumers and no published API. Do not introduce *new* legacy concepts in any form:
619
+
620
+ - No `legacy_*` error variant names.
621
+ - No `if (legacy_caller)` branches.
622
+ - No `V2`-suffixed twin types.
623
+ - No backward-compat shims.
624
+ - No "rejecting legacy X" prose in specs/docs/commit messages.
625
+ - No old command verbs or flag aliases retained beside the new form unless Indy explicitly asks in the same session.
626
+
627
+ Edit interfaces in place; update every caller in the same commit. Name errors by *what is wrong*, not *when it was wrong* (e.g. `runtime_keys_outside_block`, not `legacy_top_level_runtime`).
628
+
629
+ #### Why
630
+
631
+ > [JUDGMENT → NLG]
632
+
633
+ Pre-alpha duplicates rot faster than documentation. Every `legacy_*` name introduces a phantom interface nobody owns; every future spec then has to reason about it. Schema Table Removal Guard already encodes this for SQL — RULE NLG generalises it to every interface (RPC, route, struct, error name, config key, spec prose).
634
+
635
+ #### Tracking-list ban
636
+
637
+ > [JUDGMENT → NLG]
638
+
639
+ Any constant, doc structure, or carve-out list whose purpose is to catalog "violations to be cleaned up later" is itself an NLG violation. Banned name patterns: `LEGACY_`, `PENDING_`, `_VIOLATIONS`, `_CARVE_OUTS` (when meant for deferred cleanup), `TO_FIX_`, `DEFERRED_`, or any equivalent.
640
+
641
+ Either fix every entry in the same diff that introduces or touches the list, or delete the list and let the next touch fix the underlying violations. The tracking list **legitimises deferral**; the rule exists to prevent that.
642
+
643
+ **Vendor-immortal carve-outs** — paths or names dictated by external commitments that genuinely cannot be renamed (e.g. OAuth callback URLs that Slack/GitHub register with us) — are a separate class. Name those explicitly with `VENDOR_` or `EXTERNAL_` prefix so the distinction from "deferred cleanup" is mechanical, and add a comment line stating the external commitment that pins the name.
644
+
645
+ #### Required output (when violation found)
646
+
647
+ > [JUDGMENT → NLG]
648
+
649
+ ```
650
+ NLG: <file>:<line> | new legacy framing: <description> | action: rename | suggested name: <what-is-wrong-form>
651
+ ```
652
+
653
+ #### Full text
654
+
655
+ > [JUDGMENT → NLG]
656
+
657
+ `docs/greptile-learnings/RULES.md` RULE NLG.
658
+
659
+ ### Legacy-Design Consult Guard
660
+
661
+ > [JUDGMENT → LDC]
662
+
663
+
664
+ **Family:** Legacy-control. Sibling rules: **RULE NDC**, **RULE NLR**, **RULE NLG**. **Source:** `AGENTS.md` (project-side guard, not a greptile-derived rule).
665
+
666
+ **Triggers** — STOP and consult before any of these:
667
+
668
+ - Patching legacy to fit new architecture ("compensating code").
669
+ - Keeping legacy for "backward compat" pre-alpha when there are no external consumers.
670
+ - Defensive `orelse` / fail-open whose only reason is legacy nullability.
671
+ - Authoring tests that exercise the legacy path.
672
+ - Choosing patch-vs-remove silently — never your call.
673
+
674
+ **Override:** none — user decides A/B/C in the consult block.
675
+
676
+ #### Definition — "legacy design"
677
+
678
+ > [JUDGMENT → LDC]
679
+
680
+ Any code path, env-var, table, route, or API that the surrounding milestone work is deprecating, that predates the current architectural direction, or that exists solely as a smoke-test / bootstrap / pre-migration shim. Signals:
681
+
682
+ - Comments like `// legacy`, `// pre-M*`, `// bootstrap`, `// TODO remove`, `// temporary`.
683
+ - Runtime warn logs that announce themselves (`legacy path`, `deprecated`, `*_bootstrap_*`).
684
+ - Env vars / principals / roles / schema cols whose only live consumer is a fallback branch or pre-signup/dev-only path.
685
+
686
+ #### Required output
687
+
688
+ > [JUDGMENT → LDC]
689
+
690
+ ```
691
+ LEGACY CONSULT: <desc> | found:<file:line> | (A) remove [blast:<files>] / (B) patch [risk] / (C) keep [why] | rec:<A|B|C> because <reason> | WAITING.
692
+ ```
693
+
694
+ Block on the user's reply. If the user previously approved one *class* of legacy decisions this session, note that and proceed — but every *new* class of finding still triggers a consult.
695
+
696
+ #### Escape hatch
697
+
698
+ > [JUDGMENT → LDC]
699
+
700
+ Legacy findings unambiguously in-scope of the active spec's Dead Code Sweep or Out-of-Scope list skip the consult and follow the spec.
701
+
702
+ #### Discovery capture
703
+
704
+ > [JUDGMENT → LDC]
705
+
706
+ Every triggered consult is logged in the active spec's **Discovery** section, or filed as a new pending spec in `docs/v{N}/pending/` if pushed to follow-up.
707
+
708
+ #### Family
709
+
710
+ > [JUDGMENT → LDC]
711
+
712
+ NLR is the cleanup-on-touch arm; NLG bans new legacy framing pre-v2.0.0; this guard covers the harder judgment calls ("should this whole subsystem exist") that need the user's input.
713
+
714
+ ## Comment Voice — New to the Codebase, Familiar with the Goal
715
+
716
+ > [JUDGMENT → NEW:CMT]
717
+
718
+ Indy's standing instruction (Jul 2026): write comments for a reader who is **new
719
+ to the codebase but familiar with the goal of the project**. That reader knows
720
+ WHAT the product does and why it exists; they don't know where things live, which
721
+ invariants bind, or why this file deviates from the obvious approach.
722
+
723
+ - **Explain the local WHY** — the constraint, invariant, or cross-module
724
+ interplay the code alone can't show (*"drained before deinit because the pool
725
+ recycles the conn"*, not *"call drain then deinit"*).
726
+ - **Name the non-obvious neighbours** — when correctness depends on a sibling
727
+ file, registry, or cross-runtime pairing, say so by path/symbol. That map is
728
+ exactly what a codebase-newcomer lacks.
729
+ - **Don't narrate the language or restate the line** (*"increment i"*), and
730
+ **don't re-explain the product goal** the reader already has.
731
+ - **Review-context comments stay banned** — nothing about what changed, which
732
+ spec asked for it (MILESTONE-ID gate), or why the diff is correct; that is
733
+ PR/Session-Notes material, not code. Acronym expansion applies inside comments
734
+ (`AGENTS.md` §Owner & Style).
735
+
736
+ ## Mid-Task Repo-Wide Flips Need a Blast-Radius Grep
737
+
738
+ > [JUDGMENT → NEW:BLAST]
739
+
740
+ Before flipping or removing any identifier/value referenced beyond the file in hand — a skill ref, package name, template slug, config key — run `git grep -rn -w '<token>'` from the repo root with **no path filter**, even for a "one-line change". Word-boundary, never quoted-literal (YAML and multiline-string refs carry no quotes). Separate true targets from same-spelling-different-meaning hits (a skill ref vs the GitHub repo slug) and update every true hit in the same diff. Grepping only the "obvious" files is the trap: an installer rename can leave a simultaneously edited document advertising the old slug. A selected specification façade applies the same rule to teardown and rename specs.