@azure-id/orc 1.4.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +186 -0
- package/README.md +63 -50
- package/bin/cli.js +5732 -133
- package/bin/mockrun-catalog.js +4 -0
- package/bin/verify-contracts.js +4509 -4426
- package/bin/verify-package.js +24 -2
- package/bin/webui/api.js +108 -8
- package/bin/webui/app.html +9 -0
- package/bin/webui/css/06-responsive.css +14 -0
- package/bin/webui/css/panels/test.css +132 -0
- package/bin/webui/fixtures/index.js +23 -1
- package/bin/webui/fixtures/test.js +547 -0
- package/bin/webui/i18n/en/common.json +1 -0
- package/bin/webui/i18n/en/nav.json +1 -0
- package/bin/webui/i18n/en/overview.json +5 -0
- package/bin/webui/i18n/en/test.json +141 -0
- package/bin/webui/i18n/id/common.json +1 -0
- package/bin/webui/i18n/id/nav.json +1 -0
- package/bin/webui/i18n/id/overview.json +5 -0
- package/bin/webui/i18n/id/test.json +141 -0
- package/bin/webui/js/01-i18n.js +1 -0
- package/bin/webui/js/panels/challenge.js +8 -3
- package/bin/webui/js/panels/overview.js +18 -0
- package/bin/webui/js/panels/test.js +733 -0
- package/bin/webui/serve.js +572 -557
- package/mock-run/orc-test.md +327 -0
- package/package.json +1 -1
- package/templates/agents/MODEL-MAPPING.md +2 -0
- package/templates/agents/orc-test-designer-opus-5-high.md +158 -0
- package/templates/agents/orc-test-interpreter-opus-5-low.md +130 -0
- package/templates/commands/orc-test.md +66 -0
- package/templates/hooks/README.md +110 -1
- package/templates/hooks/orc-read-gate.js +279 -0
- package/templates/skills/_shared/extra-dispatch.md +1346 -1337
- package/templates/skills/_shared/live-target.md +188 -0
- package/templates/skills/_shared/phases/trace.md +2 -2
- package/templates/skills/_shared/wait.md +241 -240
- package/templates/skills/orc-test/SKILL.md +233 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,192 @@ Format: `### v<version> — <title> _(<date>)_`.
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
### v1.6.0 - the rule that can finally say no _(2026-09-07)_
|
|
14
|
+
|
|
15
|
+
**Still on the unscoped `orc` package?** Do this once first - your `orc upgrade`
|
|
16
|
+
is the pre-v0.56.0 one and cannot install itself. Full detail in the CAUTION at
|
|
17
|
+
the top of this file.
|
|
18
|
+
|
|
19
|
+
- **Step 1 - release the command from the old package:** `npm uninstall -g orc`
|
|
20
|
+
- **Step 2 - install the current package:** `npm i -g @azure-id/orc`
|
|
21
|
+
- **Step 3 - re-apply it to your project:** `orc update`
|
|
22
|
+
|
|
23
|
+
**Do not use `npm i -g -f`.** Full detail in v0.56.0 below.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
ORC's read discipline has always said the same thing in three places: the main
|
|
27
|
+
session reads to **find**, and dispatches an agent to **understand**. It was
|
|
28
|
+
prose in `_shared/read-ladder.md`, in `/orc-doc` hard rule 0, and on line 21 of
|
|
29
|
+
`/orc-quick` - and **nothing checked any of it**.
|
|
30
|
+
|
|
31
|
+
`orc-read-gate.js` is a `PreToolUse` hook on `Read` that can refuse. It ships
|
|
32
|
+
**off**, and `off` is byte-identical to not having it - asserted by a test, not
|
|
33
|
+
by intention.
|
|
34
|
+
|
|
35
|
+
**The threshold is ORC's own number, measured.** The pattern this came from uses
|
|
36
|
+
350 lines, derived from a 10-30 second delegation round trip. ORC's round trip
|
|
37
|
+
is nothing like that: a dispatch measured **p50 76s, p90 188s** (n=125), and one
|
|
38
|
+
real read-only dispatch cost **13,276 tokens** to read a four-line file. At the
|
|
39
|
+
measured 55.2 chars/line across 316 sampled reads, break-even is **~1000 lines**.
|
|
40
|
+
Copying 350 would delegate work whose overhead exceeds its saving.
|
|
41
|
+
|
|
42
|
+
**`agent_id` is the only discriminator, and that was MEASURED, not assumed.**
|
|
43
|
+
`PreToolUse` **does** fire inside a dispatched subagent - the assumption that
|
|
44
|
+
hooks are session-level was wrong. `session_id` and `transcript_path` are
|
|
45
|
+
**identical** in both contexts, so a gate written against either would block the
|
|
46
|
+
full read an executor must perform before an `Edit`, and a reconstructed
|
|
47
|
+
`old_string` corrupts files. The gate tests for the **presence** of `agent_id`,
|
|
48
|
+
never for the absence of some other key, which asserts nothing.
|
|
49
|
+
|
|
50
|
+
**Everything it stays silent on, and each one is deliberate:** any read by a
|
|
51
|
+
subagent - outside an open ORC run - a targeted `offset`/`limit` read - a file
|
|
52
|
+
under the threshold - build logs, test results and `.jsonl` that a gate parses
|
|
53
|
+
whole, because a truncated red build reads **green** - and any error at all,
|
|
54
|
+
because **a read gate that throws and blocks a read has broken the tool.**
|
|
55
|
+
|
|
56
|
+
**A block always names the cheaper path.** A gate that only refuses is a gate
|
|
57
|
+
people switch off. It names the targeted read, the agent dispatch, and the
|
|
58
|
+
config key that turns it down.
|
|
59
|
+
|
|
60
|
+
**Every `warn` and `block` writes one trace line** (`READ-GATE`), an allow
|
|
61
|
+
writes none. That is affordable here for a structural reason: the gate only acts
|
|
62
|
+
while a run is open, so a trace always exists. `orc doctor` gains
|
|
63
|
+
`read-gate-unwired` and `read-gate-fallback`, both reported **only while the
|
|
64
|
+
feature is armed** - a doctor that warns about the default is one people learn
|
|
65
|
+
to ignore.
|
|
66
|
+
|
|
67
|
+
**Two config keys**, both on the `SEED_EMPTY` allowlist with an empty `lanes[]`,
|
|
68
|
+
because a hook has no lane and cannot resolve config: `read_gate`
|
|
69
|
+
(`off`|`warn`|`block`, default `off`) and `read_gate_max_lines` (default 1000).
|
|
70
|
+
|
|
71
|
+
**What this release deliberately does NOT do**, with the reasons recorded so
|
|
72
|
+
nobody re-proposes them: no gate on `Bash` reads (`cat`/`head`/`tail`) - it is
|
|
73
|
+
several times the false-positive surface, and the measurement says the `Read`
|
|
74
|
+
tool is only about 7.5% of the actual read surface here - and no
|
|
75
|
+
`context-reader` slot in `EXTRA_SLOTS`, because at a 1000-line threshold the
|
|
76
|
+
addressable population is five reads across 239 sampled sessions, which is
|
|
77
|
+
infrastructure for nothing.
|
|
78
|
+
|
|
79
|
+
**An honest note on the measurement.** The audit behind this release found the
|
|
80
|
+
existing prose is largely *working*: median full read is 85 lines, p90 is 288,
|
|
81
|
+
and 46% of reads already use `offset`/`limit` without being told. Oversized
|
|
82
|
+
reads are about **1% of price-weighted main-session ingest** at a generous upper
|
|
83
|
+
bound. This hook is a guardrail on a road most runs already stay on - it is off
|
|
84
|
+
by default for exactly that reason.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### v1.5.0 - the lane that runs the test _(2026-09-07)_
|
|
89
|
+
|
|
90
|
+
**Still on the unscoped `orc` package?** Do this once first - your `orc upgrade`
|
|
91
|
+
is the pre-v0.56.0 one and cannot install itself. Full detail in the CAUTION at
|
|
92
|
+
the top of this file.
|
|
93
|
+
|
|
94
|
+
- **Step 1 - release the command from the old package:** `npm uninstall -g orc`
|
|
95
|
+
- **Step 2 - install the current package:** `npm i -g @azure-id/orc`
|
|
96
|
+
- **Step 3 - re-apply it to your project:** `orc update`
|
|
97
|
+
|
|
98
|
+
**Do not use `npm i -g -f`.** Full detail in v0.56.0 below.
|
|
99
|
+
|
|
100
|
+
Every other testing surface in ORC **writes** tests. `/orc-test` **runs** them.
|
|
101
|
+
|
|
102
|
+
It points at a running API - yours, on your laptop, or one you were given
|
|
103
|
+
permission to touch - sends real requests, writes down what came back, and
|
|
104
|
+
stops. The split is the whole design:
|
|
105
|
+
|
|
106
|
+
> **The CLI executes and measures. The model designs and interprets.**
|
|
107
|
+
> A model never sends a request. The CLI never decides what a response means.
|
|
108
|
+
|
|
109
|
+
That split is what makes the report worth reading. A number in it was measured
|
|
110
|
+
by a program. A sentence in it was written by a model that only ever saw
|
|
111
|
+
evidence on disk.
|
|
112
|
+
|
|
113
|
+
**Three rules hold it together**, each a registered contract:
|
|
114
|
+
|
|
115
|
+
- **`a lane that reports a result it did not observe`** - three verdicts, and
|
|
116
|
+
`unknown` is the honest one. A case nobody ran is never a pass.
|
|
117
|
+
- **`a lane that sends traffic to a target nobody authorized`** - the target is
|
|
118
|
+
frozen once, a remote one **requires** a written statement of who said yes,
|
|
119
|
+
and every request is fenced to one origin.
|
|
120
|
+
- **`a lane that fixes the system under test`** - it reports and hands back. Not
|
|
121
|
+
to fix a boot failure, not to add a `data-testid`, not to relax a check.
|
|
122
|
+
|
|
123
|
+
**The free pass runs first, and always.** `orc test surface` reads your
|
|
124
|
+
repository and the target's own spec document for zero model tokens, and the
|
|
125
|
+
**code-vs-live diff** falls out of it: a route that answers at the target and is
|
|
126
|
+
in **no file in your repository** is undocumented surface nobody reviews and
|
|
127
|
+
nobody patches. It is derived from the two spec documents and from nothing else
|
|
128
|
+
- no route is ever called to produce it. With no spec at the target the diff
|
|
129
|
+
reads **NOT MEASURED, with the reason**, because an empty list there would say
|
|
130
|
+
"no shadow APIs", which is a claim nobody made.
|
|
131
|
+
|
|
132
|
+
**The case matrix derives itself.** The tiers come out of the schema for free.
|
|
133
|
+
What costs anything is a **gap** - what a schema cannot know, like what a valid
|
|
134
|
+
email looks like in your business, or which order two calls have to happen in.
|
|
135
|
+
That is the only part a model is paid for.
|
|
136
|
+
|
|
137
|
+
**The security tier is a closed set** - the OWASP API Top 10 (2023), ten rows,
|
|
138
|
+
never extended on the fly, and **all ten render in every state**. A category ORC
|
|
139
|
+
could not measure says `UNCHECKABLE`, **keeps its slot**, carries its own
|
|
140
|
+
reason, never becomes a pass, and never changes the exit code. Reporting an
|
|
141
|
+
unmeasured category as clean is the single most damaging thing a security report
|
|
142
|
+
can do, because somebody then ships on it. It **detects and never exploits**:
|
|
143
|
+
every probe shows the condition and stops there.
|
|
144
|
+
|
|
145
|
+
**When your app is broken, it stops.** `orc test env` finds the start command,
|
|
146
|
+
sees the process is alive, reads the exact line in the log - and **will not open
|
|
147
|
+
that file**. It names the environment values that are absent and writes a value
|
|
148
|
+
for none of them, because a placeholder in a real environment is a
|
|
149
|
+
credential-shaped lie and only you know the right one.
|
|
150
|
+
|
|
151
|
+
**The runner is the only place in ORC that sends a request to a system it did
|
|
152
|
+
not start**, so everything in it exists to make sure ORC's own run is never the
|
|
153
|
+
incident. A red happy path **stops the ladder** - a 500 on every request will
|
|
154
|
+
"prove" a dozen vulnerabilities that are one bug. The pace is the target's, and
|
|
155
|
+
the CLI applies it. **A 429 is a result**: it means rate limiting works, so ORC
|
|
156
|
+
records it and backs off rather than pushing through, which is how a staging
|
|
157
|
+
scan becomes a lockout. Failed logins are capped per identity. **Redaction is
|
|
158
|
+
structural** - every credential is replaced before the bytes reach disk, not in
|
|
159
|
+
a review step afterwards.
|
|
160
|
+
|
|
161
|
+
**A finding has to point at something.** One that cites evidence which does not
|
|
162
|
+
resolve on disk is **dropped by name and counted**. Severity is ORC's; if the
|
|
163
|
+
interpreting model disagrees, its word is kept beside ORC's and printed, so a
|
|
164
|
+
disagreement is visible instead of invisible. **A flake is recorded, never
|
|
165
|
+
retried away** - the instability is the finding.
|
|
166
|
+
|
|
167
|
+
**The front end drives Playwright as a script**, not as an agent loop: a step an
|
|
168
|
+
LLM took is not a step you can re-run. ORC **names** the install command and
|
|
169
|
+
never runs it - it does not install into your project.
|
|
170
|
+
|
|
171
|
+
**`orc ui` ▸ Test** renders all of it and works out none of it. Five tabs, a
|
|
172
|
+
button for every free action, and **`orc test run` as a command you copy** -
|
|
173
|
+
never a button. It costs no model tokens, so the usual rule would make it one;
|
|
174
|
+
it sends real traffic, and a page that can start a scan against a live host is a
|
|
175
|
+
page that can start one by accident.
|
|
176
|
+
|
|
177
|
+
**The run folder is never staged.** It holds real response bodies from a real
|
|
178
|
+
system - the most sensitive thing ORC writes to disk. `orc doctor` tells you if
|
|
179
|
+
it is not git-ignored, and it will not edit `.gitignore` for you.
|
|
180
|
+
|
|
181
|
+
Five config keys: `test_gate` (warn only - the payoff is knowing, not gating),
|
|
182
|
+
`test_security_tier`, `test_max_rps`, `test_case_budget`, `test_ui_driver`. Five
|
|
183
|
+
more were **refused**, with the reasons written down so nobody proposes them
|
|
184
|
+
again - among them `test_retries`, because a flake is recorded rather than
|
|
185
|
+
retried away, and `test_auto_fix`, which is this lane's premise inverted.
|
|
186
|
+
|
|
187
|
+
**And one bug that was not in this lane.** Writing the tests isolated an
|
|
188
|
+
intermittent failure that turned out to be real: `orc ui` compressed its API
|
|
189
|
+
answers but not its **static files**, guarded by a comment reading *"No asset is
|
|
190
|
+
over 64 KiB today"* that had been false for two releases. `js/panels/extra.js`
|
|
191
|
+
is 138 KB. Over a Windows loopback socket a response that size loses its tail
|
|
192
|
+
roughly one time in six - which in a browser is a panel script that stops in the
|
|
193
|
+
middle of a function, with nothing in any log to say so. Both paths now share
|
|
194
|
+
one encoder. **A stale comment can guard a live bug for longer than the bug
|
|
195
|
+
would have survived alone.**
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
13
199
|
### v1.4.2 - the panel that stops reloading, and the fields you could not read back _(2026-09-05)_
|
|
14
200
|
|
|
15
201
|
**Still on the unscoped `orc` package?** Do this once first - your `orc upgrade`
|
package/README.md
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
*Intake → analyze → plan → score → parallel subagents → review → verify → ship.*
|
|
8
8
|
|
|
9
9
|

|
|
10
|
-

|
|
11
11
|

|
|
12
12
|

|
|
13
13
|

|
|
14
14
|

|
|
15
15
|

|
|
16
16
|
|
|
17
|
-
**Latest: v1.
|
|
17
|
+
**Latest: v1.6.0** · updated 2026-09-07 · [full changelog](CHANGELOG.md)
|
|
18
18
|
|
|
19
19
|
**On npm: [`@azure-id/orc`](https://www.npmjs.com/package/@azure-id/orc)** — `npm i -g @azure-id/orc`
|
|
20
20
|
|
|
@@ -287,6 +287,7 @@ ORC have terminal hook to see: Context Window %, 5 Hour usage %, Weekly usage %
|
|
|
287
287
|
| **`/orc-challenge`** | Grades a **finished** artifact — a TSD, a PRD, an ADR, an API contract, a README, a module — against a goal **you** state, then **stops and makes you fix it in a different session**. ORC judges, you fix, ORC re-judges: **it never fixes what it judged**, because a session that just wrote the fix would grade its own homework. **And it never guesses what "good" means here.** | [see it](mock-run/orc-challenge.md) |
|
|
288
288
|
| **`/orc-pact`** | The promises your project makes, and which are in doubt right now. Four states, all **computed on read**: HOLDING · DRIFTED · **UNCHECKABLE** (the honest state — never a failure) · BROKEN. It never invents a promise and never retires one for you. | [see it](mock-run/orc-pact.md) |
|
|
289
289
|
| **`/orc-boundary`** | What ORC should **not** try here, and exactly what would change that. EXECUTE · ESCALATE · REFUSE, per area. **A REFUSE always names what would make it a yes.** It gates ORC's own dispatch, never your instructions. | [see it](mock-run/orc-boundary.md) |
|
|
290
|
+
| **`/orc-test`** | **Runs** the test against a running system - yours, or one you were given permission to touch. Sends the requests, writes down what came back, and stops. **The CLI executes and measures; the model designs and interprets.** Three verdicts, and **`unknown` is the honest one**. The OWASP API Top 10 (2023) as a **closed set** where a category it could not measure keeps its slot and never becomes a pass. It never edits the system it is testing. | [see it](mock-run/orc-test.md) |
|
|
290
291
|
| **`/orc-verify`** | Verifies only your git-modified changes: build, tests, diff sanity, findings on a P0–P3 ladder. Read-only. | [see it](templates/skills/orc-verify/examples/verify-mock.md) |
|
|
291
292
|
| **`/orc-aftermath`** | Did what we shipped hold up? Graded from the repository's own future: churn, reverts, deleted tests, broken promises. **Churn is a signal, never a verdict**, and it never names a person. | [see it](mock-run/orc-aftermath.md) |
|
|
292
293
|
| **`/orc-budget`** | What a run costs, in the unit you are billed in. A **token vector** — fresh input, cache write, cache read, output, never blended — shown four ways: tokens, dollars, percent of your 5-hour window, and context risk. Needs a plan, not a sentence. | [see it](mock-run/orc-budget.md) |
|
|
@@ -575,7 +576,7 @@ a current audit: [EVAL-REPORT.md](EVAL-REPORT.md).
|
|
|
575
576
|
**Full history: [CHANGELOG.md](CHANGELOG.md)** — or `orc changelog`, which prints
|
|
576
577
|
only what is newer than the version you have.
|
|
577
578
|
|
|
578
|
-
### v1.
|
|
579
|
+
### v1.6.0 - the rule that can finally say no _(2026-09-07)_
|
|
579
580
|
|
|
580
581
|
**Still on the unscoped `orc` package?** Do this once first - your `orc upgrade`
|
|
581
582
|
is the pre-v0.56.0 one and cannot install itself. Full detail in the CAUTION at
|
|
@@ -587,54 +588,66 @@ the top of this file.
|
|
|
587
588
|
|
|
588
589
|
**Do not use `npm i -g -f`.** Full detail in v0.56.0 below.
|
|
589
590
|
|
|
590
|
-
Four more defects in `orc ui` ▸ **CLI Hook Interface**. Three were the same
|
|
591
|
-
defect wearing three faces.
|
|
592
|
-
|
|
593
|
-
**Staging a change reloaded the whole panel** - every staged op re-entered the
|
|
594
|
-
router, so four endpoints were refetched and everything was rebuilt from a
|
|
595
|
-
skeleton. That made a staged change look like an applied one, and it left the
|
|
596
|
-
open editor showing values from before the change you had just made: you picked
|
|
597
|
-
a shape and nothing moved. Nothing is fetched now until something actually moves
|
|
598
|
-
the disk; a staged op repaints from cache with **zero requests**, keeps your
|
|
599
|
-
scroll position, and the open editor repaints with it - caret and all.
|
|
600
|
-
|
|
601
|
-
**And the fields could not be read back.** `orc statusline show --json` emitted
|
|
602
|
-
twelve of the twenty-four fields a part can carry, so `case`, `prefix`,
|
|
603
|
-
`min_cols`, `precision` and seven more were written to disk correctly and then
|
|
604
|
-
were invisible to the panel that wrote them. Every field ships now, plus
|
|
605
|
-
`authored`, so a control can say **which values are yours** rather than
|
|
606
|
-
inherited. `--json is not a summary`, found again.
|
|
607
|
-
|
|
608
|
-
**Six parts per line, not five.** **The dropdown nobody could read** turned out
|
|
609
|
-
to be a panel with no colours at all: `hookui.css` was written against four CSS tokens that do not
|
|
610
|
-
exist, so every one of those declarations was silently dropped.
|
|
611
|
-
|
|
612
|
-
**The preview is drawn as a terminal now** - a window of the stated width with a
|
|
613
|
-
column ruler - and `orc statusline preview` takes `--theme` and `--glyphs` as a
|
|
614
|
-
**render-only** override, so a new drawer shows the same bar under all four
|
|
615
|
-
colour sets and all seven symbol sets. Picking a colour set from its name is a
|
|
616
|
-
guess; picking it from its picture is a decision.
|
|
617
|
-
|
|
618
|
-
**And the sixth part still reset the bar to the default**, because the cap was in
|
|
619
|
-
three places: the hook re-checks the compiled file with a cheap shape guard of
|
|
620
|
-
its own, and that guard held its own `5` — and counted spacers, which the
|
|
621
|
-
validator never has. The cap now travels on the lock file and the compiler marks
|
|
622
|
-
each structural item, so one cap and one counting rule serve all three readers.
|
|
623
|
-
|
|
624
|
-
**And a name you typed sometimes went nowhere.** Seven of the thirty-five shapes
|
|
625
|
-
draw a name; the other twenty-eight ignore it, which is the design — a bar with a
|
|
626
|
-
word in front of it is a different shape. The Name box was offered on all
|
|
627
|
-
thirty-five anyway. Each renderer now publishes the fields it **uses**, and the
|
|
628
|
-
editor greys out what this shape ignores, keeping the slot and naming the shapes
|
|
629
|
-
that do.
|
|
630
|
-
|
|
631
|
-
**And it still blinked with the network tab empty:** `hkPaint` replaces every
|
|
632
|
-
child, and the stylesheet faded each one in on a stagger. Entrance animations are
|
|
633
|
-
off from the second paint onward.
|
|
634
|
-
|
|
635
|
-
Full detail in [CHANGELOG.md](CHANGELOG.md).
|
|
636
591
|
|
|
637
|
-
|
|
592
|
+
ORC's read discipline has always said the same thing in three places: the main
|
|
593
|
+
session reads to **find**, and dispatches an agent to **understand**. It was
|
|
594
|
+
prose in `_shared/read-ladder.md`, in `/orc-doc` hard rule 0, and on line 21 of
|
|
595
|
+
`/orc-quick` - and **nothing checked any of it**.
|
|
596
|
+
|
|
597
|
+
`orc-read-gate.js` is a `PreToolUse` hook on `Read` that can refuse. It ships
|
|
598
|
+
**off**, and `off` is byte-identical to not having it - asserted by a test, not
|
|
599
|
+
by intention.
|
|
600
|
+
|
|
601
|
+
**The threshold is ORC's own number, measured.** The pattern this came from uses
|
|
602
|
+
350 lines, derived from a 10-30 second delegation round trip. ORC's round trip
|
|
603
|
+
is nothing like that: a dispatch measured **p50 76s, p90 188s** (n=125), and one
|
|
604
|
+
real read-only dispatch cost **13,276 tokens** to read a four-line file. At the
|
|
605
|
+
measured 55.2 chars/line across 316 sampled reads, break-even is **~1000 lines**.
|
|
606
|
+
Copying 350 would delegate work whose overhead exceeds its saving.
|
|
607
|
+
|
|
608
|
+
**`agent_id` is the only discriminator, and that was MEASURED, not assumed.**
|
|
609
|
+
`PreToolUse` **does** fire inside a dispatched subagent - the assumption that
|
|
610
|
+
hooks are session-level was wrong. `session_id` and `transcript_path` are
|
|
611
|
+
**identical** in both contexts, so a gate written against either would block the
|
|
612
|
+
full read an executor must perform before an `Edit`, and a reconstructed
|
|
613
|
+
`old_string` corrupts files. The gate tests for the **presence** of `agent_id`,
|
|
614
|
+
never for the absence of some other key, which asserts nothing.
|
|
615
|
+
|
|
616
|
+
**Everything it stays silent on, and each one is deliberate:** any read by a
|
|
617
|
+
subagent - outside an open ORC run - a targeted `offset`/`limit` read - a file
|
|
618
|
+
under the threshold - build logs, test results and `.jsonl` that a gate parses
|
|
619
|
+
whole, because a truncated red build reads **green** - and any error at all,
|
|
620
|
+
because **a read gate that throws and blocks a read has broken the tool.**
|
|
621
|
+
|
|
622
|
+
**A block always names the cheaper path.** A gate that only refuses is a gate
|
|
623
|
+
people switch off. It names the targeted read, the agent dispatch, and the
|
|
624
|
+
config key that turns it down.
|
|
625
|
+
|
|
626
|
+
**Every `warn` and `block` writes one trace line** (`READ-GATE`), an allow
|
|
627
|
+
writes none. That is affordable here for a structural reason: the gate only acts
|
|
628
|
+
while a run is open, so a trace always exists. `orc doctor` gains
|
|
629
|
+
`read-gate-unwired` and `read-gate-fallback`, both reported **only while the
|
|
630
|
+
feature is armed** - a doctor that warns about the default is one people learn
|
|
631
|
+
to ignore.
|
|
632
|
+
|
|
633
|
+
**Two config keys**, both on the `SEED_EMPTY` allowlist with an empty `lanes[]`,
|
|
634
|
+
because a hook has no lane and cannot resolve config: `read_gate`
|
|
635
|
+
(`off`|`warn`|`block`, default `off`) and `read_gate_max_lines` (default 1000).
|
|
636
|
+
|
|
637
|
+
**What this release deliberately does NOT do**, with the reasons recorded so
|
|
638
|
+
nobody re-proposes them: no gate on `Bash` reads (`cat`/`head`/`tail`) - it is
|
|
639
|
+
several times the false-positive surface, and the measurement says the `Read`
|
|
640
|
+
tool is only about 7.5% of the actual read surface here - and no
|
|
641
|
+
`context-reader` slot in `EXTRA_SLOTS`, because at a 1000-line threshold the
|
|
642
|
+
addressable population is five reads across 239 sampled sessions, which is
|
|
643
|
+
infrastructure for nothing.
|
|
644
|
+
|
|
645
|
+
**An honest note on the measurement.** The audit behind this release found the
|
|
646
|
+
existing prose is largely *working*: median full read is 85 lines, p90 is 288,
|
|
647
|
+
and 46% of reads already use `offset`/`limit` without being told. Oversized
|
|
648
|
+
reads are about **1% of price-weighted main-session ingest** at a generous upper
|
|
649
|
+
bound. This hook is a guardrail on a road most runs already stay on - it is off
|
|
650
|
+
by default for exactly that reason.
|
|
638
651
|
|
|
639
652
|
## Requirements
|
|
640
653
|
|