@kontextmind/kxm 0.6.0 → 0.7.10

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 (175) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/agents/coordinator.yaml +9 -0
  3. package/.kxm/agents/critic-arch.yaml +13 -0
  4. package/.kxm/agents/critic-cli.yaml +13 -0
  5. package/.kxm/agents/implementer.yaml +13 -0
  6. package/.kxm/gates.yaml +8 -0
  7. package/.kxm/producers.yaml +22 -0
  8. package/.kxm/project.yaml +15 -0
  9. package/.kxm/roles/writer.yaml +7 -0
  10. package/.kxm/workflows/default.yaml +47 -0
  11. package/CHANGELOG.md +39 -7
  12. package/README.md +1 -0
  13. package/docs/README.md +5 -0
  14. package/docs/adr/ADR-0002-browser-automation-steel-doks.md +103 -0
  15. package/docs/agent-skills.md +135 -0
  16. package/docs/architecture.md +1 -1
  17. package/docs/assignment-runner.md +21 -8
  18. package/docs/browser-automation.md +116 -0
  19. package/docs/configuration.md +11 -2
  20. package/docs/getting-started.md +21 -0
  21. package/docs/kb/how-credentials-retrieved-safely.md +31 -0
  22. package/docs/kb/how-to-capture-and-annotate-section.md +60 -0
  23. package/docs/kb/how-to-connect-playwright-to-steel.md +54 -0
  24. package/docs/kb/how-to-recover-expired-session-or-orphan.md +54 -0
  25. package/docs/kb/how-to-resume-after-mfa.md +28 -0
  26. package/docs/kb/how-to-take-over-session.md +32 -0
  27. package/docs/kb/why-authentication-disappeared.md +32 -0
  28. package/docs/kb/why-automation-opened-different-browser.md +32 -0
  29. package/docs/kb/why-session-viewer-cannot-control.md +31 -0
  30. package/docs/kxm-handbook.md +3 -3
  31. package/docs/operations.md +24 -0
  32. package/docs/operator-pi-packages.md +63 -0
  33. package/docs/prompts/browser-annotate-feedback.md +41 -0
  34. package/docs/prompts/browser-diagnose-recover.md +38 -0
  35. package/docs/prompts/browser-explore.md +42 -0
  36. package/docs/prompts/browser-repro-fix.md +48 -0
  37. package/docs/prompts/browser-start.md +41 -0
  38. package/docs/prompts/browser-takeover.md +50 -0
  39. package/docs/skills/repo-work-delivery.md +107 -0
  40. package/docs/skills.md +2 -0
  41. package/docs/test-matrix.md +4 -3
  42. package/docs/troubleshooting.md +41 -1
  43. package/docs/vnext/validation.md +9 -0
  44. package/docs/webhook-workflows.md +2 -2
  45. package/examples/README.md +1 -1
  46. package/package.json +16 -17
  47. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  48. package/plugins/kxm/README.md +1 -1
  49. package/plugins/kxm/dist/cli.js +41620 -35578
  50. package/plugins/kxm/dist/core.js +271 -34
  51. package/plugins/kxm/dist/extension.js +7759 -86
  52. package/plugins/kxm/dist/mcp-server.js +75 -21
  53. package/plugins/kxm/dist/runtime.js +8218 -2328
  54. package/plugins/kxm/dist/server.js +3125 -2260
  55. package/plugins/kxm/dist/vnext-runtime-supervisor.js +5961 -661
  56. package/plugins/kxm/package.json +1 -1
  57. package/plugins/kxm/skills/SUITE.md +5 -0
  58. package/plugins/kxm/skills/hints.json +103 -0
  59. package/plugins/kxm/skills/kxm/SKILL.md +30 -83
  60. package/plugins/kxm/skills/kxm-browser-annotate/SKILL.md +90 -0
  61. package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +47 -0
  62. package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +48 -0
  63. package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +48 -0
  64. package/plugins/kxm/skills/kxm-browser-session/SKILL.md +94 -0
  65. package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +87 -0
  66. package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +71 -0
  67. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +69 -0
  68. package/plugins/kxm/skills/kxm-definitions/SKILL.md +65 -0
  69. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +34 -0
  70. package/plugins/kxm/skills/kxm-harvest/SKILL.md +48 -0
  71. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +43 -0
  72. package/plugins/kxm/skills/kxm-insights/SKILL.md +48 -0
  73. package/plugins/kxm/skills/kxm-mind/SKILL.md +59 -0
  74. package/plugins/kxm/skills/kxm-peer/SKILL.md +110 -0
  75. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +42 -0
  76. package/plugins/kxm/skills/kxm-projects/SKILL.md +43 -0
  77. package/plugins/kxm/skills/kxm-protocol/SKILL.md +66 -0
  78. package/plugins/kxm/skills/kxm-query/SKILL.md +45 -0
  79. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +30 -0
  80. package/plugins/kxm/skills/kxm-runs/SKILL.md +29 -0
  81. package/plugins/kxm/skills/kxm-setup/SKILL.md +55 -0
  82. package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +31 -0
  83. package/plugins/kxm/skills/kxm-tasks/SKILL.md +33 -0
  84. package/plugins/kxm/skills/kxm-triage/SKILL.md +47 -0
  85. package/plugins/kxm/skills/kxm-work/SKILL.md +44 -0
  86. package/plugins/kxm/skills/kxm-workflow/SKILL.md +45 -0
  87. package/plugins/kxm/src/autocomplete.ts +9 -3
  88. package/plugins/kxm/src/browser.ts +603 -0
  89. package/plugins/kxm/src/cli/context-skills.ts +373 -0
  90. package/plugins/kxm/src/cli/hub.ts +614 -0
  91. package/plugins/kxm/src/cli/roles.ts +615 -0
  92. package/plugins/kxm/src/cli/system.ts +906 -0
  93. package/plugins/kxm/src/cli/tasks.ts +364 -0
  94. package/plugins/kxm/src/cli/types.ts +270 -0
  95. package/plugins/kxm/src/cli/vnext.ts +698 -0
  96. package/plugins/kxm/src/cli/workflows.ts +699 -0
  97. package/plugins/kxm/src/cli.ts +362 -2849
  98. package/plugins/kxm/src/commands.ts +150 -8
  99. package/plugins/kxm/src/completion-install.ts +223 -0
  100. package/plugins/kxm/src/config.ts +7 -4
  101. package/plugins/kxm/src/context-packet.ts +172 -0
  102. package/plugins/kxm/src/database.ts +1 -1
  103. package/plugins/kxm/src/extension.ts +36 -1
  104. package/plugins/kxm/src/external-effects.ts +357 -8
  105. package/plugins/kxm/src/hub-env.ts +193 -0
  106. package/plugins/kxm/src/hub.ts +2 -4
  107. package/plugins/kxm/src/improve.ts +72 -0
  108. package/plugins/kxm/src/init-guide-setup.ts +547 -0
  109. package/plugins/kxm/src/local-snapshot.ts +1 -1
  110. package/plugins/kxm/src/mcp-server.ts +1 -1
  111. package/plugins/kxm/src/model-inventory.ts +127 -0
  112. package/plugins/kxm/src/modes.ts +348 -0
  113. package/plugins/kxm/src/policy-draft.d.mts +55 -0
  114. package/plugins/kxm/src/policy-draft.mjs +565 -0
  115. package/plugins/kxm/src/price-calc.ts +17 -18
  116. package/plugins/kxm/src/prices.ts +32 -16
  117. package/plugins/kxm/src/producers.ts +71 -0
  118. package/plugins/kxm/src/protocol.ts +111 -0
  119. package/plugins/kxm/src/restricted-yaml.d.mts +31 -0
  120. package/plugins/kxm/src/restricted-yaml.mjs +145 -0
  121. package/plugins/kxm/src/role.ts +710 -0
  122. package/plugins/kxm/src/routing.ts +99 -1
  123. package/plugins/kxm/src/runtime.ts +4 -0
  124. package/plugins/kxm/src/safety-integrity.ts +76 -0
  125. package/plugins/kxm/src/session-work.ts +9 -2
  126. package/plugins/kxm/src/sqlite.ts +76 -0
  127. package/plugins/kxm/src/ssh-remote.ts +560 -0
  128. package/plugins/kxm/src/store.ts +1 -1
  129. package/plugins/kxm/src/studio-layout.ts +660 -17
  130. package/plugins/kxm/src/subagent-control.ts +312 -0
  131. package/plugins/kxm/src/suggest.ts +7 -13
  132. package/plugins/kxm/src/telemetry.ts +82 -0
  133. package/plugins/kxm/src/tui.ts +140 -0
  134. package/plugins/kxm/src/vnext-bindings.ts +1 -1
  135. package/plugins/kxm/src/vnext-config.ts +53 -111
  136. package/plugins/kxm/src/vnext-engine-command.ts +2 -0
  137. package/plugins/kxm/src/vnext-engine.ts +214 -62
  138. package/plugins/kxm/src/vnext-harness.ts +336 -84
  139. package/plugins/kxm/src/vnext-oneshot-evidence.ts +117 -0
  140. package/plugins/kxm/src/vnext-oneshot-process.ts +187 -0
  141. package/plugins/kxm/src/vnext-oneshot-producer.ts +182 -224
  142. package/plugins/kxm/src/vnext-pi-producer.ts +11 -7
  143. package/plugins/kxm/src/vnext-runtime-store.ts +36 -2
  144. package/plugins/kxm/src/vnext-runtime-supervisor.ts +122 -5
  145. package/plugins/kxm/src/vnext-runtime.ts +14 -0
  146. package/plugins/kxm/src/workflow-manager.ts +392 -0
  147. package/plugins/kxm/src/workflow-tui.ts +255 -0
  148. package/plugins/kxm/src/workflow.ts +144 -0
  149. package/schemas/policy-draft/README.md +17 -0
  150. package/schemas/policy-draft/model.v2.schema.json +140 -0
  151. package/schemas/policy-draft/role.v2.schema.json +91 -0
  152. package/schemas/vnext/modes.schema.json +56 -0
  153. package/schemas/vnext/role.schema.json +76 -0
  154. package/schemas/vnext/run-event.schema.json +1 -0
  155. package/scripts/assignment-run.d.mts +1 -1
  156. package/scripts/assignment-run.mjs +44 -35
  157. package/scripts/check-generated.mjs +33 -9
  158. package/scripts/emit-codex-artifacts.mjs +255 -11
  159. package/scripts/harness-run.d.mts +12 -4
  160. package/scripts/harness-run.mjs +65 -17
  161. package/scripts/kxm-bump-version.mjs +146 -0
  162. package/scripts/kxm-hub.mjs +150 -2
  163. package/scripts/kxm-publish-npm.mjs +3 -1
  164. package/scripts/kxm-release-github.mjs +3 -1
  165. package/scripts/kxm.mjs +0 -0
  166. package/scripts/native-critic.d.mts +5 -0
  167. package/scripts/native-critic.mjs +60 -0
  168. package/.kxm/config/README.md +0 -5
  169. package/.kxm/config/agents.json +0 -43
  170. package/.kxm/config/env.example +0 -56
  171. package/.kxm/config/update.example.yaml +0 -9
  172. package/.kxm/config/workflows/fix.json +0 -160
  173. package/.kxm/config/workflows/jira-development.json +0 -116
  174. package/.kxm/config/workflows/provenance-quorum.json +0 -150
  175. package/.kxm/config/workflows/v04-dogfood.json +0 -72
@@ -0,0 +1,91 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.kxm.dev/policy-draft/role.v2.schema.json",
4
+ "title": "KXM policy-draft role",
5
+ "description": "Passive kxm.role.v2 scaffolding. Specialty identity is separate from purpose. Roster order is configured preference, not a measured ranking. Not live admission or operator settings.",
6
+ "type": "object",
7
+ "required": ["schema", "purpose", "permission", "description", "roster"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schema": { "const": "kxm.role.v2" },
11
+ "id": {
12
+ "type": "string",
13
+ "pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
14
+ "minLength": 1,
15
+ "maxLength": 64
16
+ },
17
+ "purpose": {
18
+ "enum": ["writer", "planner", "reviewer-arch", "reviewer-cli", "experiment"]
19
+ },
20
+ "permission": { "enum": ["edit", "read-only"] },
21
+ "description": { "type": "string", "minLength": 1, "maxLength": 2000 },
22
+ "roster": {
23
+ "type": "array",
24
+ "maxItems": 32,
25
+ "items": {
26
+ "type": "object",
27
+ "required": ["route"],
28
+ "additionalProperties": false,
29
+ "properties": {
30
+ "route": {
31
+ "type": "string",
32
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
33
+ "minLength": 1,
34
+ "maxLength": 64
35
+ },
36
+ "effort": {
37
+ "enum": ["off", "minimal", "low", "medium", "high", "xhigh", "max"]
38
+ },
39
+ "mode": { "enum": ["headless", "interactive", "either"] }
40
+ }
41
+ }
42
+ },
43
+ "skills": {
44
+ "type": "array",
45
+ "items": { "type": "string", "minLength": 1, "maxLength": 128 },
46
+ "maxItems": 64
47
+ },
48
+ "tools": {
49
+ "type": "object",
50
+ "additionalProperties": false,
51
+ "properties": {
52
+ "preset": { "type": "string" },
53
+ "allow": { "type": "array", "items": { "type": "string" } },
54
+ "deny": { "type": "array", "items": { "type": "string" } }
55
+ }
56
+ },
57
+ "produces": {
58
+ "type": "array",
59
+ "items": {
60
+ "type": "object",
61
+ "required": ["template"],
62
+ "additionalProperties": false,
63
+ "properties": {
64
+ "template": { "type": "string" },
65
+ "schema": { "type": "string" }
66
+ }
67
+ }
68
+ },
69
+ "consumes": {
70
+ "type": "array",
71
+ "items": {
72
+ "type": "object",
73
+ "required": ["template"],
74
+ "additionalProperties": false,
75
+ "properties": {
76
+ "template": { "type": "string" },
77
+ "schema": { "type": "string" }
78
+ }
79
+ }
80
+ },
81
+ "policy": {
82
+ "type": "object",
83
+ "additionalProperties": false,
84
+ "properties": {
85
+ "vendorIndependenceRequired": { "type": "boolean" },
86
+ "maxTransitions": { "type": "integer", "minimum": 1 },
87
+ "requiresGateVerification": { "type": "boolean" }
88
+ }
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.kxm.dev/vnext/modes.schema.json",
4
+ "title": "KXM Modes Configuration",
5
+ "description": "Declarative major modes and domain modules for selective tool loading and context scoping",
6
+ "type": "object",
7
+ "required": ["schema", "majorModes"],
8
+ "properties": {
9
+ "schema": { "const": "kxm.modes.v1" },
10
+ "majorModes": {
11
+ "type": "object",
12
+ "additionalProperties": {
13
+ "type": "object",
14
+ "required": ["baseTools"],
15
+ "properties": {
16
+ "description": { "type": "string" },
17
+ "baseTools": {
18
+ "type": "array",
19
+ "items": { "type": "string" }
20
+ },
21
+ "contextFiles": {
22
+ "type": "array",
23
+ "items": { "type": "string" }
24
+ },
25
+ "thinkingLevel": {
26
+ "type": "string",
27
+ "enum": ["low", "medium", "high", "xhigh"]
28
+ },
29
+ "model": { "type": "string" }
30
+ },
31
+ "additionalProperties": false
32
+ }
33
+ },
34
+ "domains": {
35
+ "type": "object",
36
+ "additionalProperties": {
37
+ "type": "object",
38
+ "required": ["tools"],
39
+ "properties": {
40
+ "description": { "type": "string" },
41
+ "tools": {
42
+ "type": "array",
43
+ "items": { "type": "string" }
44
+ },
45
+ "contextFiles": {
46
+ "type": "array",
47
+ "items": { "type": "string" }
48
+ },
49
+ "promptSnippet": { "type": "string" }
50
+ },
51
+ "additionalProperties": false
52
+ }
53
+ }
54
+ },
55
+ "additionalProperties": false
56
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.kxm.dev/vnext/role.schema.json",
4
+ "title": "KXM role definition",
5
+ "description": "The filename determines the role identity unless explicitly declared.",
6
+ "type": "object",
7
+ "required": ["schema", "description"],
8
+ "properties": {
9
+ "schema": { "const": "kxm.role.v1" },
10
+ "id": { "$ref": "common.schema.json#/$defs/identifier" },
11
+ "description": { "type": "string", "minLength": 1, "maxLength": 2000 },
12
+ "skills": {
13
+ "type": "array",
14
+ "items": { "type": "string", "minLength": 1, "maxLength": 128 },
15
+ "maxItems": 64
16
+ },
17
+ "tools": {
18
+ "type": "object",
19
+ "properties": {
20
+ "preset": { "type": "string" },
21
+ "allow": { "type": "array", "items": { "type": "string" } },
22
+ "deny": { "type": "array", "items": { "type": "string" } }
23
+ },
24
+ "additionalProperties": false
25
+ },
26
+ "produces": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "object",
30
+ "required": ["template"],
31
+ "properties": {
32
+ "template": { "type": "string" },
33
+ "schema": { "type": "string" }
34
+ },
35
+ "additionalProperties": false
36
+ }
37
+ },
38
+ "consumes": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "object",
42
+ "required": ["template"],
43
+ "properties": {
44
+ "template": { "type": "string" },
45
+ "schema": { "type": "string" }
46
+ },
47
+ "additionalProperties": false
48
+ }
49
+ },
50
+ "roster": {
51
+ "type": "array",
52
+ "items": {
53
+ "type": "object",
54
+ "required": ["harness", "model"],
55
+ "properties": {
56
+ "harness": { "$ref": "common.schema.json#/$defs/identifier" },
57
+ "model": { "$ref": "common.schema.json#/$defs/modelSelector" },
58
+ "provider": { "type": "string" },
59
+ "effort": { "enum": ["low", "medium", "high", "xhigh"] },
60
+ "mode": { "enum": ["headless", "interactive", "either"] }
61
+ },
62
+ "additionalProperties": false
63
+ }
64
+ },
65
+ "policy": {
66
+ "type": "object",
67
+ "properties": {
68
+ "vendorIndependenceRequired": { "type": "boolean" },
69
+ "maxTransitions": { "type": "integer", "minimum": 1 },
70
+ "requiresGateVerification": { "type": "boolean" }
71
+ },
72
+ "additionalProperties": false
73
+ }
74
+ },
75
+ "additionalProperties": false
76
+ }
@@ -323,6 +323,7 @@
323
323
  "properties": {
324
324
  "workflowId": { "$ref": "common.schema.json#/$defs/identifier" },
325
325
  "displayTitle": { "type": "string", "minLength": 1, "maxLength": 200 },
326
+ "prompt": { "type": "string", "maxLength": 64000 },
326
327
  "promptHash": { "$ref": "common.schema.json#/$defs/sha256" },
327
328
  "runPlanHash": { "$ref": "common.schema.json#/$defs/sha256" },
328
329
  "capabilityHash": { "$ref": "common.schema.json#/$defs/sha256" },
@@ -33,7 +33,7 @@ export function resolveRequiredCritics(policy: unknown): Readonly<Record<string,
33
33
  vendor: string;
34
34
  route_id?: string;
35
35
  }>>>;
36
- export function getRosterPolicy(deps?: Record<string, unknown>): unknown;
36
+ export function getRosterPolicy(deps?: Record<string, unknown>): import("./roster-policy.mjs").RosterPolicy;
37
37
  export const ASSIGNMENT_KINDS: readonly string[];
38
38
  export const KIND_ROLES: Readonly<Record<string, string>>;
39
39
  export const REVIEW_KINDS: readonly string[];
@@ -746,30 +746,35 @@ function validateRework(reworkOf, taskDir, taskId, assignmentId, deps) {
746
746
  return priorId;
747
747
  }
748
748
 
749
- let cachedDiskPolicy = null;
750
- export function getRosterPolicy(deps = {}) {
751
- if (deps.rosterPolicy) {
752
- return deps.rosterPolicy.policy ?? deps.rosterPolicy;
749
+ function requireRosterPolicyShape(policy) {
750
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
751
+ throw failClosed("roster policy is invalid or unparsed", "route_invalid");
753
752
  }
754
- if (typeof deps.loadTrustedRosterPolicy === "function") {
755
- const loaded = deps.loadTrustedRosterPolicy();
756
- return loaded?.policy ?? loaded;
753
+ if (!policy.routes || typeof policy.routes !== "object" || Array.isArray(policy.routes) || Object.keys(policy.routes).length === 0) {
754
+ throw failClosed("roster policy is invalid or unparsed", "route_invalid");
757
755
  }
756
+ if (!policy.lineup || typeof policy.lineup !== "object" || Array.isArray(policy.lineup)) {
757
+ throw failClosed("roster policy is invalid or unparsed", "route_invalid");
758
+ }
759
+ return policy;
760
+ }
761
+
762
+ function unwrapRosterPolicy(loaded) {
763
+ return requireRosterPolicyShape(loaded?.policy ?? loaded);
764
+ }
765
+
766
+ export function getRosterPolicy(deps = {}) {
758
767
  try {
759
- return loadTrustedRosterPolicy().policy;
760
- } catch (error) {
761
- // When executing in a local development checkout where git status has dirty
762
- // uncommitted edits or during unit tests running against in-flight code,
763
- // fallback to parsing the local .kxm/roster.json.
764
- if (!cachedDiskPolicy) {
765
- try {
766
- const diskPath = join(fileURLToPath(new URL(".", import.meta.url)), "..", ".kxm/roster.json");
767
- cachedDiskPolicy = Object.freeze(JSON.parse(readFileSync(diskPath, "utf8")));
768
- } catch {
769
- throw failClosed(error?.message ?? "roster policy unavailable", "route_invalid");
770
- }
768
+ if (Object.hasOwn(deps, "rosterPolicy")) {
769
+ return unwrapRosterPolicy(deps.rosterPolicy);
770
+ }
771
+ if (typeof deps.loadTrustedRosterPolicy === "function") {
772
+ return unwrapRosterPolicy(deps.loadTrustedRosterPolicy());
771
773
  }
772
- return cachedDiskPolicy;
774
+ return unwrapRosterPolicy(loadTrustedRosterPolicy());
775
+ } catch (error) {
776
+ if (error?.runnerCode === "route_invalid") throw error;
777
+ throw failClosed(error?.message ?? "roster policy unavailable", "route_invalid");
773
778
  }
774
779
  }
775
780
 
@@ -979,6 +984,15 @@ const MODEL_CLAIM_OVERRIDE_KEYS = Object.freeze([
979
984
  "usage",
980
985
  "transport",
981
986
  ]);
987
+ function injectedPolicyDeps(source = {}) {
988
+ const deps = {};
989
+ if (Object.hasOwn(source, "rosterPolicy")) deps.rosterPolicy = source.rosterPolicy;
990
+ if (typeof source.loadTrustedRosterPolicy === "function") {
991
+ deps.loadTrustedRosterPolicy = source.loadTrustedRosterPolicy;
992
+ }
993
+ return deps;
994
+ }
995
+
982
996
  function ioDeps(deps = {}) {
983
997
  return {
984
998
  spawnSync: deps.spawnSync ?? spawnSync,
@@ -996,8 +1010,7 @@ function ioDeps(deps = {}) {
996
1010
  rmSync: deps.rmSync ?? rmSync,
997
1011
  manifestBytes: deps.manifestBytes,
998
1012
  now: deps.now,
999
- rosterPolicy: deps.rosterPolicy,
1000
- loadTrustedRosterPolicy: deps.loadTrustedRosterPolicy,
1013
+ ...injectedPolicyDeps(deps),
1001
1014
  };
1002
1015
  }
1003
1016
 
@@ -3374,7 +3387,7 @@ function closedObserved(pr, ci) {
3374
3387
 
3375
3388
  export function resolveRequiredCritics(policy) {
3376
3389
  if (!policy || typeof policy !== "object" || !policy.required_critics || !policy.routes) {
3377
- return REQUIRED_ACCEPT_CRITICS;
3390
+ throw failClosed("roster policy is invalid or unparsed", "route_invalid");
3378
3391
  }
3379
3392
  const result = {};
3380
3393
  for (const [kind, routeId] of Object.entries(policy.required_critics)) {
@@ -3409,11 +3422,10 @@ function assertEligibleWriter(bound, policy) {
3409
3422
  if (!(transport?.status === "completed" && transport?.ok === true)) {
3410
3423
  throw failClosed("writer transport is not completed/ok", "witness_binding_invalid");
3411
3424
  }
3412
- if (policy?.lineup?.writer && policy?.routes) {
3413
- const matched = findLineupRoute(policy, "writer", bound.identity.harness, bound.identity.model);
3414
- if (!matched) {
3415
- throw failClosed(`writer ${bound.identity.harness}/${bound.identity.model} is not admitted in lineup`, "witness_binding_invalid");
3416
- }
3425
+ requireRosterPolicyShape(policy);
3426
+ const matched = findLineupRoute(policy, "writer", bound.identity.harness, bound.identity.model);
3427
+ if (!matched) {
3428
+ throw failClosed(`writer ${bound.identity.harness}/${bound.identity.model} is not admitted in lineup`, "witness_binding_invalid");
3417
3429
  }
3418
3430
  }
3419
3431
 
@@ -3669,12 +3681,7 @@ export async function acceptAssignment(request, deps = {}) {
3669
3681
  const commitValue = request?.commit;
3670
3682
  const writerRecordValue = request?.recordDir ?? request?.record_dir;
3671
3683
  const criticValues = request?.critics;
3672
- let policy;
3673
- try {
3674
- policy = getRosterPolicy(deps);
3675
- } catch {
3676
- policy = null;
3677
- }
3684
+ const policy = getRosterPolicy(deps);
3678
3685
  const criticSpecs = resolveRequiredCritics(policy);
3679
3686
  const requiredKinds = Object.keys(criticSpecs);
3680
3687
  if (!Array.isArray(criticValues) || criticValues.length !== requiredKinds.length) {
@@ -4337,6 +4344,7 @@ export async function main(argv = process.argv, io = { stdin: process.stdin, std
4337
4344
  spawnSync: io.spawnSync ?? spawnSync,
4338
4345
  env: io.env,
4339
4346
  now: io.now,
4347
+ ...injectedPolicyDeps(io),
4340
4348
  });
4341
4349
  io.stdout.write(`${JSON.stringify(accepted, undefined, 2)}\n`);
4342
4350
  process.exitCode = 0;
@@ -4395,6 +4403,7 @@ export async function main(argv = process.argv, io = { stdin: process.stdin, std
4395
4403
  spawnSync: io.spawnSync ?? spawnSync,
4396
4404
  env: io.env,
4397
4405
  now: io.now,
4406
+ ...injectedPolicyDeps(io),
4398
4407
  });
4399
4408
  io.stdout.write(`${JSON.stringify(receipt, undefined, 2)}\n`);
4400
4409
  process.exitCode = receipt.result === "passed" ? 0 : 1;
@@ -4418,7 +4427,7 @@ export async function main(argv = process.argv, io = { stdin: process.stdin, std
4418
4427
  const bytes = readFileSync(manifestPath);
4419
4428
  const manifest = parseJsonFile(manifestPath, bytes, "assignment manifest");
4420
4429
  try {
4421
- const completion = await runAssignment(manifest, { manifestBytes: bytes });
4430
+ const completion = await runAssignment(manifest, { manifestBytes: bytes, ...injectedPolicyDeps(io) });
4422
4431
  io.stdout.write(`${JSON.stringify(completion, undefined, 2)}\n`);
4423
4432
  process.exitCode = completion.transport?.ok === true && completion.recording?.status === "ok" ? 0 : 1;
4424
4433
  return completion;
@@ -4,8 +4,9 @@ import { existsSync } from "node:fs";
4
4
  import { resolve } from "node:path";
5
5
  import { spawnSync } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
+ import { listSkillMirrorArtifacts, loadSkillSuiteManifest } from "./emit-codex-artifacts.mjs";
7
8
 
8
- export const GENERATED_ARTIFACTS = [
9
+ export const STATIC_GENERATED_ARTIFACTS = Object.freeze([
9
10
  "plugins/kxm/dist/cli.js",
10
11
  "plugins/kxm/dist/server.js",
11
12
  "plugins/kxm/dist/mcp-server.js",
@@ -14,13 +15,34 @@ export const GENERATED_ARTIFACTS = [
14
15
  "plugins/kxm/dist/runtime.js",
15
16
  "plugins/kxm/dist/client.js",
16
17
  "plugins/kxm/dist/extension.js",
17
- ".agents/skills/kxm/SKILL.md",
18
- ".agents/skills/kxm/references/protocol.md",
19
- ".agents/skills/kxm-session/SKILL.md",
20
18
  "AGENTS.md",
21
19
  "CLAUDE.md",
22
20
  "GEMINI.md",
23
- ];
21
+ ]);
22
+
23
+ /**
24
+ * Compute the generated artifact list from the committed suite manifest.
25
+ * The manifest is required; missing or invalid manifests fail closed.
26
+ *
27
+ * @param {string} [repository]
28
+ * @returns {string[]}
29
+ */
30
+ export function computeGeneratedArtifacts(repository = process.cwd()) {
31
+ const root = resolve(repository);
32
+ loadSkillSuiteManifest(root);
33
+ return Object.freeze([
34
+ ...STATIC_GENERATED_ARTIFACTS,
35
+ ...listSkillMirrorArtifacts(root),
36
+ ]);
37
+ }
38
+
39
+ /**
40
+ * Synchronous snapshot for the current repository. Tests that need a
41
+ * fixture-specific list should call computeGeneratedArtifacts(root).
42
+ *
43
+ * @type {readonly string[]}
44
+ */
45
+ export const GENERATED_ARTIFACTS = computeGeneratedArtifacts();
24
46
 
25
47
  function runGit(repository, args, allowFailure = false) {
26
48
  const result = spawnSync("git", args, {
@@ -39,12 +61,14 @@ export function checkGeneratedArtifacts(repository = process.cwd()) {
39
61
  const requestedRoot = resolve(repository);
40
62
  const topLevel = runGit(requestedRoot, ["rev-parse", "--show-toplevel"]).stdout.trim();
41
63
  const root = resolve(topLevel);
42
- const missing = GENERATED_ARTIFACTS.filter((path) => !existsSync(resolve(root, path)));
64
+ const artifacts = computeGeneratedArtifacts(root);
65
+
66
+ const missing = artifacts.filter((path) => !existsSync(resolve(root, path)));
43
67
  if (missing.length > 0) {
44
68
  throw new Error(`generated artifacts are missing: ${missing.join(", ")}`);
45
69
  }
46
70
 
47
- const untracked = GENERATED_ARTIFACTS.filter((path) => runGit(
71
+ const untracked = artifacts.filter((path) => runGit(
48
72
  root,
49
73
  ["--literal-pathspecs", "ls-files", "--error-unmatch", "--", path],
50
74
  true,
@@ -58,12 +82,12 @@ export function checkGeneratedArtifacts(repository = process.cwd()) {
58
82
  "diff",
59
83
  "--name-only",
60
84
  "--",
61
- ...GENERATED_ARTIFACTS,
85
+ ...artifacts,
62
86
  ]).stdout.trim();
63
87
  if (status) {
64
88
  throw new Error(`generated artifacts changed after build:\n${status}`);
65
89
  }
66
- return { root, artifacts: [...GENERATED_ARTIFACTS] };
90
+ return { root, artifacts: [...artifacts] };
67
91
  }
68
92
 
69
93
  if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {