@azure-id/orc 2.1.0 → 2.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/CHANGELOG.md +374 -0
- package/README.md +49 -36
- package/bin/build-agents.js +7 -5
- package/bin/cli.js +1006 -235
- package/bin/fix.js +21 -18
- package/bin/graph-extract.js +314 -6
- package/bin/graph-gain.js +83 -2
- package/bin/graph-map.js +23 -1
- package/bin/graph-notes.js +1 -1
- package/bin/graph-query.js +1362 -37
- package/bin/graph-resolve.js +1 -1
- package/bin/graph-shard.js +29 -6
- package/bin/graph-signals.js +5 -3
- package/bin/graph.js +7 -3
- package/bin/habit.js +1 -1
- package/bin/pricing.json +8 -1
- package/bin/trace-write.js +40 -11
- package/bin/verify-contracts.js +169 -60
- package/bin/verify-package.js +3 -3
- package/bin/webui/api.js +8 -0
- package/bin/webui/fixtures/extra.js +9 -9
- package/bin/webui/fixtures/flow.js +1 -1
- package/bin/webui/fixtures/index.js +2 -2
- package/bin/webui/fixtures/knowledge.js +8 -1
- package/bin/webui/fixtures/pact.js +112 -111
- package/bin/webui/fixtures/stats.js +2 -2
- package/bin/webui/i18n/en/knowledge.json +2 -0
- package/bin/webui/i18n/id/knowledge.json +2 -0
- package/bin/webui/js/panels/knowledge.js +17 -0
- package/guides/code-graph-benefit.md +116 -0
- package/guides/configuration.md +150 -0
- package/guides/documents.md +262 -0
- package/guides/extra-models.md +448 -0
- package/guides/habits-and-gotchas.md +152 -0
- package/guides/knowledge-reads.md +190 -0
- package/guides/model-selection.md +186 -0
- package/guides/rules.md +305 -0
- package/guides/status-line.md +344 -0
- package/mock-run/a-normal-day.md +2 -2
- package/mock-run/extra-slots.md +177 -177
- package/mock-run/orc-cli.md +3 -2
- package/mock-run/orc-doc.md +452 -448
- package/mock-run/orc-fast.md +2 -2
- package/mock-run/orc-quick.md +146 -146
- package/mock-run/orc.md +4 -4
- package/package.json +2 -1
- package/templates/agents/MODEL-MAPPING.md +12 -12
- package/templates/agents/{orc-executor-haiku-4-5.md → orc-executor-haiku-5-high.md} +21 -8
- package/templates/agents/orc-executor-opus-4-7-high.md +17 -5
- package/templates/agents/orc-executor-opus-4-7-med.md +17 -5
- package/templates/agents/orc-executor-opus-4-8-high.md +17 -5
- package/templates/agents/orc-executor-opus-5-high.md +17 -5
- package/templates/agents/orc-executor-opus-5-low.md +17 -5
- package/templates/agents/orc-executor-opus-5-med.md +17 -5
- package/templates/agents/orc-executor-sonnet-4-6-high.md +17 -5
- package/templates/agents/orc-executor-sonnet-5-high.md +17 -5
- package/templates/agents/orc-executor-sonnet-5-low.md +17 -5
- package/templates/agents/orc-executor-sonnet-5-med.md +17 -5
- package/templates/agents/{orc-graph-noter-sonnet-4-6-med.md → orc-graph-noter-haiku-5-high.md} +4 -4
- package/templates/agents/orc-planner-mini-opus-5-med.md +3 -1
- package/templates/agents/orc-planner-mini-sonnet-5-high.md +3 -1
- package/templates/agents/orc-planner-opus-5-med.md +3 -1
- package/templates/agents/{orc-recon-sonnet-4-6-med.md → orc-recon-sonnet-5-med.md} +4 -4
- package/templates/agents/{orc-trace-writer-haiku-4-5.md → orc-trace-writer-haiku-5-high.md} +4 -3
- package/templates/commands/orc-doc.md +128 -128
- package/templates/commands/orc-export.md +51 -46
- package/templates/commands/orc-fast.md +10 -10
- package/templates/hooks/README.md +8 -3
- package/templates/hooks/orc-effort-guard.js +105 -27
- package/templates/hooks/orc-graph-hook.js +252 -4
- package/templates/hooks/orc-statusline.js +13 -6
- package/templates/hooks/orc-trace.js +1 -1
- package/templates/skills/_shared/README.md +2 -2
- package/templates/skills/_shared/code-graph.md +185 -251
- package/templates/skills/_shared/extra-dispatch.md +4 -4
- package/templates/skills/_shared/lane-contract.md +7 -3
- package/templates/skills/_shared/opus5-only.md +3 -3
- package/templates/skills/_shared/phases/execution.md +13 -11
- package/templates/skills/_shared/phases/planning.md +27 -9
- package/templates/skills/_shared/phases/preflight.md +3 -3
- package/templates/skills/_shared/phases/review.md +4 -3
- package/templates/skills/_shared/phases/ship.md +7 -10
- package/templates/skills/_shared/phases/summary.md +2 -3
- package/templates/skills/_shared/phases/trace-verbs.md +21 -16
- package/templates/skills/_shared/phases/trace.md +3 -3
- package/templates/skills/_shared/phases/wiki-consult.md +2 -2
- package/templates/skills/_shared/pr-templates.md +108 -106
- package/templates/skills/_shared/read-ladder.md +8 -2
- package/templates/skills/_shared/return-validation.md +10 -6
- package/templates/skills/_shared/review-slice.md +1 -1
- package/templates/skills/_shared/stack-plan.md +135 -135
- package/templates/skills/_shared/wait.md +14 -2
- package/templates/skills/context-combiner/SKILL.md +2 -2
- package/templates/skills/orc/SKILL.md +7 -7
- package/templates/skills/orc/config.md +2 -2
- package/templates/skills/orc/examples/full-run-mock.md +7 -7
- package/templates/skills/orc/references/effort-and-mode.md +222 -222
- package/templates/skills/orc/schemas/planning-output.md +3 -0
- package/templates/skills/orc-aftermath/SKILL.md +15 -15
- package/templates/skills/orc-analyze/SKILL.md +2 -2
- package/templates/skills/orc-analyze-mini/SKILL.md +2 -2
- package/templates/skills/orc-boundary/SKILL.md +15 -15
- package/templates/skills/orc-boundary/references/card.md +81 -78
- package/templates/skills/orc-boundary/references/gate.md +113 -113
- package/templates/skills/orc-brainstorm/SKILL.md +15 -15
- package/templates/skills/orc-budget/SKILL.md +15 -15
- package/templates/skills/orc-challenge/README.md +1 -1
- package/templates/skills/orc-challenge/SKILL.md +16 -16
- package/templates/skills/orc-challenge/examples/council-full-roster.md +1 -1
- package/templates/skills/orc-claude/SKILL.md +4 -4
- package/templates/skills/orc-claude/examples/claude-run-mock.md +1 -1
- package/templates/skills/orc-diy/SKILL.md +2 -2
- package/templates/skills/orc-diy/references/blocks/wiki.md +26 -26
- package/templates/skills/orc-diy/references/flow-schema.md +2 -2
- package/templates/skills/orc-doc/README.md +2 -2
- package/templates/skills/orc-doc/SKILL.md +501 -490
- package/templates/skills/orc-doc/examples/orc-doc-prd-run.md +12 -1
- package/templates/skills/orc-doc/references/chunking.md +6 -4
- package/templates/skills/orc-doc/references/gates.md +311 -311
- package/templates/skills/orc-doc/references/resume-protocol.md +229 -228
- package/templates/skills/orc-explain/SKILL.md +88 -88
- package/templates/skills/orc-export/SKILL.md +190 -187
- package/templates/skills/orc-fast/SKILL.md +17 -14
- package/templates/skills/orc-grill/SKILL.md +16 -16
- package/templates/skills/orc-grill/references/grill-doc.md +1 -1
- package/templates/skills/orc-handoff/SKILL.md +11 -11
- package/templates/skills/orc-learn/SKILL.md +13 -13
- package/templates/skills/orc-learn/examples/learn-run-mock.md +2 -2
- package/templates/skills/orc-mini/SKILL.md +12 -12
- package/templates/skills/orc-mini/examples/mini-run-mock.md +4 -5
- package/templates/skills/orc-pact/SKILL.md +15 -15
- package/templates/skills/orc-pact/references/gate.md +70 -70
- package/templates/skills/orc-pattern/SKILL.md +3 -3
- package/templates/skills/orc-poly/SKILL.md +4 -4
- package/templates/skills/orc-poly/examples/poly-run-mock.md +51 -51
- package/templates/skills/orc-pr-driver/SKILL.md +4 -3
- package/templates/skills/orc-pr-driver/references/orc-run-split.md +108 -99
- package/templates/skills/orc-pr-setup/SKILL.md +2 -2
- package/templates/skills/orc-quick/README.md +11 -12
- package/templates/skills/orc-quick/SKILL.md +29 -32
- package/templates/skills/orc-quick/references/context-doc.md +5 -5
- package/templates/skills/orc-quick/references/dispatch-gate.md +10 -10
- package/templates/skills/orc-quick/references/gh-mode.md +2 -1
- package/templates/skills/orc-quick/references/look.md +34 -12
- package/templates/skills/orc-retro/SKILL.md +2 -2
- package/templates/skills/orc-retro/examples/retro-mock.md +2 -2
- package/templates/skills/orc-route/SKILL.md +2 -2
- package/templates/skills/orc-test/SKILL.md +6 -3
- package/templates/skills/orc-verify/SKILL.md +2 -2
- package/templates/skills/orc-wait/SKILL.md +10 -2
- package/templates/skills/orc-wiki/SKILL.md +4 -4
- package/templates/skills/orc-wiki/references/pattern-prewarm.md +19 -19
- package/templates/agents/orc-executor-sonnet-4-6-med.md +0 -155
package/bin/webui/api.js
CHANGED
|
@@ -1294,6 +1294,14 @@ async function handleApi(req, res, url, ctx) {
|
|
|
1294
1294
|
const build = READS[route];
|
|
1295
1295
|
if (!build) return json(res, 404, { error: "unknown endpoint " + route });
|
|
1296
1296
|
const out = readCli(build(q), ctx);
|
|
1297
|
+
// v2.2.0 — `graph status` without --if-enabled answers the INDEX state, so
|
|
1298
|
+
// a `semi` project looks like `on`. The configured mode is passed through
|
|
1299
|
+
// from `orc config get` so the panel can show the SEMI badge.
|
|
1300
|
+
if (route === "/api/graph" && out.ok && out.data && !out.data.mode) {
|
|
1301
|
+
const cfgRes = readCli(["config", "get", "code_graph"], ctx);
|
|
1302
|
+
const row = cfgRes.data && Array.isArray(cfgRes.data.keys) ? cfgRes.data.keys.find((k) => k.key === "code_graph") : null;
|
|
1303
|
+
if (row) out.data.mode = String(row.value).toLowerCase();
|
|
1304
|
+
}
|
|
1297
1305
|
// v0.49.2 — a read that produced no parseable object still has to SAY why.
|
|
1298
1306
|
// The body already carried `stderr` and `stdout`; nothing named `error`, so
|
|
1299
1307
|
// the client fell through to "request failed (500)" and one corrupt ledger
|
|
@@ -838,10 +838,10 @@ const extraRole = {
|
|
|
838
838
|
verify_state: "VERIFIED", verify_age_days: 1, model_known: false,
|
|
839
839
|
meaning: "the user picks the agent for every entry anyway, so a foreign worker is one more option on a menu they already read.",
|
|
840
840
|
asks: true, announce_point: "the dispatch gate — offered as a third option, never a default",
|
|
841
|
-
claude: { via: "claude", agent: "orc-executor-sonnet-
|
|
841
|
+
claude: { via: "claude", agent: "orc-executor-sonnet-5-med", agents: ["orc-executor-sonnet-5-med", "orc-executor-opus-5-low"], table: "shipped" },
|
|
842
842
|
resolved: "extra", held_back: null,
|
|
843
|
-
why: "slot row quick-executor holds this position and outranks orc-executor-sonnet-
|
|
844
|
-
announce: "quick-executor (/orc-quick) → deepseek/deepseek-coder via api (profile cheap) — displaces orc-executor-sonnet-
|
|
843
|
+
why: "slot row quick-executor holds this position and outranks orc-executor-sonnet-5-med.",
|
|
844
|
+
announce: "quick-executor (/orc-quick) → deepseek/deepseek-coder via api (profile cheap) — displaces orc-executor-sonnet-5-med; this sends the slice to a third party.",
|
|
845
845
|
next: null,
|
|
846
846
|
},
|
|
847
847
|
{
|
|
@@ -851,10 +851,10 @@ const extraRole = {
|
|
|
851
851
|
verify_state: "STALE", verify_age_days: 15, model_known: true,
|
|
852
852
|
meaning: "one executor, one slice, a build+test smoke gate behind it — the checks that catch a bad implementation here are engine-blind.",
|
|
853
853
|
asks: false, announce_point: "the F0 preflight `extra:` line, before wave 1",
|
|
854
|
-
claude: { via: "claude", agent: "orc-executor-sonnet-
|
|
854
|
+
claude: { via: "claude", agent: "orc-executor-sonnet-5-med", agents: ["orc-executor-sonnet-5-med"], table: "shipped" },
|
|
855
855
|
resolved: "extra", held_back: null,
|
|
856
|
-
why: "slot row fast-executor holds this position and outranks orc-executor-sonnet-
|
|
857
|
-
announce: "fast-executor (/orc-fast) → zai/glm-4.6 via claude-shim (profile glm) — displaces orc-executor-sonnet-
|
|
856
|
+
why: "slot row fast-executor holds this position and outranks orc-executor-sonnet-5-med; verification is 15d old (STALE — still routes, re-pinged before the wave).",
|
|
857
|
+
announce: "fast-executor (/orc-fast) → zai/glm-4.6 via claude-shim (profile glm) — displaces orc-executor-sonnet-5-med; this sends the slice to a third party.",
|
|
858
858
|
next: null,
|
|
859
859
|
},
|
|
860
860
|
{
|
|
@@ -876,7 +876,7 @@ const extraRole = {
|
|
|
876
876
|
slot: "doc-checker", lane: "/orc-doc", routed: false,
|
|
877
877
|
profile: null, model: null, small_model: null, max_turns: null, added_at: null,
|
|
878
878
|
provider: null, engine: null, verify_state: null, verify_age_days: null, model_known: null,
|
|
879
|
-
meaning: "the checker reads
|
|
879
|
+
meaning: "the checker reads only the bounded part files of one slice and reports; it rewrites nothing, so a finding it makes is a finding you read.",
|
|
880
880
|
asks: false, announce_point: "before the wave",
|
|
881
881
|
claude: { via: "claude", agent: "orc-doc-checker-opus-5-low", agents: ["orc-doc-checker-opus-5-low"], table: "shipped" },
|
|
882
882
|
resolved: "claude", held_back: null,
|
|
@@ -962,7 +962,7 @@ const extraLanes = {
|
|
|
962
962
|
{
|
|
963
963
|
lane: "/orc-fast", shape: "slot", agent: null, routes: "roles",
|
|
964
964
|
slots: [
|
|
965
|
-
{ slot: "fast-executor", routes: true, profile: "glm", model: "glm-4.6", claude: "orc-executor-sonnet-
|
|
965
|
+
{ slot: "fast-executor", routes: true, profile: "glm", model: "glm-4.6", claude: "orc-executor-sonnet-5-med", held_back: null, why: "slot row fast-executor holds this position and outranks orc-executor-sonnet-5-med." },
|
|
966
966
|
],
|
|
967
967
|
detail: "fast-executor is held by a non-Claude worker; every position with no row stays on its pinned Claude agent.",
|
|
968
968
|
},
|
|
@@ -976,7 +976,7 @@ const extraLanes = {
|
|
|
976
976
|
// user gets to make, per entry.
|
|
977
977
|
lane: "/orc-quick", shape: "gated-choice", agent: null, routes: "offered",
|
|
978
978
|
slots: [
|
|
979
|
-
{ slot: "quick-executor", routes: true, profile: "cheap", model: "deepseek-chat", claude: "orc-executor-sonnet-
|
|
979
|
+
{ slot: "quick-executor", routes: true, profile: "cheap", model: "deepseek-chat", claude: "orc-executor-sonnet-5-med", held_back: null, why: "slot row quick-executor holds this position and outranks orc-executor-haiku-5-high." },
|
|
980
980
|
],
|
|
981
981
|
detail: "quick-executor is OFFERED as one more option at the dispatch gate. It never becomes a default and never sticks — this lane asks before every dispatch.",
|
|
982
982
|
},
|
|
@@ -31,7 +31,7 @@ const diy = {
|
|
|
31
31
|
{ key: "planning", value: "own-planner", default: "auto", is_set: true, desc: "Planning route.", options: ["auto", "own-planner", "superpowers", "openspec"] },
|
|
32
32
|
{ key: "pattern", value: "ask", default: "ask", is_set: false, desc: "Code-pattern gate on a cache miss.", options: ["ask", "off", "on"] },
|
|
33
33
|
{ key: "scoring", value: "on", default: "on", is_set: false, desc: "Rubric scoring.", options: ["on", "off"] },
|
|
34
|
-
{ key: "fixed_executor", value: "", default: "", is_set: false, desc: "Executor used for every task when scoring is off.", options: ["orc-executor-haiku-
|
|
34
|
+
{ key: "fixed_executor", value: "", default: "", is_set: false, desc: "Executor used for every task when scoring is off.", options: ["orc-executor-haiku-5-high", "orc-executor-sonnet-5-high", "orc-executor-opus-4-8-high", "orc-executor-opus-5-high"] },
|
|
35
35
|
{ key: "review", value: "blocking-only", default: "full", is_set: true, desc: "Review phase depth.", options: ["on", "off", "blocking-only"] },
|
|
36
36
|
{ key: "security", value: "off", default: "off", is_set: false, desc: "Security pass.", options: ["off", "ask", "on", "always"] },
|
|
37
37
|
{ key: "verify", value: "smoke", default: "full", is_set: true, desc: "Verification depth.", options: ["full", "off", "smoke"] },
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// case here; it is never an edit to a 1 700-line module.
|
|
22
22
|
const { PROJECT, doctor, where } = require("./shell.js");
|
|
23
23
|
const { config } = require("./settings.js");
|
|
24
|
-
const { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph, graphGain, graphGainMeasured } = require("./knowledge.js");
|
|
24
|
+
const { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph, graphSemi, graphGain, graphGainMeasured } = require("./knowledge.js");
|
|
25
25
|
const { runs, runDetail, runDetailClosed, aftermath } = require("./runs.js");
|
|
26
26
|
const { stats, budgetForecast, budgetRates } = require("./stats.js");
|
|
27
27
|
const { pact } = require("./pact.js");
|
|
@@ -309,7 +309,7 @@ module.exports.get = function get(route, q) {
|
|
|
309
309
|
return wikiImpact;
|
|
310
310
|
// v1.8.0 — the code graph, DRIFTED (the state the card must be designed for).
|
|
311
311
|
case "/api/graph":
|
|
312
|
-
return graph;
|
|
312
|
+
return q && q.mode === "semi" ? graphSemi : graph;
|
|
313
313
|
// v1.8.2 W4b — the gain meter, and the A/B behind its second button.
|
|
314
314
|
case "/api/graph/gain":
|
|
315
315
|
return graphGain;
|
|
@@ -309,6 +309,8 @@ const graph = {
|
|
|
309
309
|
generation: 42,
|
|
310
310
|
gen_id: "c878981d",
|
|
311
311
|
state: "drifted",
|
|
312
|
+
// v2.2.0 — the configured `code_graph`, added by the API (not by the CLI).
|
|
313
|
+
mode: "on",
|
|
312
314
|
behind: { added: 1, changed: 3, deleted: 0 },
|
|
313
315
|
engine_stale: false,
|
|
314
316
|
auto_update: true,
|
|
@@ -334,6 +336,11 @@ const graph = {
|
|
|
334
336
|
|
|
335
337
|
// v1.8.2 W4b — `orc graph gain --json`. The fixture carries the ESTIMATE word
|
|
336
338
|
// and a RANGE, because those are the two things the card must never lose.
|
|
339
|
+
// v2.2.0 — the same index under `code_graph: semi`. `graph status` (no
|
|
340
|
+
// --if-enabled) answers the index state; the API adds `mode` from the config.
|
|
341
|
+
// Served on /api/graph?mode=semi so the default tab stays DRIFTED.
|
|
342
|
+
const graphSemi = { ...graph, state: "fresh", behind: { added: 0, changed: 0, deleted: 0 }, mode: "semi" };
|
|
343
|
+
|
|
337
344
|
const graphGain = {
|
|
338
345
|
ok: true,
|
|
339
346
|
state: "rows",
|
|
@@ -376,4 +383,4 @@ const graphGainMeasured = {
|
|
|
376
383
|
"executor windows and session totals are kept apart: the main session is 74–80% of a run and the graph never touches it. Every delta is printed beside the OFF group's own spread — a delta smaller than that spread is noise, not a result.",
|
|
377
384
|
};
|
|
378
385
|
|
|
379
|
-
module.exports = { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph, graphGain, graphGainMeasured };
|
|
386
|
+
module.exports = { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph, graphSemi, graphGain, graphGainMeasured };
|
|
@@ -1,111 +1,112 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* fixtures/pact.js — canned data for `orc ui --fixtures`.
|
|
3
|
-
The invariant ledger, carrying one of each state — BROKEN, DRIFTED,
|
|
4
|
-
UNCHECKABLE, HOLDING.
|
|
5
|
-
|
|
6
|
-
THE RULE FOR EVERY FILE IN HERE: carry ONE OF EVERY STATE, including the
|
|
7
|
-
ugly ones. You cannot DESIGN a STALE chip on a fresh wiki, and a state
|
|
8
|
-
with no fixture is a state nobody has ever looked at. A per-state count
|
|
9
|
-
test asserts this, so a new state cannot ship without one.
|
|
10
|
-
|
|
11
|
-
Shapes MUST match what `bin/cli.js --json` really emits — a drifted
|
|
12
|
-
fixture is worse than no fixture. */
|
|
13
|
-
|
|
14
|
-
const { PROJECT } = require("./shell.js");
|
|
15
|
-
|
|
16
|
-
const pact = {
|
|
17
|
-
ok: true,
|
|
18
|
-
ledger: PROJECT + "/.claude/orc/pact/ledger.json",
|
|
19
|
-
doc: PROJECT + "/PACT.md",
|
|
20
|
-
doc_exists: true,
|
|
21
|
-
entries: 5,
|
|
22
|
-
retired: 1,
|
|
23
|
-
counts: { HOLDING: 2, DRIFTED: 1, UNCHECKABLE: 1, BROKEN: 1 },
|
|
24
|
-
line: "pact: 2 holding · 1 drifted · 1 uncheckable · 1 BROKEN",
|
|
25
|
-
rows: [
|
|
26
|
-
{
|
|
27
|
-
id: "PACT-014",
|
|
28
|
-
statement: "A payment is never written to the ledger twice for one idempotency key.",
|
|
29
|
-
origin: { lane: "orc-grill", run: "run-grill-checkout-100826-141130", kind: "constraint" },
|
|
30
|
-
anchors: ["src/payments/ledger.ts:88", "src/payments/idempotency.ts"],
|
|
31
|
-
check: { kind: "test", ref: "npm test -- idempotency" },
|
|
32
|
-
verified_commit: "8a62b4f1c9",
|
|
33
|
-
confidence: "high",
|
|
34
|
-
last_check: { status: "fail", commit: "8a62b4f1c9", at: "09-08-2026 11:20:04", ref: "npm test -- idempotency" },
|
|
35
|
-
// A real-looking failure, not a placeholder: the panel has to lay out a
|
|
36
|
-
// multi-line check output that does not fit its card.
|
|
37
|
-
history: [
|
|
38
|
-
{ at: "09-08-2026 11:20:04", status: "fail", commit: "8a62b4f1c9" },
|
|
39
|
-
{ at: "02-08-2026 09:14:51", status: "pass", commit: "c273793aa1" },
|
|
40
|
-
],
|
|
41
|
-
retired: false,
|
|
42
|
-
state: "BROKEN",
|
|
43
|
-
why: "check failed at 8a62b4f1 (09-08-2026 11:20:04)",
|
|
44
|
-
distance: 3,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: "PACT-002",
|
|
48
|
-
statement: "Refund windows are configured, never hardcoded.",
|
|
49
|
-
origin: { lane: "orc-brainstorm", run: "run-brainstorm-refunds-010826-101010", kind: "constraint" },
|
|
50
|
-
anchors: ["src/billing/refund.ts"],
|
|
51
|
-
check: { kind: "grep", ref: "REFUND_WINDOW_DAYS" },
|
|
52
|
-
verified_commit: "c273793aa1",
|
|
53
|
-
confidence: "medium",
|
|
54
|
-
last_check: { status: "pass", commit: "c273793aa1", at: "01-08-2026 10:11:02", ref: "REFUND_WINDOW_DAYS" },
|
|
55
|
-
history: [{ at: "01-08-2026 10:11:02", status: "pass", commit: "c273793aa1" }],
|
|
56
|
-
retired: false,
|
|
57
|
-
state: "DRIFTED",
|
|
58
|
-
why: "6 commits since c273793a touched 1 anchored file",
|
|
59
|
-
distance: 6,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
/* fixtures/pact.js — canned data for `orc ui --fixtures`.
|
|
3
|
+
The invariant ledger, carrying one of each state — BROKEN, DRIFTED,
|
|
4
|
+
UNCHECKABLE, HOLDING.
|
|
5
|
+
|
|
6
|
+
THE RULE FOR EVERY FILE IN HERE: carry ONE OF EVERY STATE, including the
|
|
7
|
+
ugly ones. You cannot DESIGN a STALE chip on a fresh wiki, and a state
|
|
8
|
+
with no fixture is a state nobody has ever looked at. A per-state count
|
|
9
|
+
test asserts this, so a new state cannot ship without one.
|
|
10
|
+
|
|
11
|
+
Shapes MUST match what `bin/cli.js --json` really emits — a drifted
|
|
12
|
+
fixture is worse than no fixture. */
|
|
13
|
+
|
|
14
|
+
const { PROJECT } = require("./shell.js");
|
|
15
|
+
|
|
16
|
+
const pact = {
|
|
17
|
+
ok: true,
|
|
18
|
+
ledger: PROJECT + "/.claude/orc/pact/ledger.json",
|
|
19
|
+
doc: PROJECT + "/PACT.md",
|
|
20
|
+
doc_exists: true,
|
|
21
|
+
entries: 5,
|
|
22
|
+
retired: 1,
|
|
23
|
+
counts: { HOLDING: 2, DRIFTED: 1, UNCHECKABLE: 1, BROKEN: 1 },
|
|
24
|
+
line: "pact: 2 holding · 1 drifted · 1 uncheckable · 1 BROKEN",
|
|
25
|
+
rows: [
|
|
26
|
+
{
|
|
27
|
+
id: "PACT-014",
|
|
28
|
+
statement: "A payment is never written to the ledger twice for one idempotency key.",
|
|
29
|
+
origin: { lane: "orc-grill", run: "run-grill-checkout-100826-141130", kind: "constraint" },
|
|
30
|
+
anchors: ["src/payments/ledger.ts:88", "src/payments/idempotency.ts"],
|
|
31
|
+
check: { kind: "test", ref: "npm test -- idempotency" },
|
|
32
|
+
verified_commit: "8a62b4f1c9",
|
|
33
|
+
confidence: "high",
|
|
34
|
+
last_check: { status: "fail", commit: "8a62b4f1c9", at: "09-08-2026 11:20:04", ref: "npm test -- idempotency" },
|
|
35
|
+
// A real-looking failure, not a placeholder: the panel has to lay out a
|
|
36
|
+
// multi-line check output that does not fit its card.
|
|
37
|
+
history: [
|
|
38
|
+
{ at: "09-08-2026 11:20:04", status: "fail", commit: "8a62b4f1c9" },
|
|
39
|
+
{ at: "02-08-2026 09:14:51", status: "pass", commit: "c273793aa1" },
|
|
40
|
+
],
|
|
41
|
+
retired: false,
|
|
42
|
+
state: "BROKEN",
|
|
43
|
+
why: "check failed at 8a62b4f1 (09-08-2026 11:20:04)",
|
|
44
|
+
distance: 3,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "PACT-002",
|
|
48
|
+
statement: "Refund windows are configured, never hardcoded.",
|
|
49
|
+
origin: { lane: "orc-brainstorm", run: "run-brainstorm-refunds-010826-101010", kind: "constraint" },
|
|
50
|
+
anchors: ["src/billing/refund.ts"],
|
|
51
|
+
check: { kind: "grep", ref: "REFUND_WINDOW_DAYS" },
|
|
52
|
+
verified_commit: "c273793aa1",
|
|
53
|
+
confidence: "medium",
|
|
54
|
+
last_check: { status: "pass", commit: "c273793aa1", at: "01-08-2026 10:11:02", ref: "REFUND_WINDOW_DAYS" },
|
|
55
|
+
history: [{ at: "01-08-2026 10:11:02", status: "pass", commit: "c273793aa1" }],
|
|
56
|
+
retired: false,
|
|
57
|
+
state: "DRIFTED",
|
|
58
|
+
why: "6 commits since c273793a touched 1 of 1 anchored file (src/billing/refund.ts)",
|
|
59
|
+
distance: 6,
|
|
60
|
+
touched_files: ["src/billing/refund.ts"],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "PACT-007",
|
|
64
|
+
statement: "The admin export never contains a raw email address.",
|
|
65
|
+
origin: { lane: "user", run: null, kind: "constraint" },
|
|
66
|
+
anchors: ["src/admin/export.ts"],
|
|
67
|
+
check: { kind: "manual", ref: null },
|
|
68
|
+
verified_commit: "783f6971aa",
|
|
69
|
+
confidence: "low",
|
|
70
|
+
last_check: null,
|
|
71
|
+
history: [],
|
|
72
|
+
retired: false,
|
|
73
|
+
state: "UNCHECKABLE",
|
|
74
|
+
// The long day count: an UNCHECKABLE promise nobody has looked at for
|
|
75
|
+
// months is the case this state exists to make visible.
|
|
76
|
+
why: "no cheap check exists — this promise is held by review, not by a runner",
|
|
77
|
+
distance: null,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "PACT-001",
|
|
81
|
+
statement: "Every outbound webhook is signed with the tenant's current secret.",
|
|
82
|
+
origin: { lane: "orc", run: "run-orc-webhooks-120726-084500", kind: "constraint" },
|
|
83
|
+
anchors: ["src/webhooks/sign.ts:40"],
|
|
84
|
+
check: { kind: "command", ref: "npm run test:webhooks" },
|
|
85
|
+
verified_commit: "e9dad01bb2",
|
|
86
|
+
confidence: "high",
|
|
87
|
+
last_check: { status: "pass", commit: "e9dad01bb2", at: "05-08-2026 16:02:11", ref: "npm run test:webhooks" },
|
|
88
|
+
history: [{ at: "05-08-2026 16:02:11", status: "pass", commit: "e9dad01bb2" }],
|
|
89
|
+
retired: false,
|
|
90
|
+
state: "HOLDING",
|
|
91
|
+
why: "verified at e9dad01b; no commit since has touched its anchors",
|
|
92
|
+
distance: 0,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "PACT-009",
|
|
96
|
+
statement: "Session cookies are always SameSite=Lax.",
|
|
97
|
+
origin: { lane: "orc-grill", run: "run-grill-auth-200626-113000", kind: "constraint" },
|
|
98
|
+
anchors: ["src/auth/session.ts"],
|
|
99
|
+
check: { kind: "grep", ref: "SameSite=Lax" },
|
|
100
|
+
verified_commit: "cc4778e0aa",
|
|
101
|
+
confidence: "high",
|
|
102
|
+
last_check: { status: "pass", commit: "cc4778e0aa", at: "21-06-2026 09:00:00", ref: "SameSite=Lax" },
|
|
103
|
+
history: [{ at: "21-06-2026 09:00:00", status: "pass", commit: "cc4778e0aa" }],
|
|
104
|
+
retired: false,
|
|
105
|
+
state: "HOLDING",
|
|
106
|
+
why: "verified at cc4778e0; no commit since has touched its anchors",
|
|
107
|
+
distance: 0,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
module.exports = { pact };
|
|
@@ -82,7 +82,7 @@ const budgetForecast = {
|
|
|
82
82
|
fixed_roles: [
|
|
83
83
|
{ role: "orc-system-analyst-opus-5-high", samples: 11, p50: { input: 6100, cache_write: 41000, cache_read: 82000, output: 9000 }, p90: { input: 8600, cache_write: 58000, cache_read: 116000, output: 13000 } },
|
|
84
84
|
{ role: "orc-planner-opus-5-med", samples: 11, p50: { input: 5000, cache_write: 33000, cache_read: 68000, output: 7400 }, p90: { input: 7100, cache_write: 47000, cache_read: 96000, output: 10500 } },
|
|
85
|
-
{ role: "orc-trace-writer-haiku-
|
|
85
|
+
{ role: "orc-trace-writer-haiku-5-high", samples: 41, p50: { input: 900, cache_write: 3000, cache_read: 5400, output: 700 }, p90: { input: 1300, cache_write: 4400, cache_read: 7800, output: 1000 } },
|
|
86
86
|
],
|
|
87
87
|
low_confidence_bands: 1,
|
|
88
88
|
min_samples: 5,
|
|
@@ -117,7 +117,7 @@ const budgetRates = {
|
|
|
117
117
|
},
|
|
118
118
|
roles: {
|
|
119
119
|
"orc-system-analyst-opus-5-high": { samples: 11, p50: { input: 6100, cache_write: 41000, cache_read: 82000, output: 9000 }, p90: { input: 8600, cache_write: 58000, cache_read: 116000, output: 13000 }, peak_p50: 88000, peak_p90: 131000 },
|
|
120
|
-
"orc-trace-writer-haiku-
|
|
120
|
+
"orc-trace-writer-haiku-5-high": { samples: 41, p50: { input: 900, cache_write: 3000, cache_read: 5400, output: 700 }, p90: { input: 1300, cache_write: 4400, cache_read: 7800, output: 1000 }, peak_p50: 9000, peak_p90: 14000 },
|
|
121
121
|
},
|
|
122
122
|
lanes: {},
|
|
123
123
|
unattributed: { blocks: 12, tokens: { input: 900, cache_write: 12000, cache_read: 24000, output: 1100 } },
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
"knowledge.graph.unknownHint": "The CLI did not answer. Run the command in a terminal to see why.",
|
|
6
6
|
"knowledge.graph.off": "The code graph is off.",
|
|
7
7
|
"knowledge.graph.offHint": "It is off by default. When it is on, code lanes build a local map of the code for free and keep it current.",
|
|
8
|
+
"knowledge.graph.semi": "semi — kept current, lanes do not read it",
|
|
9
|
+
"knowledge.graph.semiHint": "The graph is kept current, but no lane reads it. To enable reading, run this command:",
|
|
8
10
|
"knowledge.graph.none": "The graph is on, but no index exists yet. The next code lane builds it, or press the button.",
|
|
9
11
|
"knowledge.graph.files": "Files",
|
|
10
12
|
"knowledge.graph.symbols": "Symbols",
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
"knowledge.graph.unknownHint": "CLI tidak menjawab. Jalankan perintahnya di terminal untuk melihat penyebabnya.",
|
|
6
6
|
"knowledge.graph.off": "Graf kode mati.",
|
|
7
7
|
"knowledge.graph.offHint": "Default-nya mati. Jika menyala, lane kode membuat peta kode lokal secara gratis dan menjaganya tetap baru.",
|
|
8
|
+
"knowledge.graph.semi": "semi — selalu diperbarui, lane tidak membacanya",
|
|
9
|
+
"knowledge.graph.semiHint": "Graph selalu diperbarui, tetapi tidak ada lane yang membacanya. Untuk mengaktifkan pembacaan, jalankan perintah ini:",
|
|
8
10
|
"knowledge.graph.none": "Graf menyala, tetapi indeksnya belum ada. Lane kode berikutnya akan membuatnya, atau tekan tombolnya.",
|
|
9
11
|
"knowledge.graph.files": "File",
|
|
10
12
|
"knowledge.graph.symbols": "Simbol",
|
|
@@ -149,6 +149,19 @@ function knowledgeHeaderStrip(d) {
|
|
|
149
149
|
return strip;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/* v2.2.0 — code_graph: semi. `graph status` answers the INDEX state, and
|
|
153
|
+
the API adds the configured `mode`. A semi graph is kept current but no lane
|
|
154
|
+
reads it, so the panel says so next to the state, with the one command that
|
|
155
|
+
enables reading. The mode word is a config VALUE — never translated. */
|
|
156
|
+
function graphSemiNote(g) {
|
|
157
|
+
if (!g || g.mode !== "semi") return null;
|
|
158
|
+
const box = el("div", "cg-semi");
|
|
159
|
+
const row = el("div", "row-actions");
|
|
160
|
+
row.append(chip("SEMI", "idle"), el("span", "note", t("knowledge.graph.semi")));
|
|
161
|
+
box.append(row, el("div", "note", t("knowledge.graph.semiHint")), el("pre", "cmd", "orc config set code_graph on"));
|
|
162
|
+
return box;
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
/* THE CODE GRAPH CARD (v1.8.0). Every value is `orc graph status --json`'s, and
|
|
153
166
|
the state word is never replaced by a friendlier synonym. An update is FREE
|
|
154
167
|
(parser only, no model), so it is a button; an OFF graph shows the one config
|
|
@@ -167,6 +180,8 @@ function graphCard(g, body, gain, exact) {
|
|
|
167
180
|
return c;
|
|
168
181
|
}
|
|
169
182
|
if (exact) c.append(el("div", "note", t("knowledge.cg.exact.lead")));
|
|
183
|
+
const semiG = graphSemiNote(g);
|
|
184
|
+
if (semiG && !exact) c.append(semiG);
|
|
170
185
|
const head = el("div", "row-actions");
|
|
171
186
|
head.append(chip(String(g.state).toUpperCase(), g.state === "fresh" ? "ok" : g.state === "drifted" ? "warn" : "idle", g.state === "drifted"));
|
|
172
187
|
const upd = el("button", "btn btn-sm", "orc graph update");
|
|
@@ -1228,6 +1243,8 @@ function cgStateCard(g, body) {
|
|
|
1228
1243
|
return c;
|
|
1229
1244
|
}
|
|
1230
1245
|
c.append(el("div", "note", t("knowledge.cg.state.lead")));
|
|
1246
|
+
const semiS = graphSemiNote(g);
|
|
1247
|
+
if (semiS) c.append(semiS);
|
|
1231
1248
|
const ladder = el("div", "cg-ladder");
|
|
1232
1249
|
const steps = [
|
|
1233
1250
|
["OFF", t("knowledge.cg.state.off"), t("knowledge.cg.state.toNone")],
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# When the code graph helps (with live numbers)
|
|
2
|
+
|
|
3
|
+
Short version: **the code graph saves search work when the task must find code
|
|
4
|
+
or follow a change across files.** It does not help a change inside one file
|
|
5
|
+
that you already named. It gives no benefit until it has its data.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. What the code graph does for you
|
|
10
|
+
|
|
11
|
+
The code graph is a structure index of your project: files, symbols, callers,
|
|
12
|
+
imports, routes and tests. The lanes read it when they plan and slice a task.
|
|
13
|
+
The graph hook reads it when the main session searches for a known symbol.
|
|
14
|
+
The key `code_graph` sets the mode: `off`, `semi` or `on`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 2. Before you see a benefit: the graph needs its data
|
|
19
|
+
|
|
20
|
+
> [!NOTE]
|
|
21
|
+
> - Run `orc graph update` ONE time first. This is a cold build. In the eval
|
|
22
|
+
> repo (a company Vue repo: 799 files, 8,531 symbols) it took about 3.5 s.
|
|
23
|
+
> With no index, every graph read answers "no index". The lane then works as
|
|
24
|
+
> with the graph off.
|
|
25
|
+
> - After that, the hooks keep the index current: after each executor, and on
|
|
26
|
+
> read. `orc graph status` shows `fresh`.
|
|
27
|
+
> - The 2.2.0 upgrade rebuilds the index one time (engine `graph@7`).
|
|
28
|
+
> - Notes (one sentence per function) are OPTIONAL stored knowledge. A noter
|
|
29
|
+
> dispatch writes them, and that dispatch costs tokens. The structure layer
|
|
30
|
+
> is free.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 3. The evidence (real repo, live runs)
|
|
35
|
+
|
|
36
|
+
How we measured:
|
|
37
|
+
|
|
38
|
+
- The repo is a real company Vue 2.7 dashboard repo.
|
|
39
|
+
- The lane is `/orc-quick`. The main session is Opus 5.5 medium. The executor
|
|
40
|
+
is pinned to `orc-executor-opus-5-low`.
|
|
41
|
+
- Each task ran with the graph OFF and with the graph ON. The table shows
|
|
42
|
+
medians (or means where it says so).
|
|
43
|
+
- We checked each run: lint, jest, and a Playwright journey where the task has
|
|
44
|
+
a UI. The same grader graded each run against the same lane dossier.
|
|
45
|
+
|
|
46
|
+
| Task | What it is | Runs (OFF / ON) | Cost | Time | Tokens | Grade |
|
|
47
|
+
|---|---|---|---|---|---|---|
|
|
48
|
+
| RMT1 | Build step 1 of a new 4-step flow from a requirement document (route shell, API call, i18n, journey spec) | 5 / 3 | −9.6 % | −28.4 % | −15.8 % | equal (84) |
|
|
49
|
+
| RMT2 | Change one helper's signature and every caller (5 caller files, 1 test) | 3 / 3 | −2.0 % | −8.6 % | −4.2 % | equal (86) |
|
|
50
|
+
| RMT3 | Build step 2 on top of the built step 1 | 2 / 2 (means) | −8.9 % | +4.0 % | −14.0 % | 80 vs 85 (one ON run missed a display detail) |
|
|
51
|
+
| RMT4 | Change behaviour across the built step 1 + step 2 page; ON had stored notes | 2 / 2 (means) | +7.7 % | +45.5 % | +4.4 % | 77 vs 85.5 (one ON run 66 for stray git-hook files, the other 88) |
|
|
52
|
+
|
|
53
|
+
e2e: every RMT1/RMT3/RMT4 run that passed reached every journey step in both
|
|
54
|
+
arms.
|
|
55
|
+
|
|
56
|
+
N = 2–5 per arm. A difference under about 10 % is inside the run-to-run noise
|
|
57
|
+
of one task.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 4. Where the graph helps
|
|
62
|
+
|
|
63
|
+
- **Finding the code to copy or to change.** The executor's exploration fell
|
|
64
|
+
40–74 % (RMT1, RMT3 phase split).
|
|
65
|
+
- **Changes that ripple across files.** Callers, props / emits, routes and the
|
|
66
|
+
tests that reach a caller come in one card. This card replaces a grep, a
|
|
67
|
+
test hunt and a coverage check.
|
|
68
|
+
- **The main session's own search.** The hook answers a search for a known
|
|
69
|
+
symbol with its callers, tests and coverage (one time per name, 3 per run at
|
|
70
|
+
most). When an executor returns, the hook gives the affected tests.
|
|
71
|
+
- **Time.** The clearest win is on new-feature work (RMT1 −28 %).
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 5. Where it does not help (or helps very little)
|
|
76
|
+
|
|
77
|
+
RMT4 is the live example. It is a small follow-up change inside a page that
|
|
78
|
+
the agent reads in a few calls. There was no gain, also with stored notes.
|
|
79
|
+
|
|
80
|
+
- **A change inside ONE named file with no outside effect.** Both arms read
|
|
81
|
+
that file. ORC skips the anchors.
|
|
82
|
+
- **Very small tasks.** The lane's own overhead (config, trace, questions) is
|
|
83
|
+
most of the cost. The graph is break-even.
|
|
84
|
+
- **Test writing and coverage work.** The graph does not write tests. A
|
|
85
|
+
project rule such as "> 90 % branch coverage" can cost more than all that the
|
|
86
|
+
graph saves. We saw this in both arms.
|
|
87
|
+
- **No index, or a stale index.** The lane falls back to Grep. There is no
|
|
88
|
+
harm and no gain.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 6. Turn it on
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
orc config set code_graph on
|
|
96
|
+
orc graph update
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Run `orc graph update` one time. The modes are:
|
|
100
|
+
|
|
101
|
+
- `on` — the hooks keep the graph current, and the lanes read it.
|
|
102
|
+
- `semi` — the hooks keep the graph current. No lane reads it.
|
|
103
|
+
- `off` — nothing.
|
|
104
|
+
|
|
105
|
+
For the other keys (`code_graph_notes`, `code_graph_card_budget` and more),
|
|
106
|
+
see [configuration](configuration.md).
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 7. What it costs
|
|
111
|
+
|
|
112
|
+
- **Structure:** zero tokens. Local files under `.claude/orc/graph/`. No
|
|
113
|
+
background process.
|
|
114
|
+
- **Cards:** a few hundred tokens each, with a cap.
|
|
115
|
+
- **Hook cards:** 3 per run at most in the main session.
|
|
116
|
+
- **Notes:** optional. Each batch costs a noter dispatch.
|