open-multi-agent-kit 0.80.5 → 0.80.6

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 (105) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/config.d.ts +4 -9
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +46 -15
  5. package/dist/config.js.map +1 -1
  6. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  7. package/dist/core/compaction/branch-summarization.js +6 -1
  8. package/dist/core/compaction/branch-summarization.js.map +1 -1
  9. package/dist/core/compaction/compaction.d.ts +82 -1
  10. package/dist/core/compaction/compaction.d.ts.map +1 -1
  11. package/dist/core/compaction/compaction.js +606 -10
  12. package/dist/core/compaction/compaction.js.map +1 -1
  13. package/dist/core/harness-control-events.d.ts +79 -0
  14. package/dist/core/harness-control-events.d.ts.map +1 -0
  15. package/dist/core/harness-control-events.js +339 -0
  16. package/dist/core/harness-control-events.js.map +1 -0
  17. package/dist/core/harness-control-replay.d.ts +26 -0
  18. package/dist/core/harness-control-replay.d.ts.map +1 -0
  19. package/dist/core/harness-control-replay.js +155 -0
  20. package/dist/core/harness-control-replay.js.map +1 -0
  21. package/dist/core/keybindings.d.ts +12 -0
  22. package/dist/core/keybindings.d.ts.map +1 -1
  23. package/dist/core/keybindings.js +31 -1
  24. package/dist/core/keybindings.js.map +1 -1
  25. package/dist/core/mcp-inventory.d.ts +40 -0
  26. package/dist/core/mcp-inventory.d.ts.map +1 -0
  27. package/dist/core/mcp-inventory.js +104 -0
  28. package/dist/core/mcp-inventory.js.map +1 -0
  29. package/dist/core/settings-manager.d.ts +3 -0
  30. package/dist/core/settings-manager.d.ts.map +1 -1
  31. package/dist/core/settings-manager.js +4 -0
  32. package/dist/core/settings-manager.js.map +1 -1
  33. package/dist/core/slash-commands.d.ts.map +1 -1
  34. package/dist/core/slash-commands.js +4 -1
  35. package/dist/core/slash-commands.js.map +1 -1
  36. package/dist/core/spec-kit/compiler.d.ts +67 -0
  37. package/dist/core/spec-kit/compiler.d.ts.map +1 -0
  38. package/dist/core/spec-kit/compiler.js +268 -0
  39. package/dist/core/spec-kit/compiler.js.map +1 -0
  40. package/dist/core/transaction-coordinator.d.ts +30 -0
  41. package/dist/core/transaction-coordinator.d.ts.map +1 -0
  42. package/dist/core/transaction-coordinator.js +173 -0
  43. package/dist/core/transaction-coordinator.js.map +1 -0
  44. package/dist/migrations.d.ts +35 -0
  45. package/dist/migrations.d.ts.map +1 -1
  46. package/dist/migrations.js +409 -3
  47. package/dist/migrations.js.map +1 -1
  48. package/dist/modes/interactive/components/control-panel.d.ts +29 -0
  49. package/dist/modes/interactive/components/control-panel.d.ts.map +1 -0
  50. package/dist/modes/interactive/components/control-panel.js +90 -0
  51. package/dist/modes/interactive/components/control-panel.js.map +1 -0
  52. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/custom-editor.js +21 -13
  54. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  55. package/dist/modes/interactive/components/index.d.ts +3 -0
  56. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  57. package/dist/modes/interactive/components/index.js +3 -0
  58. package/dist/modes/interactive/components/index.js.map +1 -1
  59. package/dist/modes/interactive/components/mcp-selector.d.ts +28 -0
  60. package/dist/modes/interactive/components/mcp-selector.d.ts.map +1 -0
  61. package/dist/modes/interactive/components/mcp-selector.js +138 -0
  62. package/dist/modes/interactive/components/mcp-selector.js.map +1 -0
  63. package/dist/modes/interactive/components/model-selector.d.ts +12 -3
  64. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/model-selector.js +120 -27
  66. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  67. package/dist/modes/interactive/components/settings-selector.d.ts +19 -1
  68. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/settings-selector.js +21 -8
  70. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  71. package/dist/modes/interactive/components/skills-selector.d.ts +32 -0
  72. package/dist/modes/interactive/components/skills-selector.d.ts.map +1 -0
  73. package/dist/modes/interactive/components/skills-selector.js +130 -0
  74. package/dist/modes/interactive/components/skills-selector.js.map +1 -0
  75. package/dist/modes/interactive/components/thinking-selector.d.ts +7 -8
  76. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  77. package/dist/modes/interactive/components/thinking-selector.js +7 -48
  78. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  79. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  80. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  81. package/dist/modes/interactive/interactive-mode.js +340 -97
  82. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  83. package/dist/modes/interactive/theme/catppuccin-mocha.json +86 -0
  84. package/dist/modes/interactive/theme/dracula.json +86 -0
  85. package/dist/modes/interactive/theme/gruvbox-dark.json +86 -0
  86. package/dist/modes/interactive/theme/kanagawa-dragon.json +86 -0
  87. package/dist/modes/interactive/theme/omk-control-panel.json +91 -0
  88. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  89. package/dist/modes/interactive/theme/theme.js +41 -16
  90. package/dist/modes/interactive/theme/theme.js.map +1 -1
  91. package/dist/modes/interactive/theme/tokyo-night-storm.json +86 -0
  92. package/docs/compaction.md +21 -8
  93. package/docs/quickstart.md +1 -1
  94. package/docs/themes.md +10 -2
  95. package/docs/usage.md +2 -1
  96. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  97. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  98. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  99. package/examples/extensions/gondolin/package-lock.json +2 -2
  100. package/examples/extensions/gondolin/package.json +1 -1
  101. package/examples/extensions/sandbox/package-lock.json +2 -2
  102. package/examples/extensions/sandbox/package.json +1 -1
  103. package/examples/extensions/with-deps/package-lock.json +2 -2
  104. package/examples/extensions/with-deps/package.json +1 -1
  105. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
1
  import { APP_NAME } from "../config.js";
2
2
  export const BUILTIN_SLASH_COMMANDS = [
3
3
  { name: "settings", description: "Open settings menu" },
4
- { name: "model", description: "Select model (opens selector UI)" },
4
+ { name: "think", description: "Select thinking level" },
5
+ { name: "model", description: "Select model (opens selector UI, then thinking selector)" },
5
6
  { name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },
6
7
  { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
7
8
  { name: "import", description: "Import and resume a session from a JSONL file" },
@@ -20,6 +21,8 @@ export const BUILTIN_SLASH_COMMANDS = [
20
21
  { name: "compact", description: "Manually compact the session context" },
21
22
  { name: "resume", description: "Resume a different session" },
22
23
  { name: "reload", description: "Reload keybindings, extensions, skills, prompts, and themes" },
24
+ { name: "mcp", description: "Show configured MCP servers (read-only inventory)" },
25
+ { name: "skills", description: "Show loaded skills and diagnostics" },
23
26
  { name: "quit", description: `Quit ${APP_NAME}` },
24
27
  ];
25
28
  //# sourceMappingURL=slash-commands.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAiBxC,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACzE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACvD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,kCAAkC,EAAE;IAClE,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,0CAA0C,EAAE;IAClF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8DAA8D,EAAE;IAC/F,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;IAChF,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uCAAuC,EAAE;IACvE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACrE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACzD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,EAAE;IAC5D,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,gDAAgD,EAAE;IAC/E,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uDAAuD,EAAE;IACvF,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,yCAAyC,EAAE;IACxE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,mCAAmC,EAAE;IACnE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gCAAgC,EAAE;IACjE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACnD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACxE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;IAC9F,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,QAAQ,EAAE,EAAE;CACjD,CAAC","sourcesContent":["import { APP_NAME } from \"../config.ts\";\nimport type { SourceInfo } from \"./source-info.ts\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"changelog\", description: \"Show changelog entries\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous user message\" },\n\t{ name: \"clone\", description: \"Duplicate the current session at the current position\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"login\", description: \"Configure provider authentication\" },\n\t{ name: \"logout\", description: \"Remove provider authentication\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, and themes\" },\n\t{ name: \"quit\", description: `Quit ${APP_NAME}` },\n];\n"]}
1
+ {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAiBxC,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACzE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACvD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE;IACvD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,0DAA0D,EAAE;IAC1F,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,0CAA0C,EAAE;IAClF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8DAA8D,EAAE;IAC/F,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;IAChF,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uCAAuC,EAAE;IACvE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACrE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACzD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,EAAE;IAC5D,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,gDAAgD,EAAE;IAC/E,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uDAAuD,EAAE;IACvF,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,yCAAyC,EAAE;IACxE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,mCAAmC,EAAE;IACnE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gCAAgC,EAAE;IACjE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACnD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACxE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;IAC9F,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,mDAAmD,EAAE;IACjF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oCAAoC,EAAE;IACrE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,QAAQ,EAAE,EAAE;CACjD,CAAC","sourcesContent":["import { APP_NAME } from \"../config.ts\";\nimport type { SourceInfo } from \"./source-info.ts\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"think\", description: \"Select thinking level\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI, then thinking selector)\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"changelog\", description: \"Show changelog entries\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous user message\" },\n\t{ name: \"clone\", description: \"Duplicate the current session at the current position\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"login\", description: \"Configure provider authentication\" },\n\t{ name: \"logout\", description: \"Remove provider authentication\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, and themes\" },\n\t{ name: \"mcp\", description: \"Show configured MCP servers (read-only inventory)\" },\n\t{ name: \"skills\", description: \"Show loaded skills and diagnostics\" },\n\t{ name: \"quit\", description: `Quit ${APP_NAME}` },\n];\n"]}
@@ -0,0 +1,67 @@
1
+ export interface SpecRequirement {
2
+ id: string;
3
+ title: string;
4
+ priority: string;
5
+ acceptance: string[];
6
+ }
7
+ export interface SpecTask {
8
+ id: string;
9
+ title: string;
10
+ completed: boolean;
11
+ role?: string;
12
+ deps: string[];
13
+ lane?: string;
14
+ files: string[];
15
+ verify?: string;
16
+ gate?: string;
17
+ risk?: string;
18
+ requirementIds: string[];
19
+ }
20
+ export interface CompiledHarnessDagNode {
21
+ id: string;
22
+ title: string;
23
+ dependsOn: string[];
24
+ files: string[];
25
+ verify?: string;
26
+ gate?: string;
27
+ requirementIds: string[];
28
+ }
29
+ export interface TraceabilityEntry {
30
+ requirementId: string;
31
+ taskIds: string[];
32
+ verifyCommands: string[];
33
+ evidenceGates: string[];
34
+ }
35
+ export interface EvidenceManifestEntry {
36
+ taskId: string;
37
+ gate?: string;
38
+ verify?: string;
39
+ files: string[];
40
+ evidencePaths: string[];
41
+ }
42
+ export interface CompiledSpecKit {
43
+ requirements: SpecRequirement[];
44
+ tasks: SpecTask[];
45
+ compiledDag: CompiledHarnessDagNode[];
46
+ traceability: TraceabilityEntry[];
47
+ evidenceManifest: EvidenceManifestEntry[];
48
+ specHash: string;
49
+ }
50
+ export interface SpecKitValidationResult {
51
+ ok: boolean;
52
+ errors: string[];
53
+ warnings: string[];
54
+ }
55
+ export interface SpecKitCompileInput {
56
+ specMarkdown: string;
57
+ tasksMarkdown: string;
58
+ planMarkdown?: string;
59
+ templateMarkdown?: string;
60
+ }
61
+ export declare function parseSpecRequirements(markdown: string): SpecRequirement[];
62
+ export declare function parseSpecTasks(markdown: string): SpecTask[];
63
+ export declare function collectSpecEvidencePaths(tasks: readonly SpecTask[]): string[];
64
+ export declare function validateTaskCompletionDependencies(tasks: readonly SpecTask[]): string[];
65
+ export declare function validateSpecKit(input: SpecKitCompileInput, compiled?: CompiledSpecKit): SpecKitValidationResult;
66
+ export declare function compileSpecKit(input: SpecKitCompileInput): CompiledSpecKit;
67
+ //# sourceMappingURL=compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../../src/core/spec-kit/compiler.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,sBAAsB,EAAE,CAAC;IACtC,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,gBAAgB,EAAE,qBAAqB,EAAE,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAwCD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE,CAkBzE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmD3D;AA4CD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,EAAE,CAc7E;AAED,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,EAAE,CAavF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,uBAAuB,CAgC/G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,eAAe,CAqC1E","sourcesContent":["import { createHash } from \"node:crypto\";\n\nexport interface SpecRequirement {\n\tid: string;\n\ttitle: string;\n\tpriority: string;\n\tacceptance: string[];\n}\n\nexport interface SpecTask {\n\tid: string;\n\ttitle: string;\n\tcompleted: boolean;\n\trole?: string;\n\tdeps: string[];\n\tlane?: string;\n\tfiles: string[];\n\tverify?: string;\n\tgate?: string;\n\trisk?: string;\n\trequirementIds: string[];\n}\n\nexport interface CompiledHarnessDagNode {\n\tid: string;\n\ttitle: string;\n\tdependsOn: string[];\n\tfiles: string[];\n\tverify?: string;\n\tgate?: string;\n\trequirementIds: string[];\n}\n\nexport interface TraceabilityEntry {\n\trequirementId: string;\n\ttaskIds: string[];\n\tverifyCommands: string[];\n\tevidenceGates: string[];\n}\n\nexport interface EvidenceManifestEntry {\n\ttaskId: string;\n\tgate?: string;\n\tverify?: string;\n\tfiles: string[];\n\tevidencePaths: string[];\n}\n\nexport interface CompiledSpecKit {\n\trequirements: SpecRequirement[];\n\ttasks: SpecTask[];\n\tcompiledDag: CompiledHarnessDagNode[];\n\ttraceability: TraceabilityEntry[];\n\tevidenceManifest: EvidenceManifestEntry[];\n\tspecHash: string;\n}\n\nexport interface SpecKitValidationResult {\n\tok: boolean;\n\terrors: string[];\n\twarnings: string[];\n}\n\nexport interface SpecKitCompileInput {\n\tspecMarkdown: string;\n\ttasksMarkdown: string;\n\tplanMarkdown?: string;\n\ttemplateMarkdown?: string;\n}\n\nfunction stableJson(value: unknown): string {\n\tif (value === null || typeof value !== \"object\") return JSON.stringify(value);\n\tif (Array.isArray(value)) return `[${value.map(stableJson).join(\",\")}]`;\n\treturn `{${Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableJson((value as Record<string, unknown>)[key])}`)\n\t\t.join(\",\")}}`;\n}\n\nfunction hashText(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction parseBracketList(value: string | undefined): string[] {\n\tif (!value) return [];\n\tconst trimmed = value.trim();\n\tif (trimmed === \"none\" || trimmed === \"[]\") return [];\n\tif (!trimmed.startsWith(\"[\") || !trimmed.endsWith(\"]\")) {\n\t\treturn trimmed\n\t\t\t.split(\",\")\n\t\t\t.map((entry) => entry.trim())\n\t\t\t.filter(Boolean);\n\t}\n\treturn trimmed\n\t\t.slice(1, -1)\n\t\t.split(\",\")\n\t\t.map((entry) => entry.trim().replace(/^`|`$/g, \"\"))\n\t\t.filter(Boolean);\n}\n\nfunction extractSection(markdown: string, heading: string): string {\n\tconst start = markdown.indexOf(heading);\n\tif (start < 0) return \"\";\n\tconst rest = markdown.slice(start + heading.length);\n\tconst next = rest.search(/\\n#{2,3}\\s/);\n\treturn next < 0 ? rest : rest.slice(0, next);\n}\n\nexport function parseSpecRequirements(markdown: string): SpecRequirement[] {\n\tconst matches = [...markdown.matchAll(/^###\\s+(R\\d+)\\s+[—-]\\s+(.+?)(?:\\s+\\((P\\d+)\\))?\\s*$/gm)];\n\treturn matches.map((match, index) => {\n\t\tconst id = match[1]!;\n\t\tconst nextIndex = index + 1 < matches.length ? matches[index + 1]!.index! : markdown.length;\n\t\tconst body = markdown.slice(match.index!, nextIndex);\n\t\tconst acceptance = extractSection(body, \"**Acceptance**\")\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter((line) => /^\\d+\\./.test(line))\n\t\t\t.map((line) => line.replace(/^\\d+\\.\\s*/, \"\"));\n\t\treturn {\n\t\t\tid,\n\t\t\ttitle: match[2]!.trim(),\n\t\t\tpriority: match[3] ?? \"P0\",\n\t\t\tacceptance,\n\t\t};\n\t});\n}\n\nexport function parseSpecTasks(markdown: string): SpecTask[] {\n\tconst lines = markdown.split(\"\\n\");\n\tconst tasks: SpecTask[] = [];\n\tlet current: SpecTask | undefined;\n\tfor (const line of lines) {\n\t\tconst taskMatch = line.match(/^- \\[([ xX])] (HCP-\\d+)\\s+(.+)$/);\n\t\tif (taskMatch) {\n\t\t\tcurrent = {\n\t\t\t\tid: taskMatch[2]!,\n\t\t\t\ttitle: taskMatch[3]!.trim(),\n\t\t\t\tcompleted: taskMatch[1]!.toLowerCase() === \"x\",\n\t\t\t\tdeps: [],\n\t\t\t\tfiles: [],\n\t\t\t\trequirementIds: [],\n\t\t\t};\n\t\t\ttasks.push(current);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!current) continue;\n\t\tconst metaMatch = line.match(/^\\s*>\\s*([A-Za-z]+):\\s*(.+)$/);\n\t\tif (!metaMatch) continue;\n\t\tconst key = metaMatch[1]!;\n\t\tconst value = metaMatch[2]!.trim();\n\t\tswitch (key) {\n\t\t\tcase \"role\":\n\t\t\t\tcurrent.role = value;\n\t\t\t\tbreak;\n\t\t\tcase \"deps\":\n\t\t\t\tcurrent.deps = parseBracketList(value);\n\t\t\t\tbreak;\n\t\t\tcase \"lane\":\n\t\t\t\tcurrent.lane = value;\n\t\t\t\tbreak;\n\t\t\tcase \"files\":\n\t\t\t\tcurrent.files = parseBracketList(value);\n\t\t\t\tbreak;\n\t\t\tcase \"verify\":\n\t\t\t\tcurrent.verify = value.replace(/^`|`$/g, \"\");\n\t\t\t\tbreak;\n\t\t\tcase \"gate\":\n\t\t\t\tcurrent.gate = value;\n\t\t\t\tbreak;\n\t\t\tcase \"risk\":\n\t\t\t\tcurrent.risk = value;\n\t\t\t\tbreak;\n\t\t\tcase \"requirementIds\":\n\t\t\t\tcurrent.requirementIds = parseBracketList(value);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn tasks;\n}\n\nfunction detectCycle(tasks: SpecTask[]): string | undefined {\n\tconst byId = new Map(tasks.map((task) => [task.id, task]));\n\tconst visiting = new Set<string>();\n\tconst visited = new Set<string>();\n\tconst visit = (id: string, path: string[]): string | undefined => {\n\t\tif (visiting.has(id)) return [...path, id].join(\" -> \");\n\t\tif (visited.has(id)) return undefined;\n\t\tvisiting.add(id);\n\t\tfor (const dep of byId.get(id)?.deps ?? []) {\n\t\t\tconst cycle = visit(dep, [...path, id]);\n\t\t\tif (cycle) return cycle;\n\t\t}\n\t\tvisiting.delete(id);\n\t\tvisited.add(id);\n\t\treturn undefined;\n\t};\n\tfor (const task of tasks) {\n\t\tconst cycle = visit(task.id, []);\n\t\tif (cycle) return cycle;\n\t}\n\treturn undefined;\n}\n\nfunction findHardcodedAuthority(input: SpecKitCompileInput): string[] {\n\tconst combined = [\n\t\tinput.specMarkdown,\n\t\tinput.tasksMarkdown,\n\t\tinput.planMarkdown ?? \"\",\n\t\tinput.templateMarkdown ?? \"\",\n\t].join(\"\\n\");\n\tconst violations: string[] = [];\n\tif (/^\\s*-\\s+\\*\\*Authority\\*\\*:\\s*.*Kimi is final writer/im.test(combined)) {\n\t\tviolations.push(\"Provider-hardcoded authority: Kimi is final writer\");\n\t}\n\treturn violations;\n}\n\nfunction extractEvidencePathsFromText(value: string | undefined): string[] {\n\tif (!value) return [];\n\treturn [...value.matchAll(/\\.omk\\/[\\w./-]+/g)].map((match) => match[0]!.replace(/[),.;:]+$/, \"\"));\n}\n\nexport function collectSpecEvidencePaths(tasks: readonly SpecTask[]): string[] {\n\tconst paths: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const task of tasks) {\n\t\tfor (const path of [\n\t\t\t...task.files.filter((file) => file.startsWith(\".omk/\")),\n\t\t\t...extractEvidencePathsFromText(task.verify),\n\t\t]) {\n\t\t\tif (seen.has(path)) continue;\n\t\t\tseen.add(path);\n\t\t\tpaths.push(path);\n\t\t}\n\t}\n\treturn paths;\n}\n\nexport function validateTaskCompletionDependencies(tasks: readonly SpecTask[]): string[] {\n\tconst errors: string[] = [];\n\tconst byId = new Map(tasks.map((task) => [task.id, task]));\n\tfor (const task of tasks) {\n\t\tif (!task.completed) continue;\n\t\tfor (const dep of task.deps) {\n\t\t\tconst dependency = byId.get(dep);\n\t\t\tif (dependency && !dependency.completed) {\n\t\t\t\terrors.push(`Task ${task.id} is completed but dependency ${dep} is incomplete`);\n\t\t\t}\n\t\t}\n\t}\n\treturn errors;\n}\n\nexport function validateSpecKit(input: SpecKitCompileInput, compiled?: CompiledSpecKit): SpecKitValidationResult {\n\tconst spec = compiled ?? compileSpecKit(input);\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst requirementIds = new Set(spec.requirements.map((requirement) => requirement.id));\n\tconst taskIds = new Set<string>();\n\tfor (const hardcoded of findHardcodedAuthority(input)) errors.push(hardcoded);\n\tfor (const task of spec.tasks) {\n\t\tif (taskIds.has(task.id)) errors.push(`Duplicate task id: ${task.id}`);\n\t\ttaskIds.add(task.id);\n\t\tif (!task.gate) errors.push(`Task ${task.id} missing evidence gate`);\n\t\tif (!task.verify) errors.push(`Task ${task.id} missing verify command`);\n\t\tif (task.requirementIds.length === 0) errors.push(`Task ${task.id} missing requirementIds`);\n\t\tfor (const dep of task.deps) {\n\t\t\tif (!taskIds.has(dep) && !spec.tasks.some((candidate) => candidate.id === dep)) {\n\t\t\t\terrors.push(`Task ${task.id} depends on missing task ${dep}`);\n\t\t\t}\n\t\t}\n\t\tfor (const requirementId of task.requirementIds) {\n\t\t\tif (!requirementIds.has(requirementId)) errors.push(`Task ${task.id} references missing ${requirementId}`);\n\t\t}\n\t}\n\terrors.push(...validateTaskCompletionDependencies(spec.tasks));\n\tconst cycle = detectCycle(spec.tasks);\n\tif (cycle) errors.push(`Task DAG has cycle: ${cycle}`);\n\tfor (const requirement of spec.requirements) {\n\t\tconst linked = spec.tasks.filter((task) => task.requirementIds.includes(requirement.id));\n\t\tif (linked.length === 0) errors.push(`Requirement ${requirement.id} has no linked task`);\n\t\tif (requirement.acceptance.length === 0)\n\t\t\twarnings.push(`Requirement ${requirement.id} has no parsed acceptance items`);\n\t}\n\treturn { ok: errors.length === 0, errors, warnings };\n}\n\nexport function compileSpecKit(input: SpecKitCompileInput): CompiledSpecKit {\n\tconst requirements = parseSpecRequirements(input.specMarkdown);\n\tconst tasks = parseSpecTasks(input.tasksMarkdown);\n\tconst compiledDag = tasks.map((task) => ({\n\t\tid: task.id,\n\t\ttitle: task.title,\n\t\tdependsOn: task.deps,\n\t\tfiles: task.files,\n\t\tverify: task.verify,\n\t\tgate: task.gate,\n\t\trequirementIds: task.requirementIds,\n\t}));\n\tconst traceability = requirements.map((requirement) => {\n\t\tconst linkedTasks = tasks.filter((task) => task.requirementIds.includes(requirement.id));\n\t\treturn {\n\t\t\trequirementId: requirement.id,\n\t\t\ttaskIds: linkedTasks.map((task) => task.id),\n\t\t\tverifyCommands: linkedTasks.flatMap((task) => (task.verify ? [task.verify] : [])),\n\t\t\tevidenceGates: linkedTasks.flatMap((task) => (task.gate ? [task.gate] : [])),\n\t\t};\n\t});\n\tconst evidenceManifest = tasks.map((task) => ({\n\t\ttaskId: task.id,\n\t\tgate: task.gate,\n\t\tverify: task.verify,\n\t\tfiles: task.files,\n\t\tevidencePaths: collectSpecEvidencePaths([task]),\n\t}));\n\tconst hashInput = stableJson({ requirements, compiledDag, traceability, evidenceManifest });\n\treturn {\n\t\trequirements,\n\t\ttasks,\n\t\tcompiledDag,\n\t\ttraceability,\n\t\tevidenceManifest,\n\t\tspecHash: hashText(hashInput),\n\t};\n}\n"]}
@@ -0,0 +1,268 @@
1
+ import { createHash } from "node:crypto";
2
+ function stableJson(value) {
3
+ if (value === null || typeof value !== "object")
4
+ return JSON.stringify(value);
5
+ if (Array.isArray(value))
6
+ return `[${value.map(stableJson).join(",")}]`;
7
+ return `{${Object.keys(value)
8
+ .sort()
9
+ .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
10
+ .join(",")}}`;
11
+ }
12
+ function hashText(value) {
13
+ return createHash("sha256").update(value).digest("hex");
14
+ }
15
+ function parseBracketList(value) {
16
+ if (!value)
17
+ return [];
18
+ const trimmed = value.trim();
19
+ if (trimmed === "none" || trimmed === "[]")
20
+ return [];
21
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
22
+ return trimmed
23
+ .split(",")
24
+ .map((entry) => entry.trim())
25
+ .filter(Boolean);
26
+ }
27
+ return trimmed
28
+ .slice(1, -1)
29
+ .split(",")
30
+ .map((entry) => entry.trim().replace(/^`|`$/g, ""))
31
+ .filter(Boolean);
32
+ }
33
+ function extractSection(markdown, heading) {
34
+ const start = markdown.indexOf(heading);
35
+ if (start < 0)
36
+ return "";
37
+ const rest = markdown.slice(start + heading.length);
38
+ const next = rest.search(/\n#{2,3}\s/);
39
+ return next < 0 ? rest : rest.slice(0, next);
40
+ }
41
+ export function parseSpecRequirements(markdown) {
42
+ const matches = [...markdown.matchAll(/^###\s+(R\d+)\s+[—-]\s+(.+?)(?:\s+\((P\d+)\))?\s*$/gm)];
43
+ return matches.map((match, index) => {
44
+ const id = match[1];
45
+ const nextIndex = index + 1 < matches.length ? matches[index + 1].index : markdown.length;
46
+ const body = markdown.slice(match.index, nextIndex);
47
+ const acceptance = extractSection(body, "**Acceptance**")
48
+ .split("\n")
49
+ .map((line) => line.trim())
50
+ .filter((line) => /^\d+\./.test(line))
51
+ .map((line) => line.replace(/^\d+\.\s*/, ""));
52
+ return {
53
+ id,
54
+ title: match[2].trim(),
55
+ priority: match[3] ?? "P0",
56
+ acceptance,
57
+ };
58
+ });
59
+ }
60
+ export function parseSpecTasks(markdown) {
61
+ const lines = markdown.split("\n");
62
+ const tasks = [];
63
+ let current;
64
+ for (const line of lines) {
65
+ const taskMatch = line.match(/^- \[([ xX])] (HCP-\d+)\s+(.+)$/);
66
+ if (taskMatch) {
67
+ current = {
68
+ id: taskMatch[2],
69
+ title: taskMatch[3].trim(),
70
+ completed: taskMatch[1].toLowerCase() === "x",
71
+ deps: [],
72
+ files: [],
73
+ requirementIds: [],
74
+ };
75
+ tasks.push(current);
76
+ continue;
77
+ }
78
+ if (!current)
79
+ continue;
80
+ const metaMatch = line.match(/^\s*>\s*([A-Za-z]+):\s*(.+)$/);
81
+ if (!metaMatch)
82
+ continue;
83
+ const key = metaMatch[1];
84
+ const value = metaMatch[2].trim();
85
+ switch (key) {
86
+ case "role":
87
+ current.role = value;
88
+ break;
89
+ case "deps":
90
+ current.deps = parseBracketList(value);
91
+ break;
92
+ case "lane":
93
+ current.lane = value;
94
+ break;
95
+ case "files":
96
+ current.files = parseBracketList(value);
97
+ break;
98
+ case "verify":
99
+ current.verify = value.replace(/^`|`$/g, "");
100
+ break;
101
+ case "gate":
102
+ current.gate = value;
103
+ break;
104
+ case "risk":
105
+ current.risk = value;
106
+ break;
107
+ case "requirementIds":
108
+ current.requirementIds = parseBracketList(value);
109
+ break;
110
+ }
111
+ }
112
+ return tasks;
113
+ }
114
+ function detectCycle(tasks) {
115
+ const byId = new Map(tasks.map((task) => [task.id, task]));
116
+ const visiting = new Set();
117
+ const visited = new Set();
118
+ const visit = (id, path) => {
119
+ if (visiting.has(id))
120
+ return [...path, id].join(" -> ");
121
+ if (visited.has(id))
122
+ return undefined;
123
+ visiting.add(id);
124
+ for (const dep of byId.get(id)?.deps ?? []) {
125
+ const cycle = visit(dep, [...path, id]);
126
+ if (cycle)
127
+ return cycle;
128
+ }
129
+ visiting.delete(id);
130
+ visited.add(id);
131
+ return undefined;
132
+ };
133
+ for (const task of tasks) {
134
+ const cycle = visit(task.id, []);
135
+ if (cycle)
136
+ return cycle;
137
+ }
138
+ return undefined;
139
+ }
140
+ function findHardcodedAuthority(input) {
141
+ const combined = [
142
+ input.specMarkdown,
143
+ input.tasksMarkdown,
144
+ input.planMarkdown ?? "",
145
+ input.templateMarkdown ?? "",
146
+ ].join("\n");
147
+ const violations = [];
148
+ if (/^\s*-\s+\*\*Authority\*\*:\s*.*Kimi is final writer/im.test(combined)) {
149
+ violations.push("Provider-hardcoded authority: Kimi is final writer");
150
+ }
151
+ return violations;
152
+ }
153
+ function extractEvidencePathsFromText(value) {
154
+ if (!value)
155
+ return [];
156
+ return [...value.matchAll(/\.omk\/[\w./-]+/g)].map((match) => match[0].replace(/[),.;:]+$/, ""));
157
+ }
158
+ export function collectSpecEvidencePaths(tasks) {
159
+ const paths = [];
160
+ const seen = new Set();
161
+ for (const task of tasks) {
162
+ for (const path of [
163
+ ...task.files.filter((file) => file.startsWith(".omk/")),
164
+ ...extractEvidencePathsFromText(task.verify),
165
+ ]) {
166
+ if (seen.has(path))
167
+ continue;
168
+ seen.add(path);
169
+ paths.push(path);
170
+ }
171
+ }
172
+ return paths;
173
+ }
174
+ export function validateTaskCompletionDependencies(tasks) {
175
+ const errors = [];
176
+ const byId = new Map(tasks.map((task) => [task.id, task]));
177
+ for (const task of tasks) {
178
+ if (!task.completed)
179
+ continue;
180
+ for (const dep of task.deps) {
181
+ const dependency = byId.get(dep);
182
+ if (dependency && !dependency.completed) {
183
+ errors.push(`Task ${task.id} is completed but dependency ${dep} is incomplete`);
184
+ }
185
+ }
186
+ }
187
+ return errors;
188
+ }
189
+ export function validateSpecKit(input, compiled) {
190
+ const spec = compiled ?? compileSpecKit(input);
191
+ const errors = [];
192
+ const warnings = [];
193
+ const requirementIds = new Set(spec.requirements.map((requirement) => requirement.id));
194
+ const taskIds = new Set();
195
+ for (const hardcoded of findHardcodedAuthority(input))
196
+ errors.push(hardcoded);
197
+ for (const task of spec.tasks) {
198
+ if (taskIds.has(task.id))
199
+ errors.push(`Duplicate task id: ${task.id}`);
200
+ taskIds.add(task.id);
201
+ if (!task.gate)
202
+ errors.push(`Task ${task.id} missing evidence gate`);
203
+ if (!task.verify)
204
+ errors.push(`Task ${task.id} missing verify command`);
205
+ if (task.requirementIds.length === 0)
206
+ errors.push(`Task ${task.id} missing requirementIds`);
207
+ for (const dep of task.deps) {
208
+ if (!taskIds.has(dep) && !spec.tasks.some((candidate) => candidate.id === dep)) {
209
+ errors.push(`Task ${task.id} depends on missing task ${dep}`);
210
+ }
211
+ }
212
+ for (const requirementId of task.requirementIds) {
213
+ if (!requirementIds.has(requirementId))
214
+ errors.push(`Task ${task.id} references missing ${requirementId}`);
215
+ }
216
+ }
217
+ errors.push(...validateTaskCompletionDependencies(spec.tasks));
218
+ const cycle = detectCycle(spec.tasks);
219
+ if (cycle)
220
+ errors.push(`Task DAG has cycle: ${cycle}`);
221
+ for (const requirement of spec.requirements) {
222
+ const linked = spec.tasks.filter((task) => task.requirementIds.includes(requirement.id));
223
+ if (linked.length === 0)
224
+ errors.push(`Requirement ${requirement.id} has no linked task`);
225
+ if (requirement.acceptance.length === 0)
226
+ warnings.push(`Requirement ${requirement.id} has no parsed acceptance items`);
227
+ }
228
+ return { ok: errors.length === 0, errors, warnings };
229
+ }
230
+ export function compileSpecKit(input) {
231
+ const requirements = parseSpecRequirements(input.specMarkdown);
232
+ const tasks = parseSpecTasks(input.tasksMarkdown);
233
+ const compiledDag = tasks.map((task) => ({
234
+ id: task.id,
235
+ title: task.title,
236
+ dependsOn: task.deps,
237
+ files: task.files,
238
+ verify: task.verify,
239
+ gate: task.gate,
240
+ requirementIds: task.requirementIds,
241
+ }));
242
+ const traceability = requirements.map((requirement) => {
243
+ const linkedTasks = tasks.filter((task) => task.requirementIds.includes(requirement.id));
244
+ return {
245
+ requirementId: requirement.id,
246
+ taskIds: linkedTasks.map((task) => task.id),
247
+ verifyCommands: linkedTasks.flatMap((task) => (task.verify ? [task.verify] : [])),
248
+ evidenceGates: linkedTasks.flatMap((task) => (task.gate ? [task.gate] : [])),
249
+ };
250
+ });
251
+ const evidenceManifest = tasks.map((task) => ({
252
+ taskId: task.id,
253
+ gate: task.gate,
254
+ verify: task.verify,
255
+ files: task.files,
256
+ evidencePaths: collectSpecEvidencePaths([task]),
257
+ }));
258
+ const hashInput = stableJson({ requirements, compiledDag, traceability, evidenceManifest });
259
+ return {
260
+ requirements,
261
+ tasks,
262
+ compiledDag,
263
+ traceability,
264
+ evidenceManifest,
265
+ specHash: hashText(hashInput),
266
+ };
267
+ }
268
+ //# sourceMappingURL=compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../src/core/spec-kit/compiler.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\n\nexport interface SpecRequirement {\n\tid: string;\n\ttitle: string;\n\tpriority: string;\n\tacceptance: string[];\n}\n\nexport interface SpecTask {\n\tid: string;\n\ttitle: string;\n\tcompleted: boolean;\n\trole?: string;\n\tdeps: string[];\n\tlane?: string;\n\tfiles: string[];\n\tverify?: string;\n\tgate?: string;\n\trisk?: string;\n\trequirementIds: string[];\n}\n\nexport interface CompiledHarnessDagNode {\n\tid: string;\n\ttitle: string;\n\tdependsOn: string[];\n\tfiles: string[];\n\tverify?: string;\n\tgate?: string;\n\trequirementIds: string[];\n}\n\nexport interface TraceabilityEntry {\n\trequirementId: string;\n\ttaskIds: string[];\n\tverifyCommands: string[];\n\tevidenceGates: string[];\n}\n\nexport interface EvidenceManifestEntry {\n\ttaskId: string;\n\tgate?: string;\n\tverify?: string;\n\tfiles: string[];\n\tevidencePaths: string[];\n}\n\nexport interface CompiledSpecKit {\n\trequirements: SpecRequirement[];\n\ttasks: SpecTask[];\n\tcompiledDag: CompiledHarnessDagNode[];\n\ttraceability: TraceabilityEntry[];\n\tevidenceManifest: EvidenceManifestEntry[];\n\tspecHash: string;\n}\n\nexport interface SpecKitValidationResult {\n\tok: boolean;\n\terrors: string[];\n\twarnings: string[];\n}\n\nexport interface SpecKitCompileInput {\n\tspecMarkdown: string;\n\ttasksMarkdown: string;\n\tplanMarkdown?: string;\n\ttemplateMarkdown?: string;\n}\n\nfunction stableJson(value: unknown): string {\n\tif (value === null || typeof value !== \"object\") return JSON.stringify(value);\n\tif (Array.isArray(value)) return `[${value.map(stableJson).join(\",\")}]`;\n\treturn `{${Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableJson((value as Record<string, unknown>)[key])}`)\n\t\t.join(\",\")}}`;\n}\n\nfunction hashText(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction parseBracketList(value: string | undefined): string[] {\n\tif (!value) return [];\n\tconst trimmed = value.trim();\n\tif (trimmed === \"none\" || trimmed === \"[]\") return [];\n\tif (!trimmed.startsWith(\"[\") || !trimmed.endsWith(\"]\")) {\n\t\treturn trimmed\n\t\t\t.split(\",\")\n\t\t\t.map((entry) => entry.trim())\n\t\t\t.filter(Boolean);\n\t}\n\treturn trimmed\n\t\t.slice(1, -1)\n\t\t.split(\",\")\n\t\t.map((entry) => entry.trim().replace(/^`|`$/g, \"\"))\n\t\t.filter(Boolean);\n}\n\nfunction extractSection(markdown: string, heading: string): string {\n\tconst start = markdown.indexOf(heading);\n\tif (start < 0) return \"\";\n\tconst rest = markdown.slice(start + heading.length);\n\tconst next = rest.search(/\\n#{2,3}\\s/);\n\treturn next < 0 ? rest : rest.slice(0, next);\n}\n\nexport function parseSpecRequirements(markdown: string): SpecRequirement[] {\n\tconst matches = [...markdown.matchAll(/^###\\s+(R\\d+)\\s+[—-]\\s+(.+?)(?:\\s+\\((P\\d+)\\))?\\s*$/gm)];\n\treturn matches.map((match, index) => {\n\t\tconst id = match[1]!;\n\t\tconst nextIndex = index + 1 < matches.length ? matches[index + 1]!.index! : markdown.length;\n\t\tconst body = markdown.slice(match.index!, nextIndex);\n\t\tconst acceptance = extractSection(body, \"**Acceptance**\")\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter((line) => /^\\d+\\./.test(line))\n\t\t\t.map((line) => line.replace(/^\\d+\\.\\s*/, \"\"));\n\t\treturn {\n\t\t\tid,\n\t\t\ttitle: match[2]!.trim(),\n\t\t\tpriority: match[3] ?? \"P0\",\n\t\t\tacceptance,\n\t\t};\n\t});\n}\n\nexport function parseSpecTasks(markdown: string): SpecTask[] {\n\tconst lines = markdown.split(\"\\n\");\n\tconst tasks: SpecTask[] = [];\n\tlet current: SpecTask | undefined;\n\tfor (const line of lines) {\n\t\tconst taskMatch = line.match(/^- \\[([ xX])] (HCP-\\d+)\\s+(.+)$/);\n\t\tif (taskMatch) {\n\t\t\tcurrent = {\n\t\t\t\tid: taskMatch[2]!,\n\t\t\t\ttitle: taskMatch[3]!.trim(),\n\t\t\t\tcompleted: taskMatch[1]!.toLowerCase() === \"x\",\n\t\t\t\tdeps: [],\n\t\t\t\tfiles: [],\n\t\t\t\trequirementIds: [],\n\t\t\t};\n\t\t\ttasks.push(current);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!current) continue;\n\t\tconst metaMatch = line.match(/^\\s*>\\s*([A-Za-z]+):\\s*(.+)$/);\n\t\tif (!metaMatch) continue;\n\t\tconst key = metaMatch[1]!;\n\t\tconst value = metaMatch[2]!.trim();\n\t\tswitch (key) {\n\t\t\tcase \"role\":\n\t\t\t\tcurrent.role = value;\n\t\t\t\tbreak;\n\t\t\tcase \"deps\":\n\t\t\t\tcurrent.deps = parseBracketList(value);\n\t\t\t\tbreak;\n\t\t\tcase \"lane\":\n\t\t\t\tcurrent.lane = value;\n\t\t\t\tbreak;\n\t\t\tcase \"files\":\n\t\t\t\tcurrent.files = parseBracketList(value);\n\t\t\t\tbreak;\n\t\t\tcase \"verify\":\n\t\t\t\tcurrent.verify = value.replace(/^`|`$/g, \"\");\n\t\t\t\tbreak;\n\t\t\tcase \"gate\":\n\t\t\t\tcurrent.gate = value;\n\t\t\t\tbreak;\n\t\t\tcase \"risk\":\n\t\t\t\tcurrent.risk = value;\n\t\t\t\tbreak;\n\t\t\tcase \"requirementIds\":\n\t\t\t\tcurrent.requirementIds = parseBracketList(value);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn tasks;\n}\n\nfunction detectCycle(tasks: SpecTask[]): string | undefined {\n\tconst byId = new Map(tasks.map((task) => [task.id, task]));\n\tconst visiting = new Set<string>();\n\tconst visited = new Set<string>();\n\tconst visit = (id: string, path: string[]): string | undefined => {\n\t\tif (visiting.has(id)) return [...path, id].join(\" -> \");\n\t\tif (visited.has(id)) return undefined;\n\t\tvisiting.add(id);\n\t\tfor (const dep of byId.get(id)?.deps ?? []) {\n\t\t\tconst cycle = visit(dep, [...path, id]);\n\t\t\tif (cycle) return cycle;\n\t\t}\n\t\tvisiting.delete(id);\n\t\tvisited.add(id);\n\t\treturn undefined;\n\t};\n\tfor (const task of tasks) {\n\t\tconst cycle = visit(task.id, []);\n\t\tif (cycle) return cycle;\n\t}\n\treturn undefined;\n}\n\nfunction findHardcodedAuthority(input: SpecKitCompileInput): string[] {\n\tconst combined = [\n\t\tinput.specMarkdown,\n\t\tinput.tasksMarkdown,\n\t\tinput.planMarkdown ?? \"\",\n\t\tinput.templateMarkdown ?? \"\",\n\t].join(\"\\n\");\n\tconst violations: string[] = [];\n\tif (/^\\s*-\\s+\\*\\*Authority\\*\\*:\\s*.*Kimi is final writer/im.test(combined)) {\n\t\tviolations.push(\"Provider-hardcoded authority: Kimi is final writer\");\n\t}\n\treturn violations;\n}\n\nfunction extractEvidencePathsFromText(value: string | undefined): string[] {\n\tif (!value) return [];\n\treturn [...value.matchAll(/\\.omk\\/[\\w./-]+/g)].map((match) => match[0]!.replace(/[),.;:]+$/, \"\"));\n}\n\nexport function collectSpecEvidencePaths(tasks: readonly SpecTask[]): string[] {\n\tconst paths: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const task of tasks) {\n\t\tfor (const path of [\n\t\t\t...task.files.filter((file) => file.startsWith(\".omk/\")),\n\t\t\t...extractEvidencePathsFromText(task.verify),\n\t\t]) {\n\t\t\tif (seen.has(path)) continue;\n\t\t\tseen.add(path);\n\t\t\tpaths.push(path);\n\t\t}\n\t}\n\treturn paths;\n}\n\nexport function validateTaskCompletionDependencies(tasks: readonly SpecTask[]): string[] {\n\tconst errors: string[] = [];\n\tconst byId = new Map(tasks.map((task) => [task.id, task]));\n\tfor (const task of tasks) {\n\t\tif (!task.completed) continue;\n\t\tfor (const dep of task.deps) {\n\t\t\tconst dependency = byId.get(dep);\n\t\t\tif (dependency && !dependency.completed) {\n\t\t\t\terrors.push(`Task ${task.id} is completed but dependency ${dep} is incomplete`);\n\t\t\t}\n\t\t}\n\t}\n\treturn errors;\n}\n\nexport function validateSpecKit(input: SpecKitCompileInput, compiled?: CompiledSpecKit): SpecKitValidationResult {\n\tconst spec = compiled ?? compileSpecKit(input);\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst requirementIds = new Set(spec.requirements.map((requirement) => requirement.id));\n\tconst taskIds = new Set<string>();\n\tfor (const hardcoded of findHardcodedAuthority(input)) errors.push(hardcoded);\n\tfor (const task of spec.tasks) {\n\t\tif (taskIds.has(task.id)) errors.push(`Duplicate task id: ${task.id}`);\n\t\ttaskIds.add(task.id);\n\t\tif (!task.gate) errors.push(`Task ${task.id} missing evidence gate`);\n\t\tif (!task.verify) errors.push(`Task ${task.id} missing verify command`);\n\t\tif (task.requirementIds.length === 0) errors.push(`Task ${task.id} missing requirementIds`);\n\t\tfor (const dep of task.deps) {\n\t\t\tif (!taskIds.has(dep) && !spec.tasks.some((candidate) => candidate.id === dep)) {\n\t\t\t\terrors.push(`Task ${task.id} depends on missing task ${dep}`);\n\t\t\t}\n\t\t}\n\t\tfor (const requirementId of task.requirementIds) {\n\t\t\tif (!requirementIds.has(requirementId)) errors.push(`Task ${task.id} references missing ${requirementId}`);\n\t\t}\n\t}\n\terrors.push(...validateTaskCompletionDependencies(spec.tasks));\n\tconst cycle = detectCycle(spec.tasks);\n\tif (cycle) errors.push(`Task DAG has cycle: ${cycle}`);\n\tfor (const requirement of spec.requirements) {\n\t\tconst linked = spec.tasks.filter((task) => task.requirementIds.includes(requirement.id));\n\t\tif (linked.length === 0) errors.push(`Requirement ${requirement.id} has no linked task`);\n\t\tif (requirement.acceptance.length === 0)\n\t\t\twarnings.push(`Requirement ${requirement.id} has no parsed acceptance items`);\n\t}\n\treturn { ok: errors.length === 0, errors, warnings };\n}\n\nexport function compileSpecKit(input: SpecKitCompileInput): CompiledSpecKit {\n\tconst requirements = parseSpecRequirements(input.specMarkdown);\n\tconst tasks = parseSpecTasks(input.tasksMarkdown);\n\tconst compiledDag = tasks.map((task) => ({\n\t\tid: task.id,\n\t\ttitle: task.title,\n\t\tdependsOn: task.deps,\n\t\tfiles: task.files,\n\t\tverify: task.verify,\n\t\tgate: task.gate,\n\t\trequirementIds: task.requirementIds,\n\t}));\n\tconst traceability = requirements.map((requirement) => {\n\t\tconst linkedTasks = tasks.filter((task) => task.requirementIds.includes(requirement.id));\n\t\treturn {\n\t\t\trequirementId: requirement.id,\n\t\t\ttaskIds: linkedTasks.map((task) => task.id),\n\t\t\tverifyCommands: linkedTasks.flatMap((task) => (task.verify ? [task.verify] : [])),\n\t\t\tevidenceGates: linkedTasks.flatMap((task) => (task.gate ? [task.gate] : [])),\n\t\t};\n\t});\n\tconst evidenceManifest = tasks.map((task) => ({\n\t\ttaskId: task.id,\n\t\tgate: task.gate,\n\t\tverify: task.verify,\n\t\tfiles: task.files,\n\t\tevidencePaths: collectSpecEvidencePaths([task]),\n\t}));\n\tconst hashInput = stableJson({ requirements, compiledDag, traceability, evidenceManifest });\n\treturn {\n\t\trequirements,\n\t\ttasks,\n\t\tcompiledDag,\n\t\ttraceability,\n\t\tevidenceManifest,\n\t\tspecHash: hashText(hashInput),\n\t};\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import { type HarnessControlEventKind, type HarnessControlEventOptions, type HarnessControlEventWriteResult } from "./harness-control-events.ts";
2
+ export type HarnessControlTransactionStatus = "completed" | "rolled_back" | "failed" | "in_doubt";
3
+ export type HarnessControlTransactionPhase = "prepare" | "apply" | "verify";
4
+ export interface HarnessControlRollbackContext<T, Prepared = unknown> {
5
+ phase: HarnessControlTransactionPhase;
6
+ prepared?: Prepared;
7
+ value?: T;
8
+ }
9
+ export interface HarnessControlTransactionOptions<T, Prepared = unknown> {
10
+ kind: HarnessControlEventKind;
11
+ data?: Record<string, unknown>;
12
+ beforeState?: unknown;
13
+ afterState?: (value: T) => unknown;
14
+ prepare?: () => Prepared | Promise<Prepared>;
15
+ commit: (prepared?: Prepared) => T | Promise<T>;
16
+ verify?: (value: T, prepared?: Prepared) => void | Promise<void>;
17
+ rollback?: (error: unknown, context?: HarnessControlRollbackContext<T, Prepared>) => void | Promise<void>;
18
+ eventOptions?: HarnessControlEventOptions;
19
+ }
20
+ export interface HarnessControlTransactionResult<T> {
21
+ status: HarnessControlTransactionStatus;
22
+ operationId: string;
23
+ value?: T;
24
+ error?: unknown;
25
+ rollbackError?: unknown;
26
+ events: HarnessControlEventWriteResult[];
27
+ }
28
+ export declare function runHarnessControlTransactionSync<T, Prepared = unknown>(options: HarnessControlTransactionOptions<T, Prepared>): HarnessControlTransactionResult<T>;
29
+ export declare function runHarnessControlTransaction<T, Prepared = unknown>(options: HarnessControlTransactionOptions<T, Prepared>): Promise<HarnessControlTransactionResult<T>>;
30
+ //# sourceMappingURL=transaction-coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-coordinator.d.ts","sourceRoot":"","sources":["../../src/core/transaction-coordinator.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAE/B,KAAK,8BAA8B,EAEnC,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,+BAA+B,GAAG,WAAW,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;AAClG,MAAM,MAAM,8BAA8B,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5E,MAAM,WAAW,6BAA6B,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO;IACnE,KAAK,EAAE,8BAA8B,CAAC;IACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,CAAC;CACV;AAED,MAAM,WAAW,gCAAgC,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO;IACtE,IAAI,EAAE,uBAAuB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,6BAA6B,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1G,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC1C;AAED,MAAM,WAAW,+BAA+B,CAAC,CAAC;IACjD,MAAM,EAAE,+BAA+B,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,8BAA8B,EAAE,CAAC;CACzC;AAwDD,wBAAgB,gCAAgC,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,EACrE,OAAO,EAAE,gCAAgC,CAAC,CAAC,EAAE,QAAQ,CAAC,GACpD,+BAA+B,CAAC,CAAC,CAAC,CA6FpC;AA8DD,wBAAsB,4BAA4B,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,EACvE,OAAO,EAAE,gCAAgC,CAAC,CAAC,EAAE,QAAQ,CAAC,GACpD,OAAO,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,CAkF7C","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\ttype HarnessControlEventKind,\n\ttype HarnessControlEventOptions,\n\ttype HarnessControlEventStatus,\n\ttype HarnessControlEventWriteResult,\n\trecordHarnessControlEvent,\n} from \"./harness-control-events.ts\";\n\nexport type HarnessControlTransactionStatus = \"completed\" | \"rolled_back\" | \"failed\" | \"in_doubt\";\nexport type HarnessControlTransactionPhase = \"prepare\" | \"apply\" | \"verify\";\n\nexport interface HarnessControlRollbackContext<T, Prepared = unknown> {\n\tphase: HarnessControlTransactionPhase;\n\tprepared?: Prepared;\n\tvalue?: T;\n}\n\nexport interface HarnessControlTransactionOptions<T, Prepared = unknown> {\n\tkind: HarnessControlEventKind;\n\tdata?: Record<string, unknown>;\n\tbeforeState?: unknown;\n\tafterState?: (value: T) => unknown;\n\tprepare?: () => Prepared | Promise<Prepared>;\n\tcommit: (prepared?: Prepared) => T | Promise<T>;\n\tverify?: (value: T, prepared?: Prepared) => void | Promise<void>;\n\trollback?: (error: unknown, context?: HarnessControlRollbackContext<T, Prepared>) => void | Promise<void>;\n\teventOptions?: HarnessControlEventOptions;\n}\n\nexport interface HarnessControlTransactionResult<T> {\n\tstatus: HarnessControlTransactionStatus;\n\toperationId: string;\n\tvalue?: T;\n\terror?: unknown;\n\trollbackError?: unknown;\n\tevents: HarnessControlEventWriteResult[];\n}\n\nfunction errorSummary(error: unknown): Record<string, string> {\n\tif (error instanceof Error) return { name: error.name, message: error.message };\n\treturn { name: \"Error\", message: String(error) };\n}\n\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n\treturn (\n\t\t(typeof value === \"object\" || typeof value === \"function\") &&\n\t\tvalue !== null &&\n\t\ttypeof (value as { then?: unknown }).then === \"function\"\n\t);\n}\n\nfunction createEventOptions(\n\tbase: HarnessControlEventOptions | undefined,\n\toperationId: string,\n\tcausationId?: string | null,\n): HarnessControlEventOptions {\n\treturn {\n\t\t...base,\n\t\toperationId,\n\t\tcorrelationId: base?.correlationId ?? operationId,\n\t\tcausationId: causationId ?? base?.causationId ?? null,\n\t};\n}\n\nfunction pushTransactionEvent(\n\tevents: HarnessControlEventWriteResult[],\n\tkind: HarnessControlEventKind,\n\tstatus: HarnessControlEventStatus,\n\tdata: Record<string, unknown>,\n\teventOptions: HarnessControlEventOptions | undefined,\n\toperationId: string,\n\tcausationId: string | null,\n\tbeforeState: unknown,\n\tafterState?: unknown,\n): string | null {\n\tconst written = recordHarnessControlEvent(kind, status, data, {\n\t\t...createEventOptions(eventOptions, operationId, causationId),\n\t\tbeforeState,\n\t\tafterState,\n\t});\n\tevents.push(written);\n\treturn written.event?.eventId ?? causationId;\n}\n\nfunction createRollbackContext<T, Prepared>(\n\tphase: HarnessControlTransactionPhase,\n\tprepared: Prepared | undefined,\n\tvalue: T | undefined,\n): HarnessControlRollbackContext<T, Prepared> {\n\treturn { phase, prepared, value };\n}\n\nexport function runHarnessControlTransactionSync<T, Prepared = unknown>(\n\toptions: HarnessControlTransactionOptions<T, Prepared>,\n): HarnessControlTransactionResult<T> {\n\tconst operationId = options.eventOptions?.operationId ?? randomUUID();\n\tconst data = options.data ?? {};\n\tconst events: HarnessControlEventWriteResult[] = [];\n\tlet causationId: string | null = null;\n\tlet phase: HarnessControlTransactionPhase = \"prepare\";\n\tlet prepared: Prepared | undefined;\n\tlet value: T | undefined;\n\n\ttry {\n\t\tconst preparedValue = options.prepare ? options.prepare() : undefined;\n\t\tif (isThenable(preparedValue)) {\n\t\t\tthrow new Error(\"Synchronous harness transaction prepare returned a Promise\");\n\t\t}\n\t\tprepared = preparedValue as Prepared | undefined;\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"prepared\",\n\t\t\t{ ...data, phase: \"prepare\" },\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\tprepared ?? options.beforeState,\n\t\t);\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"started\",\n\t\t\tdata,\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\tprepared ?? options.beforeState,\n\t\t);\n\n\t\tphase = \"apply\";\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"applying\",\n\t\t\t{ ...data, phase: \"apply\" },\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\tprepared ?? options.beforeState,\n\t\t);\n\t\tconst committed = options.commit(prepared);\n\t\tif (isThenable(committed)) {\n\t\t\tthrow new Error(\"Synchronous harness transaction commit returned a Promise\");\n\t\t}\n\t\tvalue = committed;\n\n\t\tphase = \"verify\";\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"verifying\",\n\t\t\t{ ...data, phase: \"verify\" },\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\toptions.afterState ? options.afterState(value) : value,\n\t\t);\n\t\tconst verified = options.verify ? options.verify(value, prepared) : undefined;\n\t\tif (isThenable(verified)) {\n\t\t\tthrow new Error(\"Synchronous harness transaction verify returned a Promise\");\n\t\t}\n\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(options.kind, \"completed\", data, {\n\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\tafterState: options.afterState ? options.afterState(value) : value,\n\t\t\t}),\n\t\t);\n\t\treturn { status: \"completed\", operationId, value, events };\n\t} catch (error) {\n\t\treturn handleSyncTransactionFailure(options, {\n\t\t\terror,\n\t\t\tevents,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\tphase,\n\t\t\tprepared,\n\t\t\tvalue,\n\t\t\tdata,\n\t\t});\n\t}\n}\n\nfunction handleSyncTransactionFailure<T, Prepared>(\n\toptions: HarnessControlTransactionOptions<T, Prepared>,\n\tcontext: {\n\t\terror: unknown;\n\t\tevents: HarnessControlEventWriteResult[];\n\t\toperationId: string;\n\t\tcausationId: string | null;\n\t\tphase: HarnessControlTransactionPhase;\n\t\tprepared: Prepared | undefined;\n\t\tvalue: T | undefined;\n\t\tdata: Record<string, unknown>;\n\t},\n): HarnessControlTransactionResult<T> {\n\tconst { error, events, operationId, causationId, phase, prepared, value, data } = context;\n\tif (!options.rollback) {\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(\n\t\t\t\toptions.kind,\n\t\t\t\t\"failed\",\n\t\t\t\t{ ...data, phase, error: errorSummary(error) },\n\t\t\t\t{\n\t\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\treturn { status: \"failed\", operationId, error, events };\n\t}\n\n\ttry {\n\t\toptions.rollback(error, createRollbackContext(phase, prepared, value));\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(\n\t\t\t\toptions.kind,\n\t\t\t\t\"rolled_back\",\n\t\t\t\t{ ...data, phase, error: errorSummary(error) },\n\t\t\t\t{\n\t\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\t\tafterState: options.beforeState,\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\treturn { status: \"rolled_back\", operationId, error, events };\n\t} catch (rollbackError) {\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(\n\t\t\t\toptions.kind,\n\t\t\t\t\"in_doubt\",\n\t\t\t\t{ ...data, phase, error: errorSummary(error), rollbackError: errorSummary(rollbackError) },\n\t\t\t\t{\n\t\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\treturn { status: \"in_doubt\", operationId, error, rollbackError, events };\n\t}\n}\n\nexport async function runHarnessControlTransaction<T, Prepared = unknown>(\n\toptions: HarnessControlTransactionOptions<T, Prepared>,\n): Promise<HarnessControlTransactionResult<T>> {\n\tconst operationId = options.eventOptions?.operationId ?? randomUUID();\n\tconst data = options.data ?? {};\n\tconst events: HarnessControlEventWriteResult[] = [];\n\tlet causationId: string | null = null;\n\tlet phase: HarnessControlTransactionPhase = \"prepare\";\n\tlet prepared: Prepared | undefined;\n\tlet value: T | undefined;\n\n\ttry {\n\t\tprepared = options.prepare ? await options.prepare() : undefined;\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"prepared\",\n\t\t\t{ ...data, phase: \"prepare\" },\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\tprepared ?? options.beforeState,\n\t\t);\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"started\",\n\t\t\tdata,\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\tprepared ?? options.beforeState,\n\t\t);\n\n\t\tphase = \"apply\";\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"applying\",\n\t\t\t{ ...data, phase: \"apply\" },\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\tprepared ?? options.beforeState,\n\t\t);\n\t\tvalue = await options.commit(prepared);\n\n\t\tphase = \"verify\";\n\t\tcausationId = pushTransactionEvent(\n\t\t\tevents,\n\t\t\toptions.kind,\n\t\t\t\"verifying\",\n\t\t\t{ ...data, phase: \"verify\" },\n\t\t\toptions.eventOptions,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\toptions.beforeState,\n\t\t\toptions.afterState ? options.afterState(value) : value,\n\t\t);\n\t\tif (options.verify) await options.verify(value, prepared);\n\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(options.kind, \"completed\", data, {\n\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\tafterState: options.afterState ? options.afterState(value) : value,\n\t\t\t}),\n\t\t);\n\t\treturn { status: \"completed\", operationId, value, events };\n\t} catch (error) {\n\t\treturn handleAsyncTransactionFailure(options, {\n\t\t\terror,\n\t\t\tevents,\n\t\t\toperationId,\n\t\t\tcausationId,\n\t\t\tphase,\n\t\t\tprepared,\n\t\t\tvalue,\n\t\t\tdata,\n\t\t});\n\t}\n}\n\nasync function handleAsyncTransactionFailure<T, Prepared>(\n\toptions: HarnessControlTransactionOptions<T, Prepared>,\n\tcontext: {\n\t\terror: unknown;\n\t\tevents: HarnessControlEventWriteResult[];\n\t\toperationId: string;\n\t\tcausationId: string | null;\n\t\tphase: HarnessControlTransactionPhase;\n\t\tprepared: Prepared | undefined;\n\t\tvalue: T | undefined;\n\t\tdata: Record<string, unknown>;\n\t},\n): Promise<HarnessControlTransactionResult<T>> {\n\tconst { error, events, operationId, causationId, phase, prepared, value, data } = context;\n\tif (!options.rollback) {\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(\n\t\t\t\toptions.kind,\n\t\t\t\t\"failed\",\n\t\t\t\t{ ...data, phase, error: errorSummary(error) },\n\t\t\t\t{\n\t\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\treturn { status: \"failed\", operationId, error, events };\n\t}\n\n\ttry {\n\t\tawait options.rollback(error, createRollbackContext(phase, prepared, value));\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(\n\t\t\t\toptions.kind,\n\t\t\t\t\"rolled_back\",\n\t\t\t\t{ ...data, phase, error: errorSummary(error) },\n\t\t\t\t{\n\t\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\t\tafterState: options.beforeState,\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\treturn { status: \"rolled_back\", operationId, error, events };\n\t} catch (rollbackError) {\n\t\tevents.push(\n\t\t\trecordHarnessControlEvent(\n\t\t\t\toptions.kind,\n\t\t\t\t\"in_doubt\",\n\t\t\t\t{ ...data, phase, error: errorSummary(error), rollbackError: errorSummary(rollbackError) },\n\t\t\t\t{\n\t\t\t\t\t...createEventOptions(options.eventOptions, operationId, causationId),\n\t\t\t\t\tbeforeState: options.beforeState,\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\treturn { status: \"in_doubt\", operationId, error, rollbackError, events };\n\t}\n}\n"]}