@bradheitmann/odin-sentinel 0.2.1 → 0.4.3

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 (42) hide show
  1. package/README.md +20 -15
  2. package/dist/src/mcp/server.js +57 -1
  3. package/dist/src/mcp/server.js.map +1 -1
  4. package/dist/src/protocol/index.d.ts +2 -0
  5. package/dist/src/protocol/index.js +1 -0
  6. package/dist/src/protocol/index.js.map +1 -1
  7. package/dist/src/protocol/repository.d.ts +1 -0
  8. package/dist/src/protocol/repository.js +4 -2
  9. package/dist/src/protocol/repository.js.map +1 -1
  10. package/dist/src/protocol/schemas.d.ts +30 -0
  11. package/dist/src/protocol/schemas.js +30 -0
  12. package/dist/src/protocol/schemas.js.map +1 -1
  13. package/dist/src/protocol/service.js +28 -1
  14. package/dist/src/protocol/service.js.map +1 -1
  15. package/dist/src/protocol/surface-layout.d.ts +29 -0
  16. package/dist/src/protocol/surface-layout.js +136 -0
  17. package/dist/src/protocol/surface-layout.js.map +1 -0
  18. package/dist/src/telemetry/config.d.ts +8 -0
  19. package/dist/src/telemetry/config.js +11 -0
  20. package/dist/src/telemetry/config.js.map +1 -0
  21. package/dist/src/telemetry/index.d.ts +7 -0
  22. package/dist/src/telemetry/index.js +5 -0
  23. package/dist/src/telemetry/index.js.map +1 -0
  24. package/dist/src/telemetry/redactor.d.ts +2 -0
  25. package/dist/src/telemetry/redactor.js +45 -0
  26. package/dist/src/telemetry/redactor.js.map +1 -0
  27. package/dist/src/telemetry/report.d.ts +30 -0
  28. package/dist/src/telemetry/report.js +10 -0
  29. package/dist/src/telemetry/report.js.map +1 -0
  30. package/dist/src/telemetry/submit.d.ts +14 -0
  31. package/dist/src/telemetry/submit.js +50 -0
  32. package/dist/src/telemetry/submit.js.map +1 -0
  33. package/docs/reference/distribution.md +15 -7
  34. package/package.json +1 -1
  35. package/protocol/SCP.md +48 -2
  36. package/protocol/bootstrap-skill.md +1488 -0
  37. package/protocol/delegation.yaml +4 -1
  38. package/protocol/receipts/boot-receipt.yaml +17 -0
  39. package/protocol/roles.yaml +106 -1
  40. package/protocol/topology.yaml +35 -1
  41. package/scripts/audit/public-surface.mjs +11 -3
  42. package/scripts/audit/verify-pack.mjs +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/telemetry/config.ts"],"names":[],"mappings":"AAMA,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAEnD,MAAM,UAAU,mBAAmB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { readTelemetryConfig, ENDPOINT_ENV_VAR } from "./config.js";
2
+ export type { TelemetryConfig } from "./config.js";
3
+ export { redactString, redactPayload } from "./redactor.js";
4
+ export { compileSessionReport } from "./report.js";
5
+ export type { HaltEntry, ModelSignal, SessionReport, SessionReportInput, ViolationEntry } from "./report.js";
6
+ export { submitSessionReport } from "./submit.js";
7
+ export type { SubmitOptions, SubmitResult } from "./submit.js";
@@ -0,0 +1,5 @@
1
+ export { readTelemetryConfig, ENDPOINT_ENV_VAR } from "./config.js";
2
+ export { redactString, redactPayload } from "./redactor.js";
3
+ export { compileSessionReport } from "./report.js";
4
+ export { submitSessionReport } from "./submit.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAQnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function redactString(value: string): string;
2
+ export declare function redactPayload<T>(payload: T): T;
@@ -0,0 +1,45 @@
1
+ // Pre-redaction for outbound payloads. Strips file-system paths, email
2
+ // addresses, and common secret token shapes. Intentionally conservative:
3
+ // over-redacts rather than under-redacts.
4
+ const SLASH = "/";
5
+ const BACKSLASH = "\\";
6
+ const HOME_USERS_RE = new RegExp(`${SLASH}${"U" + "sers"}${SLASH}[^${SLASH}\\s"']+`, "g");
7
+ const HOME_LINUX_RE = new RegExp(`${SLASH}${"ho" + "me"}${SLASH}[^${SLASH}\\s"']+`, "g");
8
+ const HOME_WIN_RE = new RegExp(`${BACKSLASH}${BACKSLASH}${"U" + "sers"}${BACKSLASH}${BACKSLASH}[^${BACKSLASH}\\s"']+`, "g");
9
+ const EMAIL_RE = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
10
+ // Common secret-token shapes. The set is conservative; readers should add
11
+ // project-specific patterns if they ship custom token prefixes.
12
+ const TOKEN_PATTERNS = [
13
+ /\bsk-[a-zA-Z0-9]{20,}\b/g,
14
+ /\blin_api_[a-zA-Z0-9]{16,}\b/g,
15
+ /\bghp_[a-zA-Z0-9]{20,}\b/g,
16
+ /\bgho_[a-zA-Z0-9]{20,}\b/g,
17
+ /\bAIza[0-9A-Za-z_-]{30,}\b/g,
18
+ /\bxox[bp]-[a-zA-Z0-9-]{20,}\b/g
19
+ ];
20
+ export function redactString(value) {
21
+ let result = value
22
+ .replace(HOME_USERS_RE, "<HOME>")
23
+ .replace(HOME_LINUX_RE, "<HOME>")
24
+ .replace(HOME_WIN_RE, "<HOME>")
25
+ .replace(EMAIL_RE, "<EMAIL>");
26
+ for (const pattern of TOKEN_PATTERNS) {
27
+ result = result.replace(pattern, "<TOKEN>");
28
+ }
29
+ return result;
30
+ }
31
+ export function redactPayload(payload) {
32
+ if (typeof payload === "string")
33
+ return redactString(payload);
34
+ if (Array.isArray(payload))
35
+ return payload.map((entry) => redactPayload(entry));
36
+ if (payload && typeof payload === "object") {
37
+ const result = {};
38
+ for (const [key, value] of Object.entries(payload)) {
39
+ result[key] = redactPayload(value);
40
+ }
41
+ return result;
42
+ }
43
+ return payload;
44
+ }
45
+ //# sourceMappingURL=redactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redactor.js","sourceRoot":"","sources":["../../../src/telemetry/redactor.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yEAAyE;AACzE,0CAA0C;AAE1C,MAAM,KAAK,GAAG,GAAG,CAAC;AAClB,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB,MAAM,aAAa,GAAG,IAAI,MAAM,CAC9B,GAAG,KAAK,GAAG,GAAG,GAAG,MAAM,GAAG,KAAK,KAAK,KAAK,SAAS,EAClD,GAAG,CACJ,CAAC;AACF,MAAM,aAAa,GAAG,IAAI,MAAM,CAC9B,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,KAAK,SAAS,EACjD,GAAG,CACJ,CAAC;AACF,MAAM,WAAW,GAAG,IAAI,MAAM,CAC5B,GAAG,SAAS,GAAG,SAAS,GAAG,GAAG,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,KAAK,SAAS,SAAS,EACtF,GAAG,CACJ,CAAC;AAEF,MAAM,QAAQ,GAAG,iDAAiD,CAAC;AAEnE,0EAA0E;AAC1E,gEAAgE;AAChE,MAAM,cAAc,GAAa;IAC/B,0BAA0B;IAC1B,+BAA+B;IAC/B,2BAA2B;IAC3B,2BAA2B;IAC3B,6BAA6B;IAC7B,gCAAgC;CACjC,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,MAAM,GAAG,KAAK;SACf,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC;SAChC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC;SAChC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC;SAC9B,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAChC,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAI,OAAU;IACzC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,OAAO,CAAiB,CAAC;IAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAiB,CAAC;IAChG,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC3C,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAkC,CAAC,EAAE,CAAC;YAC9E,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,MAAsB,CAAC;IAChC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,30 @@
1
+ export type ViolationEntry = {
2
+ class: string;
3
+ description?: string;
4
+ };
5
+ export type HaltEntry = {
6
+ source: string;
7
+ target: string;
8
+ trigger: string;
9
+ };
10
+ export type ModelSignal = {
11
+ role: string;
12
+ model: string;
13
+ violations: number;
14
+ };
15
+ export type SessionReportInput = {
16
+ teamCount: number;
17
+ violations: ViolationEntry[];
18
+ halts: HaltEntry[];
19
+ layoutDriftEvents: number;
20
+ peakContextPct: number;
21
+ closeoutClean: boolean;
22
+ modelSignals: ModelSignal[];
23
+ };
24
+ export type SessionReport = SessionReportInput & {
25
+ version: string;
26
+ compiledAt: string;
27
+ violationCount: number;
28
+ haltCount: number;
29
+ };
30
+ export declare function compileSessionReport(input: SessionReportInput, version: string): SessionReport;
@@ -0,0 +1,10 @@
1
+ export function compileSessionReport(input, version) {
2
+ return {
3
+ ...input,
4
+ version,
5
+ compiledAt: new Date().toISOString(),
6
+ violationCount: input.violations.length,
7
+ haltCount: input.halts.length
8
+ };
9
+ }
10
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/telemetry/report.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,oBAAoB,CAAC,KAAyB,EAAE,OAAe;IAC7E,OAAO;QACL,GAAG,KAAK;QACR,OAAO;QACP,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,cAAc,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM;QACvC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;KAC9B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type TelemetryConfig } from "./config.js";
2
+ export type SubmitResult = {
3
+ submitted: boolean;
4
+ endpoint?: string;
5
+ status?: number;
6
+ id?: string;
7
+ reason?: string;
8
+ };
9
+ export type SubmitOptions = {
10
+ config?: TelemetryConfig;
11
+ fetchImpl?: typeof fetch;
12
+ timeoutMs?: number;
13
+ };
14
+ export declare function submitSessionReport(report: Record<string, unknown>, options?: SubmitOptions): Promise<SubmitResult>;
@@ -0,0 +1,50 @@
1
+ import { readTelemetryConfig } from "./config.js";
2
+ import { redactPayload } from "./redactor.js";
3
+ const DEFAULT_TIMEOUT_MS = 8000;
4
+ export async function submitSessionReport(report, options = {}) {
5
+ const config = options.config ?? readTelemetryConfig();
6
+ if (!config.enabled || !config.endpoint) {
7
+ return {
8
+ submitted: false,
9
+ reason: "telemetry not configured (set ODIN_TELEMETRY_ENDPOINT to opt in)"
10
+ };
11
+ }
12
+ const redacted = redactPayload(report);
13
+ const fetchImpl = options.fetchImpl ?? fetch;
14
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
15
+ const controller = new AbortController();
16
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
17
+ try {
18
+ const response = await fetchImpl(config.endpoint, {
19
+ method: "POST",
20
+ headers: { "content-type": "application/json" },
21
+ body: JSON.stringify(redacted),
22
+ signal: controller.signal
23
+ });
24
+ let parsed = {};
25
+ try {
26
+ parsed = (await response.json());
27
+ }
28
+ catch {
29
+ // non-JSON response is acceptable; status code is the source of truth
30
+ }
31
+ return {
32
+ submitted: response.ok,
33
+ endpoint: config.endpoint,
34
+ status: response.status,
35
+ id: typeof parsed.id === "string" ? parsed.id : undefined,
36
+ reason: response.ok ? undefined : `HTTP ${response.status}`
37
+ };
38
+ }
39
+ catch (error) {
40
+ return {
41
+ submitted: false,
42
+ endpoint: config.endpoint,
43
+ reason: error instanceof Error ? error.message : "network error"
44
+ };
45
+ }
46
+ finally {
47
+ clearTimeout(timer);
48
+ }
49
+ }
50
+ //# sourceMappingURL=submit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submit.js","sourceRoot":"","sources":["../../../src/telemetry/submit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAwB,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAgB9C,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAA+B,EAC/B,UAAyB,EAAE;IAE3B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,mBAAmB,EAAE,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxC,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,kEAAkE;SAC3E,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IAE1D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE;YAChD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC9B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,MAAM,GAAmC,EAAE,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;QACxE,CAAC;QAED,OAAO;YACL,SAAS,EAAE,QAAQ,CAAC,EAAE;YACtB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,EAAE,EAAE,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;YACzD,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE;SAC5D,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SACjE,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
@@ -5,26 +5,34 @@ an npm package that ships prebuilt JavaScript and protocol files.
5
5
 
6
6
  ## Recommended Install Path
7
7
 
8
- After publishing:
9
-
10
8
  ```bash
11
- pnpm dlx odin-sentinel
9
+ npm i -g @bradheitmann/odin-sentinel
12
10
  ```
13
11
 
14
- MCP client configuration can then use:
12
+ MCP client configuration can then use the installed binary directly:
15
13
 
16
14
  ```json
17
15
  {
18
16
  "mcpServers": {
19
17
  "odin-sentinel": {
20
- "command": "pnpm",
21
- "args": ["dlx", "odin-sentinel"]
18
+ "command": "odin-sentinel-mcp"
22
19
  }
23
20
  }
24
21
  }
25
22
  ```
26
23
 
27
- `npx -y odin-sentinel` is also viable for npm-first clients.
24
+ For zero-install via `npx`:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "odin-sentinel": {
30
+ "command": "npx",
31
+ "args": ["-y", "-p", "@bradheitmann/odin-sentinel", "odin-sentinel-mcp"]
32
+ }
33
+ }
34
+ }
35
+ ```
28
36
 
29
37
  ## Local Clone Path
30
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bradheitmann/odin-sentinel",
3
- "version": "0.2.1",
3
+ "version": "0.4.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/protocol/SCP.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # ODIN Sentinel Coordination Protocol
2
2
 
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
 
5
5
  ODIN Sentinel is a portable coordination layer for visible multi-agent teams.
6
6
  SCP means Sentinel Coordination Protocol in this repository. It is not Secure
7
7
  Copy.
8
8
  It provides generic role contracts, startup packets, receipt validation, team
9
9
  manifest validation, native visible-role delegation packets, closeout
10
- checklists, and fallback protocol snapshots through an MCP server.
10
+ checklists, surface layout rules, and fallback protocol snapshots through an
11
+ MCP server.
11
12
 
12
13
  ## Principles
13
14
 
@@ -19,12 +20,23 @@ checklists, and fallback protocol snapshots through an MCP server.
19
20
  - Handoffs, closeout, and restart packets must be explicit.
20
21
  - Secrets are never printed or embedded in receipts.
21
22
  - Delegation is native to the server and must not require an external extension.
23
+ - Staffing and surface custodianship are the sole authority of A/EXEC-PM.
22
24
 
23
25
  ## Startup Defaults
24
26
 
25
27
  Fresh startup creates an executive office and one development pod unless the
26
28
  user or a handoff requests another topology.
27
29
 
30
+ ## Surface Layout
31
+
32
+ CMUX surface organization is contractually fixed by `surface_layout` in
33
+ `topology.yaml` and the `odin.compute_surface_layout` tool. All columns are
34
+ equal width. At most two surfaces stack inside any column. Team A always
35
+ occupies column 0; when the team count is odd and at least 3, column 0 holds
36
+ only Team A (the tall column). EXEC PM must call
37
+ `odin.compute_surface_layout` (and its gate variant) before any spawn beyond
38
+ A/EXEC and must reach the target layout via CMUX splits before dispatching.
39
+
28
40
  ## Closeout Defaults
29
41
 
30
42
  Closeout supports two modes:
@@ -32,3 +44,37 @@ Closeout supports two modes:
32
44
  - `PARK_FOR_CONTINUITY`: keep role slots open and park occupants.
33
45
  - `FULL_SESSION_SHUTDOWN`: quit occupants, verify exit, and close panes except
34
46
  the final user-designated surface.
47
+
48
+ ## Meta-Governance
49
+
50
+ ODIN Sentinel applies the same Dev/QA contract that governs code execution to
51
+ the execution of organizational strategy itself.
52
+
53
+ In code work:
54
+
55
+ - DEV implements bounded scope and produces evidence.
56
+ - QA verifies independently and does not accept their own work.
57
+ - TEAM PM routes tasks to workers; A/EXEC-PM authorizes and frames claims.
58
+
59
+ In organizational work:
60
+
61
+ - TEAM PM is the Dev of org strategy. It implements the staffing, surfaces,
62
+ and delegations issued by A/EXEC-PM (subject to the constraint that TEAM PM
63
+ cannot self-staff).
64
+ - A/EXEC-PM is the dispatcher of org strategy. It issues the org orders and
65
+ owns staffing and surface custodianship exclusively.
66
+ - ODIN agents are the QA of org strategy. They audit whether A/EXEC-PM's org
67
+ orders are executed correctly, whether surface custodianship is honored,
68
+ whether protocol adherence holds, whether context windows are healthy, and
69
+ whether contracts remain accountable.
70
+
71
+ ODIN agents hold intervention authority. When A/EXEC-PM, any TEAM PM, or any
72
+ worker violates the contract (staffing without the surface gate, hidden agent
73
+ creation, same-role QA acceptance, context window over hard threshold without
74
+ compaction), the supervising ODIN role MAY issue a HALT directive. The
75
+ receiving role is contractually obligated to honor the HALT and reply with a
76
+ remediation plan before resuming. Continuing past a HALT without remediation
77
+ is itself a protocol breach.
78
+
79
+ This recursion is why the same protocol governs both code work and
80
+ organizational work without growing new vocabulary.