@azure-id/orc 1.9.2 → 2.0.2
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 +321 -0
- package/README-id.md +21 -36
- package/README.md +25 -36
- package/bin/build-agents.js +117 -20
- package/bin/cli.js +725 -29
- package/bin/gotcha-import.js +1081 -0
- package/bin/gotcha.js +1286 -0
- package/bin/graph-query.js +1 -1
- package/bin/graph.js +717 -717
- package/bin/habit.js +1453 -0
- package/bin/mockrun-catalog.js +281 -276
- package/bin/run-undo.js +398 -0
- package/bin/trace-write.js +657 -0
- package/bin/verify-contracts.js +525 -79
- package/bin/verify-package.js +51 -4
- package/bin/webui/api.js +26 -0
- package/bin/webui/app.html +239 -232
- package/bin/webui/css/00-tokens.css +110 -92
- package/bin/webui/css/04-motion.css +87 -0
- package/bin/webui/css/06-responsive.css +203 -178
- package/bin/webui/css/panels/behaviour.css +205 -0
- package/bin/webui/fixtures/behaviour.js +532 -0
- package/bin/webui/fixtures/index.js +34 -0
- package/bin/webui/fixtures/knowledge.js +7 -1
- package/bin/webui/fixtures/stats.js +16 -0
- package/bin/webui/i18n/en/behaviour.json +143 -0
- package/bin/webui/i18n/en/nav.json +25 -24
- package/bin/webui/i18n/en/tour.json +37 -35
- package/bin/webui/i18n/id/behaviour.json +143 -0
- package/bin/webui/i18n/id/nav.json +25 -24
- package/bin/webui/i18n/id/tour.json +37 -35
- package/bin/webui/js/01-i18n.js +155 -154
- package/bin/webui/js/90-tour.js +498 -494
- package/bin/webui/js/91-shortcuts.js +126 -126
- package/bin/webui/js/99-boot.js +121 -118
- package/bin/webui/js/panels/behaviour.js +1022 -0
- package/mock-run/INDEX.md +109 -107
- package/mock-run/gotcha-import.md +118 -0
- package/mock-run/habits.md +129 -0
- package/mock-run/orc-quick.md +6 -1
- package/package.json +1 -1
- package/templates/agents/MODEL-MAPPING.md +7 -7
- package/templates/agents/orc-advisor-opus-5-xhigh.md +1 -7
- package/templates/agents/orc-analyze-mini-opus-5-med.md +1 -6
- package/templates/agents/orc-analyze-mini-sonnet-5-high.md +1 -4
- package/templates/agents/orc-claude-writer-opus-4-8-high.md +48 -53
- package/templates/agents/orc-claude-writer-opus-5-med.md +1 -8
- package/templates/agents/orc-context-combiner-opus-5-high.md +1 -11
- package/templates/agents/orc-executor-haiku-4-5.md +14 -6
- package/templates/agents/orc-executor-opus-4-7-high.md +14 -6
- package/templates/agents/orc-executor-opus-4-7-med.md +14 -6
- package/templates/agents/orc-executor-opus-4-8-high.md +14 -6
- package/templates/agents/orc-executor-opus-5-high.md +14 -6
- package/templates/agents/orc-executor-opus-5-low.md +14 -6
- package/templates/agents/orc-executor-opus-5-med.md +14 -6
- package/templates/agents/orc-executor-sonnet-4-6-high.md +14 -6
- package/templates/agents/orc-executor-sonnet-4-6-med.md +14 -6
- package/templates/agents/orc-executor-sonnet-5-high.md +14 -6
- package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +1 -10
- package/templates/agents/orc-judge-opus-5-xhigh.md +5 -9
- package/templates/agents/orc-learn-writer-opus-5-low.md +1 -7
- package/templates/agents/orc-pattern-codifier-opus-5-med.md +1 -8
- package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +58 -63
- package/templates/agents/orc-planner-mini-opus-5-med.md +1 -4
- package/templates/agents/orc-planner-mini-sonnet-5-high.md +1 -2
- package/templates/agents/orc-planner-opus-5-med.md +1 -4
- package/templates/agents/orc-recon-opus-5-low.md +1 -8
- package/templates/agents/orc-recon-sonnet-4-6-med.md +1 -8
- package/templates/agents/orc-retro-opus-5-med.md +6 -8
- package/templates/agents/orc-retro-sonnet-5-high.md +6 -7
- package/templates/agents/orc-reviewer-opus-5-med.md +52 -16
- package/templates/agents/orc-scout-opus-5-low.md +1 -6
- package/templates/agents/orc-scout-sonnet-4-6-high.md +35 -39
- package/templates/agents/orc-system-analyst-opus-5-high.md +1 -6
- package/templates/agents/orc-test-author-opus-5-med.md +4 -5
- package/templates/agents/orc-trace-writer-haiku-4-5.md +3 -7
- package/templates/agents/orc-verifier-opus-5-med.md +16 -8
- package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +74 -79
- package/templates/agents/orc-wiki-scanner-opus-5-med.md +1 -8
- package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +97 -106
- package/templates/commands/orc-analyze.md +13 -21
- package/templates/commands/orc-fast.md +10 -15
- package/templates/commands/orc-poly.md +12 -21
- package/templates/commands/orc-pr-driver.md +11 -30
- package/templates/commands/orc-pr-setup.md +10 -31
- package/templates/commands/orc-route.md +11 -41
- package/templates/commands/orc-test.md +5 -60
- package/templates/hooks/README.md +34 -0
- package/templates/hooks/orc-session-hook.js +264 -0
- package/templates/hooks/orc-statusline.js +3 -1
- package/templates/skills/_shared/README.md +9 -0
- package/templates/skills/_shared/code-graph.md +47 -55
- package/templates/skills/_shared/config-precedence.md +3 -1
- package/templates/skills/_shared/extra-dispatch.md +73 -88
- package/templates/skills/_shared/gotchas.md +228 -177
- package/templates/skills/_shared/habits.md +101 -0
- package/templates/skills/_shared/lane-contract.md +84 -0
- package/templates/skills/_shared/phases/README.md +142 -83
- package/templates/skills/_shared/phases/analyst-gates.md +10 -21
- package/templates/skills/_shared/phases/execution.md +8 -14
- package/templates/skills/_shared/phases/house-rules.md +27 -32
- package/templates/skills/_shared/phases/intake.md +127 -133
- package/templates/skills/_shared/phases/mock-example.md +46 -56
- package/templates/skills/_shared/phases/plan-handoff.md +91 -97
- package/templates/skills/_shared/phases/planning.md +7 -17
- package/templates/skills/_shared/phases/preflight.md +19 -42
- package/templates/skills/_shared/phases/review.md +23 -27
- package/templates/skills/_shared/phases/rules.md +18 -42
- package/templates/skills/_shared/phases/scoring.md +55 -65
- package/templates/skills/_shared/phases/security-checklist.md +46 -50
- package/templates/skills/_shared/phases/security.md +45 -55
- package/templates/skills/_shared/phases/ship.md +6 -15
- package/templates/skills/_shared/phases/stop-resume.md +2 -5
- package/templates/skills/_shared/phases/summary.md +73 -48
- package/templates/skills/_shared/phases/testgen.md +41 -51
- package/templates/skills/_shared/phases/trace-verbs.md +433 -0
- package/templates/skills/_shared/phases/trace.md +136 -367
- package/templates/skills/_shared/phases/verify.md +62 -70
- package/templates/skills/_shared/phases/wave-grouping.md +128 -133
- package/templates/skills/_shared/phases/wiki-consult.md +10 -6
- package/templates/skills/_shared/read-ladder.md +2 -55
- package/templates/skills/_shared/return-validation.md +17 -70
- package/templates/skills/_shared/review-slice.md +79 -0
- package/templates/skills/_shared/smoke-gate.md +46 -28
- package/templates/skills/context-combiner/SKILL.md +15 -44
- package/templates/skills/orc/SKILL.md +34 -62
- package/templates/skills/orc/references/pattern-gate.md +89 -89
- package/templates/skills/orc/references/phases/intake.md +41 -47
- package/templates/skills/orc/references/phases/integration.md +13 -19
- package/templates/skills/orc/references/preflight-report.md +8 -9
- package/templates/skills/orc/references/ultra-mode.md +8 -6
- package/templates/skills/orc/subskills/orc-execution/SKILL.md +27 -73
- package/templates/skills/orc/subskills/orc-execution/core.md +12 -99
- package/templates/skills/orc/subskills/orc-execution/subagent.md +14 -13
- package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +11 -52
- package/templates/skills/orc/subskills/orc-review-verify/core.md +52 -135
- package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -7
- package/templates/skills/orc/subskills/orc-testgen/SKILL.md +11 -22
- package/templates/skills/orc/subskills/orc-testgen/core.md +20 -59
- package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -7
- package/templates/skills/orc-advisor/SKILL.md +56 -60
- package/templates/skills/orc-analyze/SKILL.md +30 -66
- package/templates/skills/orc-analyze/schemas/report-audit.md +2 -1
- package/templates/skills/orc-analyze/schemas/report-prose.md +2 -1
- package/templates/skills/orc-analyze-mini/SKILL.md +35 -70
- package/templates/skills/orc-diy/README.md +31 -0
- package/templates/skills/orc-diy/SKILL.md +23 -74
- package/templates/skills/orc-diy/references/blocks/pattern.md +18 -18
- package/templates/skills/orc-fast/SKILL.md +44 -72
- package/templates/skills/orc-judge/SKILL.md +77 -82
- package/templates/skills/orc-mini/SKILL.md +61 -109
- package/templates/skills/orc-pattern/SKILL.md +27 -48
- package/templates/skills/orc-poly/SKILL.md +32 -61
- package/templates/skills/orc-pr-driver/SKILL.md +25 -51
- package/templates/skills/orc-pr-driver/references/green-gate.md +113 -105
- package/templates/skills/orc-pr-setup/SKILL.md +20 -45
- package/templates/skills/orc-quick/README.md +43 -2
- package/templates/skills/orc-quick/SKILL.md +76 -107
- package/templates/skills/orc-quick/references/dispatch-gate.md +16 -5
- package/templates/skills/orc-quick/references/gh-mode.md +48 -1
- package/templates/skills/orc-quick/references/look.md +3 -1
- package/templates/skills/orc-retro/SKILL.md +19 -18
- package/templates/skills/orc-retro/examples/retro-mock.md +1 -1
- package/templates/skills/orc-route/SKILL.md +25 -45
- package/templates/skills/orc-test/SKILL.md +16 -37
- package/templates/skills/orc-verify/SKILL.md +14 -32
- package/templates/skills/orc-wait/SKILL.md +156 -163
- package/templates/skills/orc-wiki/references/phases/phase-0.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-1.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-2.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-3.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-3c.md +1 -6
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* fixtures/behaviour.js — canned data for `orc ui --fixtures`.
|
|
3
|
+
The Behaviour panel (v2.0.0 W7): `orc habit show|log|points --json`,
|
|
4
|
+
`orc gotcha list|card|quality|list --candidates --json`.
|
|
5
|
+
|
|
6
|
+
THE RULE FOR EVERY FILE IN HERE: carry ONE OF EVERY STATE, including the
|
|
7
|
+
ugly ones. Here that is every habit state (observed · proposed · applied ·
|
|
8
|
+
declined · never-ask · stale · shadowed · never), every class (apply ·
|
|
9
|
+
suggest · never), every `habits` mode (off · observe · propose), every
|
|
10
|
+
`by=` value, every gotcha source and status, and the three empty answers
|
|
11
|
+
(no traces yet, below the review floor, a card with zero matches).
|
|
12
|
+
test/webui/fixtures.test.js counts them.
|
|
13
|
+
|
|
14
|
+
Shapes MUST match what `bin/cli.js --json` really emits — a drifted
|
|
15
|
+
fixture is worse than no fixture. */
|
|
16
|
+
|
|
17
|
+
const { PROJECT } = require("./shell.js");
|
|
18
|
+
|
|
19
|
+
const DAY = 86400000;
|
|
20
|
+
const NOW = Date.now();
|
|
21
|
+
const LOG_DIR = PROJECT + "/.claude/orc/logs";
|
|
22
|
+
const STATE_PATH = PROJECT + "/.claude/orc/habits-state.json";
|
|
23
|
+
const CACHE_PATH = PROJECT + "/.claude/orc/habits-cache.json";
|
|
24
|
+
const STATE_WORDS = ["observed", "proposed", "applied", "declined", "never-ask", "stale", "shadowed", "never"];
|
|
25
|
+
const BY_SET = ["user", "ledger", "learned", "config", "default"];
|
|
26
|
+
const RULE = { window: 30, max_age_days: 180, half_life_answers: 10, weights: { user: 1, override: 1, ledger: 1, learned: 0.25, preselected: 0.25 }, excluded_by: ["config", "default"], min_n: 5, lb: 0.55, z: 1.96, last_agree: 3, stale_overrides: 2, stale_of: 3, expiry_days: 90, cooldown_answers: 10, cooldown_days: 14 };
|
|
27
|
+
|
|
28
|
+
const NULL_COMMANDS = { accept: null, decline: null, never: null, forget: null, reset: null, manual: null };
|
|
29
|
+
// One row, every key `habit show --json` emits, in its order.
|
|
30
|
+
function row(o) {
|
|
31
|
+
const base = {
|
|
32
|
+
id: "H-000000",
|
|
33
|
+
qid: "",
|
|
34
|
+
lanes: [],
|
|
35
|
+
title: "",
|
|
36
|
+
class: "suggest",
|
|
37
|
+
effective_class: "suggest",
|
|
38
|
+
careful: null,
|
|
39
|
+
key: null,
|
|
40
|
+
bucket: {},
|
|
41
|
+
state: "observed",
|
|
42
|
+
why: null,
|
|
43
|
+
stale_reason: null,
|
|
44
|
+
stale_detail: null,
|
|
45
|
+
top: null,
|
|
46
|
+
top_label: null,
|
|
47
|
+
count: 0,
|
|
48
|
+
total: 0,
|
|
49
|
+
n: 0,
|
|
50
|
+
n_eff: 0,
|
|
51
|
+
p: 0,
|
|
52
|
+
lb: 0,
|
|
53
|
+
last_agree: false,
|
|
54
|
+
passes: false,
|
|
55
|
+
proposable: false,
|
|
56
|
+
option: null,
|
|
57
|
+
value: null,
|
|
58
|
+
decided_at: null,
|
|
59
|
+
last_used: null,
|
|
60
|
+
line: null,
|
|
61
|
+
rule: null,
|
|
62
|
+
undo: null,
|
|
63
|
+
options: [],
|
|
64
|
+
share: 0,
|
|
65
|
+
streak: [],
|
|
66
|
+
context: "",
|
|
67
|
+
effect: null,
|
|
68
|
+
commands: NULL_COMMANDS,
|
|
69
|
+
};
|
|
70
|
+
const r = Object.assign(base, o);
|
|
71
|
+
r.commands = Object.assign({}, NULL_COMMANDS, o.commands || {});
|
|
72
|
+
return r;
|
|
73
|
+
}
|
|
74
|
+
const ruleLine = (lb, n_eff, n) => `Wilson95 LB ${lb} (n_eff ${n_eff}, raw n ${n}) — proposes at ≥ 0.55 with n ≥ 5 and the last 3 agreeing`;
|
|
75
|
+
|
|
76
|
+
const ROWS = [
|
|
77
|
+
row({
|
|
78
|
+
id: "H-4c1a9e", qid: "quick.q3.offer.review", lanes: ["orc-quick"], title: "Code review before commit", careful: "review-first",
|
|
79
|
+
bucket: { kind: "feature" }, state: "proposed", why: "the rule passes — proposed once, at the end of a run",
|
|
80
|
+
top: "review-first", top_label: "review first", count: 9, total: 10, n: 10, n_eff: 7.82, p: 0.9, lb: 0.6, last_agree: true, passes: true, proposable: true,
|
|
81
|
+
line: 'You chose review first in 9 of your last 10 answers to "Code review before commit" (one "commit directly" on 14-09).',
|
|
82
|
+
rule: ruleLine(0.6, 7.82, 10),
|
|
83
|
+
options: [{ v: "review-first", label: "review first", n: 9 }, { v: "commit-direct", label: "commit directly", n: 1 }, { v: "stop", label: "stop", n: 0 }],
|
|
84
|
+
share: 0.9, streak: [1, 1, 0, 1, 1, 0.25, 1, 1, 1, 1], context: "kind=feature",
|
|
85
|
+
effect: "If you accept, ORC puts review first first and marks it → usual. The question is still asked.",
|
|
86
|
+
commands: { accept: "orc habit accept H-4c1a9e", decline: "orc habit decline H-4c1a9e", never: "orc habit decline H-4c1a9e --never" },
|
|
87
|
+
}),
|
|
88
|
+
row({
|
|
89
|
+
id: "H-7d20b3", qid: "orc.phase-2.pause", lanes: ["orc"], title: "How often the run pauses", class: "apply", effective_class: "apply", careful: "every-1", key: "batch_pause_every",
|
|
90
|
+
state: "stale", stale_reason: "override", stale_detail: { option: "every-2", overrides: 2, of: 3 },
|
|
91
|
+
why: "you chose pause every 2 waves 2 of the last 3 times instead of your usual pause every wave — paused, not flipped",
|
|
92
|
+
top: "every-1", top_label: "pause every wave", count: 7, total: 10, n: 10, n_eff: 7.1, p: 0.7, lb: 0.39, last_agree: false, option: "every-1", decided_at: NOW - 40 * DAY, last_used: NOW - 3 * DAY,
|
|
93
|
+
line: 'You chose pause every wave in 7 of your last 10 answers to "How often the run pauses" (3 other answers, the last "pause every 2 waves" on 25-09).',
|
|
94
|
+
rule: ruleLine(0.39, 7.1, 10), undo: "orc habit forget H-7d20b3",
|
|
95
|
+
options: [{ v: "every-1", label: "pause every wave", n: 7 }, { v: "every-2", label: "pause every 2 waves", n: 3 }],
|
|
96
|
+
share: 0.7, streak: [1, 1, 1, 1, 1, 1, 1, 0, 1, 0],
|
|
97
|
+
effect: "ORC paused this habit. The question is asked again until you decide.",
|
|
98
|
+
commands: { forget: "orc habit forget H-7d20b3" },
|
|
99
|
+
}),
|
|
100
|
+
row({
|
|
101
|
+
id: "H-a19f02", qid: "mini.phase-x.mock", lanes: ["orc-mini"], title: "Mocked runnable example", class: "apply", effective_class: "apply", key: "mock_example",
|
|
102
|
+
state: "applied", why: "answers mock_example at the learned rank",
|
|
103
|
+
top: "skip", top_label: "skip it", count: 12, total: 13, n: 13, n_eff: 9.4, p: 0.92, lb: 0.71, last_agree: true, passes: true, option: "skip", value: "off", decided_at: NOW - 21 * DAY, last_used: NOW - 1 * DAY,
|
|
104
|
+
line: 'You chose skip it in 12 of your last 13 answers to "Mocked runnable example" (one "build the example" on 02-09).',
|
|
105
|
+
rule: ruleLine(0.71, 9.4, 13), undo: "orc habit forget H-a19f02",
|
|
106
|
+
options: [{ v: "skip", label: "skip it", n: 12 }, { v: "build", label: "build the example", n: 1 }],
|
|
107
|
+
share: 0.923, streak: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
108
|
+
effect: "ORC answers mock_example with skip it at the learned rank. Your config file still wins.",
|
|
109
|
+
commands: { forget: "orc habit forget H-a19f02" },
|
|
110
|
+
}),
|
|
111
|
+
row({
|
|
112
|
+
id: "H-0b77c5", qid: "orc.phase-8.ship", lanes: ["orc"], title: "How the change ships", bucket: { branch: "feature" },
|
|
113
|
+
state: "applied", why: "marks open a PR as your usual; the question is still asked",
|
|
114
|
+
top: "pr", top_label: "open a PR", count: 10, total: 11, n: 11, n_eff: 8.2, p: 0.91, lb: 0.66, last_agree: true, passes: true, option: "pr", decided_at: NOW - 30 * DAY, last_used: NOW - 2 * DAY,
|
|
115
|
+
line: 'You chose open a PR in 10 of your last 11 answers to "How the change ships" (one "commit only" on 28-08).',
|
|
116
|
+
rule: ruleLine(0.66, 8.2, 11), undo: "orc habit forget H-0b77c5",
|
|
117
|
+
options: [{ v: "pr", label: "open a PR", n: 10 }, { v: "commit", label: "commit only", n: 1 }, { v: "leave", label: "leave it uncommitted", n: 0 }],
|
|
118
|
+
share: 0.909, streak: [1, 1, 1, 1, 0.25, 1, 1, 1, 1, 1], context: "branch=feature",
|
|
119
|
+
effect: "ORC puts open a PR first and marks it → usual. The question is still asked.",
|
|
120
|
+
commands: { forget: "orc habit forget H-0b77c5" },
|
|
121
|
+
}),
|
|
122
|
+
row({
|
|
123
|
+
id: "H-3e5d18", qid: "any.analysis.depth", lanes: ["orc", "orc-analyze"], title: "Analysis depth",
|
|
124
|
+
state: "observed", why: "3 full-weight answers — the rule needs 5",
|
|
125
|
+
top: "standard", top_label: "standard", count: 3, total: 4, n: 3, n_eff: 3.4, p: 0.75, lb: 0.3,
|
|
126
|
+
line: 'You chose standard in 3 of your last 4 answers to "Analysis depth" (one "deep" on 11-09).',
|
|
127
|
+
rule: ruleLine(0.3, 3.4, 3),
|
|
128
|
+
options: [{ v: "standard", label: "standard", n: 3 }, { v: "deep", label: "deep", n: 1 }],
|
|
129
|
+
share: 0.75, streak: [1, 0, 1, 1],
|
|
130
|
+
effect: "If you accept, ORC puts standard first and marks it → usual. The question is still asked.",
|
|
131
|
+
}),
|
|
132
|
+
row({
|
|
133
|
+
id: "H-92ab40", qid: "orc.intake.testgen", lanes: ["orc"], title: "Author test cases before ship", class: "apply", effective_class: "apply", careful: "author", key: "generate_tests",
|
|
134
|
+
state: "shadowed", why: "your config sets generate_tests: true — the file wins, so this habit is shown and inert (was: proposed)",
|
|
135
|
+
top: "author", top_label: "author tests", count: 8, total: 8, n: 8, n_eff: 6.9, p: 1, lb: 0.65, last_agree: true, passes: true,
|
|
136
|
+
line: 'You chose author tests in 8 of your last 8 answers to "Author test cases before ship".',
|
|
137
|
+
rule: ruleLine(0.65, 6.9, 8),
|
|
138
|
+
options: [{ v: "author", label: "author tests", n: 8 }, { v: "skip", label: "skip tests", n: 0 }],
|
|
139
|
+
share: 1, streak: [1, 1, 1, 1, 1, 1, 1, 1],
|
|
140
|
+
effect: "If you accept, ORC answers generate_tests with author tests at the learned rank. Your config file still wins.",
|
|
141
|
+
}),
|
|
142
|
+
row({
|
|
143
|
+
id: "H-5f6e21", qid: "orc.phase-1.planner", lanes: ["orc"], title: "Which planner writes the plan",
|
|
144
|
+
state: "declined", why: "declined — it can come back after 10 more answers (4 so far) and 14 days (6 so far)",
|
|
145
|
+
top: "planner", top_label: "ORC planner", count: 8, total: 9, n: 9, n_eff: 7, p: 0.89, lb: 0.58, last_agree: true, passes: true, decided_at: NOW - 6 * DAY,
|
|
146
|
+
line: 'You chose ORC planner in 8 of your last 9 answers to "Which planner writes the plan" (one "Superpowers" on 03-09).',
|
|
147
|
+
rule: ruleLine(0.58, 7, 9), undo: "orc habit forget H-5f6e21",
|
|
148
|
+
options: [{ v: "planner", label: "ORC planner", n: 8 }, { v: "superpowers", label: "Superpowers", n: 1 }],
|
|
149
|
+
share: 0.889, streak: [1, 1, 0, 1, 1, 1, 1, 1, 1],
|
|
150
|
+
effect: "If you accept, ORC puts ORC planner first and marks it → usual. The question is still asked.",
|
|
151
|
+
commands: { reset: "orc habit reset H-5f6e21" },
|
|
152
|
+
}),
|
|
153
|
+
row({
|
|
154
|
+
id: "H-c40d9a", qid: "fast.f0.stale-wiki", lanes: ["orc-fast"], title: "A stale wiki at preflight",
|
|
155
|
+
state: "never-ask", why: "you said never — still observed, never proposed",
|
|
156
|
+
top: "refresh", top_label: "refresh the wiki", count: 6, total: 7, n: 7, n_eff: 5.8, p: 0.86, lb: 0.56, last_agree: true, passes: true, decided_at: NOW - 12 * DAY,
|
|
157
|
+
line: 'You chose refresh the wiki in 6 of your last 7 answers to "A stale wiki at preflight" (one "fall back to /orc-mini" on 01-09).',
|
|
158
|
+
rule: ruleLine(0.56, 5.8, 7), undo: "orc habit forget H-c40d9a",
|
|
159
|
+
options: [{ v: "refresh", label: "refresh the wiki", n: 6 }, { v: "mini", label: "fall back to /orc-mini", n: 1 }],
|
|
160
|
+
share: 0.857, streak: [1, 1, 1, 0, 1, 1, 1],
|
|
161
|
+
effect: "If you accept, ORC puts refresh the wiki first and marks it → usual. The question is still asked.",
|
|
162
|
+
commands: { reset: "orc habit reset H-c40d9a" },
|
|
163
|
+
}),
|
|
164
|
+
row({
|
|
165
|
+
id: "H-e81f63", qid: "orc.phase-5-5.security", lanes: ["orc", "orc-diy"], title: "Security review", class: "apply", effective_class: "never", careful: "run", key: "security_review",
|
|
166
|
+
state: "never", why: "your usual answer (skip it) is away from the careful side, so it is never applied — toward `on` only — a habit to skip security is never applied",
|
|
167
|
+
top: "skip", top_label: "skip it", count: 9, total: 10, n: 10, n_eff: 7.6, p: 0.9, lb: 0.6, last_agree: true, passes: true,
|
|
168
|
+
line: 'You chose skip it in 9 of your last 10 answers to "Security review" (one "run the security pass" on 09-09).',
|
|
169
|
+
rule: ruleLine(0.6, 7.6, 10),
|
|
170
|
+
options: [{ v: "skip", label: "skip it", n: 9 }, { v: "run", label: "run the security pass", n: 1 }],
|
|
171
|
+
share: 0.9, streak: [1, 1, 1, 0, 1, 1, 1, 1, 1, 1],
|
|
172
|
+
effect: "ORC shows this and never proposes it: toward `on` only — a habit to skip security is never applied.",
|
|
173
|
+
commands: { manual: "orc config set security_review off" },
|
|
174
|
+
}),
|
|
175
|
+
row({
|
|
176
|
+
id: "H-18aa7f", qid: "orc.phase-2.execution", lanes: ["orc"], title: "How the waves execute", effective_class: "never",
|
|
177
|
+
state: "never", why: "the habit only orders the offer",
|
|
178
|
+
top: "other", top_label: "other", count: 5, total: 8, n: 8, n_eff: 6, p: 0.63, lb: 0.31,
|
|
179
|
+
line: 'You chose other in 5 of your last 8 answers to "How the waves execute" (3 other answers, the last "parallel" on 20-09).',
|
|
180
|
+
rule: ruleLine(0.31, 6, 8),
|
|
181
|
+
options: [{ v: "other", label: "other", n: 5 }, { v: "parallel", label: "parallel", n: 3 }],
|
|
182
|
+
share: 0.625, streak: [0, 1, 1, 0, 1, 1, 0, 1],
|
|
183
|
+
effect: "ORC shows this and never proposes it: the habit only orders the offer.",
|
|
184
|
+
}),
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
function countsOf(rows) {
|
|
188
|
+
const c = {};
|
|
189
|
+
for (const s of STATE_WORDS) c[s] = rows.filter((r) => r.state === s).length;
|
|
190
|
+
return c;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// The heat map: weekday (Mon first) × hour. Deterministic, work-hours heavy.
|
|
194
|
+
function heatMap(scale) {
|
|
195
|
+
const out = [];
|
|
196
|
+
for (let d = 0; d < 7; d++) {
|
|
197
|
+
const r = [];
|
|
198
|
+
for (let h = 0; h < 24; h++) {
|
|
199
|
+
const work = d < 5 && h >= 9 && h <= 18 ? 1 : 0;
|
|
200
|
+
const v = work ? ((d * 7 + h * 3) % 5) + (h === 14 ? 3 : 0) : d >= 5 && h === 11 ? 1 : 0;
|
|
201
|
+
r.push(Math.round(v * scale));
|
|
202
|
+
}
|
|
203
|
+
out.push(r);
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
const WEEKS = Array.from({ length: 12 }, (_, i) => new Date(NOW - (12 - i) * 7 * DAY).toISOString().slice(0, 10));
|
|
208
|
+
const HOW = [
|
|
209
|
+
[30, 4, 0, 2, 1], [28, 5, 0, 2, 1], [27, 6, 1, 3, 0], [25, 6, 2, 3, 1], [24, 7, 3, 3, 0], [22, 8, 4, 3, 1],
|
|
210
|
+
[20, 8, 5, 4, 1], [19, 9, 6, 4, 0], [17, 9, 7, 4, 1], [16, 10, 8, 5, 1], [15, 10, 9, 5, 0], [14, 11, 9, 5, 1],
|
|
211
|
+
].map((w) => Object.fromEntries(BY_SET.map((b, i) => [b, w[i]])));
|
|
212
|
+
|
|
213
|
+
function show(mode, rows) {
|
|
214
|
+
const heat = heatMap(1);
|
|
215
|
+
return {
|
|
216
|
+
ok: true,
|
|
217
|
+
mode,
|
|
218
|
+
lane: null,
|
|
219
|
+
window: "90d",
|
|
220
|
+
log_dir: LOG_DIR,
|
|
221
|
+
state_path: STATE_PATH,
|
|
222
|
+
cache_path: CACHE_PATH,
|
|
223
|
+
traces: 94,
|
|
224
|
+
answers: 412,
|
|
225
|
+
excluded_runs: [{ run: "run-orc-pr-review-120926-101500.txt", reason: "untrusted_context" }],
|
|
226
|
+
count: rows.length,
|
|
227
|
+
rows,
|
|
228
|
+
tiles: { runs: 94, answers: 412, saved: 57, saved_prev: 36, qpr: 3.9, qpr_prev: 6.8 },
|
|
229
|
+
rhythm: {
|
|
230
|
+
days: Array.from({ length: 14 }, (_, i) => ({ date: new Date(NOW - (13 - i) * DAY).toISOString().slice(0, 10), answers: [4, 6, 0, 0, 9, 7, 5, 8, 3, 0, 0, 6, 7, 5][i] })),
|
|
231
|
+
heat,
|
|
232
|
+
lanes: [{ lane: "orc-quick", n: 38 }, { lane: "orc-mini", n: 22 }, { lane: "orc", n: 17 }, { lane: "orc-fast", n: 9 }, { lane: "orc-analyze", n: 5 }, { lane: "orc-diy", n: 3 }],
|
|
233
|
+
weeks: WEEKS,
|
|
234
|
+
qpr: [6.8, 6.5, 6.1, 5.9, null, 5.2, 4.9, 4.6, 4.4, 4.2, 4.0, 3.9],
|
|
235
|
+
how: HOW,
|
|
236
|
+
by: BY_SET,
|
|
237
|
+
},
|
|
238
|
+
portrait: [
|
|
239
|
+
{ k: "lanes", line: "You run most work through orc-quick (40% of 94 runs)." },
|
|
240
|
+
{ k: "time", line: "Your busiest hour is Tue 14:00 (7 runs)." },
|
|
241
|
+
{ k: "questions", line: "You answered 3.9 questions per run, against 6.8 in the window before." },
|
|
242
|
+
{ k: "usual", line: "Mocked runnable example: usually skip it (12 of 13)." },
|
|
243
|
+
].concat(mode === "propose" ? [{ k: "proposal", line: "1 habit is ready for your decision." }] : []),
|
|
244
|
+
proposal:
|
|
245
|
+
mode === "propose" && rows.length
|
|
246
|
+
? { id: "H-4c1a9e", qid: "quick.q3.offer.review", kind: "propose", option: "review-first", line: ROWS[0].line + " Make it your usual? [yes · not now · never]", yes: "orc habit accept H-4c1a9e", keep: null, later: "orc habit decline H-4c1a9e", never: "orc habit decline H-4c1a9e --never", trace: "ASK habit.proposal :: offered=yes|later|never rec=none chose=<yes|later|never> by=user" }
|
|
247
|
+
: null,
|
|
248
|
+
state_words: STATE_WORDS,
|
|
249
|
+
rule: RULE,
|
|
250
|
+
modes: ["off", "observe", "propose"],
|
|
251
|
+
classes: ["apply", "suggest", "never"],
|
|
252
|
+
counts: countsOf(rows),
|
|
253
|
+
next_run: [
|
|
254
|
+
{ lane: "orc", id: "H-0b77c5", qid: "orc.phase-8.ship", line: '"How the change ships" offers open a PR first, marked → usual (H-0b77c5)' },
|
|
255
|
+
{ lane: "orc-mini", id: "H-a19f02", qid: "mini.phase-x.mock", line: "mock_example: off — learned (H-a19f02)" },
|
|
256
|
+
],
|
|
257
|
+
files: [LOG_DIR + "/*.txt", STATE_PATH],
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// `habits: propose` — every state, with the proposal.
|
|
262
|
+
const habitsPropose = show("propose", ROWS);
|
|
263
|
+
// `habits: observe` — the tabs fill, and NOTHING is proposed: the row the rule
|
|
264
|
+
// passes for stays `observed` (the CLI's "`habits: observe` never proposes").
|
|
265
|
+
const habitsObserve = show(
|
|
266
|
+
"observe",
|
|
267
|
+
ROWS.map((r) =>
|
|
268
|
+
r.commands.accept
|
|
269
|
+
? Object.assign({}, r, { state: "observed", why: "the rule passes — `habits: observe` never proposes", commands: NULL_COMMANDS })
|
|
270
|
+
: r
|
|
271
|
+
)
|
|
272
|
+
);
|
|
273
|
+
// `habits: off` — exit 3. It reads only the cache an earlier on-period left.
|
|
274
|
+
const habitsOff = {
|
|
275
|
+
ok: false,
|
|
276
|
+
reason: "off",
|
|
277
|
+
message: "habits is off. Turn it on with: orc config set habits observe (or propose). Nothing is ever applied without your yes.",
|
|
278
|
+
mode: "off",
|
|
279
|
+
modes: ["off", "observe", "propose"],
|
|
280
|
+
kept: 212,
|
|
281
|
+
on: ["orc config set habits observe", "orc config set habits propose"],
|
|
282
|
+
};
|
|
283
|
+
// Exit 1 — no ASK lines yet in this window.
|
|
284
|
+
const habitsEmpty = Object.assign(show("propose", []), {
|
|
285
|
+
window: "30d",
|
|
286
|
+
traces: 0,
|
|
287
|
+
answers: 0,
|
|
288
|
+
excluded_runs: [],
|
|
289
|
+
tiles: { runs: 0, answers: 0, saved: 0, saved_prev: 0, qpr: null, qpr_prev: null },
|
|
290
|
+
portrait: [],
|
|
291
|
+
proposal: null,
|
|
292
|
+
next_run: [],
|
|
293
|
+
});
|
|
294
|
+
habitsEmpty.rhythm = Object.assign({}, habitsEmpty.rhythm, {
|
|
295
|
+
heat: heatMap(0),
|
|
296
|
+
lanes: [],
|
|
297
|
+
qpr: Array(12).fill(null),
|
|
298
|
+
how: Array.from({ length: 12 }, () => Object.fromEntries(BY_SET.map((b) => [b, 0]))),
|
|
299
|
+
days: habitsEmpty.rhythm.days.map((d) => ({ date: d.date, answers: 0 })),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// `orc habit log --json --limit 40` — every by= value.
|
|
303
|
+
const LOG_ROWS = [
|
|
304
|
+
["quick.q3.offer.review", "orc-quick", "review-first", "user", "review-first"],
|
|
305
|
+
["mini.phase-x.mock", "orc-mini", "skip", "learned", "skip"],
|
|
306
|
+
["orc.phase-8.ship", "orc", "pr", "user", "pr"],
|
|
307
|
+
["orc.intake.testgen", "orc", "author", "config", null],
|
|
308
|
+
["orc.phase-5-5.security", "orc", "skip", "user", null],
|
|
309
|
+
["any.analysis.depth", "orc-analyze", "standard", "ledger", null],
|
|
310
|
+
["orc.phase-2.pause", "orc", "every-2", "user", "every-1"],
|
|
311
|
+
["any.stop.where", "orc-mini", "here", "default", null],
|
|
312
|
+
["quick.q3.offer.tests", "orc-quick", "update", "user", null],
|
|
313
|
+
["orc.phase-1.planner", "orc", "planner", "user", null],
|
|
314
|
+
];
|
|
315
|
+
const habitLog = {
|
|
316
|
+
ok: true,
|
|
317
|
+
count: LOG_ROWS.length,
|
|
318
|
+
total: 412,
|
|
319
|
+
events: LOG_ROWS.map(([qid, lane, chose, by, pre], i) => ({
|
|
320
|
+
id: "H-" + (0x1a2b3c + i * 977).toString(16).slice(0, 6),
|
|
321
|
+
run: `run-${lane}-fixture-${i}.txt`,
|
|
322
|
+
lane,
|
|
323
|
+
at: NOW - (i * 3 + 1) * 3600000,
|
|
324
|
+
qid,
|
|
325
|
+
offered: [chose, "other"],
|
|
326
|
+
rec: pre || "none",
|
|
327
|
+
chose,
|
|
328
|
+
by,
|
|
329
|
+
pre,
|
|
330
|
+
ctx: {},
|
|
331
|
+
})),
|
|
332
|
+
};
|
|
333
|
+
const habitLogEmpty = { ok: true, count: 0, total: 0, events: [] };
|
|
334
|
+
|
|
335
|
+
// `orc habit log --states --json` — every `by` a change can carry.
|
|
336
|
+
const habitStates = {
|
|
337
|
+
ok: true,
|
|
338
|
+
count: 5,
|
|
339
|
+
history: [
|
|
340
|
+
{ id: "H-7d20b3", from: "applied", to: "stale", by: "system", at: NOW - 3 * DAY, reason: "2 overrides in the last 3", inverse: null },
|
|
341
|
+
{ id: "H-5f6e21", from: "proposed", to: "declined", by: "panel", at: NOW - 6 * DAY, reason: "not now", inverse: "orc habit reset H-5f6e21" },
|
|
342
|
+
{ id: "H-c40d9a", from: "proposed", to: "never-ask", by: "lane", at: NOW - 12 * DAY, reason: "never ask", inverse: "orc habit reset H-c40d9a" },
|
|
343
|
+
{ id: "H-a19f02", from: "proposed", to: "applied", by: "user", at: NOW - 21 * DAY, reason: "usual: skip (12 of 13)", inverse: "orc habit forget H-a19f02" },
|
|
344
|
+
{ id: "H-0b77c5", from: "proposed", to: "applied", by: "lane", at: NOW - 30 * DAY, reason: "usual: pr (10 of 11)", inverse: "orc habit forget H-0b77c5" },
|
|
345
|
+
],
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// `orc habit points --json` — a slice of the registry (the shape, not all 30).
|
|
349
|
+
const habitPoints = {
|
|
350
|
+
ok: true,
|
|
351
|
+
count: 2,
|
|
352
|
+
points: [
|
|
353
|
+
{ qid: "mini.phase-x.mock", lanes: ["orc-mini"], phase: "phase-x", file: "orc-mini/SKILL.md", title: "Mocked runnable example", options: { build: "build the example", skip: "skip it" }, class: "apply", careful: null, key: "mock_example", values: { build: "on", skip: "off" }, ctx: [], why: "a mock example has no careful side, so both answers can be learned" },
|
|
354
|
+
{ qid: "quick.q3.offer.review", lanes: ["orc-quick"], phase: "q3", file: "orc-quick/SKILL.md", title: "Code review before commit", options: { "review-first": "review first", "commit-direct": "commit directly", stop: "stop" }, class: "suggest", careful: "review-first", key: null, ctx: ["kind", "branch"], why: "a review is a dispatch, so its gate is still asked — the habit only orders the offer" },
|
|
355
|
+
],
|
|
356
|
+
meta_qids: ["habit.proposal"],
|
|
357
|
+
classes: ["apply", "suggest", "never"],
|
|
358
|
+
rule: RULE,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// ── Gotchas ─────────────────────────────────────────────────────────────────
|
|
362
|
+
// The EXTRA ledger entries (G-004 …) that, with Knowledge's three, carry every
|
|
363
|
+
// source and every status. knowledge.js appends them to `gotchas`.
|
|
364
|
+
const SOURCES = ["repair", "drift", "review", "verify", "defect", "pr", "sonar", "sarif", "ci", "dismissal"];
|
|
365
|
+
const KIND_OF = { repair: "repair", drift: "drift", review: "review", verify: "verify", defect: "repair", ci: "repair", pr: "review", sonar: "review", sarif: "review", dismissal: "review" };
|
|
366
|
+
const EXTRA = [
|
|
367
|
+
["G-004", "orders", "drift", { rule: null, scope: "src/orders/**/*.ts", symptom: "the schema and the client disagree on totalCents", fix: "regenerate the client after a schema change", origin: "run-orc-orders-100926-091200 · drift" }, "active", [0, 1, 0, 2, 1, 0, 1]],
|
|
368
|
+
["G-005", "billing", "defect", { rule: "repro red→green", scope: "src/billing/**", symptom: "a refund is booked twice on retry", fix: "key the refund on the idempotency key", origin: "issue 88 · 03-09-2026" }, "active", [0, 0, 1, 0, 0, 2, 1]],
|
|
369
|
+
["G-006", "api", "pr", { rule: "PR 142 · thread 3", scope: "src/routes/**/*.js", symptom: "a handler returns 200 on a validation error", fix: "return 422 with the field name", origin: "PR 142 · thread 3 · 08-09-2026" }, "active", [1, 0, 2, 1, 0, 1, 3]],
|
|
370
|
+
["G-007", "client", "sonar", { rule: "typescript:S1854", scope: "client/src/**/*.vue", symptom: "a dead store in a computed property", fix: "remove the unused assignment", origin: "sonar · 11-09-2026" }, "active", [2, 1, 1, 0, 2, 1, 1]],
|
|
371
|
+
["G-008", "infra", "sarif", { rule: "js/path-injection", scope: "src/export/**", symptom: "a file path built from a query string", fix: "resolve under the export root and refuse ..", origin: "sarif codeql.sarif · 12-09-2026" }, "active", [0, 0, 0, 1, 0, 1, 0]],
|
|
372
|
+
["G-009", "build", "ci", { rule: "ci: lint", scope: "src/**/*.ts", symptom: "CI lint fails on an unused import", fix: "run the linter before you push", origin: "CI run 5521 · 15-09-2026" }, "quiet", [0, 0, 0, 0, 0, 0, 0]],
|
|
373
|
+
["G-010", "i18n", "dismissal", { rule: null, scope: "client/src/i18n/**", symptom: "long translation files flagged as too long", fix: "(do not flag)", origin: "dismissal · 18-09-2026", polarity: "suppress" }, "active", [0, 1, 0, 1, 1, 0, 1]],
|
|
374
|
+
["G-011", "legacy", "repair", { rule: null, scope: "src/legacy-session/**", symptom: "the old session cookie is read twice", fix: "read it once in the middleware", origin: "run-orc-auth-020826-100000 · repair" }, "orphaned", [0, 0, 0, 0, 0, 0, 0]],
|
|
375
|
+
];
|
|
376
|
+
const gotchaExtraEntries = EXTRA.map(([id, area, source, f]) => ({
|
|
377
|
+
id,
|
|
378
|
+
area,
|
|
379
|
+
kind: KIND_OF[source],
|
|
380
|
+
hits: 3,
|
|
381
|
+
last_seen: "20-09-2026",
|
|
382
|
+
trigger: f.symptom,
|
|
383
|
+
fields: Object.assign({ trigger: f.symptom, symptom: f.symptom, cause: "(see origin)", fix: f.fix, scope: f.scope, origin: f.origin, hits: "3", last_seen: "20-09-2026", source }, f.rule ? { rule: f.rule } : {}, f.polarity ? { polarity: f.polarity } : {}),
|
|
384
|
+
}));
|
|
385
|
+
// The panel view `orc gotcha list --json` carries as `panel`, for G-001 … G-011.
|
|
386
|
+
const BASE_ROWS = [
|
|
387
|
+
{ id: "G-001", source: "repair", status: "active", status_reason: null, rule: null, scope: "tests/**/*.test.js", fix: "close the http server in afterAll and await it", symptom: "`npm test` never exits; CI times out at 10 minutes", polarity: "flag", origin: "run-orc-add-billing-050826-141233 · TDD repair round 2", weeks: [1, 0, 2, 1, 0, 1, 2] },
|
|
388
|
+
{ id: "G-002", source: "review", status: "active", status_reason: null, rule: null, scope: "client/src/**/*.tsx", fix: "use parseInZone from src/time/zone.ts", symptom: "bookings land one day early for users west of UTC", polarity: "flag", origin: "run-orc-bookings-220726-093000 · review", weeks: [0, 0, 1, 0, 0, 1, 0] },
|
|
389
|
+
{ id: "G-003", source: "verify", status: "quiet", status_reason: null, rule: null, scope: ".github/workflows/**", fix: "set NODE_OPTIONS in the workflow, not in package.json", symptom: "the CI build is OOM-killed with no error line", polarity: "flag", origin: "run-orc-ci-140626-110000 · verify", weeks: [0, 0, 0, 0, 0, 0, 0] },
|
|
390
|
+
];
|
|
391
|
+
const panelRows = BASE_ROWS.concat(
|
|
392
|
+
EXTRA.map(([id, , source, f, status, weeks]) => ({
|
|
393
|
+
id,
|
|
394
|
+
source,
|
|
395
|
+
status,
|
|
396
|
+
status_reason: status === "orphaned" ? "its scope matches no tracked file" : null,
|
|
397
|
+
rule: f.rule || null,
|
|
398
|
+
scope: f.scope,
|
|
399
|
+
fix: f.fix,
|
|
400
|
+
symptom: f.symptom,
|
|
401
|
+
polarity: f.polarity || "flag",
|
|
402
|
+
origin: f.origin,
|
|
403
|
+
weeks,
|
|
404
|
+
}))
|
|
405
|
+
);
|
|
406
|
+
const bySource = {};
|
|
407
|
+
for (const r of panelRows) bySource[r.source] = (bySource[r.source] || 0) + 1;
|
|
408
|
+
const gotchaPanel = {
|
|
409
|
+
sources: SOURCES,
|
|
410
|
+
statuses: ["active", "candidate", "quiet", "orphaned"],
|
|
411
|
+
by_source: SOURCES.filter((s) => bySource[s]).map((s) => ({ source: s, n: bySource[s] })),
|
|
412
|
+
status_counts: { active: panelRows.filter((r) => r.status === "active").length, candidate: 1, quiet: 2, orphaned: 1 },
|
|
413
|
+
rows: panelRows,
|
|
414
|
+
sync: {
|
|
415
|
+
last_sync: new Date(NOW - 2 * 3600000).toISOString(),
|
|
416
|
+
sources: [
|
|
417
|
+
{ name: "sonar", state: "ok", last_ok: new Date(NOW - 2 * 3600000).toISOString(), last_error: null },
|
|
418
|
+
{ name: "pr", state: "ok", last_ok: new Date(NOW - 2 * 3600000).toISOString(), last_error: null },
|
|
419
|
+
{ name: "ci", state: "timed out", last_ok: new Date(NOW - 3 * DAY).toISOString(), last_error: "ci timed out" },
|
|
420
|
+
{ name: "defects", state: "not configured", last_ok: null, last_error: null },
|
|
421
|
+
],
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// `orc gotcha card --files … --json --full` — a match, and the zero-match ANSWER.
|
|
426
|
+
const gotchaCard = {
|
|
427
|
+
ok: true,
|
|
428
|
+
lane: null,
|
|
429
|
+
text: "gotchas (3 of 11 match):\nG-006 PR 142 · thread 3 · src/routes/**/*.js · a handler returns 200 on a validation error → return 422 with the field name (3×)\nG-001 repair · tests/**/*.test.js · `npm test` never exits; CI times out at 10 minutes → close the http server in afterAll and await it (7×)\ndo not flag: G-010 · client/src/i18n/** · long translation files flagged as too long (the team disputed this 3×)",
|
|
430
|
+
ids: ["G-006", "G-001", "G-010"],
|
|
431
|
+
matched: 3,
|
|
432
|
+
known: 11,
|
|
433
|
+
dropped: 0,
|
|
434
|
+
orphaned: ["G-011"],
|
|
435
|
+
quiet: 1,
|
|
436
|
+
budget: 600,
|
|
437
|
+
tokens: 96,
|
|
438
|
+
rows: [
|
|
439
|
+
{ id: "G-006", type: "flag", text: "G-006 PR 142 · thread 3 · src/routes/**/*.js · a handler returns 200 on a validation error → return 422 with the field name (3×)", rank: 2.1, miss: false, kept: true, dropped: null },
|
|
440
|
+
{ id: "G-001", type: "flag", text: "G-001 repair · tests/**/*.test.js · `npm test` never exits; CI times out at 10 minutes → close the http server in afterAll and await it (7×)", rank: 1.7, miss: false, kept: true, dropped: null },
|
|
441
|
+
{ id: "G-010", type: "suppress", text: "do not flag: G-010 · client/src/i18n/** · long translation files flagged as too long (the team disputed this 3×)", rank: 0.9, miss: false, kept: true, dropped: null },
|
|
442
|
+
],
|
|
443
|
+
orphaned_rows: [{ id: "G-011", why: "its scope matches no tracked file" }],
|
|
444
|
+
};
|
|
445
|
+
const gotchaCardNone = { ok: true, lane: null, text: "", ids: [], matched: 0, known: 11, dropped: 0, orphaned: [], quiet: 1, budget: 600, tokens: 0 };
|
|
446
|
+
|
|
447
|
+
// `orc gotcha quality --json` — above the floor, and below it (exit 1).
|
|
448
|
+
const SERIES = [0.48, 0.5, 0.46, 0.55, 0.58, 0.52, 0.6, 0.63, 0.61, 0.66, 0.69, 0.71];
|
|
449
|
+
const P3 = [4.1, 3.8, 3.9, 3.2, 3.0, 2.8, 2.6, 2.3, 2.1, 1.9, 1.7, 1.6];
|
|
450
|
+
const gotchaQuality = {
|
|
451
|
+
ok: true,
|
|
452
|
+
window_days: 90,
|
|
453
|
+
reviews: 12,
|
|
454
|
+
floor: 5,
|
|
455
|
+
below_floor: false,
|
|
456
|
+
findings: 118,
|
|
457
|
+
categories: [
|
|
458
|
+
{ category: "security", findings: 14, addressed: 12, disputed: 1, wontfix: 1, open: 0, acceptance: 0.857, noisy: false },
|
|
459
|
+
{ category: "functional.logic", findings: 31, addressed: 21, disputed: 6, wontfix: 2, open: 2, acceptance: 0.724, noisy: false },
|
|
460
|
+
{ category: "test", findings: 22, addressed: 10, disputed: 8, wontfix: 2, open: 2, acceptance: 0.5, noisy: false },
|
|
461
|
+
{ category: "evolvability.structure", findings: 28, addressed: 7, disputed: 16, wontfix: 3, open: 2, acceptance: 0.269, noisy: true },
|
|
462
|
+
{ category: "evolvability.documentation", findings: 23, addressed: 9, disputed: 9, wontfix: 3, open: 2, acceptance: 0.429, noisy: false },
|
|
463
|
+
],
|
|
464
|
+
gotchas: [{ id: "G-001", helpful: 4, harmful: 0, retire: false }, { id: "G-010", helpful: 0, harmful: 3, retire: true }],
|
|
465
|
+
p3_per_review: 2.75,
|
|
466
|
+
trend: { first_half: 0.53, second_half: 0.67, direction: "up" },
|
|
467
|
+
target: 0.6,
|
|
468
|
+
bands: { ok: 0.6, warn: 0.35 },
|
|
469
|
+
series: SERIES.map((a, i) => ({ run: `run-orc-review-${i + 1}`, at: new Date(NOW - (12 - i) * 4 * DAY).toISOString(), findings: 10, acceptance: a, p3: P3[i] })),
|
|
470
|
+
floor_line: null,
|
|
471
|
+
};
|
|
472
|
+
const gotchaQualityLow = {
|
|
473
|
+
ok: true,
|
|
474
|
+
window_days: 30,
|
|
475
|
+
reviews: 3,
|
|
476
|
+
floor: 5,
|
|
477
|
+
below_floor: true,
|
|
478
|
+
findings: 19,
|
|
479
|
+
categories: [{ category: "test", findings: 7, addressed: 3, disputed: 3, wontfix: 0, open: 1, acceptance: 0.5, noisy: false }],
|
|
480
|
+
gotchas: [],
|
|
481
|
+
p3_per_review: 2.33,
|
|
482
|
+
trend: { first_half: null, second_half: 0.5, direction: null },
|
|
483
|
+
target: 0.6,
|
|
484
|
+
bands: { ok: 0.6, warn: 0.35 },
|
|
485
|
+
series: [
|
|
486
|
+
{ run: "run-orc-review-a", at: new Date(NOW - 9 * DAY).toISOString(), findings: 6, acceptance: 0.5, p3: 2 },
|
|
487
|
+
{ run: "run-orc-review-b", at: new Date(NOW - 5 * DAY).toISOString(), findings: 7, acceptance: null, p3: 3 },
|
|
488
|
+
{ run: "run-orc-review-c", at: new Date(NOW - 1 * DAY).toISOString(), findings: 6, acceptance: 0.6, p3: 2 },
|
|
489
|
+
],
|
|
490
|
+
floor_line: "5 reviews needed — 3 so far.",
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// One candidate, every key `publicCand` emits.
|
|
494
|
+
function cand(o) {
|
|
495
|
+
return Object.assign({ id: "", kind: "candidate", key: "", rule: null, sig: null, category: null, scope: null, scope_refused: null, lang: null, source: "review", cases: 0, addressed: 0, disputed: 0, prs: 0, strong: false, watch: false, severity: null, cwe: null, last_at: new Date(NOW - 2 * DAY).toISOString(), promote: false, promote_rule: null, why: null, needs: null, obs: [] }, o);
|
|
496
|
+
}
|
|
497
|
+
// `orc gotcha list --candidates --json` — a candidate and a proposed suppression.
|
|
498
|
+
const gotchaCandidates = {
|
|
499
|
+
ok: true,
|
|
500
|
+
count: 3,
|
|
501
|
+
candidates: [
|
|
502
|
+
cand({ id: "C-7e11a0", kind: "candidate", key: "pr|a missing await on a repository call", source: "pr", rule: "PR 151 · thread 2", sig: "a missing await on a repository call", category: "functional.timing", severity: "P2", scope: "src/repos/**/*.ts", cases: 2, addressed: 2, disputed: 0, prs: 1, strong: true, watch: true, needs: "1 more addressed case in another PR, or your yes" }),
|
|
503
|
+
],
|
|
504
|
+
suppressions: [
|
|
505
|
+
cand({ id: "C-2b91e0", kind: "suppression", key: "suppress|long test names", source: "dismissal", sig: "long test names", category: "evolvability.documentation", severity: "P3", scope: "**/*.spec.*", cases: 3, addressed: 0, disputed: 3, prs: 3, needs: "a person accepts it: orc gotcha accept <id> (a suppression is never automatic)" }),
|
|
506
|
+
cand({ id: "C-a04c55", kind: "suppression", key: "suppress|long translation files", source: "dismissal", sig: "long translation files", category: "evolvability.structure", severity: "P3", scope: "client/src/i18n/**", cases: 4, addressed: 0, disputed: 4, prs: 2, needs: "a person accepts it: orc gotcha accept <id> (a suppression is never automatic)" }),
|
|
507
|
+
],
|
|
508
|
+
project_actions: [],
|
|
509
|
+
retire: [{ id: "G-010", helpful: 0, harmful: 3, proposal: "retire — ORC never applies it; delete the block yourself" }],
|
|
510
|
+
quiet: ["G-003", "G-009"],
|
|
511
|
+
orphaned: [{ id: "G-011", why: "its scope matches no tracked file" }],
|
|
512
|
+
observations: 164,
|
|
513
|
+
rules: { cases: 3, prs: 2, window_days: 90, jaccard_min: 0.6, suppress_disputes: 2, do_line_cases: 5 },
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
module.exports = {
|
|
517
|
+
habitsPropose,
|
|
518
|
+
habitsObserve,
|
|
519
|
+
habitsOff,
|
|
520
|
+
habitsEmpty,
|
|
521
|
+
habitLog,
|
|
522
|
+
habitLogEmpty,
|
|
523
|
+
habitStates,
|
|
524
|
+
habitPoints,
|
|
525
|
+
gotchaExtraEntries,
|
|
526
|
+
gotchaPanel,
|
|
527
|
+
gotchaCard,
|
|
528
|
+
gotchaCardNone,
|
|
529
|
+
gotchaQuality,
|
|
530
|
+
gotchaQualityLow,
|
|
531
|
+
gotchaCandidates,
|
|
532
|
+
};
|
|
@@ -25,6 +25,7 @@ const { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregister
|
|
|
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");
|
|
28
|
+
const B = require("./behaviour.js");
|
|
28
29
|
const { boundary } = require("./boundary.js");
|
|
29
30
|
const { usage, waitLanes, waitStatus } = require("./wait.js");
|
|
30
31
|
const hookui = require("./hookui.js");
|
|
@@ -339,6 +340,30 @@ module.exports.get = function get(route, q) {
|
|
|
339
340
|
return gotchaPrunePreview;
|
|
340
341
|
case "/api/stats":
|
|
341
342
|
return stats;
|
|
343
|
+
// v2.0.0 W7 — the Behaviour panel. The `habits` mode is the one fixture
|
|
344
|
+
// with STATE: the learning switch posts `/api/config/set` (below), and
|
|
345
|
+
// the next read answers in that mode, so off · observe · propose are all
|
|
346
|
+
// reachable from the panel itself. The 30d window is the EMPTY answer
|
|
347
|
+
// (no ASK lines yet, and below the review floor) — a window with nothing
|
|
348
|
+
// in it is exactly where those two states happen on a real machine.
|
|
349
|
+
case "/api/habits":
|
|
350
|
+
if (HABITS_MODE.value === "off") return B.habitsOff;
|
|
351
|
+
if (q && q.window === "30d") return B.habitsEmpty;
|
|
352
|
+
return HABITS_MODE.value === "observe" ? B.habitsObserve : B.habitsPropose;
|
|
353
|
+
case "/api/habits/log":
|
|
354
|
+
return HABITS_MODE.value === "off" ? B.habitLogEmpty : B.habitLog;
|
|
355
|
+
case "/api/habits/states":
|
|
356
|
+
return B.habitStates;
|
|
357
|
+
case "/api/habits/points":
|
|
358
|
+
return B.habitPoints;
|
|
359
|
+
// A path under a scope some gotcha holds answers the card; anything else is
|
|
360
|
+
// the zero-match ANSWER, which is the CLI's exit 1 and "no card".
|
|
361
|
+
case "/api/gotcha/card":
|
|
362
|
+
return /src\/routes\/|tests\/|client\/src\/i18n\//.test(String((q && q.files) || "")) ? B.gotchaCard : B.gotchaCardNone;
|
|
363
|
+
case "/api/gotcha/quality":
|
|
364
|
+
return q && q.window === "30d" ? B.gotchaQualityLow : B.gotchaQuality;
|
|
365
|
+
case "/api/gotcha/candidates":
|
|
366
|
+
return B.gotchaCandidates;
|
|
342
367
|
case "/api/diy":
|
|
343
368
|
return diy;
|
|
344
369
|
case "/api/crosslink":
|
|
@@ -549,7 +574,16 @@ module.exports.get = function get(route, q) {
|
|
|
549
574
|
// outcome is a file on disk, not a state.
|
|
550
575
|
const TEST_ENV = { "api-users": [1, testEnvUnhealthy], "orders-api": [1, testEnvAbsent], "staging-checkout": [2, testEnvRemote] };
|
|
551
576
|
|
|
577
|
+
// The one piece of fixture STATE: which `habits` mode the Behaviour panel sees.
|
|
578
|
+
// Only the `habits` key is canned — every other config write still answers
|
|
579
|
+
// "nothing ran", because its outcome is a file on disk, not a state.
|
|
580
|
+
const HABITS_MODE = { value: "propose" };
|
|
581
|
+
|
|
552
582
|
module.exports.post = function post(route, body) {
|
|
583
|
+
if (route === "/api/config/set" && body && body.key === "habits" && ["off", "observe", "propose"].includes(String(body.value))) {
|
|
584
|
+
HABITS_MODE.value = String(body.value);
|
|
585
|
+
return { exit_code: 0, data: { ok: true, key: "habits", value: HABITS_MODE.value } };
|
|
586
|
+
}
|
|
553
587
|
// `add` answers OK so the CONNECT FLOW can be walked end to end in fixture
|
|
554
588
|
// mode — the add is only the step before the test, and the test's outcome is
|
|
555
589
|
// the state worth designing. Every other write still answers "nothing ran".
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
fixture is worse than no fixture. */
|
|
13
13
|
|
|
14
14
|
const { PROJECT } = require("./shell.js");
|
|
15
|
+
const { gotchaExtraEntries, gotchaPanel } = require("./behaviour.js");
|
|
15
16
|
|
|
16
17
|
const wiki = {
|
|
17
18
|
state: "registered",
|
|
@@ -251,7 +252,7 @@ const patterns = {
|
|
|
251
252
|
|
|
252
253
|
const gotchas = {
|
|
253
254
|
file: PROJECT + "/.claude/orc/gotchas.md",
|
|
254
|
-
count:
|
|
255
|
+
count: 11,
|
|
255
256
|
// v0.49.1: the cap is what makes the count mean anything, and only `prune`
|
|
256
257
|
// used to print it — so "11 of 40" was not renderable from this payload.
|
|
257
258
|
gotchas_max: 40,
|
|
@@ -262,7 +263,12 @@ const gotchas = {
|
|
|
262
263
|
{ id: "G-001", area: "express", kind: "repair", hits: 7, last_seen: "05-08-2026", trigger: "Jest suite hangs unless the server handle is closed in afterAll", fields: { trigger: "Jest suite hangs unless the server handle is closed in afterAll", symptom: "`npm test` never exits; CI times out at 10 minutes", fix: "close the http server in afterAll and await it", why: "supertest keeps the listener open, and Jest waits for the handle", first_seen: "14-05-2026", hits: "7", last_seen: "05-08-2026" } },
|
|
263
264
|
{ id: "G-002", area: "react", kind: "review", hits: 2, last_seen: "22-07-2026", trigger: "Date pickers must use the tz-aware helper, never new Date(string)", fields: { trigger: "Date pickers must use the tz-aware helper, never new Date(string)", symptom: "bookings land one day early for users west of UTC", fix: "use parseInZone from src/time/zone.ts", why: "new Date(string) parses as UTC and then renders local", first_seen: "02-06-2026", hits: "2", last_seen: "22-07-2026" } },
|
|
264
265
|
{ id: "G-003", area: "build", kind: "verify", hits: 0, last_seen: "14-06-2026", trigger: "`npm run build` needs NODE_OPTIONS=--max-old-space-size=4096 on CI", fields: { trigger: "`npm run build` needs NODE_OPTIONS=--max-old-space-size=4096 on CI", symptom: "the CI build is OOM-killed with no error line", fix: "set NODE_OPTIONS in the workflow, not in package.json", why: "the runner has less memory than a dev laptop", first_seen: "14-06-2026", hits: "0", last_seen: "14-06-2026" } },
|
|
266
|
+
// v2.0.0 W7 — eight more, so every source and status the Behaviour
|
|
267
|
+
// panel's Gotchas tab draws is designable. They live in behaviour.js.
|
|
268
|
+
...gotchaExtraEntries,
|
|
265
269
|
],
|
|
270
|
+
// v2.0.0 W7 — the panel view `orc gotcha list --json` carries.
|
|
271
|
+
panel: gotchaPanel,
|
|
266
272
|
};
|
|
267
273
|
|
|
268
274
|
// STALE, so the Flow panel's gated state is designable.
|
|
@@ -27,6 +27,22 @@ const stats = {
|
|
|
27
27
|
// v1.8.2 W4b — the code-graph row. `paid` is exact; the avoided pair is an
|
|
28
28
|
// ESTIMATE and always a RANGE, which is why the panel never shows one number.
|
|
29
29
|
graph: { calls: 41, runs: 6, paid: 4912, avoided_low: 18900, avoided_high: 71400, estimate: true },
|
|
30
|
+
// v2.0.0 W6b (Q7) — questions per run. `null` = could not be computed.
|
|
31
|
+
questions: {
|
|
32
|
+
runs: 61,
|
|
33
|
+
per_run_p50: 2,
|
|
34
|
+
per_run_trend: [
|
|
35
|
+
{ run: "run-quick-fix-guest-charge-060826-101500.txt", lane: "quick", date: "2026-08-06", asked: 3, answered_for_you: 1 },
|
|
36
|
+
{ run: "run-mini-add-export-070826-140000.txt", lane: "mini", date: "2026-08-07", asked: 0, answered_for_you: 2 },
|
|
37
|
+
],
|
|
38
|
+
asked: 131,
|
|
39
|
+
answered_for_you: 58,
|
|
40
|
+
subagent_questions: 12,
|
|
41
|
+
by: { user: 104, ledger: 31, learned: 15, config: 20, default: 7 },
|
|
42
|
+
by_lane: { quick: { runs: 17, asked: 44, answered_for_you: 30, per_run_p50: 2 }, mini: { runs: 9, asked: 11, answered_for_you: 6, per_run_p50: 1 } },
|
|
43
|
+
by_point: { "quick.q3.offer.review": { asked: 12, answered_for_you: 9, lanes: ["quick"] } },
|
|
44
|
+
rule: "asked = ASK by=user|learned + QUESTION count=<n>; answered for you = ASK by=ledger|config|default",
|
|
45
|
+
},
|
|
30
46
|
};
|
|
31
47
|
|
|
32
48
|
/* ============================================================ v0.46.0 ====== */
|