Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion dispatcher/codex-local/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
`ir.json` as every other back-end; it does not read profile YAML and it does not route through the
Claude SDK dispatcher.

The one-shot capability profile remains deliberately Setup-only:
The one-shot capability profile supports Setup and a closed generic assertion contract. The
assertion arm is selected solely from complete IR anatomy (`assertive` / `tool` / `scheduled` /
`assertion`) plus exact capability and guardrail closure, never from a profile, component ID, or
verb. It is deliberately stateless: each external/manual activation supplies one invocation and
Warble returns one result. It owns no scheduler, cron/launchd entry, automation registry,
notification destination, run history, Codex Scheduled task, or persistent thread.

Setup keeps its existing profile:

- analytical `skill` realization;
- `one_shot` trigger and `none` outcome;
Expand Down Expand Up @@ -117,6 +124,29 @@ explicit tier bindings and purpose-built Wren MCP tools. `answer_query` or `gene
select the analytical contract; the latter runs strong planning followed by cheap composition and
emits a `render_artifact` event before its terminal answer:

For an assertion, the scheduler (or manual caller) stays the trusted activation authority and must
first execute the read-only Wren operation. The invocation JSON records that successful operation,
its model/timestamp/timing evidence. The effective model and cadence are read only from compiled,
pinned IR `binds`; the caller may not override them. `source: "wren"` is only a typed caller
claim, not cryptographic provenance; a deployment needing independent attestation must validate or
sign the envelope before calling Warble. A fresh reading never starts Codex. A stale reading starts
one ephemeral cheap-model severity turn with no MCP tools; Warble validates `warn`/`critical` plus a
bounded rationale, assembles the verdict, and returns the IR-declared signal for caller-owned
routing:

```bash
node dist/cli.js dispatch ../../examples/monitor-agent/ir.golden.json \
--component monitor_freshness --cheap-model <cheap-model> \
--invocation '{
"activation":{"authority":"external","kind":"scheduled","occurrence_id":"run-1","occurred_at":"2026-08-17T12:00:00Z"},
"evidence":{"source":"wren","operation":"read_only_sql","success":true,"read_only":true,"model":"orders","timestamp_column":"updated_at","observed_at":"2026-08-17T12:00:00Z","latest_timestamp":"2026-08-15T12:00:00Z"}
}'
```

The returned `freshness_breach` is a signal for the caller's notification transport; this package
does not persist or deliver it. Event-triggered, gated-tool/mutating, incomplete/ambiguous, or
capability-incomplete shapes wall-hit before Codex starts.

```bash
node dist/cli.js manifest ../../genbi-default/ir.golden.json \
--component answer_query \
Expand Down
253 changes: 253 additions & 0 deletions dispatcher/codex-local/src/assertion_prepare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import { CodexDispatchError } from "./error.js";
import { assertDispatchableComponentIdentity } from "./dispatch_registry.js";
import {
parseIr,
SUPPORTED_IR_VERSION,
TARGET,
type ComponentNode,
type WarbleIr,
} from "./ir.js";
import {
ASSERTION_CAPABILITIES,
type CapabilityResolution,
guardrailMatches,
hasExactCapabilities,
resolveAssertionCapabilities,
} from "./target_profile.js";

export interface AssertionWhenGuard {
guard: "on_flag";
target: string;
}

export interface PreparedAssertionStep {
name: string;
tier: "cheap";
model: string;
prompt: string;
consumes: [string];
produces: string;
when: AssertionWhenGuard;
}

export interface PreparedAssertionComponent {
target: typeof TARGET;
profile: string;
node: ComponentNode;
componentId: string;
modelBinding: string;
cadenceBinding: string;
/** Effective compiler-resolved values from IR `binds`, never caller overrides. */
pinnedModel: string;
pinnedCadenceMs: number;
step: PreparedAssertionStep;
capabilities: CapabilityResolution[];
verdictType: string;
emittedSignals: string[];
}

export interface PrepareAssertionInput {
ir: string | WarbleIr;
component: string;
/** Concrete cheap-model binding. No persistent Codex home/session is involved. */
model: string;
}

export function parseDurationMs(value: string, field = "duration"): number {
const match = /^(\d+)(ms|s|m|h|d)$/.exec(value.trim());
if (!match) throw new CodexDispatchError(`${field} must be a positive duration such as '24h'`);
const amount = Number(match[1]);
const unit = match[2]!;
const multiplier = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 }[unit]!;
const result = amount * multiplier;
if (!Number.isSafeInteger(result) || result <= 0) {
throw new CodexDispatchError(`${field} must be a bounded positive duration`);
}
return result;
}

function hasStatusRenderBlock(node: ComponentNode): boolean {
return (
node.effect.render_blocks.length === 1 &&
typeof node.effect.render_blocks[0] === "object" &&
node.effect.render_blocks[0] !== null &&
!Array.isArray(node.effect.render_blocks[0]) &&
(node.effect.render_blocks[0] as Record<string, unknown>)["type"] === "status"
);
}

function validateAssertionShape(node: ComponentNode): {
modelBinding: string;
cadenceBinding: string;
pinnedModel: string;
pinnedCadenceMs: number;
} {
assertDispatchableComponentIdentity(node);
if (
node.type !== "assertive" ||
node.realization_kind !== "tool" ||
node.trigger.kind !== "scheduled" ||
node.effect.outcome.kind !== "assertion"
) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: requires assertive/tool/scheduled/assertion`,
);
}
if (node.context_binding.binding_mode !== "pinned") {
throw new CodexDispatchError(`component '${node.id}' wall-hit: requires a pinned context binding`);
}
if (
typeof node.effect.outcome.verdict_type !== "string" ||
node.effect.outcome.verdict_type.trim().length === 0 ||
!Array.isArray(node.effect.outcome.emits) ||
node.effect.outcome.emits.length === 0 ||
node.effect.outcome.emits.some((signal) => signal.trim().length === 0) ||
new Set(node.effect.outcome.emits).size !== node.effect.outcome.emits.length ||
!hasStatusRenderBlock(node)
) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: assertion requires verdict_type, unique emitted signals, and one status block`,
);
}
if (!hasExactCapabilities(node.required_capabilities, ASSERTION_CAPABILITIES)) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: supports exactly scheduler, sql_execution:read_only, notify_channel, and llm:cheap capabilities`,
);
}
if (
node.guardrails.length !== 2 ||
!guardrailMatches(node.guardrails[0], "read_only_execution", { requireScopeAbsent: true }) ||
!guardrailMatches(node.guardrails[1], "alert_routing", { requireScopeAbsent: true })
) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: requires locked read_only_execution and unlocked alert_routing guardrails`,
);
}
if (
node.precondition_result === null ||
node.precondition_result.status !== "pass" ||
node.precondition_result.checks.length === 0 ||
node.precondition_result.checks.some((check) => check.outcome !== "pass")
) {
throw new CodexDispatchError(`component '${node.id}' wall-hit: assertion preconditions must be resolved pass checks`);
}
const modelParams = node.params.filter((param) => param.bind === "required");
const cadenceParams = node.params.filter(
(param) => param.bind === "optional" && typeof param.default === "string",
);
if (modelParams.length !== 1 || cadenceParams.length !== 1) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: requires exactly one required model binding and one optional cadence binding`,
);
}
// The parameter default is only the authored fallback. `binds` is the compiler's effective
// pinned profile map (for example genbi-monitor binds 48h over a 24h component default), and
// therefore is the only value the runtime may execute.
parseDurationMs(cadenceParams[0]!.default as string, `${node.id}.${cadenceParams[0]!.name}`);
if (node.binds === null) {
throw new CodexDispatchError(`component '${node.id}' wall-hit: pinned assertion requires effective binds`);
}
const modelBinding = modelParams[0]!.name;
const cadenceBinding = cadenceParams[0]!.name;
const bindNames = Object.keys(node.binds);
if (
bindNames.length !== 2 ||
!Object.prototype.hasOwnProperty.call(node.binds, modelBinding) ||
!Object.prototype.hasOwnProperty.call(node.binds, cadenceBinding) ||
typeof node.binds[modelBinding] !== "string" ||
node.binds[modelBinding].trim().length === 0 ||
typeof node.binds[cadenceBinding] !== "string"
) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: effective binds must contain exactly the pinned model and cadence strings`,
);
}
const pinnedCadenceMs = parseDurationMs(
node.binds[cadenceBinding] as string,
`${node.id}.binds.${cadenceBinding}`,
);
if (node.llm_calls.length !== 1) {
throw new CodexDispatchError(`component '${node.id}' wall-hit: assertion supports exactly one conditional cheap step`);
}
const step = node.llm_calls[0]!;
const when = step.when;
if (
!step.conditional ||
step.tier !== "cheap" ||
step.produces === null ||
step.consumes.length !== 1 ||
typeof when !== "object" ||
when === null ||
Array.isArray(when) ||
(when as Record<string, unknown>)["guard"] !== "on_flag" ||
typeof (when as Record<string, unknown>)["target"] !== "string" ||
(when as Record<string, string>)["target"] !== `${step.consumes[0]}.stale`
) {
throw new CodexDispatchError(
`component '${node.id}' wall-hit: assertion requires a cheap on_flag(<consumed freshness reading>.stale) step`,
);
}
return {
modelBinding,
cadenceBinding,
pinnedModel: node.binds[modelBinding] as string,
pinnedCadenceMs,
};
}

export function matchesAssertionContractShape(node: ComponentNode): boolean {
try {
validateAssertionShape(node);
return true;
} catch (error) {
if (error instanceof CodexDispatchError) return false;
throw error;
}
}

export function assertionContractMismatchReason(node: ComponentNode): string | null {
try {
validateAssertionShape(node);
return null;
} catch (error) {
if (error instanceof CodexDispatchError) return error.message;
throw error;
}
}

export function prepareAssertion(input: PrepareAssertionInput): PreparedAssertionComponent {
if (input.model.trim().length === 0) throw new CodexDispatchError("assertion model binding must not be empty");
const ir = typeof input.ir === "string" ? parseIr(input.ir) : input.ir;
if (ir.warble_ir_version !== SUPPORTED_IR_VERSION) {
throw new CodexDispatchError(
`unsupported warble_ir_version '${ir.warble_ir_version}' (supported: ${SUPPORTED_IR_VERSION})`,
);
}
const node = ir.components.find((candidate) => candidate.id === input.component);
if (!node) throw new CodexDispatchError(`component '${input.component}' was not found in profile '${ir.profile}'`);
const shape = validateAssertionShape(node);
const call = node.llm_calls[0]!;
const when = call.when as Record<string, string>;
return {
target: TARGET,
profile: ir.profile,
node,
componentId: node.id,
modelBinding: shape.modelBinding,
cadenceBinding: shape.cadenceBinding,
pinnedModel: shape.pinnedModel,
pinnedCadenceMs: shape.pinnedCadenceMs,
step: {
name: call.name,
tier: "cheap",
model: input.model,
prompt: call.prompt,
consumes: [call.consumes[0]!],
produces: call.produces!,
when: { guard: "on_flag", target: when["target"]! },
},
capabilities: resolveAssertionCapabilities(node.required_capabilities),
verdictType: node.effect.outcome.verdict_type!,
emittedSignals: [...node.effect.outcome.emits!],
};
}
Loading