@agentsfleet/orly 0.10.14 → 0.12.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 (156) hide show
  1. package/.githooks/pre-push +2 -1
  2. package/AGENTS.md +21 -31
  3. package/README.md +94 -28
  4. package/SOUL.md +13 -23
  5. package/audits/agents-md.md +67 -57
  6. package/audits/deinit-pairs.sh +13 -18
  7. package/audits/design-tokens.sh +9 -23
  8. package/audits/doc-read.sh +10 -33
  9. package/audits/doc-reads.ts +41 -0
  10. package/audits/error-codes.sh +18 -25
  11. package/audits/logging-rust.awk +145 -0
  12. package/audits/logging.sh +10 -166
  13. package/audits/msid-ui.sh +45 -18
  14. package/audits/rule-ledger-lib.sh +9 -6
  15. package/audits/rust-error.sh +20 -15
  16. package/audits/scope.sh +88 -0
  17. package/audits/spec-template.sh +2 -2
  18. package/audits/spec-template.ts +1 -1
  19. package/audits/sql-mod.sh +11 -14
  20. package/audits/ufs-strings.awk +164 -0
  21. package/audits/ufs.sh +10 -188
  22. package/core/operating-model.md +8 -8
  23. package/dispatch/edit_rules.md +8 -6
  24. package/dispatch/lib.sh +41 -26
  25. package/dispatch/lifecycle.md +12 -5
  26. package/dispatch/name_architecture.md +8 -3
  27. package/dispatch/verify.md +1 -1
  28. package/dispatch/write_any.md +10 -3
  29. package/dispatch/write_changelog.md +7 -4
  30. package/dispatch/write_go.md +1 -1
  31. package/dispatch/write_http.md +5 -7
  32. package/dispatch/write_python.md +1 -1
  33. package/dispatch/write_rust.md +17 -1
  34. package/dispatch/write_shell.md +1 -1
  35. package/dispatch/write_spec.md +3 -1
  36. package/dispatch/write_sql.md +28 -6
  37. package/dispatch/write_ts_adhere_bun.md +11 -0
  38. package/dispatch/write_zig.md +53 -21
  39. package/docs/CHANGELOG.md +10 -0
  40. package/docs/CHANGELOG_VOICE.md +11 -2
  41. package/docs/EXECUTE_DOC_READS.md +10 -10
  42. package/docs/HARNESS_VERIFY_OUTPUT.md +6 -3
  43. package/docs/JUDGMENTS.md +156 -0
  44. package/docs/LIFECYCLE_PATTERNS.md +26 -16
  45. package/docs/LOGGING_STANDARD.md +39 -19
  46. package/docs/ORLY_ARCHITECTURE.md +77 -6
  47. package/docs/RELEASE_TEMPLATE.md +21 -5
  48. package/docs/REST_API_DESIGN_GUIDELINES.md +86 -21
  49. package/docs/RULE_ENFORCEMENT.md +6 -6
  50. package/docs/SCHEMA_CONVENTIONS.md +24 -3
  51. package/docs/TEMPLATE.md +4 -4
  52. package/docs/VERIFY_TIERS.md +2 -2
  53. package/docs/greptile-learnings/RULES.md +81 -26
  54. package/llms.txt +46 -34
  55. package/package.json +6 -1
  56. package/packs/language/go/rules.md +1 -1
  57. package/packs/language/python/rules.md +1 -1
  58. package/packs/language/rust/rules.md +17 -1
  59. package/packs/language/shell/rules.md +1 -1
  60. package/registry.json +22 -7
  61. package/schemas/profile.schema.json +48 -5
  62. package/skills/orly-babysit-prs/SKILL.md +87 -34
  63. package/skills/orly-spec-new/SKILL.md +19 -6
  64. package/skills/orly-write-integration-test/SKILL.md +4 -4
  65. package/skills/orly-write-unit-test/SKILL.md +10 -1
  66. package/src/cli.test.ts +2 -2
  67. package/src/cli.ts +19 -75
  68. package/src/cli_help.ts +69 -0
  69. package/src/command_limits.ts +20 -0
  70. package/src/command_process.test.ts +109 -0
  71. package/src/command_process.ts +88 -0
  72. package/src/command_runner.test.ts +88 -0
  73. package/src/command_runner.ts +33 -0
  74. package/src/comprehension_cache.test.ts +123 -0
  75. package/src/config.test.ts +5 -4
  76. package/src/config.ts +26 -164
  77. package/src/config_discovery.ts +159 -0
  78. package/src/config_validation.test.ts +56 -0
  79. package/src/config_validation.ts +25 -0
  80. package/src/criteria.test.ts +1 -1
  81. package/src/criteria.ts +10 -26
  82. package/src/criteria_spec.ts +12 -20
  83. package/src/criteria_support.ts +2 -19
  84. package/src/doc_reads.test.ts +42 -0
  85. package/src/doc_rules.ts +2 -2
  86. package/src/evaluation_resources.test.ts +64 -0
  87. package/src/execution/config.ts +22 -0
  88. package/src/execution/plan.test.ts +65 -0
  89. package/src/execution/plan.ts +51 -0
  90. package/src/gates.ts +9 -6
  91. package/src/gates_spec.test.ts +52 -6
  92. package/src/gates_test_support.ts +9 -7
  93. package/src/install.test.ts +38 -222
  94. package/src/install.ts +117 -308
  95. package/src/install_loaders.test.ts +203 -0
  96. package/src/install_nested.test.ts +2 -2
  97. package/src/install_packs.test.ts +66 -6
  98. package/src/installation/hooks.ts +63 -0
  99. package/src/installation/lock.test.ts +97 -0
  100. package/src/installation/lock.ts +56 -0
  101. package/src/installation/migration.test.ts +212 -0
  102. package/src/installation/migration.ts +65 -0
  103. package/src/installation/plan.ts +96 -0
  104. package/src/installation/transaction.ts +126 -0
  105. package/src/installation_preservation.test.ts +48 -0
  106. package/src/judgments/advice.test.ts +38 -0
  107. package/src/judgments/advice.ts +32 -0
  108. package/src/judgments/command.integration.test.ts +47 -0
  109. package/src/judgments/command.ts +90 -0
  110. package/src/judgments/constants.ts +55 -0
  111. package/src/judgments/evidence.test.ts +48 -0
  112. package/src/judgments/evidence.ts +34 -0
  113. package/src/judgments/files.test.ts +62 -0
  114. package/src/judgments/files.ts +73 -0
  115. package/src/judgments/questions.test.ts +19 -0
  116. package/src/judgments/questions.ts +85 -0
  117. package/src/judgments/replay.test.ts +43 -0
  118. package/src/judgments/replay.ts +66 -0
  119. package/src/judgments/runner.integration.test.ts +165 -0
  120. package/src/judgments/runner.ts +97 -0
  121. package/src/judgments/scan.test.ts +19 -0
  122. package/src/judgments/scan.ts +40 -0
  123. package/src/judgments/syntax.test.ts +115 -0
  124. package/src/judgments/syntax.ts +71 -0
  125. package/src/judgments/syntax_worker.ts +70 -0
  126. package/src/judgments/test_support.ts +60 -0
  127. package/src/judgments/transport.integration.test.ts +63 -0
  128. package/src/judgments/transport.ts +35 -0
  129. package/src/judgments/types.test.ts +43 -0
  130. package/src/judgments/types.ts +66 -0
  131. package/src/judgments/wire.test.ts +48 -0
  132. package/src/judgments/wire.ts +40 -0
  133. package/src/loaders.test.ts +13 -16
  134. package/src/loaders.ts +28 -10
  135. package/src/model.test.ts +20 -3
  136. package/src/model.ts +7 -18
  137. package/src/pack_hygiene.test.ts +42 -1
  138. package/src/pack_hygiene.ts +18 -8
  139. package/src/references.ts +15 -7
  140. package/src/release_inventory.test.ts +80 -0
  141. package/src/render.test.ts +1 -1
  142. package/src/render.ts +3 -8
  143. package/src/required_scanner.test.ts +36 -0
  144. package/src/review_poll.test.ts +158 -0
  145. package/src/scanner_scope.test.ts +202 -0
  146. package/src/setup.test.ts +9 -6
  147. package/src/spec_baseline.test.ts +1 -1
  148. package/src/spec_completion.test.ts +74 -0
  149. package/src/spec_obligations.ts +64 -0
  150. package/src/spec_rehearsal.test.ts +182 -0
  151. package/src/spec_template_io.test.ts +11 -9
  152. package/src/surfaces.test.ts +2 -0
  153. package/src/surfaces.ts +1 -1
  154. package/src/validation.ts +7 -4
  155. package/src/verify.test.ts +18 -2
  156. package/src/verify.ts +7 -3
@@ -9,7 +9,7 @@ Pre-design rules, decisive defaults, and the anti-patterns each rule exists to p
9
9
  Triggers on every `Edit`/`Write` that adds, removes, or changes a log emit:
10
10
 
11
11
  - `*.zig` outside `vendor/`/`third_party/`/`.zig-cache/` — `std.log.*`, `std.debug.print`, `std.io.getStdErr().writer().print`, any helper in `src/lib/logging/`.
12
- - `*.rs` under `rustd/` outside test and benchmark scopes — `tracing::{error,warn,info,debug,trace}!`, `println!`, `eprintln!`, and `dbg!`.
12
+ - Runtime `*.rs` outside test and benchmark scopes — `tracing::{error,warn,info,debug,trace}!`, `println!`, `eprintln!`, and `dbg!`.
13
13
  - `*.ts`/`*.tsx`/`*.js`/`*.jsx` outside `vendor/`/`node_modules/` — `console.*`, custom logger calls.
14
14
  - `*.sh` outside generated directories — `echo`, `printf` to `&2`.
15
15
 
@@ -19,8 +19,10 @@ Out of scope (explicitly):
19
19
  - Build/release scripts — the harness's own `audits/*`, `audits/release-*`, and any governance-repo tooling — toolchain output, not application logs.
20
20
  - Generated framework noise (Next.js startup banners, Bun runtime warnings) — out of our control.
21
21
 
22
- The **LOGGING GATE** (`dispatch/write_any.md`, Logging Gate) sits on top of this file — it fires in addition to the language-level rules in `dispatch/write_zig.md` and `dispatch/write_ts_adhere_bun.md`, not instead of them.
22
+ The LOGGING GATE in `dispatch/write_any.md` applies alongside the repository's
23
+ selected language rules.
23
24
 
25
+ <!-- oracle-packs:start product.agentsfleet -->
24
26
  ## §2 · Today's de-facto standard (survey-derived)
25
27
 
26
28
  Documented honestly, not aspirationally. Pre-M62 baseline (the fix-pass converges every existing emit toward §3 onward):
@@ -39,6 +41,7 @@ Documented honestly, not aspirationally. Pre-M62 baseline (the fix-pass converge
39
41
  - No collector-friendly format. Logs are a mix of free-form English and ad-hoc `key={value}` fragments.
40
42
 
41
43
  The proposed standard below is what every new emit must conform to and what the fix-pass converges existing emits toward.
44
+ <!-- oracle-packs:end -->
42
45
 
43
46
  ## §3 · Wire format — logfmt
44
47
 
@@ -168,12 +171,16 @@ Log the source (`source=env:OPENAI_API_KEY`), never the value, at every level.
168
171
 
169
172
  > [DETERMINISTIC → LOG]
170
173
 
171
- Every `err` and `warn` record that maps to a domain error MUST carry `error_code=UZ-XXX-NNN` where `UZ-XXX-NNN` is declared in `rustd/crates/afd_core/src/error_code.rs`.
174
+ Every `err` or `warn` record for a domain error carries its code from the
175
+ repository's declared error registry. Validate new references through its
176
+ configured checker; installing this pack does not select a code prefix.
172
177
 
178
+ <!-- oracle-packs:start product.agentsfleet -->
173
179
  - **Used-but-undeclared** (`UZ-FAKE-999` appearing in code, no entry in registry): **blocking** in `make lint`.
174
180
  - **Declared-but-unreferenced** (registry has `UZ-LEGACY-007`, no code references it): **informational**. Deletion may be deferred to a sweep milestone.
175
181
 
176
182
  Embed the code as a struct field on the logger call: `log.err("event_name", .{ .error_code = error_codes.ERR_X, .err = @errorName(err) })`. The encoding helpers in `src/lib/logging/mod.zig` serialize it as `error_code=UZ-XXX-NNN` per §3. CLI (`agentsfleet`) renders the code in human format and as `code: "UZ-XXX-NNN"` in `--json` output (see §8).
183
+ <!-- oracle-packs:end -->
177
184
 
178
185
  System-level failures with no domain meaning (e.g. raw `EACCES` from a syscall before we attribute it to a tenant operation) emit without `error_code`. The follow-up rule: if a syscall failure surfaces to a user, it gets attributed to a registry code at the boundary.
179
186
 
@@ -181,20 +188,30 @@ System-level failures with no domain meaning (e.g. raw `EACCES` from a syscall b
181
188
 
182
189
  > [UNENFORCED → broad principle; no scan covers every log call site in the codebase for credential-shaped values]
183
190
 
184
- Inherits the redaction list from M42_002 (`src/runner/engine/runner_progress.zig`). Same secret values must not appear anywhere in log records.
191
+ Secrets must not appear in log records at any level. Read the repository's
192
+ redactor and verify coverage of every stream used by the changed operation.
185
193
 
186
194
  > [JUDGMENT → REDACT]
187
195
 
188
196
  - **Allocator outputs** (Postgres connection strings, Redis URLs, OAuth tokens, OpenAI keys) — never log. If a record needs to identify a config source, log the *source* (`source=env:OPENAI_API_KEY`), not the *value*.
189
- - **Tenant-supplied secrets** (workspace API keys, BYOK credentials) — redaction harness covers stdout/stderr from executor children. Log emit sites in this codebase MUST NOT bypass the harness.
190
- - **Stderr coverage gap** — today's redactor covers child stdout only. Closing this gap (extending to stderr) is part of M62_001's fix-pass, not a separate milestone.
197
+ - **Tenant-supplied secrets** — log emit sites must preserve the repository's
198
+ redaction boundary. Check both stdout and stderr; never assume a stream is
199
+ covered merely because another product's harness covers it.
191
200
 
192
201
  > [JUDGMENT → MSG-REVIEW]
193
202
 
194
- - **`msg=` fields** — values copy-pasted into `msg=` are the most common leak source. Audit script flags long `msg=` values; reviewer must verify they don't carry credentials.
203
+ - **`msg=` fields** — reviewers inspect copied values for credentials and test
204
+ renderer bounds. The source leaf does not detect long messages.
195
205
 
196
206
  When in doubt, omit. A missing field is recoverable; a leaked secret is not.
197
207
 
208
+ ## Repository logger binding
209
+
210
+ Use the consuming repository's structured logger and error registry.
211
+ Read its exports and test actual rendered records for the fields, bounds and
212
+ escaping above; installing a pack does not create a logging module.
213
+
214
+ <!-- oracle-packs:start product.agentsfleet -->
198
215
  ## §7 · Per-language binding — Zig
199
216
 
200
217
  The wire format above is produced by helpers in `src/lib/logging/mod.zig`, exposed as the named module `log` so any layer-isolated tree (auth/, executor/) can import it without violating layer rules. Call sites use:
@@ -279,11 +296,11 @@ Schema mirrors Bun's `SystemError` extern struct (`bun:src/bun.js/bindings/Syste
279
296
 
280
297
  > [DETERMINISTIC → LOG]
281
298
 
282
- **Logging emit sites** in `cli/src/**` use a thin Bun-runtime logger that produces logfmt records to `stderr`. `console.log` / `console.error` are forbidden in source per `dispatch/write_ts_adhere_bun.md` §10 — `logging.sh` enforces this for TS/JS.
299
+ **Logging emit sites** in `cli/src/**` use a thin Bun-runtime logger that produces logfmt records to `stderr`. `console.log` / `console.error` are forbidden in source per `dispatch/write_ts_adhere_bun.md` §10 — `logging.sh` enforces this for TS/JS. <!-- oracle-packs:language.typescript,language.javascript -->
283
300
 
284
301
  > [JUDGMENT → TS-STYLE]
285
302
 
286
- **Module-level error style** is governed by `dispatch/write_ts_adhere_bun.md` §9 (one style per module — throw OR Result, never both). The error type itself is the same:
303
+ **Module-level error style** is governed by the selected language's error discipline. The error type itself is the same:
287
304
 
288
305
  ```ts
289
306
  class AgentError extends Error {
@@ -295,6 +312,7 @@ class AgentError extends Error {
295
312
 
296
313
  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
314
 
315
+ <!-- oracle-packs:end -->
298
316
  ## §8A · Per-language binding — Rust
299
317
 
300
318
  > [DETERMINISTIC → LOG]
@@ -390,16 +408,18 @@ Failure modes the audit script and reviewer must close. These are **not aspirati
390
408
  | # | Rationalization | Closure |
391
409
  |---|---|---|
392
410
  | 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. |
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. |
411
+ | L2 | "The standard logger already has a scope" | Use the repository's structured logger and prove the §3 record shape. For `agentsfleet`, this is the named `log` module's `logging.scoped`, as §7 specifies. The leaf does not enforce logger imports or reject every `std.log` call; the reviewer checks this binding. |
394
412
  | 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. |
395
413
  | 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. |
396
- | L5 | "Operator needs the full stack trace in `msg=`" | `msg=` capped at 300 chars; total fields per record capped at 15. Stack traces emit as a separate `event=stack_trace` record at `debug` level, correlated by `correlation_id`, not stuffed into `msg`. |
397
- | L6 | "Embedded newlines because I copy-pasted output" | Audit greps for raw newline byte inside quoted logfmt values. Must be `\n` literal (two chars). |
414
+ | L5 | "Operator needs the full stack trace in `msg=`" | `msg=` stays capped at 300 characters and each record at 15 fields. Emit correlated stack traces separately at `debug`. These are renderer and reviewer checks; the leaf emits no message-size or field-count verdict. |
415
+ | L6 | "Embedded newlines because I copy-pasted output" | Encode newline characters as `\n` within values. Verify the actual rendered record in tests and review; the leaf cannot inspect runtime output. |
398
416
  | L7 | "Auto-mode is on, the gate block is ceremony" | **Auto-mode does NOT cover gate skips.** Skip without an explicit user-given override = automatic violation. No size threshold lets an edit bypass the gate. |
399
417
  | L8 | "I read this doc at session start; subsequent edits don't need re-print" | Gate fires **per-edit**. The printed `🔴 LOGGING GATE` block is required before every triggered Edit/Write, not once per session. |
400
418
  | L9 | "Fix-pass touches every line; printing per-line is noise" | Fix-pass produces **one combined gate block per file**, not per-line. The block lists all violations addressed in that file. Still required, just consolidated. |
401
419
 
402
- These are enforced by `logging.sh` (mechanical) and the dispatch façade (`dispatch/write_any.md`, Logging Gate — output discipline). When in conflict, the façade wins — it is the enforcement layer.
420
+ The leaf checks source emit forms; reviewers and renderer tests check the
421
+ record shape, logger binding, bounds and escaping named above.
422
+ The dispatch façade requires both kinds of evidence.
403
423
 
404
424
  ## §11 · Anti-patterns (named, banned)
405
425
 
@@ -410,7 +430,7 @@ These are enforced by `logging.sh` (mechanical) and the dispatch façade (`dispa
410
430
  | Rust `println!`, `eprintln!`, or `dbg!` outside tests | No level, event, or structured fields — dev diagnostics that escaped to main. |
411
431
  | Rust `tracing` emit without `event` | Drops the stable event key required by §3. |
412
432
  | Positional formatting in a Rust `tracing` emit | Values become opaque message text instead of queryable fields. |
413
- | `console.log` in `agentsfleet` source | Bypasses logger, breaks `--json` mode, violates `dispatch/write_ts_adhere_bun.md` §10. |
433
+ | `console.log` in `agentsfleet` source | Bypasses logger, breaks `--json` mode, violates the selected language's error discipline. |
414
434
  | Logging on hot per-iteration paths at `info` | Floods collectors. Use `debug` (gated off by default). |
415
435
  | `error_code=` missing on `err`/`warn` mapping to registry codes | Breaks traceability; future operator can't link log to docs. |
416
436
  | Logging credentials, tokens, or BYOK keys | Severe leak surface. Redaction harness mandatory. |
@@ -430,10 +450,10 @@ immediately preceding the edit. Generic "scope creep" is not a valid reason —
430
450
 
431
451
  ## §13 · Family
432
452
 
433
- - `dispatch/write_ts_adhere_bun.md` §10 — banned `console.log` in TS/JS source. Cross-referenced by `logging.sh`.
434
- - `dispatch/write_ts_adhere_bun.md` §9 — module-level error style (throw vs Result) for `agentsfleet`.
435
- - `dispatch/write_zig.md` — Zig discipline umbrella; this doc's §7 is the logging-specific layer.
436
- - `dispatch/write_rust.md` — Rust discipline umbrella; this doc's §8A is the logging-specific layer.
453
+ - `dispatch/write_ts_adhere_bun.md` §10 — banned `console.log` in runtime source. <!-- oracle-packs:language.typescript,language.javascript -->
454
+ - `dispatch/write_ts_adhere_bun.md` §9 — module-level error style. <!-- oracle-packs:language.typescript,language.javascript -->
455
+ - `dispatch/write_zig.md` — Zig authoring discipline. <!-- oracle-packs:language.zig -->
456
+ - `dispatch/write_rust.md` — Rust authoring discipline. <!-- oracle-packs:language.rust -->
437
457
  - `LIFECYCLE_PATTERNS.md` — orthogonal: ownership/cleanup of structs, including allocator wiring for the thread-local log buffer.
438
458
  - M42_002 redaction harness (`src/runner/engine/runner_progress.zig`) — secret-redaction precondition; this doc's §6 inherits.
439
459
  - Universal rules (RULE UFS, RULE TGU, RULE PRI, RULE FLL, RULE ORP, RULE TST-NAM) live in `docs/greptile-learnings/RULES.md`.
@@ -455,7 +475,7 @@ its line covers the clauses under it until the next heading.
455
475
  | `[JUDGMENT → BOUNDARY]` | is this operation boundary-crossing, and is its `_started`/`_completed`\|`_failed` pair complete | the agent at write time; the reviewer at `/review` |
456
476
  | `[JUDGMENT → REDACT]` | is this value a secret, or the label of one | the agent at write time; the M42_002 harness covers only executor-child output |
457
477
  | `[JUDGMENT → MSG-REVIEW]` | does this `msg=` carry a credential | the reviewer; no length or content check runs today |
458
- | `[JUDGMENT → TS-STYLE]` | throw-style or Result-style for this module | `dispatch/write_ts_adhere_bun.md` §9, agent-decided |
478
+ | `[JUDGMENT → TS-STYLE]` | throw-style or Result-style for this module | the selected language façade, reviewer-decided |
459
479
  | `[JUDGMENT → RUST-STYLE]` | does a Rust failure map to a registry code, and are field expressions safely hoisted | the agent at write time; the reviewer at `/review` |
460
480
  | `[JUDGMENT → EVENT-COMPAT]` | does a language port preserve the event bytes consumed by existing dashboards | the agent compares old and new event constants; the reviewer confirms intentional renames |
461
481
  | `[JUDGMENT → SECTION-SCAN]` | which sections of this file the current sub-task needs | the agent; `audits/doc-read.sh` records façade reads, not delegated-doc reads |
@@ -31,7 +31,7 @@ unaffected by anything below.
31
31
  Every other repository gets its rules a different way: `orly init` materialises
32
32
  the packs its own sources select — rendered `AGENTS.md`, the rule docs, the gate
33
33
  scripts, the hooks that run them — into that repository, and writes
34
- `.oracle/orly.json` recording the engine version and every file it wrote,
34
+ `.orly/orly.json` recording the engine version and every file it wrote,
35
35
  alongside the repository's own packs, commands, and surfaces. `orly update` re-materialises against the currently installed engine
36
36
  version; `orly doctor` reports drift between the lock and disk instead of
37
37
  silently tolerating it. A materialised repository needs no checkout of this
@@ -55,7 +55,7 @@ restores storage, but not the failure mode that got it removed: `orly update`
55
55
  turns a rule change into one command per repository instead of the manual
56
56
  sync M01 rejected, and the lock makes staleness a reported condition —
57
57
  `orly doctor` — instead of a silent one. cache-kit.rs is the evidence for both
58
- failure modes in the same repository: its `.oracle/` snapshot from the
58
+ failure modes in the same repository: its `.orly/` snapshot from the
59
59
  pre-thin model sat frozen for four weeks with no update path, and its
60
60
  generated `AGENTS.md` told a Rust crate its project name was `agentsfleet` —
61
61
  a persona/product leak M03 also closes, by fencing both behind opt-in packs a
@@ -90,8 +90,11 @@ Four behaviours worth knowing:
90
90
  with a printed reason, so an ad-hoc bug fix meets the quality gates without
91
91
  being told to write a spec. A spec moved to `done/` on the branch is still
92
92
  discovered through its `Branch:` header and gates the PR — CHORE(close)
93
- never skip-passes the criteria it exists to satisfy. Two active specs is an
94
- error — one stream per worktree.
93
+ never skip-passes the criteria it exists to satisfy. Headers accept a plain
94
+ token or an inline-code token. Active specs explicitly owned by other branches
95
+ do not displace this branch's spec; active specs without a branch remain gated.
96
+ Multiple specs may fold into one owner through `Folded-into`; two independent
97
+ owners on the current branch remain an error.
95
98
  - **A worktree is its repository.** `repositories.json` registers primary
96
99
  checkouts only; a linked worktree resolves through the set of checkouts git
97
100
  reports for the shared object store. Streams stay ephemeral and unregistered,
@@ -113,13 +116,15 @@ malformed trailer is not an override; the gate stays red.
113
116
 
114
117
  ## Profiles
115
118
 
116
- `.oracle/orly.json` names any opt-in packs, the command surface, and optionally the diff
119
+ `.orly/orly.json` names any opt-in packs, the command surface, and optionally the diff
117
120
  surfaces:
118
121
 
119
122
  ```json
120
123
  {
124
+ "schema_version": 1,
121
125
  "commands": { "conform": [["make", "harness-verify"]],
122
126
  "verify.unit": [["make", "test-unit-all"]] },
127
+ "limits": { "verify.unit": { "timeout_ms": 1800000, "output_bytes": 67108864 } },
123
128
  "surfaces": { "user": ["src/http/", "cli/src/"],
124
129
  "docs": ["docs/"] }
125
130
  }
@@ -132,6 +137,14 @@ checks without declaring application test suites. `init` reports missing
132
137
  commands; `doctor` rejects incomplete setup without running those commands.
133
138
  The final tree check includes the spec, so its evidence must be committed.
134
139
 
140
+ `schemas/profile.schema.json` describes the repository file, including ownership records and disabled execution settings.
141
+ Both configuration readers refuse unknown settings, malformed types and unsupported execution modes before selecting commands.
142
+ Each invocation defaults to 30 minutes and 64 mebibytes of combined output; `limits` supplies per-command overrides.
143
+
144
+ The synchronous gate calls a Bun supervisor that reads both streams and stops the owned process group on either limit.
145
+ Limited commands return a failure with measured duration, byte count and a private receipt directory containing all captured bytes.
146
+ Normal commands remove their temporary files after the gate reads the complete output; limit receipts remain until the caller removes them.
147
+
135
148
  ## Usage telemetry
136
149
 
137
150
  Orly keeps usage telemetry off until a person enables anonymous collection in
@@ -154,5 +167,63 @@ the Fleet is an analysis consumer and never the public ingestion boundary.
154
167
  ## Evidence
155
168
 
156
169
  `orly verify --all --write-evidence` records the source commit and every check
157
- into `.oracle/evidence.json` (git-ignored). Pre-push writes it when the pushed
170
+ into `.orly/evidence.json` (git-ignored). Pre-push writes it when the pushed
158
171
  range touches governance paths.
172
+
173
+ ## TypeScript judgment experiment
174
+
175
+ The 0.11.0 experiment keeps the Bun command line and installed `.orly/`
176
+ layout. `orly judge` accompanies the deterministic gates with explicit advice.
177
+ Indy canceled the native rewrite and its Milestone 07 plans on Oct 04, 2026.
178
+
179
+ ### Timing and authority
180
+
181
+ | Stage | Input | Bounded decision | Follow-up |
182
+ |---|---|---|---|
183
+ | PLAN | One spec requirement and its selected section | Are prerequisites explicit? Is the result observable? | Clarify setup or expected results before implementation |
184
+ | VERIFY | Required behavior, complete implementation and linked test | Does the assertion discriminate correct behavior from the supplied incorrect behavior? | Add an exact assertion before accepting test evidence |
185
+ | REVIEW | One failure path or one rule with selected code | Is the failure handled? Does this rule apply? | Inspect the named path or rule before disposing of the finding |
186
+ | DOCUMENT | One claim and selected implementation or measured output | Does supplied evidence support this claim? | Narrow the claim or gather the missing proof |
187
+ | Commit, push and PR | Existing deterministic facts and command results | Existing checks only | Model advice cannot change gate outcomes |
188
+
189
+ Questions and possible next actions live in a fixed catalog. Noul supplies a
190
+ probability for one property. Choice selects an assertion category. There is
191
+ no overall quality score, model-written command or automatic owner decision.
192
+ The agent invokes advice explicitly. Skill instructions explain optional timing;
193
+ hooks never upload source and never request a model answer.
194
+
195
+ ### Evidence and upload
196
+
197
+ A stage-specific manifest names the required behavior and role-labeled source
198
+ references. Each reference selects a complete file, TypeScript function, named
199
+ test, or Markdown section. Missing, ambiguous, escaping and oversized inputs
200
+ are refused before inference. Evidence remains data even when it contains
201
+ instructions. Necessary unchanged context must be named by the manifest.
202
+
203
+ The shipped command uses the official TypeSafe System One API and pins
204
+ `jev-1.13.0`. Only `--refresh` permits a live request. The provider key comes
205
+ from `TYPESAFE_API_KEY` at runtime. A passing secret scan is required for the
206
+ entire selected state before any request. Source and credentials stay out of
207
+ reports and error messages. Limits cover source, state, request and response
208
+ bytes, item count, subprocess duration, fetch and body-reading duration.
209
+ Requests are sequential and receive no automatic retries.
210
+
211
+ Evidence selection uses Babel's JavaScript parser in a bounded Bun worker.
212
+ The installed command runs TypeScript source through Bun, with the same `bunx`
213
+ installation as 0.10.x. TypeScript 7 supplies development checks. The shipped
214
+ runtime has no native compiler dependency or compiler installation step.
215
+
216
+ ### Replay and proof
217
+
218
+ Default mode reads a local exact-input replay. Identity binds project root,
219
+ model, catalog definitions, requirements, selectors and selected source bytes.
220
+ A changed input or invalid record is incomplete. Replay files include typed
221
+ answers and integrity metadata, without source text. Local replay is editable
222
+ by the repository owner and is advisory evidence, never approval or a gate.
223
+
224
+ A consumer experiment uses a new `agentsfleet` worktree and the locally packed,
225
+ unpublished 0.11.0 package. The controls compare the same incorrect locale code
226
+ with a weak assertion and an exact assertion, plus healthy exact behavior.
227
+ Repeated live observations report successes, misses, uncertainty, latency and
228
+ token usage. The trial proves bounded usefulness only; broader accuracy and
229
+ runtime superiority require different evidence.
@@ -1,6 +1,20 @@
1
- # Release doc template — `changelog.mdx` `<Update>` block
1
+ # Release documentation template
2
2
 
3
- Source of truth: `~/Projects/docs/changelog.mdx`. New `<Update>` blocks go at the top, after the leading `<Tip>`/`<Note>`. Labels are date-only — never a semver prefix. `VERSION` is the single source of truth for binary version (binaries via `make sync-version` propagate to `build.zig.zon`, `agentsfleet/package.json`, `agentsfleet/src/cli.js`); changelog stays chronological. Decoupled by design — avoids parallel-branch collisions.
3
+ Use the consuming repository's declared changelog and version source.
4
+ Preserve its selected format; a Markdown changelog does not acquire a Mintlify
5
+ site, sibling checkout or `VERSION` file by installing this pack.
6
+ Use its declared synchronization command when it maintains generated versions.
7
+
8
+ <!-- oracle-packs:start product.agentsfleet -->
9
+ For `agentsfleet`, the changelog is `~/Projects/docs/changelog.mdx` and `VERSION`
10
+ is the binary version source. `make sync-version` updates `build.zig.zon`,
11
+ `agentsfleet/package.json` and `agentsfleet/src/cli.js`.
12
+ New `<Update>` blocks go after the leading `<Tip>` or `<Note>`.
13
+ <!-- oracle-packs:end -->
14
+
15
+ For a repository already using Mintlify, use the block and label rules below.
16
+ Other formats carry the same applicable upgrading, behavior and API information
17
+ in their existing entry structure.
4
18
 
5
19
  ## Block template
6
20
 
@@ -23,7 +37,7 @@ Source of truth: `~/Projects/docs/changelog.mdx`. New `<Update>` blocks go at th
23
37
  {User-visible bugs fixed — observed behavior before/after. Omit if none.}
24
38
 
25
39
  ## CLI
26
- {`agentsfleet` additions or shape changes. Omit if none.}
40
+ {Command additions or shape changes. Omit if none.}
27
41
  </Update>
28
42
  ```
29
43
 
@@ -36,11 +50,13 @@ Source of truth: `~/Projects/docs/changelog.mdx`. New `<Update>` blocks go at th
36
50
  - Every breaking change appears under `Upgrading` with a migration step, even if also mentioned elsewhere.
37
51
  - Body copy may reference a past entry by date (`"…that shipped on Apr 22, 2026"`); do not reference past releases by semver (`"shipped in v0.27.0"`) — that drags the two timelines back together.
38
52
 
39
- ## Version bumps (`VERSION`, not the changelog label)
53
+ ## Version bumps (the repository's version source)
40
54
 
41
55
  - Feature milestone → minor (`0.7.0` → `0.8.0`).
42
56
  - Bug fix → patch.
43
57
  - Pre-v1.0 breaking → minor (semver 0.x carve-out); call out under Upgrading.
44
58
  - Post-v1.0 breaking → major.
45
59
  - Internal-only refactor: terse `<Update>` with `tags={["Internal", ...]}`, one-paragraph summary, skip section structure. Prefer folding into the next user-visible release.
46
- - Parallel branches bumping `VERSION` do not coordinate through the changelog — whichever lands second rebases `VERSION` and re-runs `make sync-version`.
60
+ - Parallel branches reconcile their version source against the current default
61
+ branch and run the repository's declared synchronization check. This never
62
+ authorizes rebasing or force-pushing a published branch.
@@ -1,4 +1,4 @@
1
- # REST API Design Guidelines — `agentsfleet/agentsfleetd`
1
+ # REST API Design Guidelines
2
2
 
3
3
  **Status:** Canonical instruction set. Read this before adding, modifying, or removing any HTTP endpoint.
4
4
  **Trigger:** the global instruction `HTTP handler or OpenAPI changes → read docs/REST_API_DESIGN_GUIDELINES.md first` fires when the diff touches a handler, `public/openapi.json`, or any `route_*` file. If you're an agent reading this — you got here because that trigger fired. Follow this doc as a checklist, not as background reading.
@@ -9,10 +9,11 @@ This is a goal-oriented instruction set. Each rule states the goal it serves so
9
9
 
10
10
  ## Audience — who this binds
11
11
 
12
- This doc binds **whoever ships the change** — Kishore directly, OR an agent acting on Kishore's behalf in auto mode under a start-instruction (per `~/.claude/CLAUDE.md` autonomy rules). Concretely:
12
+ This guide binds the author and reviewer in the consuming repository.
13
+ Publishing and merge authorization come from that repository's operating model.
13
14
 
14
15
  - **The agent** runs the Quick checklist below as part of `CHORE(close)` and the §10 pre-PR gate, opens the PR via `gh pr create`, and answers `orly-babysit-prs` review feedback. The agent is the primary enforcer.
15
- - **Kishore** opens the PR directly when working without an agent. Same checklist applies.
16
+ - **The human author** follows the same checklist when working without an agent.
16
17
  - **A reviewer** (human or `/review`) checks the PR against this doc adversarially. A red box on the checklist that's not justified in the PR description is grounds to block merge.
17
18
 
18
19
  When this doc says "you" it means the agent or the human author — same rules either way. When it says "MUST" it means a missing or wrong implementation blocks merge; "SHOULD" means deviate only with a one-line rationale in the PR description.
@@ -21,14 +22,16 @@ When this doc says "you" it means the agent or the human author — same rules e
21
22
 
22
23
  ## Quick checklist — adding an endpoint
23
24
 
24
- Run this checklist as part of `CHORE(close)` (per `~/.claude/CLAUDE.md` lifecycle), before `gh pr create`. Every box must be checked, OR the PR description must call out the deviation with a reason. An unchecked box that the description ignores blocks merge.
25
+ Run this checklist at the repository's review boundary before creating a Pull
26
+ Request (PR). Every applicable item needs evidence or an owner-approved
27
+ exception; an unchecked item cannot silently become a pass.
25
28
 
26
29
  - [ ] **URL design** — plural noun resource, hierarchical path, no verbs (§1); operation-style `:verb` declared as one of the three allowed categories (§1)
27
30
  - [ ] **Path params + trailing slash** — `{resource_id}` matches body field name; no trailing slash (§1)
28
31
  - [ ] **HTTP method** chosen by semantics; PATCH idempotency guarantee stated; `Idempotency-Key` honored if applicable (§2)
29
32
  - [ ] **Long-running ops** use the canonical `202 + /v1/operations/{id}` shape (§2)
30
33
  - [ ] **Request body shape** matches the path (no path-param IDs in body) (§3)
31
- - [ ] **Pagination** uses Stripe-style `?starting_after=&limit=` with `next_cursor` response field; default 50, max 100 (§3)
34
+ - [ ] **Pagination** uses keyset cursors with shared, declared default and maximum bounds (§3)
32
35
  - [ ] **List envelope** is exactly `{items, total: int|null, next_cursor: string|null}` — no synonyms (§3)
33
36
  - [ ] **Bulk endpoints** use `207` with the canonical per-item shape (§3)
34
37
  - [ ] **Null vs omit** — absent optionals omitted, `null` reserved for "explicitly cleared" (§3)
@@ -37,10 +40,10 @@ Run this checklist as part of `CHORE(close)` (per `~/.claude/CLAUDE.md` lifecycl
37
40
  - [ ] **Status codes** — 409 includes `current_state`; 412 includes `etag`; 429 includes `Retry-After` + `X-RateLimit-*` (§4)
38
41
  - [ ] **ETag/`If-Match`** wired for any resource with realistic concurrent edits (§4)
39
42
  - [ ] **Error responses** use the registry; `detail` follows hygiene rules (no IDs, no SQL, no paths, ≤200 chars) (§5)
40
- - [ ] **OpenAPI document** regenerated from the build, not hand-edited; the coverage gate is green (§6)
41
- - [ ] **Route registered** in all six places (§7)
42
- - [ ] **Handler signature** takes only the extractors it reads and returns `Result<Response, Refusal>` (§8)
43
- - [ ] **Middleware policy** picked from the table; raw handlers carry first-10-lines comment (§7)
43
+ - [ ] **OpenAPI document** matches the repository's canonical source and declared generation or validation checks (§6)
44
+ - [ ] **Route registered** in the repository's actual router and discovery graph (§7)
45
+ - [ ] **Handler signature** follows its framework and uses the inputs it reads (§8)
46
+ - [ ] **Authentication and ownership** are proved on the served route before data access (§7–§8)
44
47
  - [ ] **Versioning** — added/renamed/removed surface listed in PR description; deprecation uses `Deprecation` + `Sunset` headers; new response fields declare `x-stability` (§9)
45
48
  - [ ] **Tests** — happy path + one error per refusal + idempotency double-PATCH + `Idempotency-Key` replay (where applicable) + ETag mismatch (§10)
46
49
  - [ ] **Logging** — sensitive ID values are DEBUG-only or carry `// log-id-allowed:` comment; secret-shaped fields are write-only or one-time-read (§11)
@@ -215,7 +218,15 @@ GET /products?status=active&sort=-created_at&starting_after=01HZQ...&limit=50
215
218
  - **Time ranges:** `?created_after=<ts_ms>&created_before=<ts_ms>`. Bracket grammar (`?created_at[gte]=...`) is forbidden.
216
219
  - **No boolean explosions.** Don't add `?include_x=true&include_y=true` — use `?include=x,y` with a documented enum of legal values, OR don't expose a knob.
217
220
  - **Sorting:** `sort=field` ascending; `sort=-field` descending. Single sort key per request — no multi-key.
218
- - **Pagination — Stripe-style keyset only.** Request: `?starting_after=<resource_id>&limit=<int>`. Response: `next_cursor: <resource_id> | null` (the field is named `next_cursor` even though the request param is `starting_after`). Cursor encode/decode goes through `afd_core::paging::cursor`, and both the parameter name and the bounds are constants there: `QUERY_STARTING_AFTER`, `DEFAULT_LIMIT` (50) and `MAX_LIMIT` (100). Read the limit through that module rather than parsing the query string in a handler. To page forward, send the response's `next_cursor` value back as the next request's `starting_after`. **Forbidden:** page-based `?page=&page_size=`, and custom request-side `?cursor=` names. Both spellings predate this rule where they survive; do not copy either into a new endpoint.
221
+ - **Pagination — keyset.** Request: `?starting_after=<resource_id>&limit=<int>`;
222
+ response: `next_cursor: <resource_id> | null`. Send `next_cursor` back as the
223
+ next request's `starting_after`; use shared repository parsing and constants
224
+ for the default and maximum, with bounds tested at the served endpoint.
225
+ <!-- oracle-packs:start product.agentsfleet -->
226
+ - For `agentsfleet`, use `afd_core::paging::cursor`, `Page::parse` and its route
227
+ `Ceiling`, or `afd_validate::Limit::parse`. `DEFAULT_LIMIT` is 50 and
228
+ `MAX_LIMIT` is 100; an empty limit uses the route default.
229
+ <!-- oracle-packs:end -->
219
230
  - **Sparse fieldsets / `?include=` / `?fields=`:** not supported in v1. If you need to slim a payload, design a smaller endpoint. Don't invent.
220
231
 
221
232
  ### Bulk operations
@@ -242,7 +253,9 @@ This rule is binding on responses AND requests. SDK consumers branch on `field =
242
253
 
243
254
  ### IDs
244
255
 
245
- - Use **UUIDv7** for all externally-exposed IDs (sortable, time-encoded). See [uuid7.com](https://uuid7.com).
256
+ - Preserve the repository's documented public identifier format and test its
257
+ parsing at the endpoint boundary. Do not substitute another product's format.
258
+ - `agentsfleet` uses Universally Unique Identifier version 7 (UUIDv7) for public identifiers. <!-- oracle-packs:product.agentsfleet -->
246
259
  - Do not expose database serial integers.
247
260
  - Sensitive IDs (workspace_id, agent_id) live in the path, not the body. Never log their values at INFO level (§11).
248
261
 
@@ -252,6 +265,11 @@ This rule is binding on responses AND requests. SDK consumers branch on `field =
252
265
 
253
266
  ### Success — return the wire type
254
267
 
268
+ Return the repository's typed response through its framework's response
269
+ renderer. Share the public response shape with its schema source so field names,
270
+ status and content type stay consistent.
271
+
272
+ <!-- oracle-packs:start product.agentsfleet -->
255
273
  ```rust
256
274
  Ok(Json(FleetDetailResponse::from(&detail)).into_response())
257
275
  Ok((StatusCode::CREATED, Json(CreatedResponse { id, key })).into_response())
@@ -262,6 +280,7 @@ The wire type owns the field names and `IntoResponse` writes the status and
262
280
  content-type. A response shape belongs in `afd_wire`, not spelled inline in a
263
281
  handler, so the same struct is what the OpenAPI document is generated from.
264
282
  Build a response by hand only for a Server-Sent Events stream.
283
+ <!-- oracle-packs:end -->
265
284
 
266
285
  ### Status codes
267
286
 
@@ -323,13 +342,20 @@ For any resource where concurrent edits are realistic (anything mutable that two
323
342
 
324
343
  ### Use the error registry
325
344
 
326
- The error-code registry (`rustd/crates/afd_core/src/error_code.rs` and the family modules beside it) owns the HTTP status, RFC 7807 `title`, and `docs_uri`. Your handler supplies only the code and a human-readable `detail`:
345
+ Use the repository's error registry and renderer for status, title and public
346
+ documentation links. Handlers supply the code and a safe human-readable detail;
347
+ they do not duplicate the registry's response shape.
348
+
349
+ <!-- oracle-packs:start product.agentsfleet -->
350
+ The `agentsfleet` registry is `rustd/crates/afd_core/src/error_code.rs` and its
351
+ family modules. Its handlers use these refusal constructors:
327
352
 
328
353
  ```rust
329
354
  Refusal::malformed(DETAIL_WORKSPACE_ID) // the sentence is a constant
330
355
  Refusal::coded(FLEET_NOT_FOUND, DETAIL_NOT_FOUND)
331
356
  Refusal::preconditioned(error.code(), error.detail(), current)
332
357
  ```
358
+ <!-- oracle-packs:end -->
333
359
 
334
360
  Never assemble a `Problem` by hand and never write a status and body directly:
335
361
  the status, title and documentation link come from the registry entry, and a
@@ -371,16 +397,20 @@ The title must be safe to render verbatim in a UI toast — think "what would I
371
397
  - State: `"<noun> already exists"` / `"<noun> not found"` / `"<noun> expired"` — `Agent name already exists`, `token expired`.
372
398
  - Format help: `"<param>: use <format>"` — `invalid_since_format: use Go-style duration (15s, 30m, 2h, 7d) or RFC 3339 (YYYY-MM-DDTHH:MM:SSZ)`.
373
399
 
374
- When you write a new refusal, find the closest existing call site under `rustd/crates/afd_api_*/src/handler/**` and copy its shape. Don't freelance.
400
+ When writing a refusal, read the closest existing handler and the repository's
401
+ canonical renderer before choosing the shape.
375
402
 
376
403
  ### Internal 500s — direct calls
377
404
 
378
- A datastore or service failure is not a sentence a handler writes. Lift it with
379
- `Refusal::at`, naming the operation that failed:
405
+ A datastore or service failure uses the repository's canonical refusal renderer.
406
+ Name the failed operation in internal diagnostics without exposing its details
407
+ to the caller.
380
408
 
409
+ <!-- oracle-packs:start product.agentsfleet -->
381
410
  ```rust
382
411
  services.fleet_detail(&context, fleet_id).await.map_err(Refusal::at(FLEET_READ))?
383
412
  ```
413
+ <!-- oracle-packs:end -->
384
414
 
385
415
  The operation constant is what identifies the failure in logs and telemetry;
386
416
  the caller gets the registry's 500 and no internal detail.
@@ -410,6 +440,11 @@ Don't invent other extensions without amending this doc.
410
440
 
411
441
  ## §6 — OpenAPI editing
412
442
 
443
+ Locate the repository's canonical OpenAPI source. Update that source and run
444
+ its declared generation or validation command; do not assume a Rust daemon,
445
+ generated artifact or build target from another product.
446
+
447
+ <!-- oracle-packs:start product.agentsfleet -->
413
448
  **The document is generated. There is nothing to hand-edit.**
414
449
 
415
450
  `public/openapi.json` is emitted from the daemon's own handlers — the route
@@ -449,8 +484,15 @@ generated document, so a description that breaks the wording rules fails
449
484
  for maintainers rather than for API consumers belongs in a `//` comment beside
450
485
  it, which the document does not carry.
451
486
 
487
+ <!-- oracle-packs:end -->
452
488
  ## §7 — Registering a route
453
489
 
490
+ Register the served method and path in the repository's router and discovery
491
+ graph. Prove authentication and resource ownership are enforced before access;
492
+ a helper that no served route mounts is not evidence of authorization.
493
+
494
+ <!-- oracle-packs:start product.agentsfleet -->
495
+
454
496
  **The router is built FROM the route table, not beside it.** `Route::all()` is
455
497
  walked once at startup and every template it yields is mounted, so a path
456
498
  cannot be served under a spelling the table does not carry, and it cannot be
@@ -582,8 +624,16 @@ When in doubt, mirror an existing handler:
582
624
  | Signature-proven ingress | `rustd/crates/afd_api_ingress/src/handler/webhook/receive_route.rs` |
583
625
  | A runner speaking for itself | `rustd/crates/afd_api_runner/src/handler/runner/lease.rs` |
584
626
 
627
+ <!-- oracle-packs:end -->
585
628
  ## §8 — Handler signature rule
586
629
 
630
+ Follow the repository's framework and read its canonical handler first.
631
+ Use validated inputs and the shared response and error renderers.
632
+ Enforce authentication and ownership through its canonical mechanism, with tests
633
+ against the actual served route; do not duplicate mounted checks by habit.
634
+
635
+ <!-- oracle-packs:start product.agentsfleet -->
636
+
587
637
  A handler is an axum handler. The router built it into a stack that has already
588
638
  admitted the request, proven the caller, checked the capability and confirmed
589
639
  ownership, so a handler receives what it needs as extractors and constructs
@@ -612,7 +662,14 @@ pub async fn my_endpoint<D: Services>(
612
662
  envelope is built for you.
613
663
  - **Bounds are declared on the request type**, with `garde`, not re-spelled per
614
664
  handler. The refusal sentence a field earns is mapped from the path `garde`
615
- reports.
665
+ reports, through a `const` `afd_validate::Sentences` table beside the
666
+ handler (`PathTable<Variant>` when a crate answers with its own error
667
+ variant). A rule garde lacks — `finite`, `nul_free`, `ascii_digits`,
668
+ `charset` — comes from `afd_validate`, never a one-off check.
669
+ - **A parser a bound protects takes `&garde::Valid<T>`.** garde runs every
670
+ rule on a field with no short-circuit, so a bound and a parser on one field
671
+ would still hand the parser oversized input; the bound goes on a struct and
672
+ the parser accepts only the proved value.
616
673
  - **Never authenticate inside a handler.** The guard on the route's row did it.
617
674
  Never call an ownership check by hand either; the template mounted it.
618
675
  - **Refusal sentences live beside the parser that produces them**, as
@@ -621,12 +678,15 @@ pub async fn my_endpoint<D: Services>(
621
678
  ### What NOT to do
622
679
 
623
680
  - ❌ Reading a query string directly for `limit` or `starting_after` — use
624
- `afd_core::paging`.
681
+ `afd_core::paging`, or `afd_validate::Limit` with the route's `Ceiling`.
682
+ - ❌ A hand-written function from a `garde::Report` to a sentence — declare a
683
+ `Sentences` table.
625
684
  - ❌ Building a `Problem` by hand — construct it from the declared error code so
626
685
  the status, title and documentation link come from the registry (§5).
627
686
  - ❌ A second unique spelling of a capability check inside the handler body.
628
687
  - ❌ Re-parsing a path parameter the extractor already typed.
629
688
 
689
+ <!-- oracle-packs:end -->
630
690
  ## §9 — Versioning
631
691
 
632
692
  URI-based: `/v1/...`. All current endpoints sit under `/v1`. Bump to `/v2` only when a breaking change is unavoidable; default to additive evolution within `/v1`.
@@ -697,11 +757,10 @@ Every PR that changes the HTTP surface MUST open its description with a "Surface
697
757
 
698
758
  Before opening a PR touching any handler:
699
759
 
700
- - [ ] `zig build` clean
701
- - [ ] `zig build test` passes
760
+ - [ ] Declared build and unit commands have observed successful results
702
761
  - [ ] the declared `verify.unit` command passes — the route-scope match and its tests cover the auth gate matrix
703
762
  - [ ] the repository's integration suite passes end to end against real datastores, where it declares one
704
- - [ ] Cross-compile: `zig build -Dtarget=x86_64-linux && zig build -Dtarget=aarch64-linux`
763
+ - [ ] Applicable platform targets and test graphs match the repository's declared matrix
705
764
  - [ ] the declared `verify.lint` command passes — every language gate the repository declares
706
765
  - [ ] Handler file ≤ 350 lines; split if it grows
707
766
  - [ ] Integration test covers the happy path AND at least one error path per refusal the handler can return
@@ -729,12 +788,18 @@ Before opening a PR touching any handler:
729
788
 
730
789
  ## §12 — Performance
731
790
 
732
- Targets (measured in CI bench under `make bench`):
791
+ Use the consuming repository's declared performance budgets and benchmark lane.
792
+ Measure applicable latency percentiles, memory and query bounds under the load
793
+ profile the endpoint is expected to serve.
794
+
795
+ <!-- oracle-packs:start product.agentsfleet -->
796
+ `agentsfleet` targets (measured by its declared benchmark lane):
733
797
 
734
798
  - p99 latency < 200 ms
735
799
  - p95 latency < 150 ms
736
800
  - Zero allocator leaks (`std.testing.allocator` integration tests pass)
737
801
  - No unbounded query loops. Pagination caps live in §3 (default `limit=50`, max `limit=100`).
802
+ <!-- oracle-packs:end -->
738
803
 
739
804
  If your endpoint can't meet these in a normal load profile, the spec's "Performance Considerations" section MUST contain ALL of:
740
805
 
@@ -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 | 30/30 | mechanical |
10
- | `docs/REST_API_DESIGN_GUIDELINES.md` | — | 0 | 0 | 0/65 | mechanical |
11
- | `docs/SCHEMA_CONVENTIONS.md` | — | 0 | 0 | 0/16 | mechanical |
9
+ | `docs/LOGGING_STANDARD.md` | `LOG` → `logging.sh` | 10 | 13 | 28/28 | mechanical |
10
+ | `docs/REST_API_DESIGN_GUIDELINES.md` | — | 0 | 0 | 0/67 | mechanical |
11
+ | `docs/SCHEMA_CONVENTIONS.md` | — | 0 | 0 | 0/17 | mechanical |
12
12
  | `docs/DOCUMENTATION_RULES.md` | — | 0 | 0 | 0/22 | latent |
13
13
  | `docs/LIFECYCLE_PATTERNS.md` | — | 0 | 0 | 0/22 | mechanical |
14
- | `docs/CHANGELOG_VOICE.md` | — | 0 | 0 | 0/4 | latent |
14
+ | `docs/CHANGELOG_VOICE.md` | — | 0 | 0 | 0/3 | latent |
15
15
  | `docs/VERIFY_TIERS.md` | — | 0 | 0 | 0/4 | uncited |
16
- | `docs/greptile-learnings/RULES.md` | — | 0 | 0 | 0/114 | mechanical |
17
- | **corpus** | | | | **30/277** | |
16
+ | `docs/greptile-learnings/RULES.md` | — | 0 | 0 | 0/115 | mechanical |
17
+ | **corpus** | | | | **28/278** | |
18
18
 
19
19
  ## How to read this
20
20