@eir-labs/coltrane 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +85 -0
  3. package/agents/anticipation-mapper.json +34 -0
  4. package/agents/claim-amender.json +34 -0
  5. package/agents/claim-architect.json +34 -0
  6. package/agents/claim-rewriter.json +32 -0
  7. package/agents/delivery-finalizer.json +40 -0
  8. package/agents/diamond-cutter.json +33 -0
  9. package/agents/disclosure-analyst.json +26 -0
  10. package/agents/domain-explorer.json +37 -0
  11. package/agents/e2e-runner.json +34 -0
  12. package/agents/e2e-suite-judge.json +26 -0
  13. package/agents/event-clusterer.json +29 -0
  14. package/agents/novelty-searcher.json +40 -0
  15. package/agents/patent-browser-scout.json +49 -0
  16. package/agents/patent-examiner.json +36 -0
  17. package/agents/phase_agents/claim-rewriter.md +44 -0
  18. package/agents/phase_agents/delivery-finalizer.md +30 -0
  19. package/agents/phase_agents/diamond-cutter.md +47 -0
  20. package/agents/phase_agents/domain-explorer.md +27 -0
  21. package/agents/phase_agents/novelty-searcher.md +50 -0
  22. package/agents/phase_agents/problem-definer.md +32 -0
  23. package/agents/phase_agents/solution-developer.md +26 -0
  24. package/agents/phase_agents/verdict-judger.md +70 -0
  25. package/agents/players/audience-modeler.md +40 -0
  26. package/agents/players/chain-audit-keeper.md +41 -0
  27. package/agents/players/illumination-reviewer.md +42 -0
  28. package/agents/players/methodology-cadence-keeper.md +42 -0
  29. package/agents/players/substrate-edge-keeper.md +45 -0
  30. package/agents/prior-art-scout.json +45 -0
  31. package/agents/problem-definer.json +35 -0
  32. package/agents/seeds/steve_seed_a.json +12 -0
  33. package/agents/seeds/steve_seed_b.json +12 -0
  34. package/agents/seeds/steve_seed_c.json +12 -0
  35. package/agents/seeds/steve_seed_d.json +12 -0
  36. package/agents/sensor.json +22 -0
  37. package/agents/solution-developer.json +35 -0
  38. package/agents/source-walker.json +38 -0
  39. package/agents/spec-drafter.json +31 -0
  40. package/agents/sub_thread_soft_judge.json +26 -0
  41. package/agents/sub_thread_trace_sensor.json +22 -0
  42. package/agents/summarizer.json +25 -0
  43. package/agents/synthesis-writer.json +29 -0
  44. package/agents/triage-judge.json +39 -0
  45. package/agents/user_flow_judge.json +26 -0
  46. package/agents/user_flow_sensor.json +22 -0
  47. package/agents/verdict-judger.json +39 -0
  48. package/core_types/artifact.json +17 -0
  49. package/core_types/interpretation.json +16 -0
  50. package/core_types/judgment.json +16 -0
  51. package/core_types/plan.json +16 -0
  52. package/core_types/signal.json +16 -0
  53. package/core_types/verdict.json +27 -0
  54. package/dist/src/access_grant.d.ts +46 -0
  55. package/dist/src/access_grant.js +76 -0
  56. package/dist/src/access_grant.js.map +1 -0
  57. package/dist/src/acoustics.d.ts +9 -0
  58. package/dist/src/acoustics.js +65 -0
  59. package/dist/src/acoustics.js.map +1 -0
  60. package/dist/src/agent_profile.d.ts +36 -0
  61. package/dist/src/agent_profile.js +57 -0
  62. package/dist/src/agent_profile.js.map +1 -0
  63. package/dist/src/bifrost_invoker.d.ts +33 -0
  64. package/dist/src/bifrost_invoker.js +85 -0
  65. package/dist/src/bifrost_invoker.js.map +1 -0
  66. package/dist/src/canonical_core_types.d.ts +2 -0
  67. package/dist/src/canonical_core_types.js +111 -0
  68. package/dist/src/canonical_core_types.js.map +1 -0
  69. package/dist/src/canonical_form.d.ts +55 -0
  70. package/dist/src/canonical_form.js +93 -0
  71. package/dist/src/canonical_form.js.map +1 -0
  72. package/dist/src/charter.d.ts +37 -0
  73. package/dist/src/charter.js +86 -0
  74. package/dist/src/charter.js.map +1 -0
  75. package/dist/src/circle_of_fifths.d.ts +27 -0
  76. package/dist/src/circle_of_fifths.js +78 -0
  77. package/dist/src/circle_of_fifths.js.map +1 -0
  78. package/dist/src/claude_invoker.d.ts +102 -0
  79. package/dist/src/claude_invoker.js +760 -0
  80. package/dist/src/claude_invoker.js.map +1 -0
  81. package/dist/src/coltrane_profile.d.ts +87 -0
  82. package/dist/src/coltrane_profile.js +132 -0
  83. package/dist/src/coltrane_profile.js.map +1 -0
  84. package/dist/src/composition.d.ts +57 -0
  85. package/dist/src/composition.js +447 -0
  86. package/dist/src/composition.js.map +1 -0
  87. package/dist/src/core_types.d.ts +6 -0
  88. package/dist/src/core_types.js +25 -0
  89. package/dist/src/core_types.js.map +1 -0
  90. package/dist/src/document_factory.d.ts +89 -0
  91. package/dist/src/document_factory.js +176 -0
  92. package/dist/src/document_factory.js.map +1 -0
  93. package/dist/src/genome_schema.d.ts +512 -0
  94. package/dist/src/genome_schema.js +168 -0
  95. package/dist/src/genome_schema.js.map +1 -0
  96. package/dist/src/genome_writer.d.ts +67 -0
  97. package/dist/src/genome_writer.js +179 -0
  98. package/dist/src/genome_writer.js.map +1 -0
  99. package/dist/src/gig_song.d.ts +29 -0
  100. package/dist/src/gig_song.js +56 -0
  101. package/dist/src/gig_song.js.map +1 -0
  102. package/dist/src/gig_tracker.d.ts +54 -0
  103. package/dist/src/gig_tracker.js +109 -0
  104. package/dist/src/gig_tracker.js.map +1 -0
  105. package/dist/src/harmonic_validation.d.ts +11 -0
  106. package/dist/src/harmonic_validation.js +35 -0
  107. package/dist/src/harmonic_validation.js.map +1 -0
  108. package/dist/src/hooks.d.ts +27 -0
  109. package/dist/src/hooks.js +2 -0
  110. package/dist/src/hooks.js.map +1 -0
  111. package/dist/src/index.d.ts +36 -0
  112. package/dist/src/index.js +37 -0
  113. package/dist/src/index.js.map +1 -0
  114. package/dist/src/jsong.d.ts +47 -0
  115. package/dist/src/jsong.js +130 -0
  116. package/dist/src/jsong.js.map +1 -0
  117. package/dist/src/judges/user_flow_judge.d.ts +89 -0
  118. package/dist/src/judges/user_flow_judge.js +200 -0
  119. package/dist/src/judges/user_flow_judge.js.map +1 -0
  120. package/dist/src/learner.d.ts +37 -0
  121. package/dist/src/learner.js +30 -0
  122. package/dist/src/learner.js.map +1 -0
  123. package/dist/src/ledger.d.ts +225 -0
  124. package/dist/src/ledger.js +306 -0
  125. package/dist/src/ledger.js.map +1 -0
  126. package/dist/src/loader.d.ts +87 -0
  127. package/dist/src/loader.js +504 -0
  128. package/dist/src/loader.js.map +1 -0
  129. package/dist/src/mcp.d.ts +23 -0
  130. package/dist/src/mcp.js +125 -0
  131. package/dist/src/mcp.js.map +1 -0
  132. package/dist/src/modulation_path.d.ts +42 -0
  133. package/dist/src/modulation_path.js +60 -0
  134. package/dist/src/modulation_path.js.map +1 -0
  135. package/dist/src/output_validation.d.ts +19 -0
  136. package/dist/src/output_validation.js +51 -0
  137. package/dist/src/output_validation.js.map +1 -0
  138. package/dist/src/outputs.d.ts +125 -0
  139. package/dist/src/outputs.js +452 -0
  140. package/dist/src/outputs.js.map +1 -0
  141. package/dist/src/overtones.d.ts +42 -0
  142. package/dist/src/overtones.js +98 -0
  143. package/dist/src/overtones.js.map +1 -0
  144. package/dist/src/player_to_claude_code.d.ts +17 -0
  145. package/dist/src/player_to_claude_code.js +141 -0
  146. package/dist/src/player_to_claude_code.js.map +1 -0
  147. package/dist/src/playwright_cage.d.ts +28 -0
  148. package/dist/src/playwright_cage.js +47 -0
  149. package/dist/src/playwright_cage.js.map +1 -0
  150. package/dist/src/polyphony.d.ts +46 -0
  151. package/dist/src/polyphony.js +91 -0
  152. package/dist/src/polyphony.js.map +1 -0
  153. package/dist/src/portfolio.d.ts +52 -0
  154. package/dist/src/portfolio.js +186 -0
  155. package/dist/src/portfolio.js.map +1 -0
  156. package/dist/src/pricing.d.ts +23 -0
  157. package/dist/src/pricing.js +16 -0
  158. package/dist/src/pricing.js.map +1 -0
  159. package/dist/src/registry.d.ts +46 -0
  160. package/dist/src/registry.js +199 -0
  161. package/dist/src/registry.js.map +1 -0
  162. package/dist/src/runtime.d.ts +304 -0
  163. package/dist/src/runtime.js +1099 -0
  164. package/dist/src/runtime.js.map +1 -0
  165. package/dist/src/server.d.ts +88 -0
  166. package/dist/src/server.js +1692 -0
  167. package/dist/src/server.js.map +1 -0
  168. package/dist/src/server_entry.d.ts +2 -0
  169. package/dist/src/server_entry.js +40 -0
  170. package/dist/src/server_entry.js.map +1 -0
  171. package/dist/src/server_relay.d.ts +82 -0
  172. package/dist/src/server_relay.js +341 -0
  173. package/dist/src/server_relay.js.map +1 -0
  174. package/dist/src/simulate.d.ts +60 -0
  175. package/dist/src/simulate.js +72 -0
  176. package/dist/src/simulate.js.map +1 -0
  177. package/dist/src/skill_runner.mjs +34 -0
  178. package/dist/src/skill_subprocess.d.ts +65 -0
  179. package/dist/src/skill_subprocess.js +132 -0
  180. package/dist/src/skill_subprocess.js.map +1 -0
  181. package/dist/src/skills.d.ts +85 -0
  182. package/dist/src/skills.js +258 -0
  183. package/dist/src/skills.js.map +1 -0
  184. package/dist/src/subthread_recorder.d.ts +68 -0
  185. package/dist/src/subthread_recorder.js +166 -0
  186. package/dist/src/subthread_recorder.js.map +1 -0
  187. package/dist/src/tensor_read.d.ts +38 -0
  188. package/dist/src/tensor_read.js +60 -0
  189. package/dist/src/tensor_read.js.map +1 -0
  190. package/dist/src/test_honest_broker.d.ts +34 -0
  191. package/dist/src/test_honest_broker.js +155 -0
  192. package/dist/src/test_honest_broker.js.map +1 -0
  193. package/dist/src/test_topology.d.ts +22 -0
  194. package/dist/src/test_topology.js +137 -0
  195. package/dist/src/test_topology.js.map +1 -0
  196. package/dist/src/tones.d.ts +20 -0
  197. package/dist/src/tones.js +81 -0
  198. package/dist/src/tones.js.map +1 -0
  199. package/dist/src/tool_providers.d.ts +45 -0
  200. package/dist/src/tool_providers.js +104 -0
  201. package/dist/src/tool_providers.js.map +1 -0
  202. package/dist/src/type_versioning.d.ts +23 -0
  203. package/dist/src/type_versioning.js +31 -0
  204. package/dist/src/type_versioning.js.map +1 -0
  205. package/dist/src/version.d.ts +28 -0
  206. package/dist/src/version.js +86 -0
  207. package/dist/src/version.js.map +1 -0
  208. package/domain_types/claim-draft.json +34 -0
  209. package/domain_types/coverage-report.json +31 -0
  210. package/domain_types/draft-agent-profile.json +31 -0
  211. package/domain_types/draft-domain-type.json +28 -0
  212. package/domain_types/draft-standard.json +46 -0
  213. package/domain_types/e2e-observation.json +32 -0
  214. package/domain_types/e2e-report.json +30 -0
  215. package/domain_types/e2e-target.json +26 -0
  216. package/domain_types/e2e-verdict.json +26 -0
  217. package/domain_types/examine-round-record.json +40 -0
  218. package/domain_types/examiner-rejection.json +28 -0
  219. package/domain_types/failure-modes.json +31 -0
  220. package/domain_types/invention-analysis.json +34 -0
  221. package/domain_types/invention-spec.json +34 -0
  222. package/domain_types/novelty-analysis.json +28 -0
  223. package/domain_types/novelty-verdict.json +35 -0
  224. package/domain_types/parsed-conversation-trace.json +38 -0
  225. package/domain_types/pattern-extraction.json +62 -0
  226. package/domain_types/prior-art-hit.json +50 -0
  227. package/domain_types/project-charter.json +71 -0
  228. package/domain_types/provisional-draft.json +34 -0
  229. package/domain_types/raw-note.json +19 -0
  230. package/domain_types/repo-survey.json +28 -0
  231. package/domain_types/seeding-verdict.json +70 -0
  232. package/domain_types/soft-verdict.json +55 -0
  233. package/domain_types/summary.json +19 -0
  234. package/domain_types/triage-verdict.json +59 -0
  235. package/domain_types/user-flow-transcript.json +43 -0
  236. package/domain_types/user-flow-verdict.json +60 -0
  237. package/domain_types/verdict-record.json +39 -0
  238. package/package.json +82 -0
  239. package/skills/citation-verify/fixtures/basic.json +14 -0
  240. package/skills/citation-verify/meta.json +10 -0
  241. package/skills/citation-verify/skill.mjs +26 -0
  242. package/skills/claim-element-decompose/fixtures/basic.json +11 -0
  243. package/skills/claim-element-decompose/meta.json +10 -0
  244. package/skills/claim-element-decompose/skill.mjs +14 -0
  245. package/skills/diamond-cutting-discipline/fixtures/basic.json +13 -0
  246. package/skills/diamond-cutting-discipline/meta.json +19 -0
  247. package/skills/diamond-cutting-discipline/skill.md +46 -0
  248. package/skills/element-mapping-matrix/fixtures/basic.json +13 -0
  249. package/skills/element-mapping-matrix/meta.json +10 -0
  250. package/skills/element-mapping-matrix/skill.mjs +20 -0
  251. package/skills/number-adder/fixtures/basic.json +9 -0
  252. package/skills/number-adder/meta.json +10 -0
  253. package/skills/number-adder/skill.mjs +12 -0
  254. package/skills/patent-fetch/fixtures/parse.json +18 -0
  255. package/skills/patent-fetch/meta.json +11 -0
  256. package/skills/patent-fetch/skill.mjs +34 -0
  257. package/skills/query-expand/fixtures/basic.json +14 -0
  258. package/skills/query-expand/meta.json +10 -0
  259. package/skills/query-expand/skill.mjs +29 -0
  260. package/skills/run-vitest-band/fixtures/basic.json +9 -0
  261. package/skills/run-vitest-band/meta.json +18 -0
  262. package/skills/run-vitest-band/skill.mjs +22 -0
  263. package/skills/statutory-checklist/fixtures/basic.json +16 -0
  264. package/skills/statutory-checklist/meta.json +10 -0
  265. package/skills/statutory-checklist/skill.mjs +25 -0
  266. package/skills/summarize-tight/fixtures/basic.json +13 -0
  267. package/skills/summarize-tight/meta.json +20 -0
  268. package/skills/summarize-tight/skill.md +1 -0
  269. package/skills/verdict-gate/fixtures/basic.json +9 -0
  270. package/skills/verdict-gate/meta.json +10 -0
  271. package/skills/verdict-gate/skill.mjs +82 -0
  272. package/standards/e2e-suite-v0.json +44 -0
  273. package/standards/patent-triage-v0.json +98 -0
  274. package/standards/patent-triage-v1.json +84 -0
  275. package/standards/seed-from-local-repos-v0.json +115 -0
  276. package/standards/sub_thread_invocation.json +50 -0
  277. package/standards/summarize.json +40 -0
  278. package/standards/synthesis-walk-v0.json +108 -0
  279. package/standards/user_flow_correctness.json +57 -0
@@ -0,0 +1,258 @@
1
+ // Skills-as-first-class — the API surface (docs/skills-as-first-class.md).
2
+ //
3
+ // Execution + the fixture/determinism runner are IMPLEMENTED in skill_subprocess.ts.
4
+ // The rest of the contract — package loading + code_hash, code-first/model-residual
5
+ // resolution, the per-resolution telemetry record, determinism_ratio computed from
6
+ // those records, the evolution gate, and composition — is declared here as typed
7
+ // stubs so the RED contract suite typechecks and fails honestly on assertions (not on
8
+ // missing imports). Each stub throws NotImplemented; the build fills them in green.
9
+ import { readFileSync, existsSync, mkdtempSync, mkdirSync, copyFileSync, readdirSync, rmSync, appendFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { tmpdir } from "node:os";
12
+ import { createHash } from "node:crypto";
13
+ import { readSkillMeta, executeSkill, loadFixtures, runSkillFixtures, } from "./skill_subprocess.js";
14
+ export { executeSkill, runSkillFixtures, loadFixtures, readSkillMeta, tierFlags, } from "./skill_subprocess.js";
15
+ class NotImplemented extends Error {
16
+ constructor(what) {
17
+ super(`not implemented: ${what}`);
18
+ this.name = "NotImplemented";
19
+ }
20
+ }
21
+ // Typed errors at the skill layer (mirror the genome carve: hard-fail where there's no
22
+ // recoverable order, soft-fail-with-named-error where a definition is just broken).
23
+ export class SkillLoadError extends Error {
24
+ }
25
+ export class SkillCompositionCycleError extends Error {
26
+ }
27
+ // Declare the hash format so implementations match.
28
+ export const CODE_HASH_ALGO = "sha256";
29
+ export function hashSkillCode(codePath) {
30
+ return `${CODE_HASH_ALGO}:${createHash(CODE_HASH_ALGO).update(readFileSync(codePath)).digest("hex")}`;
31
+ }
32
+ /** Load a skill package: meta + code (content-hashed) + fixtures + the reasoning half.
33
+ * A malformed package (unreadable/sluggless meta, bad fixture shape) raises SkillLoadError;
34
+ * an ABSENT code half is allowed (codeHash null) — that degrades to pure reasoning at run. */
35
+ export function loadSkillPackage(dir) {
36
+ let meta;
37
+ try {
38
+ meta = JSON.parse(readFileSync(join(dir, "meta.json"), "utf-8"));
39
+ }
40
+ catch (e) {
41
+ throw new SkillLoadError(`skill package ${dir}: cannot read meta.json — ${e instanceof Error ? e.message : String(e)}`);
42
+ }
43
+ if (!meta || typeof meta.slug !== "string" || meta.slug.trim() === "") {
44
+ throw new SkillLoadError(`skill package ${dir}: meta.json is missing a slug`);
45
+ }
46
+ const codePath = join(dir, "skill.mjs");
47
+ const codeHash = existsSync(codePath) ? hashSkillCode(codePath) : null;
48
+ const mdPath = existsSync(join(dir, "skill.md")) ? join(dir, "skill.md") : null;
49
+ const fixtures = loadFixtures(dir);
50
+ for (const fx of fixtures) {
51
+ if (typeof fx.id !== "string" || fx.id.trim() === "" || fx.input === undefined) {
52
+ throw new SkillLoadError(`skill package ${meta.slug}: a fixture has an invalid shape (each needs a non-empty id and an input)`);
53
+ }
54
+ }
55
+ return { dir, meta, codeHash, fixtures, mdPath };
56
+ }
57
+ function pick(o, keys) {
58
+ const r = {};
59
+ for (const k of keys)
60
+ if (k in o)
61
+ r[k] = o[k];
62
+ return r;
63
+ }
64
+ // Warn once (not per-call) when resolutions run with no chainDir: every such call records
65
+ // NO SkillChainEvent, so determinism_ratio never accumulates. The absence is a real gap, not
66
+ // a no-op — surface it once so it's visible without spamming a tight resolve loop.
67
+ let warnedNoChainDir = false;
68
+ /** Run code first, compute the residual, let the model fill only the gap, tag origins.
69
+ * When `opts.chainDir` is set, append a SkillChainEvent recording this resolution. */
70
+ export async function resolveSkill(dir, input, invoke, opts) {
71
+ if (!opts?.chainDir && !warnedNoChainDir) {
72
+ warnedNoChainDir = true;
73
+ console.warn("[skills] resolveSkill running without chainDir — no SkillChainEvent recorded; determinism_ratio will not accumulate.");
74
+ }
75
+ const pkg = loadSkillPackage(dir);
76
+ // schema fields bound what code/model may resolve. No schema → the code's own keys are
77
+ // the shape (nothing is "unresolved"), so the model is never asked.
78
+ const props = pkg.meta.output_schema?.properties;
79
+ const schemaFields = props ? Object.keys(props) : null;
80
+ // 1. code first — unless the code can't be trusted/run (graceful degradation, never throws)
81
+ let resolved = {};
82
+ let degraded;
83
+ let duration_ms = 0;
84
+ if (pkg.codeHash === null) {
85
+ degraded = { reason: "no code half — resolving via pure reasoning" };
86
+ }
87
+ else if (typeof pkg.meta.code_hash === "string" && pkg.meta.code_hash !== pkg.codeHash) {
88
+ degraded = { reason: `code_hash mismatch — declared ${pkg.meta.code_hash}, on disk ${pkg.codeHash}; unverified code not run` };
89
+ }
90
+ else {
91
+ const r = executeSkill(dir, input);
92
+ duration_ms = r.duration_ms;
93
+ if (!r.ok) {
94
+ degraded = { reason: `code half threw / produced no output: ${r.error}` };
95
+ }
96
+ else {
97
+ const out = (r.output && typeof r.output === "object" ? r.output : {});
98
+ // typing guarantee: code can only resolve fields that exist in the output schema
99
+ resolved = schemaFields ? pick(out, schemaFields) : out;
100
+ }
101
+ }
102
+ // 2. residual = schema − what the code resolved (subset of the schema, always)
103
+ const resolvedKeys = Object.keys(resolved);
104
+ const residual = schemaFields ? schemaFields.filter((f) => !resolvedKeys.includes(f)) : [];
105
+ // 3. the model fills ONLY the residual; resolved fields are verified context it can't override
106
+ let modelFields = {};
107
+ if (residual.length > 0) {
108
+ const md = pkg.mdPath ? readFileSync(pkg.mdPath, "utf-8") : "";
109
+ const m = await invoke({ unresolved: residual, resolved, md });
110
+ modelFields = pick(m ?? {}, residual);
111
+ }
112
+ // 4. union + origins
113
+ const output = { ...resolved, ...modelFields };
114
+ const field_origins = {};
115
+ for (const k of Object.keys(resolved))
116
+ field_origins[k] = "code";
117
+ for (const k of Object.keys(modelFields))
118
+ field_origins[k] = "model";
119
+ if (opts?.chainDir) {
120
+ appendSkillChainEvent(opts.chainDir, {
121
+ slug: pkg.meta.slug,
122
+ version: pkg.meta.version,
123
+ code_hash: pkg.codeHash ?? "",
124
+ tier: pkg.meta.permission?.tier ?? 0,
125
+ duration_ms,
126
+ permission_violations: [],
127
+ field_origins,
128
+ ...(degraded ? { degraded_reason: degraded.reason } : {}),
129
+ });
130
+ }
131
+ return { output, resolved, residual, field_origins, ...(degraded ? { degraded } : {}) };
132
+ }
133
+ function appendSkillChainEvent(chainDir, ev) {
134
+ mkdirSync(chainDir, { recursive: true });
135
+ appendFileSync(join(chainDir, `${ev.slug}.jsonl`), JSON.stringify(ev) + "\n");
136
+ }
137
+ /** Read the recorded resolution events for a skill+version — the log determinism reads from. */
138
+ export function skillChainEvents(slug, version, opts) {
139
+ const dir = opts?.chainDir;
140
+ if (!dir)
141
+ return []; // ambient store (from sealed records) is a Phase-3 open question
142
+ const path = join(dir, `${slug}.jsonl`);
143
+ if (!existsSync(path))
144
+ return [];
145
+ const events = readFileSync(path, "utf-8")
146
+ .split("\n")
147
+ .filter((l) => l.trim() !== "")
148
+ .map((l) => JSON.parse(l));
149
+ return version === undefined ? events : events.filter((e) => e.version === version);
150
+ }
151
+ export function computeDeterminismRatio(slug, version, opts) {
152
+ const window = opts?.window ?? 100;
153
+ const events = skillChainEvents(slug, version, opts).slice(-window);
154
+ // each event's code-resolved fraction; the ratio is the rolling average across the window
155
+ const fractions = events.map((e) => {
156
+ const origins = Object.values(e.field_origins);
157
+ return origins.length ? origins.filter((o) => o === "code").length / origins.length : 0;
158
+ });
159
+ const ratio = fractions.length ? fractions.reduce((a, b) => a + b, 0) / fractions.length : 0;
160
+ return { slug, version, ratio, window, samples: events.length };
161
+ }
162
+ /** Run a candidate skill.mjs against the package's fixtures; accept iff none regress. The
163
+ * fixtures are the evolution gate: an "improvement" that breaks any committed contract is
164
+ * rejected, and the broken fixtures are named. */
165
+ export function evolveSkill(dir, candidateCodePath) {
166
+ const tmp = mkdtempSync(join(tmpdir(), "coltrane-evolve-"));
167
+ try {
168
+ copyFileSync(join(dir, "meta.json"), join(tmp, "meta.json"));
169
+ const fxSrc = join(dir, "fixtures");
170
+ if (existsSync(fxSrc)) {
171
+ mkdirSync(join(tmp, "fixtures"), { recursive: true });
172
+ for (const f of readdirSync(fxSrc))
173
+ copyFileSync(join(fxSrc, f), join(tmp, "fixtures", f));
174
+ }
175
+ copyFileSync(candidateCodePath, join(tmp, "skill.mjs"));
176
+ const report = runSkillFixtures(tmp);
177
+ const failing_fixtures = report.results.filter((r) => !r.passed).map((r) => r.id);
178
+ return { accepted: failing_fixtures.length === 0, failing_fixtures };
179
+ }
180
+ finally {
181
+ rmSync(tmp, { recursive: true, force: true });
182
+ }
183
+ }
184
+ /** Compose multiple skills for one chair: validate composable_with, order by deps,
185
+ * detect cycles (SkillCompositionCycleError), merge the residual. */
186
+ export function composeSkills(dirs) {
187
+ const pkgs = dirs.map((d) => loadSkillPackage(d));
188
+ const slugs = pkgs.map((p) => p.meta.slug);
189
+ const fields = (p, half) => Object.keys((p.meta[half]?.properties ?? {}));
190
+ // 1. composable_with: every pair must be declared composable by at least one side
191
+ for (let i = 0; i < pkgs.length; i++) {
192
+ for (let j = i + 1; j < pkgs.length; j++) {
193
+ const a = pkgs[i], b = pkgs[j];
194
+ const ok = (a.meta.composable_with ?? []).includes(b.meta.slug) || (b.meta.composable_with ?? []).includes(a.meta.slug);
195
+ if (!ok)
196
+ throw new SkillLoadError(`skills "${a.meta.slug}" and "${b.meta.slug}" are not composable_with each other`);
197
+ }
198
+ }
199
+ // 2. shared output fields must declare identical types
200
+ const seen = new Map();
201
+ for (const p of pkgs) {
202
+ const props = (p.meta.output_schema?.properties ?? {});
203
+ for (const [f, def] of Object.entries(props)) {
204
+ const prev = seen.get(f);
205
+ if (prev && JSON.stringify(prev.type) !== JSON.stringify(def?.type)) {
206
+ throw new SkillLoadError(`type conflict on shared output field "${f}": "${prev.owner}" declares ${JSON.stringify(prev.type)}, "${p.meta.slug}" declares ${JSON.stringify(def?.type)}`);
207
+ }
208
+ if (!prev)
209
+ seen.set(f, { type: def?.type, owner: p.meta.slug });
210
+ }
211
+ }
212
+ // 3. dependency edges: B depends on A if B's input references a field A outputs
213
+ const outputs = new Map(pkgs.map((p) => [p.meta.slug, new Set(fields(p, "output_schema"))]));
214
+ const deps = new Map(slugs.map((s) => [s, new Set()]));
215
+ for (const p of pkgs) {
216
+ for (const other of pkgs) {
217
+ if (other.meta.slug === p.meta.slug)
218
+ continue;
219
+ const outs = outputs.get(other.meta.slug);
220
+ if (fields(p, "input_schema").some((f) => outs.has(f)))
221
+ deps.get(p.meta.slug).add(other.meta.slug);
222
+ }
223
+ }
224
+ // 4. topological order (deterministic, independent of input order); cycle → cycle error.
225
+ // The error names the FULL cycle path (A → B → C → A), not just the node we re-entered —
226
+ // a one-node message can't be regression-tested for the right cycle, and a multi-node
227
+ // cycle is undebuggable without its path. The in-progress (color-1) stack IS the path.
228
+ const order = [];
229
+ const state = new Map();
230
+ const visit = (s, path) => {
231
+ const st = state.get(s) ?? 0;
232
+ if (st === 2)
233
+ return;
234
+ if (st === 1) {
235
+ const from = path.indexOf(s);
236
+ const cycle = [...path.slice(from), s].join(" → ");
237
+ throw new SkillCompositionCycleError(`skill composition cycle: ${cycle}`);
238
+ }
239
+ state.set(s, 1);
240
+ for (const d of [...deps.get(s)].sort())
241
+ visit(d, [...path, s]);
242
+ state.set(s, 2);
243
+ order.push(s);
244
+ };
245
+ for (const s of [...slugs].sort())
246
+ visit(s, []);
247
+ // 5. merged residual: fields no skill's code resolves (a pure-reasoning skill resolves none)
248
+ const merged = new Set();
249
+ for (const p of pkgs)
250
+ if (p.codeHash === null)
251
+ for (const f of fields(p, "output_schema"))
252
+ merged.add(f);
253
+ return { order, merged_residual: [...merged] };
254
+ }
255
+ // Re-export to keep the type checker honest about unused-import in stubs.
256
+ void readSkillMeta;
257
+ void executeSkill;
258
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ export interface SubthreadEntry {
2
+ session_id: string;
3
+ turn_idx: number;
4
+ parent_session_id: string | null;
5
+ api_version: string;
6
+ genome_hash: string;
7
+ run_fingerprint: string;
8
+ tool_call_sequence: string[];
9
+ started_at: string;
10
+ finished_at: string | null;
11
+ error?: string;
12
+ observability_log?: Record<string, unknown>;
13
+ }
14
+ export interface RecorderOptions {
15
+ path: string;
16
+ session_id: string;
17
+ parent_session_id?: string | null | undefined;
18
+ api_version: string;
19
+ genome_hash: string;
20
+ run_fingerprint: string;
21
+ }
22
+ export declare class ApiVersionMismatchError extends Error {
23
+ readonly recorded: string;
24
+ readonly current: string;
25
+ constructor(recorded: string, current: string);
26
+ }
27
+ /**
28
+ * A file-backed recorder that captures one entry per sub-thread turn, keyed on
29
+ * session_id. Each turn is one server lifetime; resume across lifetimes increments
30
+ * turn_idx. Entries carry deterministic provenance fields (genome_hash,
31
+ * run_fingerprint, tool_call_sequence) plus lifecycle timestamps that callers
32
+ * can exclude from hash-stability checks.
33
+ */
34
+ export declare class SubthreadRecorder {
35
+ readonly path: string;
36
+ readonly session_id: string;
37
+ readonly parent_session_id: string | null;
38
+ readonly api_version: string;
39
+ readonly genome_hash: string;
40
+ readonly run_fingerprint: string;
41
+ readonly turn_idx: number;
42
+ private tool_call_sequence;
43
+ private observability_log;
44
+ private started_at;
45
+ private finished_at;
46
+ private error;
47
+ private flushed;
48
+ private constructor();
49
+ /**
50
+ * Open a recorder for this server lifetime. Detects api_version mismatch
51
+ * against prior turns for the same session_id and throws
52
+ * `ApiVersionMismatchError`; the typed error is also sealed into the recorder
53
+ * before throwing so the failure is auditable.
54
+ */
55
+ static open(opts: RecorderOptions): SubthreadRecorder;
56
+ recordToolCall(name: string): void;
57
+ recordObservability(key: string, value: unknown): void;
58
+ flush(): void;
59
+ private current;
60
+ private persist;
61
+ }
62
+ /**
63
+ * Stable hash over a recorder file scoped to deterministic provenance fields —
64
+ * excludes session_id (varies per Claude session), timestamps, and observability
65
+ * payloads. Used by reproducibility checks to compare two runs against the same
66
+ * genome.
67
+ */
68
+ export declare function hashRecorderDeterministicFields(path: string): string;
@@ -0,0 +1,166 @@
1
+ import { appendFileSync, existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ export class ApiVersionMismatchError extends Error {
5
+ recorded;
6
+ current;
7
+ constructor(recorded, current) {
8
+ super(`api_version_mismatch: recorded=${recorded} current=${current}`);
9
+ this.name = "ApiVersionMismatchError";
10
+ this.recorded = recorded;
11
+ this.current = current;
12
+ }
13
+ }
14
+ function readAllEntries(path) {
15
+ if (!existsSync(path))
16
+ return [];
17
+ const lines = readFileSync(path, "utf-8").split("\n").filter((l) => l.length > 0);
18
+ const out = [];
19
+ for (const line of lines) {
20
+ try {
21
+ out.push(JSON.parse(line));
22
+ }
23
+ catch {
24
+ /* skip malformed */
25
+ }
26
+ }
27
+ return out;
28
+ }
29
+ function writeAllEntries(path, entries) {
30
+ mkdirSync(dirname(path), { recursive: true });
31
+ const body = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length > 0 ? "\n" : "");
32
+ writeFileSync(path, body);
33
+ }
34
+ /**
35
+ * A file-backed recorder that captures one entry per sub-thread turn, keyed on
36
+ * session_id. Each turn is one server lifetime; resume across lifetimes increments
37
+ * turn_idx. Entries carry deterministic provenance fields (genome_hash,
38
+ * run_fingerprint, tool_call_sequence) plus lifecycle timestamps that callers
39
+ * can exclude from hash-stability checks.
40
+ */
41
+ export class SubthreadRecorder {
42
+ path;
43
+ session_id;
44
+ parent_session_id;
45
+ api_version;
46
+ genome_hash;
47
+ run_fingerprint;
48
+ turn_idx;
49
+ tool_call_sequence = [];
50
+ observability_log = {};
51
+ started_at;
52
+ finished_at = null;
53
+ error = null;
54
+ flushed = false;
55
+ constructor(opts, turn_idx) {
56
+ this.path = opts.path;
57
+ this.session_id = opts.session_id;
58
+ this.parent_session_id = opts.parent_session_id ?? null;
59
+ this.api_version = opts.api_version;
60
+ this.genome_hash = opts.genome_hash;
61
+ this.run_fingerprint = opts.run_fingerprint;
62
+ this.turn_idx = turn_idx;
63
+ this.started_at = new Date().toISOString();
64
+ }
65
+ /**
66
+ * Open a recorder for this server lifetime. Detects api_version mismatch
67
+ * against prior turns for the same session_id and throws
68
+ * `ApiVersionMismatchError`; the typed error is also sealed into the recorder
69
+ * before throwing so the failure is auditable.
70
+ */
71
+ static open(opts) {
72
+ const prior = readAllEntries(opts.path).filter((e) => e.session_id === opts.session_id);
73
+ const turn_idx = prior.length;
74
+ if (prior.length > 0) {
75
+ const firstApi = prior[0].api_version;
76
+ if (firstApi !== opts.api_version) {
77
+ const mismatchEntry = {
78
+ session_id: opts.session_id,
79
+ turn_idx,
80
+ parent_session_id: opts.parent_session_id ?? null,
81
+ api_version: opts.api_version,
82
+ genome_hash: opts.genome_hash,
83
+ run_fingerprint: opts.run_fingerprint,
84
+ tool_call_sequence: [],
85
+ started_at: new Date().toISOString(),
86
+ finished_at: new Date().toISOString(),
87
+ error: `api_version_mismatch: recorded=${firstApi} current=${opts.api_version}`,
88
+ };
89
+ appendFileSync(opts.path, JSON.stringify(mismatchEntry) + "\n");
90
+ throw new ApiVersionMismatchError(firstApi, opts.api_version);
91
+ }
92
+ }
93
+ const rec = new SubthreadRecorder(opts, turn_idx);
94
+ rec.persist();
95
+ return rec;
96
+ }
97
+ recordToolCall(name) {
98
+ if (this.flushed)
99
+ return;
100
+ this.tool_call_sequence.push(name);
101
+ this.persist();
102
+ }
103
+ recordObservability(key, value) {
104
+ if (this.flushed)
105
+ return;
106
+ this.observability_log[key] = value;
107
+ this.persist();
108
+ }
109
+ flush() {
110
+ if (this.flushed)
111
+ return;
112
+ this.finished_at = new Date().toISOString();
113
+ this.flushed = true;
114
+ this.persist();
115
+ }
116
+ current() {
117
+ const entry = {
118
+ session_id: this.session_id,
119
+ turn_idx: this.turn_idx,
120
+ parent_session_id: this.parent_session_id,
121
+ api_version: this.api_version,
122
+ genome_hash: this.genome_hash,
123
+ run_fingerprint: this.run_fingerprint,
124
+ tool_call_sequence: [...this.tool_call_sequence],
125
+ started_at: this.started_at,
126
+ finished_at: this.finished_at,
127
+ observability_log: { ...this.observability_log },
128
+ };
129
+ if (this.error)
130
+ entry.error = this.error;
131
+ return entry;
132
+ }
133
+ persist() {
134
+ const all = readAllEntries(this.path);
135
+ const idx = all.findIndex((e) => e.session_id === this.session_id && e.turn_idx === this.turn_idx);
136
+ const next = this.current();
137
+ if (idx >= 0)
138
+ all[idx] = next;
139
+ else
140
+ all.push(next);
141
+ writeAllEntries(this.path, all);
142
+ }
143
+ }
144
+ /**
145
+ * Stable hash over a recorder file scoped to deterministic provenance fields —
146
+ * excludes session_id (varies per Claude session), timestamps, and observability
147
+ * payloads. Used by reproducibility checks to compare two runs against the same
148
+ * genome.
149
+ */
150
+ export function hashRecorderDeterministicFields(path) {
151
+ const entries = readAllEntries(path);
152
+ if (entries.length === 0)
153
+ return "EMPTY";
154
+ const scoped = entries.map((e) => ({
155
+ turn_idx: e.turn_idx,
156
+ api_version: e.api_version,
157
+ genome_hash: e.genome_hash,
158
+ run_fingerprint: e.run_fingerprint,
159
+ tool_call_sequence: e.tool_call_sequence,
160
+ parent_present: e.parent_session_id !== null,
161
+ }));
162
+ // Order by turn_idx so resume-order matters but session-id-suffix doesn't leak in.
163
+ scoped.sort((a, b) => a.turn_idx - b.turn_idx);
164
+ return createHash("sha256").update(JSON.stringify(scoped)).digest("hex");
165
+ }
166
+ //# sourceMappingURL=subthread_recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subthread_recorder.js","sourceRoot":"","sources":["../../src/subthread_recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export interface ChannelReading<T = unknown> {
2
+ channel: string;
3
+ produced_by: string;
4
+ payload: T;
5
+ }
6
+ export interface TensorRead {
7
+ gig_id: string;
8
+ readings: readonly ChannelReading[];
9
+ }
10
+ export declare class TensorReadError extends Error {
11
+ }
12
+ export declare function emptyTensorRead(gig_id: string): TensorRead;
13
+ export declare function addChannel(tensor: TensorRead, reading: ChannelReading): TensorRead;
14
+ export declare function readChannel(tensor: TensorRead, channel: string): ChannelReading | undefined;
15
+ export interface ChannelDiff {
16
+ channel: string;
17
+ present_in_both: boolean;
18
+ a_only: boolean;
19
+ b_only: boolean;
20
+ a_produced_by?: string | undefined;
21
+ b_produced_by?: string | undefined;
22
+ }
23
+ export interface TensorDiff {
24
+ gig_a: string;
25
+ gig_b: string;
26
+ channels: readonly ChannelDiff[];
27
+ }
28
+ export declare function compareTensorReads(a: TensorRead, b: TensorRead): TensorDiff;
29
+ /**
30
+ * Pure cardinality helpers. Return NUMBERS so a downstream policy layer can
31
+ * weight them inside the tensor — never as standalone verdicts.
32
+ */
33
+ export declare function channelCount(tensor: TensorRead): number;
34
+ export declare function uniqueChannels(diff: TensorDiff): {
35
+ in_both: number;
36
+ a_only: number;
37
+ b_only: number;
38
+ };
@@ -0,0 +1,60 @@
1
+ // Multi-channel tensor read. Each channel of a gig (score, output, song, etc)
2
+ // produces its own structured reading. The TensorRead holds them side-by-side
3
+ // WITHOUT collapsing to a scalar. Validation across channels reads pairwise
4
+ // diffs; the tensor itself is never reduced to "pass/fail."
5
+ export class TensorReadError extends Error {
6
+ }
7
+ export function emptyTensorRead(gig_id) {
8
+ return { gig_id, readings: [] };
9
+ }
10
+ export function addChannel(tensor, reading) {
11
+ if (tensor.readings.some((r) => r.channel === reading.channel)) {
12
+ throw new TensorReadError(`tensor already has a reading for channel "${reading.channel}"`);
13
+ }
14
+ return {
15
+ gig_id: tensor.gig_id,
16
+ readings: [...tensor.readings, reading],
17
+ };
18
+ }
19
+ export function readChannel(tensor, channel) {
20
+ return tensor.readings.find((r) => r.channel === channel);
21
+ }
22
+ export function compareTensorReads(a, b) {
23
+ const channels = new Set();
24
+ for (const r of a.readings)
25
+ channels.add(r.channel);
26
+ for (const r of b.readings)
27
+ channels.add(r.channel);
28
+ const diffs = [];
29
+ for (const ch of channels) {
30
+ const ra = readChannel(a, ch);
31
+ const rb = readChannel(b, ch);
32
+ const diff = {
33
+ channel: ch,
34
+ present_in_both: !!ra && !!rb,
35
+ a_only: !!ra && !rb,
36
+ b_only: !ra && !!rb,
37
+ };
38
+ if (ra?.produced_by)
39
+ diff.a_produced_by = ra.produced_by;
40
+ if (rb?.produced_by)
41
+ diff.b_produced_by = rb.produced_by;
42
+ diffs.push(diff);
43
+ }
44
+ return { gig_a: a.gig_id, gig_b: b.gig_id, channels: diffs };
45
+ }
46
+ /**
47
+ * Pure cardinality helpers. Return NUMBERS so a downstream policy layer can
48
+ * weight them inside the tensor — never as standalone verdicts.
49
+ */
50
+ export function channelCount(tensor) {
51
+ return tensor.readings.length;
52
+ }
53
+ export function uniqueChannels(diff) {
54
+ return {
55
+ in_both: diff.channels.filter((c) => c.present_in_both).length,
56
+ a_only: diff.channels.filter((c) => c.a_only).length,
57
+ b_only: diff.channels.filter((c) => c.b_only).length,
58
+ };
59
+ }
60
+ //# sourceMappingURL=tensor_read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tensor_read.js","sourceRoot":"","sources":["../../src/tensor_read.ts"],"names":[],"mappings":"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"}