@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.
Files changed (38) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/README.md +63 -50
  3. package/bin/cli.js +5732 -133
  4. package/bin/mockrun-catalog.js +4 -0
  5. package/bin/verify-contracts.js +4509 -4426
  6. package/bin/verify-package.js +24 -2
  7. package/bin/webui/api.js +108 -8
  8. package/bin/webui/app.html +9 -0
  9. package/bin/webui/css/06-responsive.css +14 -0
  10. package/bin/webui/css/panels/test.css +132 -0
  11. package/bin/webui/fixtures/index.js +23 -1
  12. package/bin/webui/fixtures/test.js +547 -0
  13. package/bin/webui/i18n/en/common.json +1 -0
  14. package/bin/webui/i18n/en/nav.json +1 -0
  15. package/bin/webui/i18n/en/overview.json +5 -0
  16. package/bin/webui/i18n/en/test.json +141 -0
  17. package/bin/webui/i18n/id/common.json +1 -0
  18. package/bin/webui/i18n/id/nav.json +1 -0
  19. package/bin/webui/i18n/id/overview.json +5 -0
  20. package/bin/webui/i18n/id/test.json +141 -0
  21. package/bin/webui/js/01-i18n.js +1 -0
  22. package/bin/webui/js/panels/challenge.js +8 -3
  23. package/bin/webui/js/panels/overview.js +18 -0
  24. package/bin/webui/js/panels/test.js +733 -0
  25. package/bin/webui/serve.js +572 -557
  26. package/mock-run/orc-test.md +327 -0
  27. package/package.json +1 -1
  28. package/templates/agents/MODEL-MAPPING.md +2 -0
  29. package/templates/agents/orc-test-designer-opus-5-high.md +158 -0
  30. package/templates/agents/orc-test-interpreter-opus-5-low.md +130 -0
  31. package/templates/commands/orc-test.md +66 -0
  32. package/templates/hooks/README.md +110 -1
  33. package/templates/hooks/orc-read-gate.js +279 -0
  34. package/templates/skills/_shared/extra-dispatch.md +1346 -1337
  35. package/templates/skills/_shared/live-target.md +188 -0
  36. package/templates/skills/_shared/phases/trace.md +2 -2
  37. package/templates/skills/_shared/wait.md +241 -240
  38. 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
  ![npm](https://img.shields.io/npm/v/%40azure-id%2Forc?style=for-the-badge&color=cb3837&logo=npm)
10
- ![Version](https://img.shields.io/badge/version-1.4.2-blue.svg?style=for-the-badge)
10
+ ![Version](https://img.shields.io/badge/version-1.6.0-blue.svg?style=for-the-badge)
11
11
  ![License](https://img.shields.io/badge/license-MIT-green.svg?style=for-the-badge)
12
12
  ![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg?style=for-the-badge)
13
13
  ![Claude Code](https://img.shields.io/badge/Claude_Code-Skills-purple.svg?style=for-the-badge)
14
14
  ![Dependencies](https://img.shields.io/badge/dependencies-zero-lightgrey.svg?style=for-the-badge)
15
15
  ![GitHub stars](https://img.shields.io/github/stars/azure-id/orc?style=for-the-badge&color=yellow)
16
16
 
17
- **Latest: v1.4.2** · updated 2026-09-05 · [full changelog](CHANGELOG.md)
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.4.2 - the panel that stops reloading, and the fields you could not read back _(2026-09-05)_
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