@eir-labs/coltrane 0.8.0 → 0.8.1

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 (77) hide show
  1. package/agents/change-verifier.json +40 -0
  2. package/agents/code-implementer.json +42 -0
  3. package/agents/contract-definer.json +31 -0
  4. package/agents/defect-investigator.json +60 -0
  5. package/agents/grounder.json +38 -0
  6. package/agents/pr-publisher.json +48 -0
  7. package/agents/red-spec-drafter.json +46 -0
  8. package/agents/spec-publisher.json +48 -0
  9. package/agents/spec-reviewer.json +32 -0
  10. package/dist/src/bifrost_invoker.js +53 -52
  11. package/dist/src/bifrost_invoker.js.map +1 -1
  12. package/dist/src/change_set_branch.d.ts +131 -0
  13. package/dist/src/change_set_branch.js +179 -0
  14. package/dist/src/change_set_branch.js.map +1 -0
  15. package/dist/src/claude_invoker.d.ts +79 -2
  16. package/dist/src/claude_invoker.js +377 -36
  17. package/dist/src/claude_invoker.js.map +1 -1
  18. package/dist/src/cli.d.ts +1 -1
  19. package/dist/src/cli.js +26 -3
  20. package/dist/src/cli.js.map +1 -1
  21. package/dist/src/composition.d.ts +11 -0
  22. package/dist/src/composition.js +3 -1
  23. package/dist/src/composition.js.map +1 -1
  24. package/dist/src/genome_schema.d.ts +301 -10
  25. package/dist/src/genome_schema.js +228 -7
  26. package/dist/src/genome_schema.js.map +1 -1
  27. package/dist/src/genome_store.d.ts +16 -1
  28. package/dist/src/genome_store.js +68 -0
  29. package/dist/src/genome_store.js.map +1 -1
  30. package/dist/src/gig_tracker.js +4 -0
  31. package/dist/src/gig_tracker.js.map +1 -1
  32. package/dist/src/hosted_tools.js +19 -0
  33. package/dist/src/hosted_tools.js.map +1 -1
  34. package/dist/src/institution_enforcement.d.ts +68 -0
  35. package/dist/src/institution_enforcement.js +417 -0
  36. package/dist/src/institution_enforcement.js.map +1 -0
  37. package/dist/src/institution_loader.d.ts +43 -0
  38. package/dist/src/institution_loader.js +157 -0
  39. package/dist/src/institution_loader.js.map +1 -0
  40. package/dist/src/loader.d.ts +3 -1
  41. package/dist/src/loader.js +12 -1
  42. package/dist/src/loader.js.map +1 -1
  43. package/dist/src/mcp.js +8 -1
  44. package/dist/src/mcp.js.map +1 -1
  45. package/dist/src/runtime.d.ts +66 -0
  46. package/dist/src/runtime.js +202 -2
  47. package/dist/src/runtime.js.map +1 -1
  48. package/dist/src/server.d.ts +7 -0
  49. package/dist/src/server.js +90 -1
  50. package/dist/src/server.js.map +1 -1
  51. package/dist/src/venue_realize.d.ts +90 -0
  52. package/dist/src/venue_realize.js +129 -0
  53. package/dist/src/venue_realize.js.map +1 -0
  54. package/dist/src/version.d.ts +1 -1
  55. package/dist/src/version.js +1 -1
  56. package/dist/src/worker.d.ts +45 -3
  57. package/dist/src/worker.js +67 -2
  58. package/dist/src/worker.js.map +1 -1
  59. package/domain_types/change-request.json +2 -1
  60. package/domain_types/class-sweep.json +34 -0
  61. package/domain_types/defect-class.json +25 -0
  62. package/domain_types/defect-location.json +28 -0
  63. package/domain_types/defect-report.json +28 -0
  64. package/domain_types/fix-spec.json +28 -0
  65. package/domain_types/fix-verification.json +28 -0
  66. package/domain_types/grounding-dossier.json +95 -0
  67. package/domain_types/pull-request.json +43 -0
  68. package/domain_types/red-spec.json +76 -0
  69. package/domain_types/reproduction.json +28 -0
  70. package/domain_types/root-cause.json +28 -0
  71. package/domain_types/subsystem-contract.json +106 -0
  72. package/institutions/coltrane.json +166 -0
  73. package/institutions/quartet.json +143 -26
  74. package/package.json +2 -1
  75. package/standards/defect-investigation-v1.json +156 -0
  76. package/standards/software-change-pr-v1.json +151 -0
  77. package/standards/spec-drafting-v1.json +125 -0
@@ -60,6 +60,7 @@ export interface ServerDeps {
60
60
  reuse?: ReuseStore | undefined;
61
61
  toolProviders?: Map<string, ToolProvider> | undefined;
62
62
  mcpServerConfigs?: Record<string, unknown> | undefined;
63
+ output_write_mode?: "seal" | "validate" | undefined;
63
64
  hooks?: readonly ToolHook[] | undefined;
64
65
  output_mirror?: OutputMirror | undefined;
65
66
  }
@@ -91,6 +92,12 @@ export interface ToolSurfaceDeps extends ServerDeps {
91
92
  * passes through, the store authorizes (an agent token is refused there). Without it, hosted
92
93
  * gig_approve is an honest typed error. */
93
94
  approveGig?: ((args: Record<string, unknown>) => Promise<Record<string, unknown>>) | undefined;
95
+ /** Hosted gig cancel: cancel a QUEUED gig before a drain worker claims it (e.g.
96
+ * postgrestCancelGig(ctx) → coltrane_gig_cancel for a member, or rpcCancelGig(ctx) →
97
+ * coltrane_mcp_gig_cancel for an agent token). Parallel to queueGig — the engine passes
98
+ * through, the store authorizes and refuses a claimed/running row. Without it, hosted
99
+ * gig_cancel is an honest typed error. */
100
+ cancelGig?: ((args: Record<string, unknown>) => Promise<Record<string, unknown>>) | undefined;
94
101
  /** Hosted genome persistence: a successful define/compose/register also upserts through
95
102
  * this store (the governed RPC), or the definition evaporates at end-of-request. */
96
103
  store?: GenomeStore | undefined;
@@ -512,6 +512,25 @@ async function runImpl(slug, args, deps, approval) {
512
512
  const data_raw = args["data"];
513
513
  // undefined → {} (caller never sent the field); null stays null so Ajv sees it.
514
514
  const data = (data_raw === undefined ? {} : data_raw);
515
+ // THE WRITE BOUNDARY. In "validate" mode the caller is a spawned model chair sealing
516
+ // in-band: run the COMPLETE seal predicate (validateWrite === checkWritable: the substance
517
+ // floor via validateOutput + the domain schema via registry.validate + core agreement) and
518
+ // return the verdict WITHOUT persisting. On a violation the throw below becomes an in-band
519
+ // { ok:false, error } (dispatchTool's catch), so the STILL-RUNNING agent gets the exact
520
+ // reason and self-corrects by calling output_write again — no invoker re-prompt, and no
521
+ // subset check. The runtime (executeChair) is the one sealer, so validating-not-sealing
522
+ // here is what keeps the output sealed exactly once.
523
+ if (deps.output_write_mode === "validate") {
524
+ const verdict = deps.outputs.validateWrite({ core_type, domain_type, data });
525
+ if (!verdict.valid) {
526
+ throw new Error(verdict.reason ??
527
+ `output rejected: "${domain_type || core_type}" did not satisfy its output contract`);
528
+ }
529
+ return {
530
+ ok: true, requires_approval: approval,
531
+ data: { validated: true, validation_result: { valid: true } },
532
+ };
533
+ }
515
534
  const rec = deps.outputs.write({
516
535
  core_type,
517
536
  domain_type,
@@ -533,7 +552,12 @@ async function runImpl(slug, args, deps, approval) {
533
552
  for (const r of refs) {
534
553
  deps.outputs.addRef(rec.id, r.to, r.relation, primitive);
535
554
  }
536
- return { ok: true, requires_approval: approval, data: { output_id: rec.id, primitive, output: rec } };
555
+ // `validation_result` is declared on this tool AND load-bearing: the write validated
556
+ // (deps.outputs.write → checkWritable) before the row existed, so reaching here means it
557
+ // passed — an INVALID payload would have thrown and been returned as { ok:false, error }
558
+ // by dispatchTool's try/catch. Say so, in the same { valid } shape the define/compose
559
+ // tools use, rather than declaring a field and never returning it (#234 family).
560
+ return { ok: true, requires_approval: approval, data: { output_id: rec.id, primitive, output: rec, validation_result: { valid: true } } };
537
561
  }
538
562
  case "execution_history_read": {
539
563
  // Read the append-only ledger — the genome's run history. Filterable by
@@ -1842,6 +1866,32 @@ async function runImpl(slug, args, deps, approval) {
1842
1866
  data: { status, aborted, cancellable: aborted, cleanup_result: { reason } },
1843
1867
  };
1844
1868
  }
1869
+ case "gig_cancel": {
1870
+ // gig_cancel stops a QUEUED gig — one in the org gig table that no drain worker has
1871
+ // claimed yet — so no worker ever claims it. That queue is a HOSTED concept: the store's
1872
+ // gig table. A LOCAL surface has no queue (a local gig_dispatch runs immediately, tracked
1873
+ // in gig_runs), so there is nothing here to cancel. The hosted seam (deps.cancelGig) is
1874
+ // intercepted in callSurfaceTool before this handler is reached; reaching this switch is a
1875
+ // NON-hosted call. Two honest answers:
1876
+ // * a live RUNNING gig is the wrong door — cancel is pre-claim; a running gig is stopped
1877
+ // by gig_abort. Fail closed and say so.
1878
+ // * otherwise there is no local queue to cancel from — a typed hosted-only explanation.
1879
+ const gid = String(args["gig_id"] ?? "");
1880
+ const live = gid.length > 0 ? deps.gig_runs?.get(gid) : undefined;
1881
+ if (live && live.status === "running") {
1882
+ return {
1883
+ ok: false, requires_approval: approval,
1884
+ error: `gig "${gid}" is already running — gig_cancel only stops a QUEUED gig before a worker ` +
1885
+ "claims it. Use gig_abort to stop a running gig.",
1886
+ };
1887
+ }
1888
+ return {
1889
+ ok: false, requires_approval: approval,
1890
+ error: "gig_cancel stops a QUEUED gig in the org gig table before a drain worker claims it. " +
1891
+ "A local surface has no queue — cancel over the hosted store (coltrane_gig_cancel for a " +
1892
+ "member, coltrane_mcp_gig_cancel for an agent token).",
1893
+ };
1894
+ }
1845
1895
  case "gig_approve": {
1846
1896
  // Approval is a MEMBER act whose authority lives in the STORE (the coltrane_gig_approve RPC
1847
1897
  // is member-JWT-only; an agent token is refused there — the enforcement belongs store-side,
@@ -2867,6 +2917,28 @@ async function callSurfaceTool(slug, args, deps) {
2867
2917
  "parked gig over the wire.",
2868
2918
  };
2869
2919
  }
2920
+ if (slug === "gig_cancel") {
2921
+ // Cancelling a QUEUED gig reaches the org gig table, never a local run — so hosted cancel
2922
+ // is a pure pass-through to the store's cancel RPC (member JWT → coltrane_gig_cancel;
2923
+ // agent token → coltrane_mcp_gig_cancel). The store cancels only a queued row and REFUSES
2924
+ // a claimed/running one — that refusal, naming gig_abort, surfaces as a failed cancel.
2925
+ if (deps.cancelGig) {
2926
+ try {
2927
+ const data = await deps.cancelGig(args);
2928
+ return { ok: true, data };
2929
+ }
2930
+ catch (e) {
2931
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
2932
+ }
2933
+ }
2934
+ return {
2935
+ ok: false,
2936
+ hosted_unsupported: true,
2937
+ error: "hosted cancel goes through the cancel RPC (coltrane_gig_cancel for a member, " +
2938
+ "coltrane_mcp_gig_cancel for an agent token) — the gig table is the queue and the store " +
2939
+ "authorizes. Wire deps.cancelGig (parallel to deps.queueGig) to cancel the queued gig over the wire.",
2940
+ };
2941
+ }
2870
2942
  }
2871
2943
  const result = await dispatchTool(slug, args, deps);
2872
2944
  // Hosted persistence: without a genome_dir the handlers compute + validate + seal identity
@@ -3001,6 +3073,11 @@ export function bootstrapServerDeps(genomeRoot) {
3001
3073
  registry,
3002
3074
  toolProviders,
3003
3075
  mcpServerConfigs, // the SAME object handed to the invoker — the preflight guard resolves against it
3076
+ // A chair's spawn sets COLTRANE_OUTPUT_WRITE_MODE=validate so this child's coltrane server
3077
+ // adjudicates the chair's in-band output_write calls against the full seal predicate WITHOUT
3078
+ // persisting — the runtime that captures the validated payload is the one that seals. A bare
3079
+ // server start (no env) keeps "seal": a human/agent output_write durably writes as before.
3080
+ ...(process.env["COLTRANE_OUTPUT_WRITE_MODE"] === "validate" ? { output_write_mode: "validate" } : {}),
3004
3081
  output_mirror,
3005
3082
  // PR #78 follow-up: persist outputs to disk so the audit chain survives an
3006
3083
  // MCP session close (Rob cold-trial requirement). COLTRANE_OUTPUTS_DIR
@@ -3024,8 +3101,20 @@ export function bootstrapServerDeps(genomeRoot) {
3024
3101
  // own server + any the deployment registers in .mcp.json). An unresolvable grant fails closed.
3025
3102
  mcpServerConfigs,
3026
3103
  toolProviders, // the genome→provider bridge (above) — makes in_house grants resolvable
3104
+ // The production seal path: a model chair SEALS IN-BAND by calling output_write (validated at
3105
+ // the full write boundary, corrected in-band), and the invoker captures what passed. The
3106
+ // engine server config above is bridged into the spawn and its validate-mode env set, so the
3107
+ // chair's output_write adjudicates-not-persists and the runtime seals exactly once.
3108
+ sealVia: "output_write",
3027
3109
  // per-chair wall-clock bound; COLTRANE_CHAIR_TIMEOUT_MS overrides for slow deployments
3028
3110
  ...(process.env["COLTRANE_CHAIR_TIMEOUT_MS"] ? { timeout_ms: Number(process.env["COLTRANE_CHAIR_TIMEOUT_MS"]) } : {}),
3111
+ // The reserve grant (#329) had no reachable caller: it was built, tested, and set by nothing,
3112
+ // so a chair that spent its budget still died silently at the cap. This is the operator-level
3113
+ // door to it. Absent = no reserve, which is the prior behaviour exactly — an extension nobody
3114
+ // asked for is spend nobody authorised. The DURABLE fix is a per-chair `turn_reserve` declared
3115
+ // in the standard (PR #331), because a budget is a property of the work rather than of the
3116
+ // player; this env is the deployment-level stopgap until that lands, not a substitute for it.
3117
+ ...(process.env["COLTRANE_TURN_RESERVE"] ? { turn_reserve: Number(process.env["COLTRANE_TURN_RESERVE"]) } : {}),
3029
3118
  }),
3030
3119
  model_version: process.env["COLTRANE_MODEL"] ?? "claude-cli-default",
3031
3120
  skills: genome.skills, // ← skill substrate — runGig resolves agent.skill_slugs into prompt