@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,41 @@
1
+ # Rule enforcement ledger
2
+
3
+ Generated by `audits/rule-ledger.sh`. Never hand-edit: `make audit`
4
+ regenerates this file and fails on any difference. To update it, run
5
+ `bash audits/rule-ledger.sh --write docs/RULE_ENFORCEMENT.md`.
6
+
7
+ | Rule document | enforced by | judged | acknowledged | classified | trigger |
8
+ |---|---|--:|--:|--:|---|
9
+ | `docs/LOGGING_STANDARD.md` | `LOG` → `logging.sh` | 10 | 13 | 26/26 | mechanical |
10
+ | `docs/REST_API_DESIGN_GUIDELINES.md` | — | 0 | 0 | 0/74 | mechanical |
11
+ | `docs/SCHEMA_CONVENTIONS.md` | — | 0 | 0 | 0/16 | mechanical |
12
+ | `docs/DOCUMENTATION_RULES.md` | — | 0 | 0 | 0/20 | latent |
13
+ | `docs/LIFECYCLE_PATTERNS.md` | — | 0 | 0 | 0/22 | mechanical |
14
+ | `docs/CHANGELOG_VOICE.md` | — | 0 | 0 | 0/3 | latent |
15
+ | `docs/VERIFY_TIERS.md` | — | 0 | 0 | 0/4 | uncited |
16
+ | `docs/greptile-learnings/RULES.md` | — | 0 | 0 | 0/113 | mechanical |
17
+ | **corpus** | | | | **26/278** | |
18
+
19
+ ## How to read this
20
+
21
+ - **enforced by** — the rules in this document a script decides, and the script
22
+ that decides each. This is the column worth acting on: it is the difference
23
+ between a rule that holds and a rule that is merely written down. A code
24
+ reading `no helper row` is a `[DETERMINISTIC → …]` tag with nothing behind it.
25
+ - **judged / acknowledged** — clauses an agent must weigh (`[JUDGMENT → CODE]`)
26
+ and clauses nobody checks on purpose (`[UNENFORCED → reason]`, the reason
27
+ stated where a reader meets it).
28
+ - **classified** — how much of the document has been triaged at all. The
29
+ denominator counts lines carrying a normative keyword outside headings and
30
+ tables — a keyword heuristic, not a parse. It informs; it never gates.
31
+ - **trigger** — `mechanical` when a citing façade declares a file scope,
32
+ `latent` when every citing façade is prose the agent must choose to read,
33
+ `uncited` when no façade names the document at all.
34
+
35
+ A tag alone on its line covers every clause under it until the next heading; a
36
+ tag at the end of a sentence covers that sentence. Same grammar the
37
+ `dispatch/*.md` façades use, read by the same code.
38
+
39
+ Everything here is a function of the tree alone — no timestamps, no commit
40
+ hashes, no history — so the file reproduces from a checkout and any diff means
41
+ the corpus moved.
@@ -0,0 +1,93 @@
1
+ # Schema Conventions
2
+
3
+ Canonical reference for agentsfleet database schema patterns. All new tables **must** follow these conventions. Existing tables are brought into compliance when rebuilt.
4
+
5
+ ## Migration Model
6
+
7
+ **Teardown-rebuild is in force for the M154 rebuild (owner decision, Indy, Jul 31, 2026).** The development database is being dropped and re-created from empty while production is undeployed, so slots `001`–`046` are **retired wholesale** and the schema is re-authored by dependency layer. During this rebuild the frozen-slot rule below does not apply, and `ALTER`/`DROP` statements are forbidden rather than required — a change belongs in the base statement it would have patched. This is the posture RULE SCH already specifies for `VERSION < 2.0.0`; the Jul 22 additive model was the deviation.
8
+
9
+ **Additive migrations resume once the rebuild lands.** Every subsequent schema change is a **new numbered migration file** — `ALTER TABLE … ADD COLUMN`, new tables, new indexes. **Shipped slot files are then frozen history: never edit an existing `schema/NNN_*.sql`.** Migrations are version-tracked and applied incrementally (expected-vs-applied state is inspectable via `agentsfleetd doctor --schema-gate`). Use `IF NOT EXISTS` guards so a migration is idempotent against both a fresh bootstrap (all slots in order) and an already-provisioned database (new slots only).
10
+
11
+ Destructive changes (`DROP TABLE`, `DROP COLUMN`, type rewrites) require an explicit owner decision per change — additive is the default an agent may author alone.
12
+
13
+ > Historical note: slots `001`–`031` predate the additive model (teardown-rebuild with inline DDL edits, enforced by a since-removed `check-schema-gate` lint target). M154 retires them along with everything up to `046`.
14
+
15
+ ## Schema File Organization
16
+
17
+ - Each SQL file must be **≤100 lines** and **single-concern** (one table, one logical group, or one additive change).
18
+ - Files are numbered sequentially: `001_core_foundation.sql`, `002_core_workflow.sql`, etc. New migrations append the next number; shipped numbers are never reused or slid.
19
+ - Every SQL file must be registered in `schema/embed.zig` (compile-time embed) and `src/agentsfleetd/cmd/common.zig` (migration version array).
20
+ - No-op stub files (e.g., columns folded into earlier files) are kept for version history but excluded from the migrations array.
21
+
22
+ ## SQL Qualification
23
+
24
+ - Use schema-qualified table names in SQL (`core.platform_llm_keys`, `core.workspaces`, etc.) for new queries and handlers.
25
+ - Do not rely on session `search_path` defaults for correctness.
26
+ - Legacy unqualified queries may remain temporarily, but touched paths should be migrated to schema-qualified names.
27
+
28
+ ## Schema-Backed Runtime Defaults
29
+
30
+ - If a numeric schema default is also used in runtime fallback or provisioning logic, define it once in Zig as a named constant and import that constant in runtime code.
31
+ - Keep the database DDL value unchanged unless the product default is intentionally changing; the Zig constant mirrors the schema default for drift detection.
32
+ - Add an adjacent `Canonical constant:` SQL comment next to each shared numeric default so reviewers can verify the linkage quickly.
33
+
34
+ ## Identity Column
35
+
36
+ **One identity column per table, named `id` (owner decision, Indy, Jul 31, 2026 — M154).** The earlier `uid` rule paired a generated `uid` primary key with a duplicate twin column (`id`, `tenant_id`, `workspace_id`, …) holding the same value under its own `UNIQUE` constraint. That shape is retired.
37
+
38
+ - **Column:** `id`
39
+ - **Type:** Universally Unique Identifier (UUID) `PRIMARY KEY`
40
+ - **Generation:** Application-side UUID version 7 (UUIDv7) via `src/agentsfleetd/types/id_format.zig`, never `gen_random_uuid()`.
41
+ - **No second unique key on the same value.** This is a correctness rule, not only a storage one: `ON CONFLICT` can arbitrate exactly one constraint, so a table carrying two unique keys over the same value makes two sessions inserting a brand-new row race to a duplicate-key error on the *other* index instead of taking the update arm. `schema/043_runner_lifetime_counters.sql` recorded this before the rule was generalised.
42
+ - **Foreign keys reference the primary key**, never a secondary unique constraint.
43
+ - **Constraint:** every table carries a UUIDv7 CHECK:
44
+ ```sql
45
+ CONSTRAINT ck_{table}_id_uuidv7 CHECK (substring(id::text from 15 for 1) = '7')
46
+ ```
47
+ - **Adding a new table:** add a `generate{TableName}Id()` function to `src/agentsfleetd/types/id_format.zig`.
48
+ - **API shape:** unchanged — public fields keep exposing `id`, `tenant_id`, `workspace_id` and the other documented names. The column now *is* the public name in most tables, so the aliasing the old rule required mostly disappears; where a public field differs from `id`, alias at the boundary rather than renaming client-facing payloads.
49
+ - **Exceptions,** each stated in the slot that creates the table: a curated catalogue keyed by a stable slug (`core.fleet_library`) and a singleton keyed by a pinned integer (`core.model_catalogue_revision`) carry no UUID.
50
+
51
+ ## Timestamps
52
+
53
+ - **Type:** `BIGINT NOT NULL` — milliseconds since Unix epoch.
54
+ - **Generation:** `std.time.milliTimestamp()` in Zig application code.
55
+ - **Never** use `TIMESTAMPTZ`, `TIMESTAMP`, or `DEFAULT now()`.
56
+
57
+ ## Standard Columns
58
+
59
+ Every table must have:
60
+
61
+ | Column | Type | Required | Notes |
62
+ |--------|------|----------|-------|
63
+ | `id` | `UUID PRIMARY KEY` | Yes | UUIDv7, app-generated; the table's only identity column |
64
+ | `created_at` | `BIGINT NOT NULL` | Yes | Set once at INSERT |
65
+ | `updated_at` | `BIGINT NOT NULL` | If mutable | Set at INSERT and every UPDATE |
66
+
67
+ **Naming is uniform.** `created_at` and `updated_at` are the only names for row lifecycle time — not `recorded_at`, `created_at_ms`, or `updated_at_ms`. A column carrying *domain* time distinct from row birth (for example the originating event's creation instant on a money row) is named for that domain meaning and documented in its slot.
68
+
69
+ **Mutable tables** (any table where UPDATE is a valid operation) must have `updated_at`.
70
+
71
+ **Append-only/event tables** (where UPDATE is blocked by trigger or by design) are exempt from `updated_at`.
72
+
73
+ ## Audit Pattern
74
+
75
+ - **Actor tracking:** Use `actor TEXT` in event/transition tables (e.g., `policy_events`, `usage_ledger`).
76
+ - **No `updated_by` column.** Changes to mutable tables are tracked via separate event-sourced audit tables (e.g., `workspace_billing_audit`, `harness_change_log`), not inline `updated_by`.
77
+ - **Append-only enforcement:** Tables that must never be updated should have a trigger:
78
+ ```sql
79
+ CREATE OR REPLACE FUNCTION core.{table}_append_only() RETURNS trigger AS $$
80
+ BEGIN
81
+ RAISE EXCEPTION '{table} is append-only — UPDATE and DELETE are not permitted';
82
+ END;
83
+ $$ LANGUAGE plpgsql;
84
+
85
+ CREATE TRIGGER trg_{table}_append_only
86
+ BEFORE UPDATE OR DELETE ON core.{table}
87
+ FOR EACH ROW EXECUTE FUNCTION core.{table}_append_only();
88
+ ```
89
+
90
+ ## RNG
91
+
92
+ - **Always** use `std.crypto.random` (via `allocUuidV7` in `id_format.zig`).
93
+ - **Never** use custom RNG implementations or `std.rand`.
@@ -0,0 +1,411 @@
1
+ # Milestone Specification Template
2
+
3
+ > **CANONICAL TEMPLATE — two agents consume this file, in different ways.**
4
+ > The **authoring agent** (via the `orly-spec-new` skill) copies the body below the divider into `docs/v{N}/pending/M{N}_{NNN}_….md` and fills it — authoring order lives in the skill, not in this file. The **executing agent** reads the filled spec **top-to-bottom**: the body is physically ordered by execution need (understand → prepare → build → prove → record).
5
+ > Enforced by `audits/spec-template.sh` (SPEC TEMPLATE GATE, façade `dispatch/write_spec.md`): required sections present, zero template residue, prohibited patterns absent. Lifecycle: `AGENTS.md → Specification Standards`.
6
+ > A shipped instance outranks guidance: when `docs/v{N}/done/` holds specs, read the newest first and pattern-match.
7
+
8
+ ---
9
+
10
+ ## Fill grammar
11
+
12
+ Three markup classes appear below the divider. The gate tells them apart mechanically:
13
+
14
+ | Marker | Meaning | Fate in the filled spec |
15
+ |---|---|---|
16
+ | `{…}` | Fill slot — replace with instance content | Gone. Surviving slots are residue; the gate BLOCKs the known sentinels. |
17
+ | `tpl:` guidance comment | How to fill the section — written for the authoring agent only | **Deleted after filling.** The gate BLOCKs any survivor. |
18
+ | SPEC AUTHORING RULES banner comment | Standing constraints on the instance | The one comment that survives, verbatim. |
19
+
20
+ Everything else — headings, table skeletons, the standard rubric rows — is kept and filled. The result: the executing agent reads **100% instance content, zero template noise**, and the 320-line budget buys signal, not boilerplate.
21
+
22
+ ## What a spec pins — and refuses to pin
23
+
24
+ A milestone spec is a **goal rulebook** the executing agent plans and ships from *without playing 20-questions with the author*. It pins **intent + invariants + tests + pointers**; the agent has agency on everything else. It must NOT pin implementation detail that rots within a sprint: allocator/capacity choices, library versions, line-by-line code, exact Structured Query Language (SQL) Data Definition Language (DDL), import statements — the agent derives those from the repository.
25
+
26
+ > **Pseudocode litmus.** If the spec names a version (`postcard 1.1`), a variable (`var ctx = …`), or a DDL clause (`CREATE TABLE … DEFAULT 'foo'`), it's pseudocode — it will be wrong within a sprint. Replace with *"use the project's existing X"* + a pointer to where X lives.
27
+
28
+ ## Anti-patterns (reject on sight)
29
+
30
+ | # | Anti-pattern | Do instead |
31
+ |---|---|---|
32
+ | 1 | Code blocks inside section bodies | Describe WHAT the slice delivers; pin precise behaviour as a Test. Prose code drifts; tests don't. |
33
+ | 2 | Listing every variable name | Point at an existing implementation to mirror; names match local style. |
34
+ | 3 | SQL DDL line-by-line | Table shape + constraints in prose; the agent conforms to existing migrations. |
35
+ | 4 | Pinning library versions | *"Use the existing Redis client"* + import pointer; the package manifest is the source of truth. |
36
+ | 5 | Step-by-step ordering ("Step 1… Step 2…") | **Sections** (value slices); the agent sequences within. |
37
+ | 6 | Test code in the spec | Name tests + assert behaviour in prose; the agent writes them in project style. |
38
+ | 7 | One rubric row per Dimension, or pasted evidence walls | 5–12 outcome rows; Graded = ✅/❌ + one decisive output line. The Test Specification is the per-Dimension ledger. |
39
+ | 8 | Template guidance surviving in the filled spec | Delete every `tpl:` comment; the executor reads instance content only. |
40
+
41
+ Slipped into one? The fix is usually **point at a file** (read-first pointer) or **delete the detail** (the agent figures it out).
42
+
43
+ ## Hierarchy & terminology
44
+
45
+ ```
46
+ v{N}.0.0 (Prototype) major release
47
+ └── Milestone (M40, M41…) one demoable end-to-end capability + evidence
48
+ └── Workstream (M40_001…) one working function; ≤4 (a 5th only for a
49
+ │ cross-cutting concern; never beyond 5 — split)
50
+ └── Section (§1, §2…) implementation slice (what + why); 3–9 per spec
51
+ └── Dimension (3.1, 3.2…) verifiable sub-unit — the unit of DONE
52
+ ```
53
+
54
+ **Dimension** is the atomic, verifiable unit: **1 Dimension → 1 Test**, marked `DONE` individually in the same commit as its code. "Mark Dimensions DONE" (per `AGENTS.md`) resolves here. A Section is DONE when all its Dimensions are. Outcome-level grading lives in the **Acceptance Rubric** — one row per Section or failure class, never one per Dimension.
55
+
56
+ **Batch (B1, B2…)** — parallel execution group. Workstreams in the same batch run concurrently; batches are sequential (B2 starts after B1's gates clear).
57
+
58
+ A milestone is not complete until evidence is captured: commands, logs, screenshots, or a recorded walkthrough.
59
+
60
+ ## Status markers
61
+
62
+ - `PENDING` — not started. `IN_PROGRESS` — being worked. `DONE` / ✅ — complete, verified, tested.
63
+ - `DEFERRED` — designed/attempted and explicitly not shipped; lives in `done/` as a record. **Discovery** MUST capture the rationale and reactivation conditions.
64
+
65
+ ## Prohibited (gate-enforced — `dispatch/write_spec.md`)
66
+
67
+ - No time/effort estimates ("5 min", "1 hour", "2 days"). No effort columns or complexity ratings. No percentage-complete. No assigned owners. No implementation dates.
68
+ - Sizing signal is **Priority** (P0/P1/P2/P3); sequencing is **Dependencies**.
69
+
70
+ ## Guardrails
71
+
72
+ - **Length — hard upper bound 320 lines for the filled spec** (after `tpl:` comment deletion). Typical specs 150–300. Past the bound before the Acceptance Rubric → scope is two specs sharing one file; split.
73
+ - **Sections** — 3–9. **Workstreams** — ≤4 (5th cross-cutting only).
74
+ - **File naming** — `docs/v{N}/{pending|active|done}/M{N}_{NNN}_P{Priority}_{CATEGORIES}_{NAME}.md` (e.g. `M40_001_P0_API_WORKER_SUBSTRATE.md`). Categories alphabetised.
75
+
76
+ ---
77
+
78
+ # Spec Body — Copy Everything Below This Line
79
+
80
+ <!--
81
+ SPEC AUTHORING RULES (load-bearing — the one comment that survives):
82
+ - Body order = the executing agent's read order. Fill via the orly-spec-new
83
+ skill (authoring order lives there); after filling, DELETE every "tpl:"
84
+ guidance comment — the SPEC TEMPLATE GATE blocks tpl residue, unfilled
85
+ {slots}, and missing required sections (audits/spec-template.sh --staged).
86
+ - No time/effort/hour/day estimates anywhere. No effort columns, complexity
87
+ ratings, percentage-complete, implementation dates, assigned owners.
88
+ - Priority (P0/P1/P2/P3) is the only sizing signal; Dependencies are the only
89
+ sequencing signal. A section that contradicts these rules loses — delete it.
90
+ -->
91
+
92
+ # M{Milestone}_{Workstream}: {Title — testable, not vague}
93
+
94
+ **Prototype:** v{major}.{minor}.{patch}
95
+ **Milestone:** M{Number}
96
+ **Workstream:** {001-009}
97
+ **Date:** {MMM DD, YYYY}
98
+ **Status:** PENDING | IN_PROGRESS | DONE
99
+ **Priority:** P0 | P1 | P2 | P3 — {one-line reason}
100
+ **Categories:** {API | CLI | UI | SKILL | DOCS | OBS | INFRA — alphabetised, one or more}
101
+ **Batch:** B{1-4} — {parallel execution context}
102
+ **Branch:** {feat/mNN-name — added at CHORE(open)}
103
+ **Test Baseline:** set at CHORE(open) — `unit=<N> integration=<M>` from the repository's declared `verify.*` commands (`.oracle/orly.json`)
104
+ **Depends on:** {M{N}_{NNN} (one-line reason), …}
105
+ **Provenance:** human-written | LLM-drafted ({model}, {date}) | agent-generated (pre-spec, {source doc})
106
+ **Canonical architecture:** `docs/architecture/{relevant-doc}.md` §{N}
107
+
108
+ <!-- tpl: Provenance is load-bearing — the implementing agent calibrates trust by
109
+ who wrote the spec: LLM-drafted gets extra cross-checking against the codebase;
110
+ human-written assumes the author read the relevant code. Canonical architecture:
111
+ the docs/architecture/ directory is the source of truth (Architecture Consult &
112
+ Update Gate); greenfield → point at the doc that defines the shape. -->
113
+
114
+ ---
115
+
116
+ ## Overview
117
+
118
+ **Goal (testable):** {one sentence that could be a test name}
119
+ **Problem:** {observable symptoms, in user-facing terms}
120
+ **Solution summary:** {one paragraph — what changes, at what layer, what the user-visible outcome is}
121
+
122
+ <!-- tpl: Goal — bad: "Implement streaming." Good: "Server-Sent Events (SSE)
123
+ handler streams Redis pubsub messages as text/event-stream with stable ordering
124
+ and reconnection, p95 latency under 200ms." Problem — symptoms ("operators can't
125
+ see the agent's tool calls in real time"), never implementation language ("we
126
+ don't have a foo handler"). Implementation steps belong in Sections, not here. -->
127
+
128
+ ## PR Intent & comprehension handshake
129
+
130
+ - **PR title (eventual):** {imperative, ≤72 chars — what the merged Pull Request (PR) is called}
131
+ - **Intent (one sentence):** {why this PR exists, in user-facing-outcome terms}
132
+ - **Handshake** — the implementing agent fills this at PLAN, before EXECUTE: restate the Intent in its own words and list `ASSUMPTIONS I'M MAKING: …`. A mismatch between the restatement and the Intent above → STOP and reconcile before any edit.
133
+
134
+ ## Implementing agent — read these first
135
+
136
+ 1. `{path/to/file.ext}` — {why this is the right pattern to mirror}
137
+ 2. `{path/to/spec_or_doc.md}` — {what canonical knowledge lives there}
138
+ 3. {external doc URL, if relevant} — {what convention to follow}
139
+
140
+ <!-- tpl: 3–5 pointers. Fewer than 3 = authoring homework not done (the executing
141
+ agent repeats it); more than 5 = a tutorial (trim). This is where judgment is
142
+ preserved without pseudocode: point at code/docs to read BEFORE touching any
143
+ file. Greenfield (no existing pattern)? Say so explicitly and point at the
144
+ docs/architecture/ doc that defines the shape. Every pointer must resolve
145
+ in THIS repository (`test -f` each) or be a URL — a pack this repository
146
+ never selected materialised nothing here. -->
147
+
148
+ ## Files Changed (blast radius)
149
+
150
+ | File | Action | Why |
151
+ |------|--------|-----|
152
+ | `path/to/file.ext` | CREATE / EDIT / DELETE | {one line — what changes about this file's role} |
153
+
154
+ <!-- tpl: Every file created, modified, or deleted. Scopes the length gates,
155
+ orphan sweeps, and review effort; per AGENTS.md the executing agent may only
156
+ edit files in this table without explicit override. List FILES and ROLES —
157
+ never line numbers or function names (they drift). Teardown/rename/flip specs
158
+ open with a blast-radius grep first: git grep -rn -w '<token>' from repo root,
159
+ no path filter (dispatch/write_spec.md, Authoring discipline). -->
160
+
161
+ ## Applicable Rules
162
+
163
+ - **`docs/greptile-learnings/RULES.md`** — {specific rule IDs this diff trips: e.g. UFS, NDC, NLR, ORP, FLL}
164
+ - {per-surface rule file} — {why it applies to this diff}
165
+
166
+ <!-- tpl: The rule files the executing agent re-reads BEFORE EXECUTE and
167
+ re-checks during VERIFY. Name the exact rule IDs the diff will trip — generic
168
+ "follow RULES.md" earns a greptile finding; named IDs are obeyed by
169
+ construction. Per-surface menu = the dispatch table in THIS repository's
170
+ generated AGENTS.md plus the docs/ rule files those façades cite — all
171
+ materialised locally by the selected packs; a rule file absent from this
172
+ repository is not applicable by definition. Fully greenfield → write "Standard
173
+ set only — docs/greptile-learnings/RULES.md; no other rule files apply." -->
174
+
175
+ ## Applicable Gates
176
+
177
+ | Gate | Fires? | Satisfaction strategy |
178
+ |------|--------|-----------------------|
179
+ | {gate from this repository's AGENTS.md dispatch index} | {yes/no — why} | {how the diff stays clean} |
180
+ | File & Function Length (≤350/≤50/≤70) | {yes/no} | {split plan if a file approaches the cap} |
181
+
182
+ <!-- tpl: Which Action-Triggered Guards this Pull Request (PR) WILL trip, and
183
+ how each stays clean — pre-declared so the agent plans for them instead of
184
+ stalling mid-EXECUTE. The menu is THIS repository's generated AGENTS.md
185
+ dispatch index — one row per materialised gate the diff can trip; never cite a
186
+ gate from a pack this repository lacks. Rules ≠ Gates: rules are knowledge to
187
+ read; gates are guards that fire on edits. Touch nothing a gate watches →
188
+ replace the table with "N/A — docs/markdown only." -->
189
+
190
+ ## Prior-Art / Reference Implementations
191
+
192
+ - **Reference:** {path / codebase} — {one line on alignment, or the justified divergence}
193
+
194
+ <!-- tpl: SOUL.md rule — before proposing architecture, find the reference
195
+ codebase; there almost always is one. Name it so the agent mirrors a known-good
196
+ pattern instead of inventing. Menu: Command-Line Interface (CLI) → the "7
197
+ Pillars" of CLI developer experience (vendored supabase-style oss/cli): command
198
+ → handler → errors split; handler purity (no console.log / process.exit in
199
+ handlers); output as a service (human vs JSON vs env rendering chosen by the
200
+ renderer); structured JSON errors with suggestion/retry fields; 3-tier test
201
+ pyramid (handler unit / in-process integration / subprocess e2e); auto-JSON when
202
+ stdout is piped (LLM-native) — state which pillars this spec aligns with and any
203
+ divergence. API → docs/REST_API_DESIGN_GUIDELINES.md + the closest existing
204
+ handler. Schema → the nearest migration + docs/SCHEMA_CONVENTIONS.md. UI →
205
+ design-system primitives + theme.css tokens. Cite each doc only where this
206
+ repository materialises it. Greenfield → "no prior art; shape
207
+ defined in docs/architecture/{doc}.md." -->
208
+
209
+ ## Sections (implementation slices)
210
+
211
+ ### §1 — {Slice title}
212
+
213
+ {What this slice delivers in goal terms; why it must exist; what it unblocks. Non-obvious choice → **Implementation default:** `{choice}` because `{reason}`.}
214
+
215
+ - **Dimension 1.1** — {smallest verifiable behaviour} → Test `test_…`
216
+ - **Dimension 1.2** — {…} → Test `test_…`
217
+
218
+ ### §2 — {Slice title}
219
+
220
+ {Same shape.}
221
+
222
+ <!-- tpl: Each Section: WHAT one slice delivers and WHY (not how); numbered
223
+ Dimensions map 1:1 to Tests and get marked DONE in the same commit as their
224
+ code. The agent picks each Implementation default unless it has evidence to
225
+ deviate. Good: "§3 — Replay idempotency. Receiver dedupes on the delivery id.
226
+ Implementation default: 24h dedupe window matching the upstream retry window;
227
+ storage is a Redis key with a Time To Live (TTL) — the agent picks the key
228
+ shape from existing dedupe patterns. Dimension 3.1 → test_dedupes_within_window;
229
+ 3.2 → test_evicts_after_ttl." Bad: "§3 — Use redis.SET(\"webhook:dedupe:\"+id,
230
+ \"1\",\"NX\",\"EX\",86400) and check the return." — pseudocode; the agent reads
231
+ the existing dedupe pattern and writes the call. -->
232
+
233
+ ## Interfaces
234
+
235
+ ```
236
+ {HTTP endpoints, request/response shapes, internal signatures other code depends on}
237
+ ```
238
+
239
+ <!-- tpl: Lock the interface — the surface the agent must NOT change without
240
+ amending the spec. Specify input/output/error shapes; use a real example
241
+ payload where shape isn't self-evident. Write the interface, not the
242
+ implementation. -->
243
+
244
+ ## Failure Modes
245
+
246
+ | Mode | Cause | Handling (system response + what the caller observes) |
247
+ |------|-------|--------------------------------------------------------|
248
+ | {short name} | {trigger} | {response + observable} |
249
+
250
+ <!-- tpl: Every failure path the agent must handle; each row → a negative/
251
+ integration test in the Test Specification. Cover at minimum: timeout,
252
+ malformed input, auth failure, network blip, race, replay, exceeded quota,
253
+ dependency unavailable. -->
254
+
255
+ ## Invariants
256
+
257
+ 1. {Invariant} — {how it's enforced}
258
+
259
+ <!-- tpl: Each MUST be enforceable by code (compiler, lint, comptime assertion,
260
+ runtime check) — NOT by review discipline. If a human can violate it silently,
261
+ it's not an invariant. None → "N/A — no invariants." -->
262
+
263
+ ## Metrics & Observability
264
+
265
+ | Metric / event | Owner | Fires when | Properties allowed | Privacy guard | Test proof |
266
+ |----------------|-------|------------|--------------------|---------------|------------|
267
+ | `{event_name}` | {product / ops / not applicable} | {exact user or system action} | {coarse product context, resource id, duration, outcome} | {no raw email/password/token/One-Time Password (OTP)/Secure Shell (SSH) key material} | `{test_name}` |
268
+
269
+ <!-- tpl: Every realized spec declares what product or operational signal it
270
+ adds, or explicitly why none: internal-only cleanup → the single row "not
271
+ applicable — no product/operator signal changes". Prefer the project's
272
+ analytics architecture doc (root page telemetry for views, typed event
273
+ registries for actions, journey timers for funnels, authenticated identity
274
+ linking). If a workflow already emits analytics, state whether this spec adds,
275
+ renames, or leaves those events unchanged; if any funnel changes, update the
276
+ analytics/funnel playbook in the same PR — otherwise Discovery records
277
+ "Metrics review: no analytics/funnel playbook update required" with the reason.
278
+ The implementing agent revisits this table after implementation and during
279
+ /review; missing action events, funnel timers, or feature-flag exposure events
280
+ are implementation gaps, not notes for later. -->
281
+
282
+ ## Test Specification (tiered)
283
+
284
+ | Dimension | Tier | Test | Asserts (concrete inputs → expected output) |
285
+ |-----------|------|------|---------------------------------------------|
286
+ | 1.1 | {unit / integration / e2e} | `test_<short_name>` | {one-line behavioural claim} |
287
+
288
+ <!-- tpl: Prose-and-assertions only — no test code. One row per Dimension;
289
+ bound to the /orly-write-unit-test skill; ≥50% negative paths; every Failure Mode
290
+ row and every Metrics row gets a test. Tiers: unit (pure logic, handlers,
291
+ boundaries — empty/null/max/malformed/unicode) · integration (real stack, mock
292
+ only at system boundaries, deterministic failure injection per Failure Mode) ·
293
+ end-to-end (e2e) — any user-facing Category (CLI / UI / API) gets at least one
294
+ user-centric scenario via test-e2e* walking the real path (subprocess CLI /
295
+ real HTTP request / rendered UI); a unit test is not a substitute. Also
296
+ include regression rows (pre-existing behaviour that must not change — "N/A —
297
+ greenfield" if none) and idempotency/replay rows (any retry semantics).
298
+ Non-self-evident input shape → point at a fixture
299
+ (samples/fixtures/m{N}-fixtures/{name}.json); don't inline JSON. Hard-to-
300
+ describe behaviour in prose ⇒ the Goal is fuzzy — fix the Goal, not this
301
+ table. -->
302
+
303
+ ## Acceptance Rubric (single scoring surface)
304
+
305
+ | # | Criterion (observable outcome) | Verify (copy-paste) | Expected | Priority | Graded (VERIFY) |
306
+ |---|--------------------------------|---------------------|----------|----------|-----------------|
307
+ | R1 | {outcome the user can observe} (§1) | `{command}` | {exit 0 / substring / 0 matches} | P0 | |
308
+ | R2 | Diff stays inside Files Changed | `git diff --name-only origin/main...HEAD` | 0 paths missing from the Files Changed table | P0 | |
309
+ | S1 | Conform gates green | `{conform command from .oracle/orly.json, verbatim}` | exit 0 | P0 | |
310
+ | S2 | Unit tests pass | `{verify.unit command from .oracle/orly.json, verbatim}` | exit 0 | P0 | |
311
+ | S3 | Slow tier green (code-carrying branch) | `{one row per declared slow verify.* the diff's surface needs — integration / e2e / memory / cross-compile — verbatim}` | exit 0 | P0 | |
312
+ | S4 | No secrets | `gitleaks detect` | exit 0 | P0 | |
313
+ | S5 | No oversize source file | `git diff --name-only origin/main...HEAD \| grep -v '\.md$' \| xargs wc -l 2>/dev/null \| awk '$1>350 && $2!="total"'` | no output | P0 | |
314
+ | S6 | Orphan sweep | Dead Code Sweep greps | 0 matches | P0 | |
315
+
316
+ **Command source rule:** every S-row Verify command is copied **verbatim from `.oracle/orly.json`** (`conform`, `verify.*`) — the same set `orly gate` runs, so the rubric and the mechanical PR gate grade one boundary. The gate BLOCKs a staged pending/active spec whose rubric omits the declared `conform` or `verify.unit` command; a rubric naming a runner the repository does not declare is wrong by construction. `.oracle/orly.json` still a seed → complete it first (`dispatch/lifecycle.md` §Bootstrap); authoring against an unseeded config is the nondeterminism this rule exists to kill.
317
+
318
+ **Grading protocol (VERIFY):** run the Verify command verbatim; grade ONLY from its output. Graded = ✅/❌ + the one decisive output line (`342 passed`); long evidence goes to PR Session Notes with a pointer here. **Ship gate:** every row graded, every P0 ✅ → eligible for CHORE(close); any ❌ or empty cell → return to EXECUTE; a P1 ❌ ships only with an Indy-acked deferral quote in Discovery.
319
+
320
+ <!-- tpl: The single scoring surface — no other scoreboard. 5–12 rows after
321
+ pruning: one per Section outcome, failure class, or hygiene gate — never one
322
+ per Dimension (that ledger is the Test Specification). S-row commands come verbatim from
323
+ `.oracle/orly.json`; expand S3 to one row per declared slow `verify.*` the
324
+ diff's surface needs; delete rows whose command this repository does not
325
+ declare. Expected litmus: every Expected is
326
+ mechanically checkable — an exit code, a literal substring, or a match count;
327
+ can't write it that way → the criterion is fuzzy — fix the criterion, not the
328
+ grading. Authoring fills every column except Graded; VERIFY fills Graded. -->
329
+
330
+ ### Behaviour evals
331
+
332
+ - **Grounding rule:** {one sentence the output must never violate}
333
+ - **Golden set:** `samples/fixtures/{path}` — {N} cases across {3–5 coverage axes, incl. the nightmare case}. A failure found in the wild becomes a new case; the set only grows.
334
+ - **Ship threshold:** grounding 100% · task pass ≥{N}% · 0 critical failures on {nightmare case}. Each threshold is one rubric row with the command that computes it.
335
+ - **Fallback:** below threshold or low confidence → {named recoverable behaviour}; fabricated output is a P0 ❌.
336
+
337
+ <!-- tpl: Delete this whole sub-section unless the diff changes prompt/model/
338
+ agent behaviour. Grounding-rule example: "responses cite only retrieved rows,
339
+ never invented identifiers." -->
340
+
341
+ ## Dead Code Sweep
342
+
343
+ **1. Orphaned files — deleted from disk and git.**
344
+
345
+ | File to delete | Verify |
346
+ |----------------|--------|
347
+ | `{path/to/old_file.ext}` | `test ! -f {path/to/old_file.ext}` |
348
+
349
+ **2. Orphaned references — zero remaining imports/uses.**
350
+
351
+ | Deleted symbol/import | Grep | Expected |
352
+ |-----------------------|------|----------|
353
+ | `{old_symbol}` | `grep -rn "{old_symbol}" src/ \| head` | 0 matches |
354
+
355
+ <!-- tpl: Mandatory when the spec deletes or replaces files: for every deleted
356
+ file and removed/renamed public symbol, grep the repo; non-zero = stale. Use
357
+ the same word-boundary pattern as the discovery grep. No deletions → replace
358
+ both tables with "N/A — no files deleted." -->
359
+
360
+ ## Out of Scope
361
+
362
+ - {Item explicitly not in this spec — points at a follow-up spec or "future work"}
363
+
364
+ ---
365
+
366
+ <!-- tpl: The two sections below are the AUTHORING RECORD — written before the
367
+ implementation sections above, read once by the executing agent at PLAN for
368
+ intent, and never re-litigated during EXECUTE. They sit last so the executor's
369
+ working sections stay front-loaded. -->
370
+
371
+ ## Product Clarity (authoring record)
372
+
373
+ 1. **Successful user moment** — {the single observable moment that proves this worked — a scene, not a metric}
374
+ 2. **Preserved user behaviour** — {what users do today that keeps working unchanged; breaking any of it is a redesign}
375
+ 3. **Optimal-way check** — {is this the most direct way to deliver moment #1? name the gap to the unconstrained-optimal shape and why it's acceptable now}
376
+ 4. **Rebuild-vs-iterate** — {would a larger refactor serve better? verdict here; rationale in Decomposition below. A refactor that trades run-to-run determinism away is wrong by default}
377
+ 5. **What we build** — {the shortest artifact list that delivers moment #1}
378
+ 6. **What we do NOT build** — {adjacent scope rejected, one-line reason each — seeds Out of Scope}
379
+ 7. **Fit with existing features** — {what this compounds with; the one feature it must not destabilize}
380
+ 8. **Surface order** — {CLI-first (repo default), UI-first, or both; justify divergence}
381
+ 9. **Dashboard restraint** — {what the UI must hide until the signal behind it is real: no controls before evidence, no quality claims before counters}
382
+ 10. **Confused-user next step** — {the self-serve move (a command, an error message, a doc); "file a ticket" means a surface is missing from item 5}
383
+
384
+ <!-- tpl: Indy's product questions, answered in order at authoring BEFORE the
385
+ implementation sections are written — so the authoring agent holds the product
386
+ behaviour, not just the file list. One short paragraph or list item each. A
387
+ question that can't be answered yet is a [?] that blocks the spec (golden-path
388
+ rule, AGENTS.md). Internal-only work: answer 1–7 compactly; 8–10 may be "N/A —
389
+ no user surface" with the reason. -->
390
+
391
+ ## Decomposition & alternatives (patch vs refactor)
392
+
393
+ - **Chosen shape:** {why this Section/Workstream split — the decomposition rationale}
394
+ - **Alternatives considered:** {≥1 — the larger refactor or the smaller patch — and why rejected for now}
395
+ - **Patch-vs-refactor verdict:** this is a **{patch | refactor}** because {reason}. {If a larger refactor is the right long game but out of scope, name the follow-up spec rather than silently mud-patching.}
396
+
397
+ <!-- tpl: Indy's rule — don't ship a mud-patch when the problem wants a
398
+ refactor, and don't refactor when a patch is right. Match solution-size to
399
+ problem-size; surface the call BEFORE approval, not mid-PR. Weigh against the
400
+ platform constants (docs/architecture/direction.md). -->
401
+
402
+ ## Discovery (consult log)
403
+
404
+ - **Consults** — Architecture / Legacy-Design / gate-flag triage: the question asked + Indy's decision.
405
+ - **Metrics review** — events added, extra events found during `/review`, analytics/funnel playbook update or the explicit no-change reason.
406
+ - **Skill-chain outcomes** — `/orly-write-unit-test`, `/review`, `orly-babysit-prs` results (order per `AGENTS.md` CHORE(close); iteration counts, findings dispositioned).
407
+ - **Deferrals** — every "deferred to follow-up" needs an **Indy-acked verbatim quote** here, format `> Indy (YYYY-MM-DD HH:MM): "<quote>" — context: <which item, why>`. An agent-unilateral deferral is **incomplete scope, not deferral**, and blocks CHORE(close) until the item lands or the quote is captured.
408
+
409
+ <!-- tpl: Empty at creation (keep the four bullet headers). This is the spec's
410
+ running record — where deferrals and skill outcomes are proven as the work
411
+ proceeds. -->
@@ -0,0 +1,57 @@
1
+ # VERIFY — Tiers, performance, hygiene
2
+
3
+ > Parent: [`../AGENTS.md`](../AGENTS.md) §VERIFY. Verification Gate (`dispatch/verify.md`) enforces; `make` targets are canonical.
4
+
5
+ **FIRST: `/orly-write-unit-test`** — audits diff coverage vs spec's Test Specification (or changed surface when no spec). Iterate until clean. Skipping = CHORE(close) violation.
6
+
7
+ ## Correctness tiers (do not skip)
8
+
9
+ | Tier | Command | When |
10
+ |---|---|---|
11
+ | 1 | `make test-unit-all` | Every EXECUTE iteration; start of VERIFY. Unit-only — never substitutes for 2/3. Zig-only fast loop: `make test-unit-agentsfleetd`. |
12
+ | 2 | `make test-integration` | Diff touches `src/agentsfleetd/http/**`, `src/agentsfleetd/db/**`, `src/agentsfleetd/fleet/**`, `src/agentsfleetd/observability/**`, `*_integration_test.zig`, schema, migrations. Before COMMIT. |
13
+ | 3 | `make test-integration` | ≥1× per branch from clean state (after `make down`) before ship-ready. Mandatory when schema changes pre-v2.0. Tier 2 passing + 3 failing = state pollution; fix isolation. |
14
+
15
+ ## Test delta — VERIFY ends by reporting coverage growth
16
+
17
+ CHORE(open) recorded the branch-point counts in the spec header (`**Test Baseline:** unit=<N> integration=<M>`, from `make _lint_zig_test_depth`, which also writes `.tmp/agentsfleetd-test-depth.txt`). VERIFY ends with the same command and this required row in the verification block:
18
+
19
+ ```
20
+ Test Delta: unit <N₀>→<N₁> (+x) · integration <M₀>→<M₁> (+y) vs CHORE(open) baseline
21
+ Lacking: <changed surfaces whose tests did not grow, or "none">
22
+ ```
23
+
24
+ - **Delta is deterministic; Lacking is judgment (🤔)** — walk the diff's changed modules and name every one whose coverage did not move (e.g. "EgressScope Linux paths — integration lane pending"). "none" must be earned, not defaulted.
25
+ - **Zero/negative unit delta while the diff adds non-trivial code** → justify in the row (pure refactor, test consolidation) or return to EXECUTE. Deleting tests to go green is a violation, not a justification.
26
+ - Spec predates this rule (no `Test Baseline:` line) → reconstruct from the branch point (run the counter at the CHORE(open) commit or on `origin/main`) and add the line to the spec in the same commit as the VERIFY report.
27
+
28
+ ## Performance / leak (before PR)
29
+
30
+ | Gate | Command | When |
31
+ |---|---|---|
32
+ | Leak | `make memleak` | Server lifecycle (`src/agentsfleetd/http/**`, `src/agentsfleetd/cmd/serve.zig`), allocator wiring, cross-thread heap ownership. |
33
+ | Bench (local) | `make bench` | When the diff touches request-path code, allocator wiring, or startup/shutdown sequencing. |
34
+ | Bench (dev) | `API_BENCH_URL=https://api-dev.agentsfleet.net/healthz make bench` | After deploy to dev. |
35
+
36
+ **Zig coverage.** `make test-coverage-zig` runs the daemon, runner, and three shared-library unit binaries under kcov. Each component must produce a non-empty Cobertura report. The merged report lives under `coverage/zig/merged` and must meet `ZIG_COVERAGE_MIN_LINES`.
37
+
38
+ **Integration roots.** `make test-unit-agentsfleetd` runs `src/agentsfleetd/tests.zig`. `make test-integration` prepares isolated services once and runs `src/agentsfleetd/integration_tests.zig`, which owns the live PostgreSQL, Redis, and QStash imports. The runner's Linux kernel root remains independently selectable.
39
+
40
+ **Memleak lanes.** `make memleak` leak-gates all three test graphs concurrently, each named in its own lane prefix: **`agentsfleetd`** (daemon), **`runner`** (`build_runner.zig` unit tests — integration tests fork real children and are excluded), and **`lib`** (three `src/lib` binaries). Component failures are collected and reported before the final lifecycle proof. On Linux, Valgrind is blocking and propagates its exit code. On macOS, the Zig testing allocator is blocking. The advisory `leaks` rerun happens only when one lightweight preflight proves that the host can inspect child processes. `-Dopenssl=false` is a root-graph option only; the runner graph links no OpenSSL and rejects the flag. A final **`boot-drain`** lane migrates the test database, exports the integration environment, runs the filtered daemon lifecycle test under the blocking gate, and requires its run-proof marker.
41
+
42
+ Knobs (`make/test-bench.mk`): `API_BENCH_METHOD`, `_DURATION_SEC`, `_CONCURRENCY`, `_TIMEOUT_MS`, `_MAX_ERROR_RATE`, `_MAX_P95_MS`, `_MAX_RSS_GROWTH_MB`.
43
+
44
+ **Memleak evidence:** paste final `make memleak` line into PR Session Notes OR cite CI URL. Branches touching `src/agentsfleetd/http/**`/`src/agentsfleetd/cmd/serve.zig`/allocator wiring → last 3 lines verbatim. No "trust me".
45
+
46
+ ## Hygiene (always, before PR)
47
+
48
+ `make lint-all` (hard); `make _lint_zig_pg_drain` (also folded into `make lint-all` via `lint-zig`) + cross-compile `x86_64-linux` + `aarch64-linux` (any `*.zig` touched); cross-layer orphan sweep (RULE ORP — every renamed/deleted symbol → 0 hits across schema/Zig/JS/tests/docs in non-historical files); `gitleaks detect` before any Zig-including commit; 350-line / 50-fn-line check via:
49
+
50
+ ```bash
51
+ git diff --name-only origin/main \
52
+ | grep -v -E '\.md$|^vendor/|_test\.|\.test\.|\.spec\.|/tests?/' \
53
+ | xargs -I{} sh -c 'wc -l "{}"' \
54
+ | awk '$1 > 350'
55
+ ```
56
+
57
+ **Other:** after refactors, list newly dead code before removing — `NEWLY UNREACHABLE: <symbol/file> — <why now dead>. Remove? Confirm.` **Greptile learning capture:** each finding → "Could this recur?" If yes, add compact rule (Rule/Why/Tags/Ref) to RULES.md same commit. Never defer.