@bradheitmann/odin-sentinel 0.4.12 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +24 -17
  3. package/dist/src/harness-pacing/index.d.ts +10 -0
  4. package/dist/src/harness-pacing/index.js +11 -0
  5. package/dist/src/harness-pacing/index.js.map +1 -0
  6. package/dist/src/harness-pacing/recommend.d.ts +28 -0
  7. package/dist/src/harness-pacing/recommend.js +74 -0
  8. package/dist/src/harness-pacing/recommend.js.map +1 -0
  9. package/dist/src/harness-pacing/schema.d.ts +28 -0
  10. package/dist/src/harness-pacing/schema.js +2 -0
  11. package/dist/src/harness-pacing/schema.js.map +1 -0
  12. package/dist/src/harness-pacing/storage.d.ts +32 -0
  13. package/dist/src/harness-pacing/storage.js +74 -0
  14. package/dist/src/harness-pacing/storage.js.map +1 -0
  15. package/dist/src/mcp/server.js +29 -2
  16. package/dist/src/mcp/server.js.map +1 -1
  17. package/dist/src/odin-watch/backends/cmux.d.ts +6 -0
  18. package/dist/src/odin-watch/backends/cmux.js +39 -0
  19. package/dist/src/odin-watch/backends/cmux.js.map +1 -0
  20. package/dist/src/odin-watch/backends/tmux.d.ts +6 -0
  21. package/dist/src/odin-watch/backends/tmux.js +40 -0
  22. package/dist/src/odin-watch/backends/tmux.js.map +1 -0
  23. package/dist/src/odin-watch/classifier.d.ts +27 -0
  24. package/dist/src/odin-watch/classifier.js +182 -0
  25. package/dist/src/odin-watch/classifier.js.map +1 -0
  26. package/dist/src/odin-watch/index.d.ts +2 -0
  27. package/dist/src/odin-watch/index.js +200 -0
  28. package/dist/src/odin-watch/index.js.map +1 -0
  29. package/dist/src/odin-watch/snapshotter.d.ts +11 -0
  30. package/dist/src/odin-watch/snapshotter.js +2 -0
  31. package/dist/src/odin-watch/snapshotter.js.map +1 -0
  32. package/dist/src/odin-watch/writers.d.ts +8 -0
  33. package/dist/src/odin-watch/writers.js +27 -0
  34. package/dist/src/odin-watch/writers.js.map +1 -0
  35. package/dist/src/protocol/index.d.ts +3 -1
  36. package/dist/src/protocol/index.js +4 -1
  37. package/dist/src/protocol/index.js.map +1 -1
  38. package/dist/src/protocol/repository.d.ts +14 -0
  39. package/dist/src/protocol/repository.js +25 -1
  40. package/dist/src/protocol/repository.js.map +1 -1
  41. package/dist/src/protocol/schemas.d.ts +144 -0
  42. package/dist/src/protocol/schemas.js +23 -0
  43. package/dist/src/protocol/schemas.js.map +1 -1
  44. package/dist/src/protocol/service.d.ts +19 -2
  45. package/dist/src/protocol/service.js +89 -3
  46. package/dist/src/protocol/service.js.map +1 -1
  47. package/dist/src/protocol/surface-layout.d.ts +20 -0
  48. package/dist/src/protocol/surface-layout.js +20 -0
  49. package/dist/src/protocol/surface-layout.js.map +1 -1
  50. package/dist/src/protocol/version.d.ts +2 -2
  51. package/dist/src/protocol/version.js +2 -2
  52. package/dist/src/protocol/version.js.map +1 -1
  53. package/dist/src/utils/execFileNoThrow.d.ts +5 -0
  54. package/dist/src/utils/execFileNoThrow.js +18 -0
  55. package/dist/src/utils/execFileNoThrow.js.map +1 -0
  56. package/docs/adapters/cmux-adapter.md +168 -0
  57. package/docs/adapters/herdr-adapter.md +150 -0
  58. package/docs/adapters/minimux-adapter.md +152 -0
  59. package/docs/adapters/plain-terminal.md +80 -0
  60. package/docs/adapters/tmux-adapter.md +150 -0
  61. package/docs/guides/quick-start.md +7 -7
  62. package/docs/guides/quickstart-prompts.md +4 -4
  63. package/docs/lattice/odin-lattice-design.md +555 -0
  64. package/docs/reference/distribution.md +11 -5
  65. package/docs/reference/public-surface-audit.md +3 -3
  66. package/package.json +7 -5
  67. package/plugins/odin-scp/.claude-plugin/plugin.json +2 -2
  68. package/plugins/odin-scp/README.md +6 -6
  69. package/plugins/odin-scp/skills/odin-scp/CHANGELOG.md +12 -0
  70. package/plugins/odin-scp/skills/odin-scp/SKILL.md +196 -3
  71. package/plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md +0 -2
  72. package/protocol/SCP.md +2 -2
  73. package/protocol/bootstrap-skill.md +196 -3
  74. package/protocol/closeout.yaml +1 -1
  75. package/protocol/delegation.yaml +1 -1
  76. package/protocol/mission-frontrun/droids-scrutiny-feature-reviewer.md +70 -0
  77. package/protocol/mission-frontrun/orchestrator-contract.md +70 -0
  78. package/protocol/mission-frontrun/scrutiny-feature-reviewer-contract.md +73 -0
  79. package/protocol/mission-frontrun/scrutiny-validator-contract.md +77 -0
  80. package/protocol/mission-frontrun/worker-contract.md +66 -0
  81. package/protocol/model-profiles.yaml +8 -1
  82. package/protocol/receipts/boot-receipt.yaml +13 -0
  83. package/protocol/role-cards/dev-worker.md +74 -0
  84. package/protocol/role-cards/exec-asst.md +83 -0
  85. package/protocol/role-cards/exec-pm.md +66 -0
  86. package/protocol/role-cards/qa-worker.md +71 -0
  87. package/protocol/role-cards/team-pm.md +67 -0
  88. package/protocol/roles.yaml +1 -1
  89. package/protocol/skill-references/canonical-introduction-prompt.md +0 -2
  90. package/protocol/topology.yaml +1 -1
  91. package/scripts/audit/public-surface.mjs +27 -2
  92. package/scripts/audit/verify-pack.mjs +121 -5
@@ -0,0 +1,40 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { execFileNoThrow } from "../../utils/execFileNoThrow.js";
5
+ export class TmuxBackend {
6
+ archiveDir;
7
+ constructor(archiveDir) {
8
+ this.archiveDir = archiveDir;
9
+ }
10
+ async capture(pane_id) {
11
+ const result = await execFileNoThrow("tmux", [
12
+ "capture-pane",
13
+ "-p",
14
+ "-t",
15
+ pane_id,
16
+ ]);
17
+ const text = result.status === 0 ? result.stdout : "";
18
+ const ts = Date.now();
19
+ const hash = createHash("sha256").update(text).digest("hex");
20
+ // Archive raw screen text
21
+ try {
22
+ mkdirSync(this.archiveDir, { recursive: true });
23
+ const filename = `${pane_id.replace(/[^a-zA-Z0-9_-]/g, "_")}_${ts}.txt`;
24
+ writeFileSync(join(this.archiveDir, filename), text, {
25
+ encoding: "utf8",
26
+ });
27
+ }
28
+ catch {
29
+ // Archive failure is non-fatal; snapshot still returned
30
+ }
31
+ return {
32
+ pane_id,
33
+ substrate: "tmux",
34
+ text,
35
+ hash,
36
+ ts,
37
+ };
38
+ }
39
+ }
40
+ //# sourceMappingURL=tmux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../../../src/odin-watch/backends/tmux.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,MAAM,OAAO,WAAW;IACL,UAAU,CAAS;IAEpC,YAAY,UAAkB;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE;YAC3C,cAAc;YACd,IAAI;YACJ,IAAI;YACJ,OAAO;SACR,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE7D,0BAA0B;QAC1B,IAAI,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC;YACxE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE;gBACnD,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,wDAAwD;QAC1D,CAAC;QAED,OAAO;YACL,OAAO;YACP,SAAS,EAAE,MAAM;YACjB,IAAI;YACJ,IAAI;YACJ,EAAE;SACH,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import type { WakeVerdict, PromptBudgetClass } from "../protocol/schemas.js";
2
+ export interface ClassifierInput {
3
+ text: string;
4
+ hash: string;
5
+ prev_hash: string | null;
6
+ elapsed_ms: number;
7
+ now_ms: number;
8
+ mandatory_audit_interval_ms: number;
9
+ stale_threshold_ms: number;
10
+ write_scope: string[];
11
+ dirty_paths: string[];
12
+ last_verdict: WakeVerdict | null;
13
+ last_mandatory_audit_ts: number | null;
14
+ }
15
+ export interface ClassifierResult {
16
+ verdict: WakeVerdict;
17
+ wake: 0 | 1;
18
+ reason_codes: string[];
19
+ prompt_budget_class: PromptBudgetClass;
20
+ dirty_out_of_scope: string[];
21
+ last_marker: string | undefined;
22
+ }
23
+ /**
24
+ * Classify a screen snapshot using 8 ordered rules (first-match wins).
25
+ * Pure function — no I/O.
26
+ */
27
+ export declare function classify(input: ClassifierInput): ClassifierResult;
@@ -0,0 +1,182 @@
1
+ // Approval markers: exact strings and regex for numbered interactive options
2
+ const APPROVAL_STRINGS = [
3
+ "Proceed with the proposal",
4
+ "Proceed with comment",
5
+ "No and explain why",
6
+ "Enter Select",
7
+ ];
8
+ const NUMBERED_OPTION_RE = /^\d+\.\s/m;
9
+ // Crash markers
10
+ const CRASH_STRINGS = [
11
+ "panic",
12
+ "stack trace",
13
+ "program experienced a panic",
14
+ "TUI run error",
15
+ "fatal error",
16
+ ];
17
+ // Completion markers (order matters: QA_COMPLETE check before DEV_COMPLETE)
18
+ const QA_COMPLETE_STRINGS = ["QA COMPLETE", "QA_COMPLETE"];
19
+ const DEV_COMPLETE_STRINGS = [
20
+ "DEV_COMPLETE_QA_PENDING",
21
+ "implementation complete",
22
+ "ready for QA",
23
+ ];
24
+ // Blocker markers
25
+ const BLOCKER_STRINGS = [
26
+ "BLOCKED",
27
+ "cannot proceed",
28
+ "missing",
29
+ "permission denied",
30
+ "need approval",
31
+ "SPEC-AMENDMENT",
32
+ "non-fast-forward",
33
+ ];
34
+ // Working state positive evidence: active spinner chars or streaming progress patterns
35
+ const WORKING_RE = /[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]|(?:\.\.\.|Running|Generating|Compiling|Building|Installing|Fetching|Downloading|Uploading|Processing|Streaming|Writing|Thinking)/;
36
+ function containsAny(text, markers) {
37
+ for (const marker of markers) {
38
+ if (text.includes(marker))
39
+ return marker;
40
+ }
41
+ return null;
42
+ }
43
+ /**
44
+ * Classify a screen snapshot using 8 ordered rules (first-match wins).
45
+ * Pure function — no I/O.
46
+ */
47
+ export function classify(input) {
48
+ const { text, hash, prev_hash, elapsed_ms, now_ms, mandatory_audit_interval_ms, stale_threshold_ms, write_scope, dirty_paths, last_verdict, last_mandatory_audit_ts, } = input;
49
+ const dirty_out_of_scope = [];
50
+ // Compute out-of-scope dirty paths (used in rule 6)
51
+ if (write_scope.length > 0) {
52
+ for (const path of dirty_paths) {
53
+ const inScope = write_scope.some((scope) => path === scope || path.startsWith(scope + "/") || path.startsWith(scope));
54
+ if (!inScope) {
55
+ dirty_out_of_scope.push(path);
56
+ }
57
+ }
58
+ }
59
+ // Rule 1: Mandatory audit due
60
+ // elapsed since last mandatory audit >= interval → wake, preserve current verdict, budget=compact
61
+ const auditElapsed = last_mandatory_audit_ts !== null
62
+ ? now_ms - last_mandatory_audit_ts
63
+ : elapsed_ms;
64
+ if (auditElapsed >= mandatory_audit_interval_ms) {
65
+ const preservedVerdict = last_verdict ?? "UNKNOWN_NEEDS_READ";
66
+ return {
67
+ verdict: preservedVerdict,
68
+ wake: 1,
69
+ reason_codes: ["MANDATORY_AUDIT interval elapsed"],
70
+ prompt_budget_class: "compact",
71
+ dirty_out_of_scope,
72
+ last_marker: undefined,
73
+ };
74
+ }
75
+ // Rule 2: Approval markers
76
+ const approvalMarker = containsAny(text, APPROVAL_STRINGS);
77
+ if (approvalMarker !== null || NUMBERED_OPTION_RE.test(text)) {
78
+ const marker = approvalMarker ?? "numbered interactive option";
79
+ return {
80
+ verdict: "WAITING_APPROVAL",
81
+ wake: 1,
82
+ reason_codes: ["APPROVAL_MARKER"],
83
+ prompt_budget_class: "diagnostic",
84
+ dirty_out_of_scope,
85
+ last_marker: marker,
86
+ };
87
+ }
88
+ // Rule 3: Crash markers
89
+ const crashMarker = containsAny(text, CRASH_STRINGS);
90
+ if (crashMarker !== null) {
91
+ return {
92
+ verdict: "CRASHED",
93
+ wake: 1,
94
+ reason_codes: ["CRASH_MARKER"],
95
+ prompt_budget_class: "forensic",
96
+ dirty_out_of_scope,
97
+ last_marker: crashMarker,
98
+ };
99
+ }
100
+ // Rule 4: Completion markers (QA_COMPLETE checked before DEV_COMPLETE)
101
+ const qaMarker = containsAny(text, QA_COMPLETE_STRINGS);
102
+ if (qaMarker !== null) {
103
+ return {
104
+ verdict: "QA_COMPLETE",
105
+ wake: 1,
106
+ reason_codes: ["COMPLETION_MARKER"],
107
+ prompt_budget_class: "compact",
108
+ dirty_out_of_scope,
109
+ last_marker: qaMarker,
110
+ };
111
+ }
112
+ const devMarker = containsAny(text, DEV_COMPLETE_STRINGS);
113
+ if (devMarker !== null) {
114
+ return {
115
+ verdict: "DEV_COMPLETE_QA_PENDING",
116
+ wake: 1,
117
+ reason_codes: ["COMPLETION_MARKER"],
118
+ prompt_budget_class: "compact",
119
+ dirty_out_of_scope,
120
+ last_marker: devMarker,
121
+ };
122
+ }
123
+ // Rule 5: Blocker markers
124
+ const blockerMarker = containsAny(text, BLOCKER_STRINGS);
125
+ if (blockerMarker !== null) {
126
+ return {
127
+ verdict: "BLOCKED",
128
+ wake: 1,
129
+ reason_codes: ["BLOCKER_MARKER"],
130
+ prompt_budget_class: "diagnostic",
131
+ dirty_out_of_scope,
132
+ last_marker: blockerMarker,
133
+ };
134
+ }
135
+ // Rule 6: Scope violation (dirty paths outside write_scope)
136
+ if (dirty_out_of_scope.length > 0) {
137
+ return {
138
+ verdict: "BLOCKED",
139
+ wake: 1,
140
+ reason_codes: dirty_out_of_scope.map((p) => `DIRTY_OUT_OF_SCOPE ${p}`),
141
+ prompt_budget_class: "diagnostic",
142
+ dirty_out_of_scope,
143
+ last_marker: undefined,
144
+ };
145
+ }
146
+ // Rule 7: Stale hash (unchanged content beyond stale threshold)
147
+ if (prev_hash !== null &&
148
+ prev_hash === hash &&
149
+ elapsed_ms > stale_threshold_ms) {
150
+ return {
151
+ verdict: "IDLE",
152
+ wake: 1,
153
+ reason_codes: ["STALE_HASH"],
154
+ prompt_budget_class: "compact",
155
+ dirty_out_of_scope,
156
+ last_marker: undefined,
157
+ };
158
+ }
159
+ // Rule 8: All clear — WORKING requires positive evidence
160
+ // Positive evidence: text contains active working indicators
161
+ if (WORKING_RE.test(text)) {
162
+ return {
163
+ verdict: "WORKING",
164
+ wake: 0,
165
+ reason_codes: ["ALL_CLEAR"],
166
+ prompt_budget_class: "silent",
167
+ dirty_out_of_scope,
168
+ last_marker: undefined,
169
+ };
170
+ }
171
+ // Conservative bias: ambiguous (empty text with no stale trigger, or non-empty
172
+ // without positive WORKING evidence) → UNKNOWN_NEEDS_READ, wake=1
173
+ return {
174
+ verdict: "UNKNOWN_NEEDS_READ",
175
+ wake: 1,
176
+ reason_codes: ["AMBIGUOUS_STATE"],
177
+ prompt_budget_class: "compact",
178
+ dirty_out_of_scope,
179
+ last_marker: undefined,
180
+ };
181
+ }
182
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../../src/odin-watch/classifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
3
+ import { CmuxBackend } from "./backends/cmux.js";
4
+ import { TmuxBackend } from "./backends/tmux.js";
5
+ import { classify } from "./classifier.js";
6
+ import { writeWakeFiles } from "./writers.js";
7
+ import { execFileNoThrow } from "../utils/execFileNoThrow.js";
8
+ const VALID_SUBSTRATES = [
9
+ "cmux",
10
+ "tmux",
11
+ "minimux",
12
+ "herdr",
13
+ "plain",
14
+ ];
15
+ function isValidSubstrate(s) {
16
+ return VALID_SUBSTRATES.includes(s);
17
+ }
18
+ function parseCliArgs() {
19
+ const { values } = parseArgs({
20
+ options: {
21
+ surface: { type: "string" },
22
+ substrate: { type: "string" },
23
+ "write-scope": { type: "string", multiple: true },
24
+ interval: { type: "string", default: "120" },
25
+ "mandatory-audit": { type: "string", default: "600" },
26
+ "flag-dir": {
27
+ type: "string",
28
+ default: ".odin/monitor/wake/",
29
+ },
30
+ "archive-dir": {
31
+ type: "string",
32
+ default: ".odin/monitor/cmux/",
33
+ },
34
+ label: { type: "string" },
35
+ },
36
+ strict: true,
37
+ });
38
+ const surface = values["surface"];
39
+ const substrate = values["substrate"];
40
+ const label = values["label"];
41
+ if (!surface) {
42
+ console.error("Error: --surface <id> is required");
43
+ process.exit(1);
44
+ }
45
+ if (!substrate) {
46
+ console.error("Error: --substrate <type> is required");
47
+ process.exit(1);
48
+ }
49
+ if (!isValidSubstrate(substrate)) {
50
+ console.error(`Error: --substrate must be one of: ${VALID_SUBSTRATES.join(", ")}`);
51
+ process.exit(1);
52
+ }
53
+ if (!label) {
54
+ console.error("Error: --label <name> is required");
55
+ process.exit(1);
56
+ }
57
+ const intervalSeconds = parseInt(values["interval"] ?? "120", 10);
58
+ const mandatoryAuditSeconds = parseInt(values["mandatory-audit"] ?? "600", 10);
59
+ if (isNaN(intervalSeconds) || intervalSeconds <= 0) {
60
+ console.error("Error: --interval must be a positive integer");
61
+ process.exit(1);
62
+ }
63
+ if (isNaN(mandatoryAuditSeconds) || mandatoryAuditSeconds <= 0) {
64
+ console.error("Error: --mandatory-audit must be a positive integer");
65
+ process.exit(1);
66
+ }
67
+ return {
68
+ surface,
69
+ substrate,
70
+ writeScope: values["write-scope"] ?? [],
71
+ intervalSeconds,
72
+ mandatoryAuditSeconds,
73
+ flagDir: values["flag-dir"] ?? ".odin/monitor/wake/",
74
+ archiveDir: values["archive-dir"] ?? ".odin/monitor/cmux/",
75
+ label,
76
+ };
77
+ }
78
+ async function getDirtyPaths() {
79
+ const result = await execFileNoThrow("git", [
80
+ "diff",
81
+ "--name-only",
82
+ "--diff-filter=ACDMRT",
83
+ ]);
84
+ if (result.status !== 0)
85
+ return [];
86
+ return result.stdout
87
+ .split("\n")
88
+ .map((l) => l.trim())
89
+ .filter(Boolean);
90
+ }
91
+ async function getCurrentBranch() {
92
+ const result = await execFileNoThrow("git", [
93
+ "rev-parse",
94
+ "--abbrev-ref",
95
+ "HEAD",
96
+ ]);
97
+ return result.status === 0 ? result.stdout.trim() : undefined;
98
+ }
99
+ async function getCurrentHead() {
100
+ const result = await execFileNoThrow("git", ["rev-parse", "--short", "HEAD"]);
101
+ return result.status === 0 ? result.stdout.trim() : undefined;
102
+ }
103
+ async function sleep(ms) {
104
+ return new Promise((resolve) => setTimeout(resolve, ms));
105
+ }
106
+ async function main() {
107
+ const { surface, substrate, writeScope, intervalSeconds, mandatoryAuditSeconds, flagDir, archiveDir, label, } = parseCliArgs();
108
+ const intervalMs = intervalSeconds * 1000;
109
+ const mandatoryAuditIntervalMs = mandatoryAuditSeconds * 1000;
110
+ // Select backend based on substrate
111
+ let snapshotter;
112
+ switch (substrate) {
113
+ case "cmux":
114
+ snapshotter = new CmuxBackend(archiveDir);
115
+ break;
116
+ case "tmux":
117
+ snapshotter = new TmuxBackend(archiveDir);
118
+ break;
119
+ default:
120
+ throw new Error(`Substrate '${substrate}' is not yet supported by odin-watch. Supported: cmux, tmux`);
121
+ }
122
+ let prevHash = null;
123
+ let lastVerdictState = null;
124
+ let lastMandatoryAuditTs = null;
125
+ const startTs = Date.now();
126
+ console.error(`odin-watch starting: surface=${surface} substrate=${substrate} label=${label} interval=${intervalSeconds}s`);
127
+ while (true) {
128
+ const loopStart = Date.now();
129
+ const elapsed = loopStart - startTs;
130
+ try {
131
+ // Capture
132
+ const snapshot = await snapshotter.capture(surface);
133
+ // Gather dirty paths and git metadata
134
+ const [dirtyPaths, branch, head] = await Promise.all([
135
+ getDirtyPaths(),
136
+ getCurrentBranch(),
137
+ getCurrentHead(),
138
+ ]);
139
+ // Classify
140
+ const result = classify({
141
+ text: snapshot.text,
142
+ hash: snapshot.hash,
143
+ prev_hash: prevHash,
144
+ elapsed_ms: elapsed,
145
+ now_ms: Date.now(),
146
+ mandatory_audit_interval_ms: mandatoryAuditIntervalMs,
147
+ stale_threshold_ms: intervalMs * 3, // stale after 3 missed polls
148
+ write_scope: writeScope,
149
+ dirty_paths: dirtyPaths,
150
+ last_verdict: lastVerdictState,
151
+ last_mandatory_audit_ts: lastMandatoryAuditTs,
152
+ });
153
+ // If mandatory audit triggered, reset the audit clock
154
+ if (result.reason_codes.includes("MANDATORY_AUDIT interval elapsed")) {
155
+ lastMandatoryAuditTs = loopStart;
156
+ }
157
+ // Build WakeState
158
+ const wakeState = {
159
+ schema_version: "1.0",
160
+ ts: new Date(loopStart).toISOString(),
161
+ surface,
162
+ pane_id: surface,
163
+ substrate: substrate,
164
+ state: result.verdict,
165
+ wake: result.wake,
166
+ reason_codes: result.reason_codes,
167
+ dirty_paths: dirtyPaths,
168
+ dirty_out_of_scope: result.dirty_out_of_scope,
169
+ screen_hash: snapshot.hash,
170
+ screen_changed: prevHash !== null && prevHash !== snapshot.hash,
171
+ last_marker: result.last_marker,
172
+ prompt_budget_class: result.prompt_budget_class,
173
+ next_mandatory_audit_due: new Date(loopStart + mandatoryAuditIntervalMs).toISOString(),
174
+ head,
175
+ branch,
176
+ };
177
+ // Write output files
178
+ writeWakeFiles(flagDir, label, wakeState);
179
+ prevHash = snapshot.hash;
180
+ lastVerdictState = result.verdict;
181
+ console.error(`[${wakeState.ts}] verdict=${result.verdict} wake=${result.wake} hash=${snapshot.hash.slice(0, 8)}`);
182
+ }
183
+ catch (err) {
184
+ const msg = err instanceof Error ? err.message : String(err);
185
+ console.error(`odin-watch poll error: ${msg}`);
186
+ }
187
+ // Sleep until next interval
188
+ const elapsed2 = Date.now() - loopStart;
189
+ const remaining = intervalMs - elapsed2;
190
+ if (remaining > 0) {
191
+ await sleep(remaining);
192
+ }
193
+ }
194
+ }
195
+ main().catch((err) => {
196
+ const msg = err instanceof Error ? err.message : String(err);
197
+ console.error(`odin-watch fatal: ${msg}`);
198
+ process.exitCode = 1;
199
+ });
200
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/odin-watch/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { SubstrateType } from "../protocol/schemas.js";
2
+ export interface Snapshot {
3
+ pane_id: string;
4
+ substrate: SubstrateType;
5
+ text: string;
6
+ hash: string;
7
+ ts: number;
8
+ }
9
+ export interface Snapshotter {
10
+ capture(pane_id: string): Promise<Snapshot>;
11
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=snapshotter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshotter.js","sourceRoot":"","sources":["../../../src/odin-watch/snapshotter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import type { WakeState } from "../protocol/schemas.js";
2
+ /**
3
+ * Write the three output files for a single poll result:
4
+ * {flagDir}/{label}.flag — exactly one byte: "0" or "1", no newline
5
+ * {flagDir}/{label}.reason — text reason codes, one per line
6
+ * {flagDir}/{label}.state.json — WakeState JSON object
7
+ */
8
+ export declare function writeWakeFiles(flagDir: string, label: string, state: WakeState): void;
@@ -0,0 +1,27 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /**
4
+ * Write the three output files for a single poll result:
5
+ * {flagDir}/{label}.flag — exactly one byte: "0" or "1", no newline
6
+ * {flagDir}/{label}.reason — text reason codes, one per line
7
+ * {flagDir}/{label}.state.json — WakeState JSON object
8
+ */
9
+ export function writeWakeFiles(flagDir, label, state) {
10
+ mkdirSync(flagDir, { recursive: true });
11
+ // Flag file: exactly one byte ASCII character "0" or "1", no newline
12
+ const flagPath = join(flagDir, `${label}.flag`);
13
+ writeFileSync(flagPath, String(state.wake), {
14
+ encoding: "utf8",
15
+ });
16
+ // Reason file: one reason code per line
17
+ const reasonPath = join(flagDir, `${label}.reason`);
18
+ writeFileSync(reasonPath, state.reason_codes.join("\n"), {
19
+ encoding: "utf8",
20
+ });
21
+ // State JSON file
22
+ const statePath = join(flagDir, `${label}.state.json`);
23
+ writeFileSync(statePath, JSON.stringify(state, null, 2), {
24
+ encoding: "utf8",
25
+ });
26
+ }
27
+ //# sourceMappingURL=writers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writers.js","sourceRoot":"","sources":["../../../src/odin-watch/writers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,KAAa,EACb,KAAgB;IAEhB,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExC,qEAAqE;IACrE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;IAChD,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAc,EAAE;QACvD,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,wCAAwC;IACxC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC;IACpD,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACvD,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,kBAAkB;IAClB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,aAAa,CAAC,CAAC;IACvD,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACvD,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export { GOVERNED_CONTEXT_PROOF_SCHEMA, VERSION, classifyGovernedReadiness, createProtocolService, exportProtocolSnapshot, getActivationGates, getCloseoutChecklist, getDelegationPacket, getOnboardingPlan, getRoleProfile, getRuntimeNotice, getStartupPacket, harnessCategory, validateBootReceipt, validateCmuxDeliveryProof, validateDelegationPacket, validateGovernedContextProof, validateInstructionReadProof, validateTeamManifest } from "./service.js";
2
2
  export type { AssuranceLevel, CloseoutMode, DelegationPacketInput, GovernedReadinessInput, GovernedReadinessResult, GovernedReadinessState, HarnessCategory, ProtocolData, RuntimeNotice, StartupPacket, StartupPacketInput, ValidationResult } from "./service.js";
3
- export { computeSurfaceLayout, computeSurfaceLayoutGate, renderSurfaceLayoutAscii } from "./surface-layout.js";
3
+ export { computeSurfaceLayout, computeSurfaceLayoutGate, getSubstrateCapability, renderSurfaceLayoutAscii } from "./surface-layout.js";
4
4
  export type { SurfaceLayout, SurfaceLayoutColumn, SurfaceLayoutSurface, SurfaceLayoutGate } from "./surface-layout.js";
5
+ export { CapabilityFlag } from "./schemas.js";
6
+ export type { BootReceipt, DeliveryReceiptPacingExtension, HarnessPacingEvent, PacingEventType, PromptBudgetClass, ReceiptType, SubstrateCapability, SubstrateType, WakeState, WakeVerdict } from "./schemas.js";
@@ -1,3 +1,6 @@
1
1
  export { GOVERNED_CONTEXT_PROOF_SCHEMA, VERSION, classifyGovernedReadiness, createProtocolService, exportProtocolSnapshot, getActivationGates, getCloseoutChecklist, getDelegationPacket, getOnboardingPlan, getRoleProfile, getRuntimeNotice, getStartupPacket, harnessCategory, validateBootReceipt, validateCmuxDeliveryProof, validateDelegationPacket, validateGovernedContextProof, validateInstructionReadProof, validateTeamManifest } from "./service.js";
2
- export { computeSurfaceLayout, computeSurfaceLayoutGate, renderSurfaceLayoutAscii } from "./surface-layout.js";
2
+ export { computeSurfaceLayout, computeSurfaceLayoutGate, getSubstrateCapability, renderSurfaceLayoutAscii } from "./surface-layout.js";
3
+ // New types for SCP performance/portability program (E1-E5 unblocking)
4
+ // CapabilityFlag is exported as a value (runtime const) and as a type via the same export.
5
+ export { CapabilityFlag } from "./schemas.js";
3
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/protocol/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,OAAO,EACP,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,EAC5B,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAiBtB,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/protocol/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,OAAO,EACP,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,EAC5B,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAiBtB,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAS7B,uEAAuE;AACvE,2FAA2F;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -15,6 +15,20 @@ export type ProtocolData = {
15
15
  harnessSkillTargets: string;
16
16
  teamBootstrapRunbook: string;
17
17
  };
18
+ roleCards: {
19
+ execPm: string;
20
+ teamPm: string;
21
+ devWorker: string;
22
+ qaWorker: string;
23
+ execAsst: string;
24
+ };
25
+ missionFrontrun: {
26
+ orchestratorContract: string;
27
+ workerContract: string;
28
+ scrutinyValidatorContract: string;
29
+ scrutinyFeatureReviewerContract: string;
30
+ droidsScrutinyFeatureReviewer: string;
31
+ };
18
32
  };
19
33
  export type ProtocolRepository = {
20
34
  load(): ProtocolData;
@@ -17,7 +17,17 @@ export const REQUIRED_PROTOCOL_FILES = [
17
17
  "protocol/skill-references/boot-receipt-examples.md",
18
18
  "protocol/skill-references/canonical-introduction-prompt.md",
19
19
  "protocol/skill-references/harness-skill-targets.md",
20
- "protocol/skill-references/team-bootstrap-runbook.md"
20
+ "protocol/skill-references/team-bootstrap-runbook.md",
21
+ "protocol/role-cards/exec-pm.md",
22
+ "protocol/role-cards/team-pm.md",
23
+ "protocol/role-cards/dev-worker.md",
24
+ "protocol/role-cards/qa-worker.md",
25
+ "protocol/role-cards/exec-asst.md",
26
+ "protocol/mission-frontrun/orchestrator-contract.md",
27
+ "protocol/mission-frontrun/worker-contract.md",
28
+ "protocol/mission-frontrun/scrutiny-validator-contract.md",
29
+ "protocol/mission-frontrun/scrutiny-feature-reviewer-contract.md",
30
+ "protocol/mission-frontrun/droids-scrutiny-feature-reviewer.md"
21
31
  ];
22
32
  function missingProtocolFiles(root) {
23
33
  return REQUIRED_PROTOCOL_FILES.filter((file) => !existsSync(join(root, file)));
@@ -105,6 +115,20 @@ export function createFileProtocolRepository(rootDir = findRootDir()) {
105
115
  canonicalIntroductionPrompt: readFileSync(protocolPath("skill-references", "canonical-introduction-prompt.md"), "utf8"),
106
116
  harnessSkillTargets: readFileSync(protocolPath("skill-references", "harness-skill-targets.md"), "utf8"),
107
117
  teamBootstrapRunbook: readFileSync(protocolPath("skill-references", "team-bootstrap-runbook.md"), "utf8")
118
+ },
119
+ roleCards: {
120
+ execPm: readFileSync(protocolPath("role-cards", "exec-pm.md"), "utf8"),
121
+ teamPm: readFileSync(protocolPath("role-cards", "team-pm.md"), "utf8"),
122
+ devWorker: readFileSync(protocolPath("role-cards", "dev-worker.md"), "utf8"),
123
+ qaWorker: readFileSync(protocolPath("role-cards", "qa-worker.md"), "utf8"),
124
+ execAsst: readFileSync(protocolPath("role-cards", "exec-asst.md"), "utf8")
125
+ },
126
+ missionFrontrun: {
127
+ orchestratorContract: readFileSync(protocolPath("mission-frontrun", "orchestrator-contract.md"), "utf8"),
128
+ workerContract: readFileSync(protocolPath("mission-frontrun", "worker-contract.md"), "utf8"),
129
+ scrutinyValidatorContract: readFileSync(protocolPath("mission-frontrun", "scrutiny-validator-contract.md"), "utf8"),
130
+ scrutinyFeatureReviewerContract: readFileSync(protocolPath("mission-frontrun", "scrutiny-feature-reviewer-contract.md"), "utf8"),
131
+ droidsScrutinyFeatureReviewer: readFileSync(protocolPath("mission-frontrun", "droids-scrutiny-feature-reviewer.md"), "utf8")
108
132
  }
109
133
  });
110
134
  cache = { fingerprint, data };