@eir-labs/coltrane 0.10.0 → 0.12.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.
- package/agents/bandleader.json +54 -0
- package/agents/bill.json +4 -2
- package/agents/change-verifier.json +2 -1
- package/agents/code-implementer.json +7 -4
- package/agents/john.json +5 -3
- package/agents/lineage-scout-external.json +19 -6
- package/agents/lineage-scribe.json +20 -9
- package/agents/lineage-weaver.json +26 -11
- package/agents/miles.json +2 -1
- package/agents/players/bandleader.md +70 -0
- package/agents/pr-publisher.json +4 -2
- package/agents/red-spec-drafter.json +12 -6
- package/agents/room-prober.json +29 -0
- package/agents/source-walker.json +1 -1
- package/charts/room-probe-chart-v1.json +14 -0
- package/charts/worker-contract-docker-v2.json +135 -0
- package/charts/worker-contract-v3.json +206 -0
- package/dist/src/boundary_check.d.ts +18 -0
- package/dist/src/boundary_check.js +70 -0
- package/dist/src/boundary_check.js.map +1 -0
- package/dist/src/boundary_scan.d.ts +62 -0
- package/dist/src/boundary_scan.js +93 -0
- package/dist/src/boundary_scan.js.map +1 -0
- package/dist/src/chart.js +34 -1
- package/dist/src/chart.js.map +1 -1
- package/dist/src/claude_invoker.d.ts +2 -13
- package/dist/src/claude_invoker.js +242 -18
- package/dist/src/claude_invoker.js.map +1 -1
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/cli.js +115 -27
- package/dist/src/cli.js.map +1 -1
- package/dist/src/drain_preflight.d.ts +33 -0
- package/dist/src/drain_preflight.js +97 -0
- package/dist/src/drain_preflight.js.map +1 -0
- package/dist/src/genome_schema.d.ts +390 -9
- package/dist/src/genome_schema.js +163 -8
- package/dist/src/genome_schema.js.map +1 -1
- package/dist/src/genome_store.js +9 -0
- package/dist/src/genome_store.js.map +1 -1
- package/dist/src/genome_writer.d.ts +1 -1
- package/dist/src/genome_writer.js +10 -2
- package/dist/src/genome_writer.js.map +1 -1
- package/dist/src/gig_conformance.d.ts +66 -0
- package/dist/src/gig_conformance.js +153 -0
- package/dist/src/gig_conformance.js.map +1 -0
- package/dist/src/gig_tracker.js +8 -0
- package/dist/src/gig_tracker.js.map +1 -1
- package/dist/src/grounding.d.ts +44 -0
- package/dist/src/grounding.js +127 -0
- package/dist/src/grounding.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +9 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/institution_loader.d.ts +29 -5
- package/dist/src/institution_loader.js +67 -6
- package/dist/src/institution_loader.js.map +1 -1
- package/dist/src/lineage_adoption.d.ts +82 -0
- package/dist/src/lineage_adoption.js +82 -0
- package/dist/src/lineage_adoption.js.map +1 -0
- package/dist/src/lineage_persist.d.ts +34 -0
- package/dist/src/lineage_persist.js +60 -0
- package/dist/src/lineage_persist.js.map +1 -0
- package/dist/src/loader.d.ts +13 -0
- package/dist/src/loader.js +43 -2
- package/dist/src/loader.js.map +1 -1
- package/dist/src/local_queue.d.ts +105 -0
- package/dist/src/local_queue.js +380 -0
- package/dist/src/local_queue.js.map +1 -0
- package/dist/src/mcp.js +37 -4
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/org_hire.d.ts +23 -0
- package/dist/src/org_hire.js +42 -0
- package/dist/src/org_hire.js.map +1 -0
- package/dist/src/outputs.d.ts +17 -0
- package/dist/src/outputs.js +86 -1
- package/dist/src/outputs.js.map +1 -1
- package/dist/src/player_to_claude_code.js +26 -2
- package/dist/src/player_to_claude_code.js.map +1 -1
- package/dist/src/registry.d.ts +3 -1
- package/dist/src/registry.js +43 -3
- package/dist/src/registry.js.map +1 -1
- package/dist/src/repo_index.d.ts +58 -0
- package/dist/src/repo_index.js +232 -0
- package/dist/src/repo_index.js.map +1 -0
- package/dist/src/residency.d.ts +149 -0
- package/dist/src/residency.js +283 -0
- package/dist/src/residency.js.map +1 -0
- package/dist/src/reuse.d.ts +44 -4
- package/dist/src/reuse.js +0 -0
- package/dist/src/reuse.js.map +1 -1
- package/dist/src/runtime.d.ts +82 -0
- package/dist/src/runtime.js +254 -25
- package/dist/src/runtime.js.map +1 -1
- package/dist/src/server.d.ts +37 -0
- package/dist/src/server.js +422 -11
- package/dist/src/server.js.map +1 -1
- package/dist/src/server_relay.d.ts +40 -2
- package/dist/src/server_relay.js +186 -19
- package/dist/src/server_relay.js.map +1 -1
- package/dist/src/tool_providers.d.ts +27 -0
- package/dist/src/tool_providers.js +48 -0
- package/dist/src/tool_providers.js.map +1 -1
- package/dist/src/venue_credential.d.ts +41 -0
- package/dist/src/venue_credential.js +57 -0
- package/dist/src/venue_credential.js.map +1 -0
- package/dist/src/venue_realize.d.ts +23 -0
- package/dist/src/venue_realize.js +43 -3
- package/dist/src/venue_realize.js.map +1 -1
- package/dist/src/venue_realizer.d.ts +224 -0
- package/dist/src/venue_realizer.js +889 -0
- package/dist/src/venue_realizer.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/worker.d.ts +57 -0
- package/dist/src/worker.js +155 -24
- package/dist/src/worker.js.map +1 -1
- package/dist/src/worker_env.d.ts +79 -0
- package/dist/src/worker_env.js +322 -0
- package/dist/src/worker_env.js.map +1 -0
- package/dist/src/workspace.d.ts +12 -1
- package/dist/src/workspace.js +10 -3
- package/dist/src/workspace.js.map +1 -1
- package/domain_types/change-context.json +59 -13
- package/domain_types/lineage-adoption-target.json +22 -0
- package/domain_types/lineage-map.json +7 -2
- package/domain_types/lineage-record.json +55 -5
- package/domain_types/prior-art-hit.json +16 -1
- package/domain_types/red-spec.json +5 -3
- package/domain_types/woodshed-record.json +149 -0
- package/evals/gist-present.json +7 -0
- package/institutions/coltrane.json +178 -6
- package/package.json +6 -3
- package/skills/non-vacuity-probe/fixtures/fixture-001.json +17 -0
- package/skills/non-vacuity-probe/meta.json +12 -0
- package/skills/non-vacuity-probe/skill.md +27 -0
- package/skills/non-vacuity-probe/skill.mjs +81 -0
- package/standards/lineage-adopt-v0.json +37 -0
- package/standards/room-probe-v1.json +28 -0
- package/standards/software-change-pr-v1.json +15 -15
- package/standards/software-change-red-first-v0.json +183 -0
- package/standards/spec-review-and-sequence-v0.json +112 -0
- package/standards/studio-session-v0.json +101 -0
- package/tours/coltrane.json +72 -0
- package/venues/engine-room-v1.json +32 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** The lease a claimed gig carries — a continuing renewed fact (monotonic `renewed_at`), not a boot
|
|
2
|
+
* assertion. */
|
|
3
|
+
export interface LeaseState {
|
|
4
|
+
holder: string;
|
|
5
|
+
renewed_at: number;
|
|
6
|
+
expires_at: number;
|
|
7
|
+
}
|
|
8
|
+
/** A claimed local gig — the sibling of worker.ts ClaimedGig, plus the local lease and, on a
|
|
9
|
+
* re-claim, the human seat's role-keyed verdicts. */
|
|
10
|
+
export interface ClaimedLocalGig {
|
|
11
|
+
gig_id: string;
|
|
12
|
+
standard_slug: string;
|
|
13
|
+
standard_version: number | null;
|
|
14
|
+
mode: string;
|
|
15
|
+
input: Record<string, unknown>;
|
|
16
|
+
acting_for: string;
|
|
17
|
+
venue?: string | null;
|
|
18
|
+
worker: string;
|
|
19
|
+
lease: LeaseState;
|
|
20
|
+
approvals?: Record<string, {
|
|
21
|
+
verdict: Record<string, unknown>;
|
|
22
|
+
approved_by?: string;
|
|
23
|
+
}> | null;
|
|
24
|
+
}
|
|
25
|
+
/** One row's observable state — how a reader tells a live claim from an abandoned one. */
|
|
26
|
+
export interface LocalGigView {
|
|
27
|
+
gig_id: string;
|
|
28
|
+
state: "queued" | "claimed" | "awaiting_approval" | "complete" | "failed" | "cancelled";
|
|
29
|
+
holder?: string;
|
|
30
|
+
lease?: LeaseState;
|
|
31
|
+
}
|
|
32
|
+
export interface LocalQueueOptions {
|
|
33
|
+
leaseMs?: number;
|
|
34
|
+
/** Injected clock for deterministic lease/reap laws. Defaults to Date.now(). */
|
|
35
|
+
clock?: () => number;
|
|
36
|
+
}
|
|
37
|
+
/** The local queue port — every verb is offline and needs no credential. */
|
|
38
|
+
export interface LocalQueue {
|
|
39
|
+
enqueue(args: Record<string, unknown>): Promise<{
|
|
40
|
+
gig_id: string;
|
|
41
|
+
status: "queued";
|
|
42
|
+
}>;
|
|
43
|
+
claim(worker: string): Promise<ClaimedLocalGig | null>;
|
|
44
|
+
heartbeat(worker: string, gig_id: string): Promise<boolean>;
|
|
45
|
+
reap(): {
|
|
46
|
+
requeued: string[];
|
|
47
|
+
kept: number;
|
|
48
|
+
errors: string[];
|
|
49
|
+
};
|
|
50
|
+
park(worker: string, gig_id: string): Promise<boolean>;
|
|
51
|
+
approve(gig_id: string, role: string, verdict: Record<string, unknown>, approved_by?: string): Promise<boolean>;
|
|
52
|
+
cancel(args: Record<string, unknown>): Promise<{
|
|
53
|
+
gig_id: string;
|
|
54
|
+
status: "cancelled";
|
|
55
|
+
}>;
|
|
56
|
+
complete(worker: string, gig_id: string, output: Record<string, unknown>): Promise<{
|
|
57
|
+
content_sha: string;
|
|
58
|
+
duplicated: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
list(): LocalGigView[];
|
|
61
|
+
readonly leaseMs: number;
|
|
62
|
+
}
|
|
63
|
+
/** Which backing owns the queue, decided by which environment is present. */
|
|
64
|
+
export type QueueBackingChoice = {
|
|
65
|
+
backing: "file";
|
|
66
|
+
root: string;
|
|
67
|
+
} | {
|
|
68
|
+
backing: "hosted";
|
|
69
|
+
} | {
|
|
70
|
+
backing: "none";
|
|
71
|
+
why: string;
|
|
72
|
+
} | {
|
|
73
|
+
backing: "conflict";
|
|
74
|
+
why: string;
|
|
75
|
+
};
|
|
76
|
+
export interface LocalQueueModule {
|
|
77
|
+
openLocalQueue(root: string, opts?: LocalQueueOptions): LocalQueue;
|
|
78
|
+
fileQueueGig(root: string): (args: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
79
|
+
fileCancelGig(root: string): (args: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
80
|
+
selectQueueBacking(env: Record<string, string | undefined>): QueueBackingChoice;
|
|
81
|
+
LOCAL_QUEUE_DIR_VAR: string;
|
|
82
|
+
DRAIN_VARS: readonly string[];
|
|
83
|
+
}
|
|
84
|
+
/** The env var whose presence selects the local backing. */
|
|
85
|
+
export declare const LOCAL_QUEUE_DIR_VAR = "COLTRANE_QUEUE_DIR";
|
|
86
|
+
/** The drain's five-variable contract (drain_preflight.ts:55-61), kept SORTED. The local path must
|
|
87
|
+
* never read a VALUE of any of these; selectQueueBacking only ever inspects key PRESENCE. */
|
|
88
|
+
export declare const DRAIN_VARS: readonly string[];
|
|
89
|
+
/**
|
|
90
|
+
* The single backing selector — file when the local dir is present, hosted for the drain env,
|
|
91
|
+
* conflict when both, none otherwise. It reads the VALUE of exactly one variable, LOCAL_QUEUE_DIR_VAR
|
|
92
|
+
* (not a drain var), and detects hosted presence by KEY PRESENCE via Object.keys — never by reading a
|
|
93
|
+
* drain variable's value. That distinction is load-bearing for F8: the test passes an env Proxy whose
|
|
94
|
+
* `get` trap throws on any drain-var read, so hosted detection must go through `ownKeys` (Object.keys
|
|
95
|
+
* does not trip the `get` trap), letting a local-present env resolve to `file` without touching one of
|
|
96
|
+
* the five. Values of the drain vars are never needed — their mere presence is the whole signal.
|
|
97
|
+
*/
|
|
98
|
+
export declare function selectQueueBacking(env: Record<string, string | undefined>): QueueBackingChoice;
|
|
99
|
+
export declare function openLocalQueue(root: string, opts?: LocalQueueOptions): LocalQueue;
|
|
100
|
+
/** The deps.queueGig-shaped enqueue seam — byte-compatible with postgrestQueueGig / rpcQueueGig
|
|
101
|
+
* ({gig_id, status:'queued'}), so a caller cannot tell which backing answered (I1). */
|
|
102
|
+
export declare function fileQueueGig(root: string): (args: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
103
|
+
/** The deps.cancelGig-shaped seam — sibling of postgrestCancelGig ({gig_id, status:'cancelled'})
|
|
104
|
+
* (I18). */
|
|
105
|
+
export declare function fileCancelGig(root: string): (args: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// The local, file-backed gig queue — the third gig backing, the offline sibling of the two HTTP
|
|
2
|
+
// seams (postgrestQueueGig / rpcQueueGig, src/genome_store.ts:532,:493). It closes the asymmetry the
|
|
3
|
+
// SPEC (docs/specs/SPEC-local-queue-contract.md) names: the genome port already ships a LOCAL sibling
|
|
4
|
+
// (fileGenomeStore, src/genome_store.ts:77) so a genome can be READ from files, but the queue port
|
|
5
|
+
// had none — so an open-source user could run gigs in-process yet never enqueue. This module is that
|
|
6
|
+
// missing sibling: clone → build → enqueue → `coltrane work` in another terminal claims and runs it,
|
|
7
|
+
// with no Supabase, no service origin, no cdk_ key, no minting backend.
|
|
8
|
+
//
|
|
9
|
+
// MUTUAL EXCLUSION WITHOUT A DATABASE OR A LOCK. State is a directory-per-state layout under `root`
|
|
10
|
+
// (queued/, claimed/, parked/, done/, failed/, cancelled/), and every transition is a single POSIX
|
|
11
|
+
// rename(2), which is atomic within a filesystem. A claim renames queued/<id> → claimed/<id>: the
|
|
12
|
+
// winner's rename removes the source, and every loser's rename of the same source fails ENOENT — that
|
|
13
|
+
// is how a loser learns it lost (maildir new/→cur/, cr.yp.to; FSQ tmp/→queue/→done/). No lockfile
|
|
14
|
+
// (which would leave stale residue reintroducing the race), no in-process mutex (which cannot cross
|
|
15
|
+
// the server / `coltrane work` process boundary this queue exists to bridge), no SQLite (a forbidden
|
|
16
|
+
// dependency). node's own fs is sufficient, and the atomicity argument depends on staying on one FS.
|
|
17
|
+
//
|
|
18
|
+
// LEASE, not lock. A claim carries a lease {holder, renewed_at, expires_at} embedded in the moved
|
|
19
|
+
// file, so one atomic rename carries the claim AND its lease together — there is never a window where
|
|
20
|
+
// a claim exists without its lease. The holder renews with heartbeat (strictly advances the lease);
|
|
21
|
+
// a crashed holder's grip falls open by timeout, and reap() returns lapsed claims to queued while
|
|
22
|
+
// never touching a fresh one. A clock may be injected (opts.clock) so lease/reap laws are
|
|
23
|
+
// deterministic and fast; it defaults to Date.now().
|
|
24
|
+
//
|
|
25
|
+
// IDEMPOTENT EFFECT. Because a lapsed lease lets a second worker re-run a gig, the honest guarantee
|
|
26
|
+
// is at-least-once + idempotent-effect: complete() seals the output under content_sha (the same
|
|
27
|
+
// sha256Hex(canonJson(...)) primitive the rest of the codebase uses), a re-run with the SAME output
|
|
28
|
+
// re-seals rather than duplicating (duplicated:true), and a re-run that would seal a DIFFERENT output
|
|
29
|
+
// fails closed rather than forking.
|
|
30
|
+
//
|
|
31
|
+
// This module is NOT wired into any surface (deps.queueGig / deps.cancelGig) — shipping the module
|
|
32
|
+
// and its laws going green is a separate act from selecting it as a backing.
|
|
33
|
+
import * as fs from "node:fs";
|
|
34
|
+
import * as fsp from "node:fs/promises";
|
|
35
|
+
import { join } from "node:path";
|
|
36
|
+
import { randomUUID } from "node:crypto";
|
|
37
|
+
import { sha256Hex, canonJson } from "./canonical_form.js";
|
|
38
|
+
// ── The env-presence contract. ────────────────────────────────────────────────────────────────────
|
|
39
|
+
/** The env var whose presence selects the local backing. */
|
|
40
|
+
export const LOCAL_QUEUE_DIR_VAR = "COLTRANE_QUEUE_DIR";
|
|
41
|
+
/** The drain's five-variable contract (drain_preflight.ts:55-61), kept SORTED. The local path must
|
|
42
|
+
* never read a VALUE of any of these; selectQueueBacking only ever inspects key PRESENCE. */
|
|
43
|
+
export const DRAIN_VARS = [
|
|
44
|
+
"COLTRANE_DRAIN_KEY",
|
|
45
|
+
"COLTRANE_DRAIN_URL",
|
|
46
|
+
"COLTRANE_INSTANCE",
|
|
47
|
+
"COLTRANE_STORE_ANON",
|
|
48
|
+
"COLTRANE_STORE_URL",
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* The single backing selector — file when the local dir is present, hosted for the drain env,
|
|
52
|
+
* conflict when both, none otherwise. It reads the VALUE of exactly one variable, LOCAL_QUEUE_DIR_VAR
|
|
53
|
+
* (not a drain var), and detects hosted presence by KEY PRESENCE via Object.keys — never by reading a
|
|
54
|
+
* drain variable's value. That distinction is load-bearing for F8: the test passes an env Proxy whose
|
|
55
|
+
* `get` trap throws on any drain-var read, so hosted detection must go through `ownKeys` (Object.keys
|
|
56
|
+
* does not trip the `get` trap), letting a local-present env resolve to `file` without touching one of
|
|
57
|
+
* the five. Values of the drain vars are never needed — their mere presence is the whole signal.
|
|
58
|
+
*/
|
|
59
|
+
export function selectQueueBacking(env) {
|
|
60
|
+
const localRaw = env[LOCAL_QUEUE_DIR_VAR];
|
|
61
|
+
const localPresent = typeof localRaw === "string" && localRaw.length > 0;
|
|
62
|
+
const keys = Object.keys(env);
|
|
63
|
+
const hostedPresent = DRAIN_VARS.some((v) => keys.includes(v));
|
|
64
|
+
if (localPresent && hostedPresent) {
|
|
65
|
+
return {
|
|
66
|
+
backing: "conflict",
|
|
67
|
+
why: `both ${LOCAL_QUEUE_DIR_VAR} and the hosted drain environment are set — refusing to guess which backing owns the gig`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (localPresent)
|
|
71
|
+
return { backing: "file", root: localRaw };
|
|
72
|
+
if (hostedPresent)
|
|
73
|
+
return { backing: "hosted" };
|
|
74
|
+
return {
|
|
75
|
+
backing: "none",
|
|
76
|
+
why: `no queue backing configured — set ${LOCAL_QUEUE_DIR_VAR} for a local file queue, or the drain environment for the hosted queue`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function isRecord(v) {
|
|
80
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
81
|
+
}
|
|
82
|
+
const DEFAULT_LEASE_MS = 30_000;
|
|
83
|
+
export function openLocalQueue(root, opts) {
|
|
84
|
+
const leaseMs = opts?.leaseMs ?? DEFAULT_LEASE_MS;
|
|
85
|
+
const clock = opts?.clock ?? Date.now;
|
|
86
|
+
const queuedDir = join(root, "queued");
|
|
87
|
+
const claimedDir = join(root, "claimed");
|
|
88
|
+
const parkedDir = join(root, "parked");
|
|
89
|
+
const doneDir = join(root, "done");
|
|
90
|
+
const failedDir = join(root, "failed");
|
|
91
|
+
const cancelledDir = join(root, "cancelled");
|
|
92
|
+
const tmpDir = join(root, "tmp");
|
|
93
|
+
const dirsByState = [
|
|
94
|
+
[queuedDir, "queued"],
|
|
95
|
+
[claimedDir, "claimed"],
|
|
96
|
+
[parkedDir, "awaiting_approval"],
|
|
97
|
+
[doneDir, "complete"],
|
|
98
|
+
[failedDir, "failed"],
|
|
99
|
+
[cancelledDir, "cancelled"],
|
|
100
|
+
];
|
|
101
|
+
// A tmp+rename atomic write: a reader of the destination sees either the OLD bytes or the whole NEW
|
|
102
|
+
// bytes, never a mixture. tmp lives in a dedicated dir so a claim's readdir of queued/ never lists a
|
|
103
|
+
// half-written temp file (F4).
|
|
104
|
+
async function atomicWrite(dir, name, obj) {
|
|
105
|
+
await fsp.mkdir(tmpDir, { recursive: true });
|
|
106
|
+
const tmp = join(tmpDir, `${randomUUID()}.tmp`);
|
|
107
|
+
await fsp.writeFile(tmp, JSON.stringify(obj), "utf8");
|
|
108
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
109
|
+
await fsp.rename(tmp, join(dir, name));
|
|
110
|
+
}
|
|
111
|
+
async function readGig(path) {
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(await fsp.readFile(path, "utf8"));
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function isEnoent(e) {
|
|
120
|
+
return typeof e === "object" && e !== null && e.code === "ENOENT";
|
|
121
|
+
}
|
|
122
|
+
async function enqueue(args) {
|
|
123
|
+
// F10 — a payload with no standard to run is unrunnable. Refuse at enqueue and persist nothing,
|
|
124
|
+
// rather than enqueue a row that can only fail on a drain thirty minutes later.
|
|
125
|
+
const slug = args["standard_slug"];
|
|
126
|
+
if (typeof slug !== "string" || slug.length === 0) {
|
|
127
|
+
throw new Error("enqueue requires a non-empty standard_slug — nothing to run");
|
|
128
|
+
}
|
|
129
|
+
const gig_id = randomUUID();
|
|
130
|
+
const now = clock();
|
|
131
|
+
const record = {
|
|
132
|
+
gig_id,
|
|
133
|
+
standard_slug: slug,
|
|
134
|
+
standard_version: typeof args["standard_version"] === "number" ? args["standard_version"] : null,
|
|
135
|
+
mode: typeof args["mode"] === "string" ? args["mode"] : "live",
|
|
136
|
+
input: isRecord(args["input"]) ? args["input"] : {},
|
|
137
|
+
acting_for: typeof args["acting_for"] === "string" ? args["acting_for"] : "",
|
|
138
|
+
venue: typeof args["venue"] === "string" ? args["venue"] : null,
|
|
139
|
+
enqueued_at: now,
|
|
140
|
+
};
|
|
141
|
+
// F1/F4 — write then rename over queued/<gig_id>. If the root cannot be persisted to (mkdir/rename
|
|
142
|
+
// fails, e.g. ENOTDIR under a regular file), this throws and NO success is reported.
|
|
143
|
+
await atomicWrite(queuedDir, gig_id, record);
|
|
144
|
+
return { gig_id, status: "queued" };
|
|
145
|
+
}
|
|
146
|
+
async function claim(worker) {
|
|
147
|
+
let candidates;
|
|
148
|
+
try {
|
|
149
|
+
candidates = (await fsp.readdir(queuedDir)).sort();
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null; // no queued/ dir yet ⇒ nothing to claim
|
|
153
|
+
}
|
|
154
|
+
await fsp.mkdir(claimedDir, { recursive: true });
|
|
155
|
+
for (const id of candidates) {
|
|
156
|
+
const src = join(queuedDir, id);
|
|
157
|
+
const dst = join(claimedDir, id);
|
|
158
|
+
try {
|
|
159
|
+
// The arbiter. Exactly one concurrent claimer's rename of a given source succeeds; every other
|
|
160
|
+
// gets ENOENT (I4/I5/F3) and moves on to the next candidate (I19). A live gig is not in
|
|
161
|
+
// queued/ at all, so it is never a candidate here (F7).
|
|
162
|
+
await fsp.rename(src, dst);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// A lost rename race (ENOENT) is how a loser learns it lost; any other per-row fault is
|
|
166
|
+
// likewise "could not take this row". Either way, move to the next candidate — a lost row is
|
|
167
|
+
// the null sentinel, never a thrown claim (I5/F3).
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const record = await readGig(dst);
|
|
171
|
+
if (record === null)
|
|
172
|
+
continue;
|
|
173
|
+
const now = clock();
|
|
174
|
+
const lease = { holder: worker, renewed_at: now, expires_at: now + leaseMs };
|
|
175
|
+
record.worker = worker;
|
|
176
|
+
record.lease = lease;
|
|
177
|
+
// Persist the lease alongside the claim (idempotent overwrite of a file only this claimer owns).
|
|
178
|
+
await atomicWrite(claimedDir, id, record);
|
|
179
|
+
const claimed = {
|
|
180
|
+
gig_id: record.gig_id,
|
|
181
|
+
standard_slug: record.standard_slug,
|
|
182
|
+
standard_version: record.standard_version,
|
|
183
|
+
mode: record.mode,
|
|
184
|
+
input: record.input,
|
|
185
|
+
acting_for: record.acting_for,
|
|
186
|
+
venue: record.venue,
|
|
187
|
+
worker,
|
|
188
|
+
lease,
|
|
189
|
+
approvals: record.approvals ?? null,
|
|
190
|
+
};
|
|
191
|
+
return claimed;
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
async function heartbeat(worker, gig_id) {
|
|
196
|
+
const path = join(claimedDir, gig_id);
|
|
197
|
+
const record = await readGig(path);
|
|
198
|
+
// F5 — the caller must still HOLD the claim. A heartbeat against a lease that was lost (the gig
|
|
199
|
+
// lapsed and someone else took it) or that no longer exists is a loss, never a false "still held".
|
|
200
|
+
if (record === null || record.lease === undefined || record.lease.holder !== worker)
|
|
201
|
+
return false;
|
|
202
|
+
const now = clock();
|
|
203
|
+
record.lease = { holder: worker, renewed_at: now, expires_at: now + leaseMs };
|
|
204
|
+
record.worker = worker;
|
|
205
|
+
await atomicWrite(claimedDir, gig_id, record);
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
function reap() {
|
|
209
|
+
// SYNCHRONOUS and best-effort (mirrors worker.ts reapWorkerState): a single unreadable claim never
|
|
210
|
+
// aborts the sweep. Only claimed/ is scanned, so a parked or terminal gig is structurally out of
|
|
211
|
+
// reach (I13) rather than guarded by a per-file predicate a future edit could forget.
|
|
212
|
+
const requeued = [];
|
|
213
|
+
const errors = [];
|
|
214
|
+
let kept = 0;
|
|
215
|
+
const now = clock();
|
|
216
|
+
let entries;
|
|
217
|
+
try {
|
|
218
|
+
entries = fs.readdirSync(claimedDir);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return { requeued, kept, errors };
|
|
222
|
+
}
|
|
223
|
+
for (const id of entries) {
|
|
224
|
+
const path = join(claimedDir, id);
|
|
225
|
+
try {
|
|
226
|
+
const record = JSON.parse(fs.readFileSync(path, "utf8"));
|
|
227
|
+
const expires = record.lease?.expires_at;
|
|
228
|
+
if (typeof expires === "number" && expires <= now) {
|
|
229
|
+
// Lapsed (I9/I10/I11) — return it to queued for the next worker. The rename is atomic; the
|
|
230
|
+
// stale lease left in the file is inert (list() reports no lease for a queued row, and the
|
|
231
|
+
// next claim overwrites it).
|
|
232
|
+
fs.mkdirSync(queuedDir, { recursive: true });
|
|
233
|
+
fs.renameSync(path, join(queuedDir, id));
|
|
234
|
+
requeued.push(id);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
kept++; // fresh (I8/I10) — never touched
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
errors.push(`${id}: ${e instanceof Error ? e.message : String(e)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { requeued, kept, errors };
|
|
245
|
+
}
|
|
246
|
+
async function park(worker, gig_id) {
|
|
247
|
+
const src = join(claimedDir, gig_id);
|
|
248
|
+
const record = await readGig(src);
|
|
249
|
+
if (record === null || record.lease?.holder !== worker)
|
|
250
|
+
return false;
|
|
251
|
+
// Move claimed/ → parked/ first (one atomic rename), THEN clear the lease. The gig is never in two
|
|
252
|
+
// places, and the reaper (which scans only claimed/) can never mistake it for an abandoned claim
|
|
253
|
+
// (I13). awaiting_approval is not a live claim.
|
|
254
|
+
await fsp.mkdir(parkedDir, { recursive: true });
|
|
255
|
+
await fsp.rename(src, join(parkedDir, gig_id));
|
|
256
|
+
delete record.lease;
|
|
257
|
+
delete record.worker;
|
|
258
|
+
await atomicWrite(parkedDir, gig_id, record);
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
async function approve(gig_id, role, verdict, approved_by) {
|
|
262
|
+
// F6 — an empty verdict is not an approval. Fail closed rather than resume past the human chair on
|
|
263
|
+
// a verdict that says nothing.
|
|
264
|
+
if (!isRecord(verdict) || Object.keys(verdict).length === 0) {
|
|
265
|
+
throw new Error("an empty verdict is not an approval — refusing to resume past the human chair");
|
|
266
|
+
}
|
|
267
|
+
const src = join(parkedDir, gig_id);
|
|
268
|
+
const record = await readGig(src);
|
|
269
|
+
if (record === null)
|
|
270
|
+
return false;
|
|
271
|
+
const entry = approved_by !== undefined ? { verdict, approved_by } : { verdict };
|
|
272
|
+
const approvals = {
|
|
273
|
+
...(record.approvals ?? {}),
|
|
274
|
+
};
|
|
275
|
+
approvals[role] = entry;
|
|
276
|
+
record.approvals = approvals;
|
|
277
|
+
// Seal the verdict into the parked row, THEN move parked/ → queued/ in one atomic rename, so the
|
|
278
|
+
// re-claimable row appears in queued/ already carrying its verdicts (I12).
|
|
279
|
+
await atomicWrite(parkedDir, gig_id, record);
|
|
280
|
+
await fsp.mkdir(queuedDir, { recursive: true });
|
|
281
|
+
await fsp.rename(join(parkedDir, gig_id), join(queuedDir, gig_id));
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
async function cancel(args) {
|
|
285
|
+
const gig_id = typeof args["gig_id"] === "string" ? args["gig_id"] : String(args["gig_id"] ?? "");
|
|
286
|
+
// Sibling of postgrestCancelGig: cancels a QUEUED row so it can never be claimed (I18). Moving it
|
|
287
|
+
// out of queued/ is the whole mechanism — a cancelled gig is simply no longer a claim candidate.
|
|
288
|
+
try {
|
|
289
|
+
await fsp.mkdir(cancelledDir, { recursive: true });
|
|
290
|
+
await fsp.rename(join(queuedDir, gig_id), join(cancelledDir, gig_id));
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
if (!isEnoent(e))
|
|
294
|
+
throw e; // not-queued (already gone) is idempotent; a real IO fault is not
|
|
295
|
+
}
|
|
296
|
+
return { gig_id, status: "cancelled" };
|
|
297
|
+
}
|
|
298
|
+
async function locate(gig_id) {
|
|
299
|
+
for (const [dir] of dirsByState) {
|
|
300
|
+
const path = join(dir, gig_id);
|
|
301
|
+
try {
|
|
302
|
+
await fsp.access(path);
|
|
303
|
+
return { dir, path };
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
/* not in this state dir */
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
async function complete(worker, gig_id, output) {
|
|
312
|
+
const loc = await locate(gig_id);
|
|
313
|
+
if (loc === null)
|
|
314
|
+
throw new Error(`cannot complete unknown gig ${gig_id}`);
|
|
315
|
+
const record = await readGig(loc.path);
|
|
316
|
+
if (record === null)
|
|
317
|
+
throw new Error(`cannot complete unreadable gig ${gig_id}`);
|
|
318
|
+
// content_sha is a pure function of the output via the codebase's own canonical hash, so two
|
|
319
|
+
// separate completions of identical output hash identically by construction (I14).
|
|
320
|
+
const content_sha = sha256Hex(canonJson(output));
|
|
321
|
+
if (record.content_sha !== undefined) {
|
|
322
|
+
// I14 — a re-run (after a lapsed lease let a second worker take the gig) re-seals the SAME output
|
|
323
|
+
// rather than duplicating. F9 — a DIFFERENT output for the same gig fails closed, never forks.
|
|
324
|
+
if (record.content_sha === content_sha)
|
|
325
|
+
return { content_sha, duplicated: true };
|
|
326
|
+
throw new Error(`gig ${gig_id} already sealed a different output — refusing to fork the result`);
|
|
327
|
+
}
|
|
328
|
+
// Record the seal in place. The gig stays where it is (typically still claimed): a lapsed lease can
|
|
329
|
+
// then let a second worker re-run it, and that re-run dedups against this recorded content_sha.
|
|
330
|
+
record.content_sha = content_sha;
|
|
331
|
+
record.output = output;
|
|
332
|
+
await atomicWrite(loc.dir, gig_id, record);
|
|
333
|
+
return { content_sha, duplicated: false };
|
|
334
|
+
}
|
|
335
|
+
function list() {
|
|
336
|
+
const views = [];
|
|
337
|
+
for (const [dir, state] of dirsByState) {
|
|
338
|
+
let entries;
|
|
339
|
+
try {
|
|
340
|
+
entries = fs.readdirSync(dir);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
continue; // dir not created yet
|
|
344
|
+
}
|
|
345
|
+
for (const id of entries) {
|
|
346
|
+
if (state === "claimed") {
|
|
347
|
+
try {
|
|
348
|
+
const record = JSON.parse(fs.readFileSync(join(dir, id), "utf8"));
|
|
349
|
+
if (record.lease && typeof record.lease.holder === "string") {
|
|
350
|
+
views.push({ gig_id: id, state, holder: record.lease.holder, lease: record.lease });
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
/* fall through to the lease-less view */
|
|
356
|
+
}
|
|
357
|
+
views.push({ gig_id: id, state });
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
views.push({ gig_id: id, state });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return views;
|
|
365
|
+
}
|
|
366
|
+
return { enqueue, claim, heartbeat, reap, park, approve, cancel, complete, list, leaseMs };
|
|
367
|
+
}
|
|
368
|
+
/** The deps.queueGig-shaped enqueue seam — byte-compatible with postgrestQueueGig / rpcQueueGig
|
|
369
|
+
* ({gig_id, status:'queued'}), so a caller cannot tell which backing answered (I1). */
|
|
370
|
+
export function fileQueueGig(root) {
|
|
371
|
+
const q = openLocalQueue(root);
|
|
372
|
+
return (args) => q.enqueue(args);
|
|
373
|
+
}
|
|
374
|
+
/** The deps.cancelGig-shaped seam — sibling of postgrestCancelGig ({gig_id, status:'cancelled'})
|
|
375
|
+
* (I18). */
|
|
376
|
+
export function fileCancelGig(root) {
|
|
377
|
+
const q = openLocalQueue(root);
|
|
378
|
+
return (args) => q.cancel(args);
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=local_queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local_queue.js","sourceRoot":"","sources":["../../src/local_queue.ts"],"names":[],"mappings":"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"}
|
package/dist/src/mcp.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { zodToMcpProps, AgentSchema, StandardSchema, SkillSchema, DomainTypeSchema, ChartSchema,
|
|
1
|
+
import { zodToMcpProps, AgentSchema, StandardSchema, SkillSchema, DomainTypeSchema, ChartSchema, VenueObjectSchema, OrgMemberSchema } from "./genome_schema.js";
|
|
2
2
|
// A property value is either a bare JSON-schema type name (the common case) or a full schema
|
|
3
3
|
// fragment — needed for fields that are honestly nullable, e.g. a measurement the engine has no
|
|
4
4
|
// data to compute (#238). Declaring those as plain "number" is the same lie as returning 1.0.
|
|
@@ -71,7 +71,11 @@ export const MCP_TOOLS = [
|
|
|
71
71
|
// `validation_result` carries the STRUCTURED violation list (rule + detail + where), not a
|
|
72
72
|
// boolean: a chart is refused for one named reason at one named place.
|
|
73
73
|
{ slug: "chart_define", category: "build", input_schema: obj(zodToMcpProps(ChartSchema)), output_schema: obj({ chart_id: "string", chart_hash: "string", validation_result: "object" }) },
|
|
74
|
-
|
|
74
|
+
// `VenueObjectSchema`, not `VenueSchema`: the advertised surface is generated from the inner
|
|
75
|
+
// `ZodObject`'s `.shape`, which the `.superRefine`-wrapped `VenueSchema` (a `ZodEffects`) no longer
|
|
76
|
+
// exposes. The cross-field rules the refinement adds are enforced at safeParse in the handler, not
|
|
77
|
+
// advertised as arguments — so the two stay one statement of the same fact (#234).
|
|
78
|
+
{ slug: "venue_define", category: "build", input_schema: obj(zodToMcpProps(VenueObjectSchema)), output_schema: obj({ venue_id: "string", validation_result: "object" }) },
|
|
75
79
|
// #239 — `basis`/`sample_size` say WHERE the estimate came from (a measured mean of real runs,
|
|
76
80
|
// the standard's real structure, or a per-slug guess). estimated_duration_ms is the key the
|
|
77
81
|
// handler actually returns; the old `estimated_duration` was never present on a response.
|
|
@@ -120,7 +124,13 @@ export const MCP_TOOLS = [
|
|
|
120
124
|
// (dispatchTarget, src/chart.ts). A chart dispatch's reply carries the ARRANGEMENT's manifest —
|
|
121
125
|
// chart_hash, the per-movement roll-up, cumulative spend against the envelope — where a standard
|
|
122
126
|
// dispatch carries the run's.
|
|
123
|
-
|
|
127
|
+
// #20 — `gig_input_omitted` is the caller's statement that it passed NO payload (distinct from an
|
|
128
|
+
// explicit `input: {}`). On an approve-only resume whose every remaining chair is human, an omitted
|
|
129
|
+
// payload inherits the checkpoint's recorded gig_input_sha instead of drifting to sha256('{}') and
|
|
130
|
+
// refusing; a supplied `input` leaves this false, so a disagreeing payload still gates
|
|
131
|
+
// (src/runtime.ts). The handler reads it, so #234 requires it be advertised here — it is a real
|
|
132
|
+
// control, not an internal signal, and a caller must be able to find it.
|
|
133
|
+
{ slug: "gig_dispatch", category: "run", input_schema: obj({ standard_slug: "string", chart_slug: "string", venue: "string", repo_url: "string", acting_for: "string", input: "object", gig_input_omitted: "boolean", depth: "string", wait: "boolean", budget: "object", resume_gig_id: "string", reuse: "boolean", approvals: "object", approved_by: "string" }), output_schema: obj({ gig_id: "string", status: "string", awaiting: "object", depth: "string", manifest: "object", resumed_from: "string", reuse: "boolean", resume_refused: "boolean", drift: "array" }) },
|
|
124
134
|
// `skipped_chairs` / `resumed_from` / `reuse_rejected` are the ASYNC path's only report of a
|
|
125
135
|
// saving — the manifest never reaches a caller who dispatched without `wait`.
|
|
126
136
|
// `awaiting` names the human chair a parked run stopped at. On the async path — the DEFAULT
|
|
@@ -146,12 +156,35 @@ export const MCP_TOOLS = [
|
|
|
146
156
|
// enforcement belongs); hosted routes to deps.approveGig, non-hosted has no local run to
|
|
147
157
|
// approve (a local run takes its verdicts through gig_dispatch's `approvals`).
|
|
148
158
|
{ slug: "gig_approve", category: "run", input_schema: obj({ gig_id: "string", role: "string", verdict: "object" }), output_schema: obj({ gig_id: "string", role: "string", status: "string", approved: "boolean" }) },
|
|
159
|
+
// venue_credential_mint — the verb that stands up a worker without a browser. `org_slug` scopes
|
|
160
|
+
// the credential and `instance` binds it to one host; a key with an org and no instance is the
|
|
161
|
+
// org's whole authority with nothing to bind it to. The answer carries the COMPLETE worker
|
|
162
|
+
// environment (`env`, canonical names only) — not just the key — so a caller never assembles the
|
|
163
|
+
// rest by hand or infers which URL names which host (that inference is Gap 3). `credential_classes`
|
|
164
|
+
// names what was provisioned in `VenueSchema.credential_surface` vocabulary, so a `realize` room
|
|
165
|
+
// contract can be checked against the grant before a gig is dispatched. The engine ships the schema
|
|
166
|
+
// and its refusals; a deployment wires the minting backend (deps.mintVenueCredential). There is
|
|
167
|
+
// deliberately NO read-back verb (see the venue_credential_* exact-list law) and NO authorization
|
|
168
|
+
// policy in the engine — who may mint lives in the store.
|
|
169
|
+
{ slug: "venue_credential_mint", category: "run", input_schema: obj({ org_slug: "string", instance: "string" }), output_schema: obj({ instance: "string", env: "object", credential_classes: "array", expires_at: nullable("string") }) },
|
|
170
|
+
// org_hire — the verb that ADMITS an agent to an org. The org-membership analogue of
|
|
171
|
+
// venue_credential_mint: the engine ships the schema and its refusals, a deployment wires the
|
|
172
|
+
// admission backend (deps.hireMember). `input_schema` is derived from the single Zod source
|
|
173
|
+
// (OrgMemberSchema = {org_slug, agent_slug}) — never a hand-written MCP schema — precisely so no
|
|
174
|
+
// field a capability could travel can appear on it. ADMISSION IS NOT AUTHORITY: membership is
|
|
175
|
+
// BELONGING, seating (a chair, caps, an assignment) is a SEPARATE act with its own gate, and a
|
|
176
|
+
// verb that could admit AND seat in one call would be a path to mint authority. The verb carries
|
|
177
|
+
// exactly the two fields that name the belonging and nothing more. It is intercepted in
|
|
178
|
+
// callSurfaceTool BEFORE the hosted check, so it appears in neither HOSTED_UPSERT (a hire writes
|
|
179
|
+
// no genome file) nor HOSTED_BLOCKED (it is callable in hosted mode); a successful hire is sealed
|
|
180
|
+
// to the ledger as a kind:"genome_mutation" row, so who-hired-whom is auditable, not only a store row.
|
|
181
|
+
{ slug: "org_hire", category: "run", input_schema: obj(zodToMcpProps(OrgMemberSchema)), output_schema: obj({ org_slug: "string", agent_slug: "string" }) },
|
|
149
182
|
// #234 — `gig_id`, `agent_slug` and `phase` were read by the handler and advertised nowhere.
|
|
150
183
|
// This one had teeth: a skill prompt written against this schema omits `gig_id`, the handler
|
|
151
184
|
// defaults it to "", and the sealed output lands in the store attached to NO gig. A live run
|
|
152
185
|
// of the consuming product produced 509 such orphans. The provenance chain is the engine's
|
|
153
186
|
// core promise, and the field that anchors an output to its run was undiscoverable.
|
|
154
|
-
{ slug: "output_write", category: "run", input_schema: obj({ core_type: "string", primitive: "string", domain_type: "string", domain_type_version: "number", domain: "string", data: "object", input_refs: "array", refs: "array", gig_id: "string", agent_slug: "string", phase: "string", cost_usd: "number", tokens_used: "number", duration_ms: "number", model: "string", model_tier: "string" }), output_schema: obj({ output_id: "string", primitive: "string", output: "object", validation_result: "object", validated: "boolean" }) },
|
|
187
|
+
{ slug: "output_write", category: "run", input_schema: obj({ core_type: "string", primitive: "string", domain_type: "string", domain_type_version: "number", domain: "string", data: "object", input_refs: "array", refs: "array", gig_id: "string", agent_slug: "string", phase: "string", cost_usd: "number", tokens_used: "number", duration_ms: "number", model: "string", model_tier: "string" }), output_schema: obj({ output_id: "string", primitive: "string", output: "object", validation_result: "object", validated: "boolean", sealed: "boolean" }) },
|
|
155
188
|
{ slug: "agent_validate_pipeline", category: "improve", input_schema: obj({ agents: "array", standard_slug: "string", slug: "string", domain: "string", primitives: "array", phases: "array" }), output_schema: obj({ valid: "boolean", graph: "object", unsatisfied_inputs: "array", illegal_progressions: "array" }) },
|
|
156
189
|
// #238 — success_rate and trend are NULLABLE, because the engine genuinely cannot compute
|
|
157
190
|
// them (a failed gig writes no ledger row, so the denominator does not exist) and a hardcoded
|