@azure-id/orc 1.1.0 → 1.2.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/bin/cli.js CHANGED
@@ -3091,6 +3091,18 @@ const LANE_CALLS = {
3091
3091
  never: "never merge `.claude/orc.config.yaml` yourself, and never re-derive a precedence — the answer already carries it",
3092
3092
  lanes: ["context-combiner", "orc", "orc-aftermath", "orc-analyze", "orc-analyze-mini", "orc-boundary", "orc-brainstorm", "orc-budget", "orc-challenge", "orc-claude", "orc-diy", "orc-doc", "orc-explain", "orc-export", "orc-fast", "orc-grill", "orc-handoff", "orc-learn", "orc-mini", "orc-pact", "orc-pattern", "orc-poly", "orc-pr-driver", "orc-pr-setup", "orc-quick", "orc-retro", "orc-route", "orc-verify", "orc-wiki"],
3093
3093
  },
3094
+ "run-inflight": {
3095
+ cmd: "orc run inflight [--json]",
3096
+ what: "is a dispatch from this run still alive — the ONE reader of the trace's pending sidecar",
3097
+ exits: { 0: "clear — provably nothing in flight", 1: "in-flight — at least one dispatch has not returned", 2: "unknown — cannot prove either way" },
3098
+ states: ["clear", "in-flight", "unknown"],
3099
+ cost: "free",
3100
+ when: "before ANY re-dispatch, requeue or repair round, and before the first dispatch of a resumed run",
3101
+ on_absent: "exit 2 REFUSES by default — the one place an absent reading blocks, because a wrongly-refused dispatch costs a question and a wrongly-issued one costs a second Opus agent for an hour",
3102
+ canonical: "_shared/return-validation.md",
3103
+ never: "never read `clear` as proof that an AD-HOC dispatch finished — the hook writes no SPAWN for one, so no record exists",
3104
+ lanes: ["orc", "orc-doc", "orc-fast", "orc-mini", "orc-quick", "orc-wiki"],
3105
+ },
3094
3106
  "lane-phases": {
3095
3107
  cmd: "orc lane phases <lane> [--json]",
3096
3108
  what: "which SHARED phases this lane runs, in order — the file, the layers to read, and when",
@@ -9006,6 +9018,149 @@ function resume() {
9006
9018
  }
9007
9019
 
9008
9020
  // `orc run list` / `orc run show <slug|n>`
9021
+ // ── `orc run inflight` — is a previous dispatch still alive? (v1.2.0) ───────
9022
+ //
9023
+ // WHY THIS EXISTS. Claude Code's Task tool returning an error does NOT kill the
9024
+ // subagent behind it. The agent keeps running and keeps writing files. Every
9025
+ // lane's retry rule ("a broken return = a failure, re-dispatch") silently
9026
+ // assumed the opposite, so an interrupted turn produced a SECOND agent on the
9027
+ // same task while the first was still working. A graded run put THREE
9028
+ // `orc-executor-opus-5-low` agents on one task for 50m19s + 115m22s + 100m53s
9029
+ // — 266 minutes of Opus 5 for one authorised dispatch, all editing the same
9030
+ // files. The hook already recorded every one of them; nothing ever READ it.
9031
+ //
9032
+ // The pending sidecar (`<trace>.pending.json`, written by orc-trace.js on every
9033
+ // SPAWN) is the evidence. This command makes it authoritative:
9034
+ // `a lane that re-dispatches over a live attempt` has broken the contract in
9035
+ // `_shared/return-validation.md` §0.
9036
+ //
9037
+ // UNKNOWN IS NOT ZERO. A missing pointer, an unreadable sidecar or a record too
9038
+ // old to trust all exit 2 — never 0. "I cannot prove anything is running" and
9039
+ // "I proved nothing is running" are different facts, and re-dispatching on the
9040
+ // first one is exactly the bug. Only a readable sidecar that is EMPTY *and* a
9041
+ // trace whose SPAWN/RETURN counts agree earns exit 0.
9042
+ //
9043
+ // exit 0 clear — provably nothing in flight
9044
+ // exit 1 in-flight — >=1 dispatch has not returned
9045
+ // exit 2 unknown — cannot prove either way
9046
+ const INFLIGHT_STALE_MS = 6 * 60 * 60 * 1000;
9047
+
9048
+ function runInflightCmd(claudeDir) {
9049
+ const dir = resolveLogDir(claudeDir);
9050
+ const out = {
9051
+ ok: true,
9052
+ state: "unknown",
9053
+ reason: null,
9054
+ count: 0,
9055
+ entries: [],
9056
+ trace: null,
9057
+ lane: null,
9058
+ slug: null,
9059
+ log_dir: dir,
9060
+ sidecar: null,
9061
+ sidecar_readable: false,
9062
+ spawns: null,
9063
+ returns: null,
9064
+ balance_agrees: null,
9065
+ stale_entries: 0,
9066
+ };
9067
+
9068
+ const finish = (state, reason, code) => {
9069
+ out.state = state;
9070
+ out.reason = reason;
9071
+ if (wantsJson()) emitJson(out);
9072
+ else renderInflight(out);
9073
+ process.exit(code);
9074
+ };
9075
+
9076
+ // 1. The run pointer. No pointer = no open run we can reason about.
9077
+ let cur = null;
9078
+ try {
9079
+ cur = fs.readFileSync(path.join(dir, ".current"), "utf8").trim();
9080
+ } catch (_) {}
9081
+ if (!cur) return finish("unknown", "no trace pointer — no run is open, or the pointer was never written", 2);
9082
+ out.trace = cur;
9083
+ const m = TRACE_NAME.exec(cur);
9084
+ if (m) { out.lane = m[1]; out.slug = m[2]; }
9085
+
9086
+ // 2. The trace's own SPAWN/RETURN balance — an independent second opinion.
9087
+ // Counted from the hook's own skeleton lines only.
9088
+ const tracePath = path.join(dir, cur);
9089
+ try {
9090
+ const text = fs.readFileSync(tracePath, "utf8");
9091
+ out.spawns = (text.match(/\] hook\s+SPAWN /g) || []).length;
9092
+ const all = (text.match(/\] hook\s+RETURN /g) || []).length;
9093
+ const loose = (text.match(/\] hook\s+RETURN ~agent :: unattributed/g) || []).length;
9094
+ out.returns = all - loose;
9095
+ } catch (_) {
9096
+ return finish("unknown", `trace pointer names "${cur}" but it cannot be read`, 2);
9097
+ }
9098
+
9099
+ // 3. The pending sidecar — the record of what was dispatched and never closed.
9100
+ const side = path.join(dir, cur + ".pending.json");
9101
+ out.sidecar = side;
9102
+ let pend = null;
9103
+ try {
9104
+ const raw = JSON.parse(fs.readFileSync(side, "utf8"));
9105
+ if (Array.isArray(raw)) { pend = raw; out.sidecar_readable = true; }
9106
+ } catch (_) {}
9107
+
9108
+ const now = Date.now();
9109
+ if (pend) {
9110
+ out.entries = pend.map((r) => {
9111
+ const ts = typeof r.ts === "number" ? r.ts : null;
9112
+ const age = ts == null ? null : Math.round((now - ts) / 1000);
9113
+ return {
9114
+ agent: r.agent == null ? null : String(r.agent),
9115
+ desc: r.desc == null ? null : String(r.desc),
9116
+ started_ms: ts,
9117
+ age_s: age,
9118
+ stale: ts != null && now - ts > INFLIGHT_STALE_MS,
9119
+ };
9120
+ });
9121
+ out.count = out.entries.length;
9122
+ out.stale_entries = out.entries.filter((e) => e.stale).length;
9123
+ }
9124
+
9125
+ const balance = out.spawns != null && out.returns != null ? out.spawns - out.returns : null;
9126
+ out.balance_agrees = balance == null || pend == null ? null : balance === out.count;
9127
+
9128
+ // 4. Verdict. Conservative in every direction.
9129
+ if (!out.sidecar_readable) {
9130
+ if (balance != null && balance > 0)
9131
+ return finish("in-flight", `sidecar unreadable, but the trace shows ${balance} SPAWN(s) with no RETURN`, 1);
9132
+ return finish("unknown", "the pending sidecar is missing or unreadable — cannot prove a dispatch finished", 2);
9133
+ }
9134
+ if (out.count > 0) {
9135
+ if (out.stale_entries === out.count)
9136
+ return finish(
9137
+ "unknown",
9138
+ `${out.count} record(s), all older than 6h — the run probably died without closing them`,
9139
+ 2
9140
+ );
9141
+ return finish("in-flight", `${out.count} dispatch(es) have not returned`, 1);
9142
+ }
9143
+ // Sidecar empty. Only trust it when the trace agrees.
9144
+ if (balance != null && balance > 0)
9145
+ return finish("unknown", `sidecar is empty but the trace shows ${balance} unmatched SPAWN(s) — they disagree`, 2);
9146
+ return finish("clear", "no dispatch is in flight", 0);
9147
+ }
9148
+
9149
+ function renderInflight(o) {
9150
+ const head =
9151
+ o.state === "clear" ? "clear" : o.state === "in-flight" ? `IN FLIGHT (${o.count})` : "unknown";
9152
+ console.log(`${ui.color.bold("dispatch:")} ${head}`);
9153
+ console.log(` ${o.reason}`);
9154
+ if (o.trace) console.log(` trace ${o.trace}`);
9155
+ for (const e of o.entries) {
9156
+ const age = e.age_s == null ? "age unknown" : `${Math.floor(e.age_s / 60)}m${e.age_s % 60}s ago`;
9157
+ console.log(` - ${e.agent || "(unnamed)"} ${age}${e.stale ? " [stale]" : ""}`);
9158
+ if (e.desc) console.log(` ${e.desc}`);
9159
+ }
9160
+ if (o.state === "in-flight")
9161
+ console.log("\n Do NOT re-dispatch these tasks. A Task error does not kill the agent behind it.");
9162
+ }
9163
+
9009
9164
  function runCmd() {
9010
9165
  const claudeDir = resolveClaudeDir();
9011
9166
  const pos = positionals(); // ["run", <sub?>, <arg?>]
@@ -9016,6 +9171,9 @@ function runCmd() {
9016
9171
  // on disk. Neither deletes anything.
9017
9172
  if (sub === "close" || sub === "reopen") return runCloseCmd(claudeDir, runs, sub, pos[2]);
9018
9173
 
9174
+ // Read-only, and the ONE reader of the pending sidecar. 0 clear / 1 in-flight / 2 unknown.
9175
+ if (sub === "inflight") return runInflightCmd(claudeDir);
9176
+
9019
9177
  if (sub === "show") {
9020
9178
  const arg = pos[2];
9021
9179
  const pick = /^\d+$/.test(String(arg)) ? runs[Number(arg) - 1] : runs.find((r) => r.slug === arg);
@@ -33210,6 +33368,318 @@ function usageResetMs(v) {
33210
33368
  return Number.isFinite(p) ? p : NaN;
33211
33369
  }
33212
33370
 
33371
+ // ── `orc usage report` — where the window went (v1.2.0) ─────────────────────
33372
+ //
33373
+ // `orc usage check` answers "is there room". This answers the question a user
33374
+ // actually asks mid-run: "how much has THIS session eaten, and what ate it".
33375
+ //
33376
+ // THE HONEST PART, and it decides the whole shape of this command. Claude Code
33377
+ // records NO token usage for a dispatched subagent — `isSidechain` is never set
33378
+ // and no sidechain message carries a usage block, in any transcript on disk. So
33379
+ // a per-executor TOKEN figure cannot be measured, and inventing one would be
33380
+ // the same class of bug as a fake validator. What IS measured, exactly, is WALL
33381
+ // TIME, from the trace hook's own SPAWN/RETURN lines. Foreign workers are the
33382
+ // one exception: `orc extra` records real four-kind vectors, so those rows
33383
+ // carry tokens and say so. Every other row reports `tokens: null` plus the
33384
+ // reason — never 0. Unknown is not zero.
33385
+ const USAGE_TOP_N = 5;
33386
+
33387
+ // "12m43s" | "1m7s" | "45s" → seconds. Anything else → null, never 0.
33388
+ function usageDurSeconds(s) {
33389
+ if (!s) return null;
33390
+ const m = /^(?:(\d+)m)?(\d+)s$/.exec(String(s).trim());
33391
+ if (!m) return null;
33392
+ return (m[1] ? Number(m[1]) * 60 : 0) + Number(m[2]);
33393
+ }
33394
+
33395
+ // Per-agent wall time for the run the trace pointer names. Returns null when
33396
+ // there is no open run — an absent trace is an absent measurement, not zero.
33397
+ function usageRunConsumers(claudeDir) {
33398
+ const dir = resolveLogDir(claudeDir);
33399
+ let cur = null;
33400
+ try {
33401
+ cur = fs.readFileSync(path.join(dir, ".current"), "utf8").trim();
33402
+ } catch (_) {}
33403
+ if (!cur) return null;
33404
+ let text = "";
33405
+ try {
33406
+ text = fs.readFileSync(path.join(dir, cur), "utf8");
33407
+ } catch (_) {
33408
+ return null;
33409
+ }
33410
+ const nameMatch = TRACE_NAME.exec(cur);
33411
+ const by = new Map();
33412
+ const bump = (agent, secs, running) => {
33413
+ const k = agent || "(unnamed)";
33414
+ const r = by.get(k) || { agent: k, dispatches: 0, wall_seconds: 0, running: 0, unmeasured: 0 };
33415
+ r.dispatches += 1;
33416
+ if (running) r.running += 1;
33417
+ if (secs == null) r.unmeasured += 1;
33418
+ else r.wall_seconds += secs;
33419
+ by.set(k, r);
33420
+ };
33421
+ for (const line of text.split("\n")) {
33422
+ // Only the hook's own skeleton lines. `~agent :: unattributed` is a
33423
+ // bookkeeping artefact of >=2 in flight and is never a dispatch.
33424
+ const r = /\] hook\s+RETURN ~?([^\s:]+) :: /.exec(line);
33425
+ if (!r) continue;
33426
+ if (r[1] === "agent") continue;
33427
+ const d = /\bdur=(\S+)/.exec(line);
33428
+ bump(r[1], usageDurSeconds(d && d[1]), false);
33429
+ }
33430
+ // Anything still open is real spend happening RIGHT NOW — the exact case the
33431
+ // v1.2.0 in-flight guard exists for, and the one a user most wants to see.
33432
+ let pending = [];
33433
+ try {
33434
+ const raw = JSON.parse(fs.readFileSync(path.join(dir, cur + ".pending.json"), "utf8"));
33435
+ if (Array.isArray(raw)) pending = raw;
33436
+ } catch (_) {}
33437
+ const now = Date.now();
33438
+ for (const p of pending) {
33439
+ const secs = typeof p.ts === "number" ? Math.round((now - p.ts) / 1000) : null;
33440
+ bump(p.agent, secs, true);
33441
+ }
33442
+ return {
33443
+ trace: cur,
33444
+ lane: nameMatch ? nameMatch[1] : null,
33445
+ slug: nameMatch ? nameMatch[2] : null,
33446
+ agents: [...by.values()],
33447
+ in_flight: pending.length,
33448
+ };
33449
+ }
33450
+
33451
+ // Foreign dispatches DO carry measured tokens. Same file the spend report reads.
33452
+ function usageForeignSpend(claudeDir) {
33453
+ const f = path.join(claudeDir, "orc", "extra-spend.jsonl");
33454
+ let lines = [];
33455
+ try {
33456
+ lines = fs.readFileSync(f, "utf8").split("\n").filter(Boolean);
33457
+ } catch (_) {
33458
+ return { rows: [], unreadable: 0 };
33459
+ }
33460
+ const rows = [];
33461
+ let unreadable = 0;
33462
+ for (const l of lines) {
33463
+ try {
33464
+ rows.push(JSON.parse(l));
33465
+ } catch (_) {
33466
+ unreadable += 1;
33467
+ }
33468
+ }
33469
+ return { rows, unreadable };
33470
+ }
33471
+
33472
+ function usageReportCmd(claudeDir) {
33473
+ const asJson = wantsJson();
33474
+ const now = Date.now();
33475
+ const cfg = resolvedConfig(claudeDir);
33476
+ const stopPct = Math.min(50, Math.max(1, Number(cfg.usage_stop_pct) || 10));
33477
+
33478
+ // Read the bridge RAW here rather than through readUsageBridge: a stale
33479
+ // reading is still worth SHOWING, with its age, where a gate would rightly
33480
+ // discard it. The state word still comes from freshness — age is displayed,
33481
+ // never ignored.
33482
+ let raw = null;
33483
+ try {
33484
+ raw = JSON.parse(fs.readFileSync(path.join(claudeDir, "orc", "usage.json"), "utf8"));
33485
+ } catch (_) {}
33486
+ const ageMin =
33487
+ raw && typeof raw.written_at === "number" ? Math.round((now - raw.written_at) / 60000) : null;
33488
+ const stale = ageMin == null ? true : now - raw.written_at > WAIT_BRIDGE_MAX_AGE_MS;
33489
+
33490
+ const view = (o, label) => {
33491
+ if (!o || typeof o.used_percentage !== "number") return null;
33492
+ const used = Math.round(o.used_percentage);
33493
+ const at = usageResetMs(o.resets_at);
33494
+ return {
33495
+ window: label,
33496
+ used_percentage: used,
33497
+ remaining_percentage: Math.max(0, 100 - used),
33498
+ resets_at: Number.isFinite(at) ? new Date(at).toISOString() : null,
33499
+ resets_in_minutes: Number.isFinite(at) && at > now ? Math.round((at - now) / 60000) : null,
33500
+ low: 100 - used <= stopPct,
33501
+ };
33502
+ };
33503
+ const fh = raw ? view(raw.five_hour, "5h") : null;
33504
+ const sd = raw ? view(raw.seven_day, "wk") : null;
33505
+
33506
+ // Session consumption — the ledger the statusline keeps.
33507
+ let led = null;
33508
+ try {
33509
+ led = JSON.parse(fs.readFileSync(path.join(claudeDir, "orc", "usage-session.json"), "utf8"));
33510
+ } catch (_) {}
33511
+ const consumed = (w) =>
33512
+ !w
33513
+ ? null
33514
+ : {
33515
+ baseline_percentage: w.baseline,
33516
+ now_percentage: w.last,
33517
+ consumed_percentage: Math.max(0, w.accumulated + Math.max(0, w.last - w.baseline)),
33518
+ window_resets: w.resets,
33519
+ };
33520
+ const session = !led
33521
+ ? null
33522
+ : {
33523
+ session_id: led.session_id || null,
33524
+ started_at: led.started_at ? new Date(led.started_at).toISOString() : null,
33525
+ running_minutes: led.started_at ? Math.round((now - led.started_at) / 60000) : null,
33526
+ five_hour: consumed(led.five_hour),
33527
+ seven_day: consumed(led.seven_day),
33528
+ still_counting: true,
33529
+ // Never overclaim. The window is per ACCOUNT: a second Claude Code
33530
+ // window, a cloud session, or anyone else on the same key moves it too.
33531
+ caveat:
33532
+ "this is how far the window moved while this session ran — other sessions on the same account share it",
33533
+ };
33534
+
33535
+ const run = usageRunConsumers(claudeDir);
33536
+ const foreign = usageForeignSpend(claudeDir);
33537
+
33538
+ // Rank by measured wall time. A row with nothing measured keeps its slot.
33539
+ const top = [];
33540
+ if (run) {
33541
+ const sorted = run.agents.slice().sort((a, b) => b.wall_seconds - a.wall_seconds);
33542
+ for (const a of sorted.slice(0, USAGE_TOP_N))
33543
+ top.push({
33544
+ agent: a.agent,
33545
+ dispatches: a.dispatches,
33546
+ running: a.running,
33547
+ wall_seconds: a.wall_seconds,
33548
+ unmeasured_dispatches: a.unmeasured,
33549
+ tokens: null,
33550
+ tokens_source: "unavailable",
33551
+ });
33552
+ }
33553
+ for (const r of foreign.rows.slice(-USAGE_TOP_N)) {
33554
+ top.push({
33555
+ agent: "extra:" + (r.profile || "?") + "/" + (r.model || "?"),
33556
+ dispatches: 1,
33557
+ running: 0,
33558
+ wall_seconds: usageDurSeconds(r.dur),
33559
+ unmeasured_dispatches: 0,
33560
+ tokens: r.usage || null,
33561
+ tokens_source: r.usage ? "measured" : "not reported by the worker",
33562
+ });
33563
+ }
33564
+
33565
+ const windows = [fh, sd].filter(Boolean);
33566
+ const state = !windows.length || stale ? "unknown" : windows.some((w) => w.low) ? "low" : "ok";
33567
+ const code = state === "low" ? 1 : state === "unknown" ? 2 : 0;
33568
+ const out = {
33569
+ ok: true,
33570
+ state,
33571
+ five_hour: fh,
33572
+ seven_day: sd,
33573
+ context_used_percentage:
33574
+ raw && typeof raw.context_used_percentage === "number" ? raw.context_used_percentage : null,
33575
+ reading_age_minutes: ageMin,
33576
+ reading_stale: stale,
33577
+ stop_pct: stopPct,
33578
+ gate: String(cfg.usage_gate || "off"),
33579
+ session,
33580
+ run: run
33581
+ ? {
33582
+ trace: run.trace,
33583
+ lane: run.lane,
33584
+ slug: run.slug,
33585
+ dispatches: run.agents.reduce((n, a) => n + a.dispatches, 0),
33586
+ in_flight: run.in_flight,
33587
+ wall_seconds: run.agents.reduce((n, a) => n + a.wall_seconds, 0),
33588
+ }
33589
+ : null,
33590
+ top,
33591
+ foreign_spend_rows: foreign.rows.length,
33592
+ unreadable_spend_lines: foreign.unreadable,
33593
+ // Say it on every emission. A reader who does not know this reads a
33594
+ // wall-time ranking as a token ranking.
33595
+ tokens_note:
33596
+ "Claude Code records no token usage for a dispatched subagent, so a Claude agent's tokens are null and never 0. Rows are ranked by MEASURED WALL TIME. Only `orc extra` foreign workers report real token vectors.",
33597
+ };
33598
+ if (asJson) emitJson(out, code);
33599
+
33600
+ console.log(ui.header("ORC · usage"));
33601
+ console.log("");
33602
+ const pctLine = (w) =>
33603
+ !w
33604
+ ? ui.color.gray(" — (no reading)")
33605
+ : " " +
33606
+ String(w.used_percentage).padStart(3) +
33607
+ "% used · " +
33608
+ w.remaining_percentage +
33609
+ "% left" +
33610
+ (w.resets_in_minutes != null ? " · resets in " + w.resets_in_minutes + "m" : "") +
33611
+ (w.low ? " " + ui.color.yellow("LOW") : "");
33612
+ console.log(" 5 hours");
33613
+ console.log(pctLine(fh));
33614
+ console.log(" 7 days");
33615
+ console.log(pctLine(sd));
33616
+ if (out.context_used_percentage != null)
33617
+ console.log(" context " + out.context_used_percentage + "% of the window");
33618
+ if (stale)
33619
+ console.log(
33620
+ ui.color.gray(
33621
+ " reading " +
33622
+ (ageMin == null ? "none" : ageMin + "m old") +
33623
+ " — treated as unknown, and a run is never stopped on it"
33624
+ )
33625
+ );
33626
+ console.log("");
33627
+ if (session && session.five_hour) {
33628
+ const c = session.five_hour;
33629
+ console.log(
33630
+ " " +
33631
+ ui.color.bold(
33632
+ "This session has consumed " +
33633
+ c.consumed_percentage +
33634
+ "% of the 5-hour window and is still counting"
33635
+ )
33636
+ );
33637
+ console.log(
33638
+ ui.color.gray(
33639
+ " " +
33640
+ c.baseline_percentage +
33641
+ "% → " +
33642
+ c.now_percentage +
33643
+ "%" +
33644
+ (c.window_resets ? " across " + c.window_resets + " window reset(s)" : "") +
33645
+ (session.running_minutes != null ? " · " + session.running_minutes + "m in session" : "")
33646
+ )
33647
+ );
33648
+ console.log(ui.color.gray(" " + session.caveat));
33649
+ } else {
33650
+ console.log(ui.color.gray(" session consumption: no reading yet (the statusline writes it)"));
33651
+ }
33652
+ console.log("");
33653
+ if (top.length) {
33654
+ console.log(" " + ui.color.bold("Top " + USAGE_TOP_N + " by measured wall time"));
33655
+ for (const t of top.slice(0, USAGE_TOP_N)) {
33656
+ const secs = t.wall_seconds;
33657
+ const w =
33658
+ secs == null
33659
+ ? "—"
33660
+ : Math.floor(secs / 60) + "m" + String(secs % 60).padStart(2, "0") + "s";
33661
+ const tok = t.tokens ? "tokens measured" : ui.color.gray("tokens —");
33662
+ console.log(
33663
+ " " +
33664
+ w.padStart(8) +
33665
+ " " +
33666
+ t.agent +
33667
+ " " +
33668
+ ui.color.gray("x" + t.dispatches) +
33669
+ (t.running ? " " + ui.color.yellow(t.running + " RUNNING") : "") +
33670
+ " " +
33671
+ tok
33672
+ );
33673
+ }
33674
+ console.log(
33675
+ ui.color.gray(" wall time, not tokens — Claude Code does not record a subagent's tokens")
33676
+ );
33677
+ } else {
33678
+ console.log(ui.color.gray(" no dispatches in the open run (or no run is open)"));
33679
+ }
33680
+ process.exit(code);
33681
+ }
33682
+
33213
33683
  function usageCheckCmd(claudeDir) {
33214
33684
  const asJson = wantsJson();
33215
33685
  const now = Date.now();
@@ -33641,8 +34111,9 @@ function jsonCrash(err) {
33641
34111
  // v1.1.0 W4 — the ONE reader of the statusline's usage bridge.
33642
34112
  case "usage":
33643
34113
  if (positionals()[1] === "check") usageCheckCmd(resolveClaudeDir());
34114
+ else if (positionals()[1] === "report") usageReportCmd(resolveClaudeDir());
33644
34115
  else {
33645
- console.error("usage: orc usage check [--json]");
34116
+ console.error("usage: orc usage check|report [--json]");
33646
34117
  process.exit(1);
33647
34118
  }
33648
34119
  break;
@@ -384,6 +384,43 @@ const CONTRACTS = [
384
384
  binFiles: ["bin/cli.js"],
385
385
  files: ["skills/_shared/extra-dispatch.md"],
386
386
  },
387
+ {
388
+ // v1.2.0. A Task error does not kill the subagent behind it. Every lane's
389
+ // retry rule assumed it did, and one graded /orc-quick entry put THREE
390
+ // opus-5-low executors on one task for 266 minutes combined. The guard is
391
+ // only real if every lane that re-dispatches carries the same sentence and
392
+ // the CLI keeps the command it names.
393
+ name: "a re-dispatch is refused over a live attempt (v1.2.0)",
394
+ token: "a lane that re-dispatches over a live attempt",
395
+ binFiles: ["bin/cli.js"],
396
+ files: [
397
+ "skills/_shared/return-validation.md",
398
+ "skills/_shared/phases/execution.md",
399
+ "skills/orc/SKILL.md",
400
+ "skills/orc-doc/SKILL.md",
401
+ "skills/orc-wiki/SKILL.md",
402
+ "skills/orc-quick/SKILL.md",
403
+ "skills/orc-mini/SKILL.md",
404
+ "skills/orc-fast/SKILL.md",
405
+ ],
406
+ },
407
+ {
408
+ // The command the contract above names. A rename on either side silently
409
+ // turns the guard into prose nobody can run.
410
+ name: "the in-flight read is ONE command (v1.2.0)",
411
+ token: "orc run inflight",
412
+ binFiles: ["bin/cli.js"],
413
+ files: [
414
+ "skills/_shared/return-validation.md",
415
+ "skills/_shared/phases/execution.md",
416
+ "skills/orc/SKILL.md",
417
+ "skills/orc-doc/SKILL.md",
418
+ "skills/orc-wiki/SKILL.md",
419
+ "skills/orc-quick/SKILL.md",
420
+ "skills/orc-mini/SKILL.md",
421
+ "skills/orc-fast/SKILL.md",
422
+ ],
423
+ },
387
424
  {
388
425
  // The field a resumed return owes. Absent on a resume slice is MALFORMED,
389
426
  // and `restarted` on a non-empty preexisting[] is a finding — both are
@@ -3343,7 +3380,7 @@ const BUDGETS = [
3343
3380
  // 4 of 31 lanes. The shrinking in this release happens by moving prose to
3344
3381
  // `_shared/phases/` (W11–W13) or to a lane's own `references/phases/`, which
3345
3382
  // is what orc-wiki did this wave: 352 → 172.
3346
- { file: "skills/orc-mini/SKILL.md", maxLines: 250 },
3383
+ { file: "skills/orc-mini/SKILL.md", maxLines: 253 },
3347
3384
  // v0.39.0: deliberate raises 195→201 / 179→182 — the analyst gains hard rules
3348
3385
  // 2b (a source it did not author is FOREIGN input) and 4a (the read ladder);
3349
3386
  // fast gains the ladder as a slice line. Both are hard rules by nature: they
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-id/orc",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "ORC — an orchestrator skill constellation for Claude Code: intake, planning, scored parallel subagents, code-pattern matching, review, verify, ship, plus a project knowledge-base wiki.",
5
5
  "bin": {
6
6
  "orc": "bin/cli.js"