@polderlabs/bizar 10.23.7 → 10.23.8

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.
@@ -947,8 +947,9 @@ export function partitionStalePicks({ liveIds, pickedIds, disabledProviders }) {
947
947
  *
948
948
  * Behavior:
949
949
  * - Reads `settings.json` if present; preserves every other field.
950
- * - Writes `modelOverrides` as a sparse object for picked IDs that are
951
- * also in `liveIds`; Claude Code still dispatches each literal value.
950
+ * - Maps every recognized Claude alias into picked IDs that are also in
951
+ * `liveIds`, preventing internal alias normalization from reaching an
952
+ * unconfigured provider default.
952
953
  * - Atomic replace via temp-file + rename (matches `applyModels`).
953
954
  * - When `settingsJsonPath` is provided (tests), uses that instead of
954
955
  * `~/.claude/settings.json`.
@@ -1007,6 +1008,12 @@ export function applyModelOverrides({ settingsJsonPath, pickedIds, liveIds = [],
1007
1008
  // must be keys; configured gateway aliases are values. This also suppresses
1008
1009
  // print-mode `[claude-code:unrecognized_model]` diagnostics for Agent SDK calls.
1009
1010
  settings.modelOverrides = buildClaudeModelOverrides(synced);
1011
+ if (requiresGatewayModelDiscovery(synced)) {
1012
+ settings.env = {
1013
+ ...(settings.env || {}),
1014
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '1',
1015
+ };
1016
+ }
1010
1017
  const previousModel = typeof settings.model === 'string' ? settings.model : null;
1011
1018
  const previousContext = profiles?.[previousModel]?.limits?.contextTokens;
1012
1019
  const nextModel = synced[0] || null;
@@ -1068,8 +1075,20 @@ export function buildClaudeModelOverrides(modelIds) {
1068
1075
  const unique = [...new Set((Array.isArray(modelIds) ? modelIds : [])
1069
1076
  .filter((id) => typeof id === 'string' && id.trim())
1070
1077
  .map((id) => id.trim()))];
1071
- return Object.fromEntries(unique.slice(0, CLAUDE_MODEL_OVERRIDE_KEYS.length)
1072
- .map((id, index) => [CLAUDE_MODEL_OVERRIDE_KEYS[index], id]));
1078
+ if (unique.length === 0) return {};
1079
+ // Cover every built-in alias: the workflow runtime may normalize an Agent
1080
+ // request through one of these names, and no alias may escape to an
1081
+ // unconfigured Anthropic default.
1082
+ return Object.fromEntries(CLAUDE_MODEL_OVERRIDE_KEYS
1083
+ .map((key, index) => [key, unique[index % unique.length]]));
1084
+ }
1085
+
1086
+ /** Custom gateway IDs must be discoverable to Claude's SDK/subagent path. */
1087
+ export function requiresGatewayModelDiscovery(modelIds) {
1088
+ return (Array.isArray(modelIds) ? modelIds : []).some((id) => {
1089
+ if (typeof id !== 'string' || !id.trim()) return false;
1090
+ return !/^(?:claude(?:-|$)|anthropic(?:[./-]|$))/i.test(id.trim());
1091
+ });
1073
1092
  }
1074
1093
 
1075
1094
  /**
package/cli/doctor.mjs CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  REQUIRED_HOOKS,
43
43
  } from './commands/validate.mjs';
44
44
  import { configuredEnabledModels, listModels, resolveEndpoint } from './commands/models.mjs';
45
+ import { validateNativeWorkflowDirectory } from '../config/workflows/lib/native-contract.mjs';
45
46
 
46
47
  const REQUIRED_RULES = [
47
48
  'general.md', 'git.md', 'javascript.md', 'python.md',
@@ -157,6 +158,13 @@ async function checkHookFilesInstalled() {
157
158
  return `all ${REQUIRED_HOOKS.length} hook entrypoints installed`;
158
159
  }
159
160
 
161
+ async function checkNativeWorkflowsInstalled() {
162
+ const dir = join(claudeDir(), 'workflows');
163
+ if (!existsSync(dir)) throw new Error(`workflows dir missing: ${dir} — run \`bizar update\``);
164
+ const result = validateNativeWorkflowDirectory(dir);
165
+ return `${result.count} native workflows parser-compatible and filename-addressable`;
166
+ }
167
+
160
168
  /**
161
169
  * Lenient: passes if at least one of semble/skills/claude is on PATH.
162
170
  * These are informational — none of them are strictly required for
@@ -216,6 +224,7 @@ const CHECKS = [
216
224
  { name: 'skill-files-installed', run: checkSkillFilesInstalled },
217
225
  { name: 'rule-files-installed', run: checkRuleFilesInstalled },
218
226
  { name: 'hook-files-installed', run: checkHookFilesInstalled },
227
+ { name: 'native-workflows-valid', run: checkNativeWorkflowsInstalled },
219
228
  { name: 'tools-on-path', run: checkToolsAvailable },
220
229
  { name: 'bizar-home', run: checkBizarHome },
221
230
  { name: 'provider-reachable', run: checkProviderReachable },
package/cli/provision.mjs CHANGED
@@ -32,7 +32,12 @@ import { homedir } from 'node:os';
32
32
  import { dirname, join, resolve, sep } from 'node:path';
33
33
  import { fileURLToPath } from 'node:url';
34
34
  import { resolveBizarHome } from './config-paths.mjs';
35
- import { buildClaudeModelOverrides, configuredEnabledModels } from './commands/models.mjs';
35
+ import {
36
+ buildClaudeModelOverrides,
37
+ configuredEnabledModels,
38
+ requiresGatewayModelDiscovery,
39
+ } from './commands/models.mjs';
40
+ import { validateNativeWorkflowDirectory } from '../config/workflows/lib/native-contract.mjs';
36
41
 
37
42
  const __filename = fileURLToPath(import.meta.url);
38
43
  const __dirname = dirname(__filename);
@@ -921,7 +926,6 @@ export function writeClaudeSettings({ dryRun = false, force = false } = {}) {
921
926
  pickEnv('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS')
922
927
  || shipped.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS
923
928
  || '1',
924
- ...(pickEnv('ANTHROPIC_MODEL') ? { ANTHROPIC_MODEL: pickEnv('ANTHROPIC_MODEL') } : {}),
925
929
  },
926
930
  hooks: {
927
931
  UserPromptSubmit: [{ hooks: [hook('user-prompt-submit', 10)] }],
@@ -968,6 +972,10 @@ export function writeClaudeSettings({ dryRun = false, force = false } = {}) {
968
972
  if (installModel) {
969
973
  bizarSettings.model = installModel;
970
974
  bizarSettings.modelOverrides = buildClaudeModelOverrides(installModels);
975
+ bizarSettings.env.ANTHROPIC_MODEL = installModel;
976
+ if (operatorGatewayUrl && requiresGatewayModelDiscovery(installModels)) {
977
+ bizarSettings.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = '1';
978
+ }
971
979
  const configuredContext = pickEnv('CLAUDE_CODE_MAX_CONTEXT_TOKENS') || installContextTokens;
972
980
  if (configuredContext) bizarSettings.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(configuredContext);
973
981
  } else {
@@ -1013,6 +1021,9 @@ export function writeClaudeSettings({ dryRun = false, force = false } = {}) {
1013
1021
  if (installModel) {
1014
1022
  merged.model = installModel;
1015
1023
  merged.modelOverrides = bizarSettings.modelOverrides;
1024
+ merged.env = { ...(merged.env || {}), ANTHROPIC_MODEL: installModel };
1025
+ } else if (merged.env && typeof merged.env === 'object') {
1026
+ delete merged.env.ANTHROPIC_MODEL;
1016
1027
  }
1017
1028
 
1018
1029
  // Auto-compaction is part of the Bizar reliability contract. Remove legacy
@@ -1348,9 +1359,9 @@ export async function syncConfigExtras({ dryRun = false } = {}) {
1348
1359
  const workflowsSrc = join(REPO_ROOT, 'config', 'workflows');
1349
1360
  if (existsSync(workflowsSrc)) {
1350
1361
  const workflowsDst = join(CLAUDE_DIR, 'workflows');
1362
+ validateNativeWorkflowDirectory(workflowsSrc);
1351
1363
  await copyDirIfExists(workflowsSrc, workflowsDst);
1352
- counts.workflows = readdirSync(workflowsSrc, { withFileTypes: true })
1353
- .filter((entry) => entry.isFile() && entry.name.endsWith('.js')).length;
1364
+ counts.workflows = validateNativeWorkflowDirectory(workflowsDst).count;
1354
1365
  }
1355
1366
 
1356
1367
  return { ok: true, message: `synced (${counts.commands} commands, ${counts.skills} skills, ${counts.hooks} hooks, ${counts.rules} rules, ${counts.workflows} workflows)`, counts };
@@ -1374,7 +1385,6 @@ export const FORCE_CLEAN_PRESERVE_ENV_KEYS = Object.freeze([
1374
1385
  'ANTHROPIC_AUTH_TOKEN',
1375
1386
  'BIZAR_MODEL_ROUTER_URL',
1376
1387
  'BIZAR_HOME',
1377
- 'ANTHROPIC_MODEL',
1378
1388
  'CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY',
1379
1389
  'CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS',
1380
1390
  'CLAUDE_CODE_MAX_CONTEXT_TOKENS',
@@ -29,6 +29,23 @@ version-sensitive claims. Inspect installed skills before hard or specialized
29
29
  work; if stuck with no match, search skills.sh and review the candidate before
30
30
  proposing installation.
31
31
 
32
+ Before every Workflow call, read the global Bizar model router and construct a
33
+ small `args.routing` object whose `default`, `medium`, and `high` values are
34
+ explicit enabled configured model IDs (user picks win; otherwise use enabled
35
+ tier candidates). Include the user's task in the same args object under the
36
+ workflow's documented task field. Never pass only a string and never use
37
+ `inherit`, `sonnet`, `opus`, or another provider default. If no configured ID
38
+ exists, stop and ask the operator to run `bizar models`.
39
+
40
+ Invoke the selected workflow by `name` first. If Claude reports that the Bizar
41
+ name is unavailable, resolve the active Claude config directory and retry once
42
+ with the absolute installed `scriptPath` at
43
+ `<CLAUDE_CONFIG_DIR>/workflows/<name>.js` (normally
44
+ `~/.claude/workflows/<name>.js`). Never retry a bare filename or a repository
45
+ relative path. If that file is missing or invalid, stop with `bizar update`
46
+ and `bizar doctor` as the repair commands; do not improvise a primary-session
47
+ implementation around a broken workflow installation.
48
+
32
49
  ## Models
33
50
 
34
51
  For every Agent call, select the cheapest sufficient enabled configured model
@@ -36,11 +36,11 @@
36
36
  * validated by `pickFailover` against the same registry.
37
37
  * - The hard deny of out-of-pool models STILL APPLIES when
38
38
  * `routingDecisionId` is absent — the contract is opt-in.
39
- * - Registry load failures still fail open (F-176 advisory): the orchestrator
40
- * already chose a model, let Claude Code validate it once.
39
+ * - Registry load failures fail closed: allowing the dispatch would hand
40
+ * model choice back to Claude Code's unconfigured provider default.
41
41
  *
42
- * The contract is intentionally additive. Existing callers that only pass
43
- * `model` see no behavior change.
42
+ * Callers that pass only `model` are accepted only when that literal ID is in
43
+ * the enabled configured pool. Missing or inherited model selection is denied.
44
44
  */
45
45
 
46
46
  import { readFileSync } from 'node:fs';
@@ -48,16 +48,6 @@ import { pathToFileURL } from 'node:url';
48
48
 
49
49
  import { loadModelRouter } from '../../../config/agents/model-assignment.mjs';
50
50
 
51
- function advise(reason) {
52
- return {
53
- hookSpecificOutput: {
54
- hookEventName: 'PreToolUse',
55
- permissionDecision: 'allow',
56
- additionalContext: `🟡 Model override guidance: ${reason} The dispatch will proceed regardless.`,
57
- },
58
- };
59
- }
60
-
61
51
  function deny(reason) {
62
52
  return {
63
53
  hookSpecificOutput: {
@@ -72,8 +62,6 @@ function deny(reason) {
72
62
  * 10.22.0 / Phase 4: extract the operator's `disabledProviders` list from
73
63
  * the loaded registry. Whitespace-trimmed + lowercased at read time.
74
64
  * Returns `[]` for legacy configs that lack the key (no in-code default).
75
- * The hook MUST fail open on parse errors — the orchestrator already
76
- * chose a model; let Claude Code validate it once.
77
65
  */
78
66
  function readDisabledProvidersFromRegistry(registry) {
79
67
  if (!registry || typeof registry !== 'object') return [];
@@ -90,14 +78,15 @@ function readDisabledProvidersFromRegistry(registry) {
90
78
  }
91
79
 
92
80
  /**
93
- * 10.22.0 / Phase 4: case-sensitive prefix filter against the (lowercase)
94
- * disabled list. Empty / missing prefix list is a no-op (returns input).
81
+ * 10.22.0 / Phase 4: case-insensitive prefix filter against the normalized
82
+ * disabled list. Empty / missing prefix list is a no-op.
95
83
  */
96
84
  function isDisabledId(id, prefixes) {
97
85
  if (!Array.isArray(prefixes) || prefixes.length === 0) return false;
98
86
  if (typeof id !== 'string' || !id) return false;
87
+ const normalized = id.toLowerCase();
99
88
  for (const p of prefixes) {
100
- if (typeof p === 'string' && p && id.startsWith(p)) return true;
89
+ if (typeof p === 'string' && p && normalized.startsWith(p)) return true;
101
90
  }
102
91
  return false;
103
92
  }
@@ -171,18 +160,15 @@ export async function guardAgentModel(input, options = {}) {
171
160
  const failoverBlock = readFailoverBlock(toolInput);
172
161
  const hasFailoverContract = Boolean(failoverBlock.routingDecisionId) && Boolean(failoverBlock.fallback);
173
162
 
174
- // The workflow dispatcher enforces a model override. This permissive branch
175
- // keeps the hook compatible with non-Bizar Agent callers.
176
- if (!requested) return {};
177
- if (requested === 'inherit') return {};
163
+ if (!requested || requested === 'inherit') {
164
+ return deny('Bizar Agent dispatch blocked: every Agent call requires an explicit enabled model from `bizar models`; session/provider inheritance is prohibited.');
165
+ }
178
166
 
179
167
  let registry;
180
168
  try {
181
169
  registry = options.registry || loadModelRouter(options.routerPath);
182
170
  } catch {
183
- // A broken optional router must not strand subagents. The caller already
184
- // chose a model; let Claude Code/provider validate it once.
185
- return {};
171
+ return deny('Bizar Agent dispatch blocked: the global model router is missing or invalid. Run `bizar models`, then retry.');
186
172
  }
187
173
 
188
174
  // Disabled is an enforceable operator boundary. Falling through here would
@@ -201,17 +187,17 @@ export async function guardAgentModel(input, options = {}) {
201
187
  // already computed by `pickFailover` against this same registry.
202
188
  if (hasFailoverContract) {
203
189
  if (!userPicks.has(failoverBlock.fallback)) {
204
- return advise(`Bizar Agent dispatch: fallback ${failoverBlock.fallback} is outside the user-selected pool; select an enabled configured fallback.`);
190
+ return deny(`Bizar Agent dispatch blocked: fallback ${failoverBlock.fallback} is outside the user-selected pool; select an enabled configured fallback.`);
205
191
  }
206
192
  if (!allowed.has(requested)) {
207
- return advise(`Bizar Agent dispatch blocked: model override ${requested} is outside the configured dynamic tiers and the user-selected pool. Pick it via \`bizar models\`.`);
193
+ return deny(`Bizar Agent dispatch blocked: model override ${requested} is outside the configured dynamic tiers and the user-selected pool. Pick it via \`bizar models\`.`);
208
194
  }
209
195
  // Both IDs are user-selected. Accept without re-probing the gateway.
210
196
  return {};
211
197
  }
212
198
 
213
199
  if (!allowed.has(requested)) {
214
- return advise(`Bizar Agent dispatch blocked: model override ${requested} is outside the configured dynamic tiers and the user-selected pool. Pick it via \`bizar models\`.`);
200
+ return deny(`Bizar Agent dispatch blocked: model override ${requested} is outside the configured dynamic tiers and the user-selected pool. Pick it via \`bizar models\`.`);
215
201
  }
216
202
 
217
203
  // User-selected models bypass live-discovery validation. The picker is the
@@ -220,7 +206,7 @@ export async function guardAgentModel(input, options = {}) {
220
206
  if (!fromUserPick && Array.isArray(options.availableModelIds)) {
221
207
  const available = new Set(options.availableModelIds);
222
208
  if (!available.has(requested)) {
223
- return advise(`Bizar Agent dispatch blocked: ${requested} was not reported by live discovery. Select another enabled configured model; do not retry aliases.`);
209
+ return deny(`Bizar Agent dispatch blocked: ${requested} was not reported by live discovery. Select another enabled configured model; do not retry aliases.`);
224
210
  }
225
211
  }
226
212
 
@@ -122,6 +122,10 @@ function filterDisabled(ids, disabled) {
122
122
  return kept;
123
123
  }
124
124
 
125
+ function requiresGatewayModelDiscovery(modelIds) {
126
+ return modelIds.some((id) => !/^(?:claude(?:-|$)|anthropic(?:[./-]|$))/i.test(id));
127
+ }
128
+
125
129
  function readSettingsPath() {
126
130
  return join(resolveClaudeConfigDir(), 'settings.json');
127
131
  }
@@ -234,8 +238,14 @@ function syncOnce() {
234
238
  ];
235
239
  settings.modelPicker = { options };
236
240
  settings.modelOverrides = Object.fromEntries(
237
- liveIds.slice(0, overrideKeys.length).map((id, index) => [overrideKeys[index], id]),
241
+ overrideKeys.map((key, index) => [key, liveIds[index % liveIds.length]]),
238
242
  );
243
+ if (requiresGatewayModelDiscovery(liveIds)) {
244
+ settings.env = {
245
+ ...(settings.env || {}),
246
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '1',
247
+ };
248
+ }
239
249
 
240
250
  let modelChanged = false;
241
251
  if (typeof settings.model !== 'string' || !liveIds.includes(settings.model)) {
@@ -33,14 +33,18 @@ export function workflowCompletedSuccessfully(input) {
33
33
  const root = input?.tool_response ?? input?.tool_result ?? input?.toolUseResult;
34
34
  if (!root || typeof root !== 'object') return false;
35
35
  const statuses = [];
36
+ let nestedError = false;
37
+ let acceptedLaunch = false;
36
38
  const visit = (value, depth = 0) => {
37
39
  if (!value || typeof value !== 'object' || depth > 3) return;
38
40
  if (typeof value.status === 'string') statuses.push(value.status.toLowerCase());
41
+ if (value.is_error === true || value.error) nestedError = true;
42
+ if (value.status === 'async_launched' && value.taskType === 'local_workflow') acceptedLaunch = true;
39
43
  for (const key of ['result', 'workflow', 'data']) visit(value[key], depth + 1);
40
44
  };
41
45
  visit(root);
42
- if (root.is_error === true || root.error || statuses.some((status) => WORKFLOW_FAILURE.has(status))) return false;
43
- return statuses.some((status) => WORKFLOW_SUCCESS.has(status));
46
+ if (nestedError || statuses.some((status) => WORKFLOW_FAILURE.has(status))) return false;
47
+ return acceptedLaunch || statuses.some((status) => WORKFLOW_SUCCESS.has(status));
44
48
  }
45
49
 
46
50
  let raw = '';
@@ -1,6 +1,3 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { dispatchAgent, writeArtifact, barrierRef } from './lib/dispatch.js'
3
-
4
1
  export const meta = {
5
2
  name: 'bizar-debug',
6
3
  description: 'Root-cause a bug with bounded loop-until-dry: RCA hypothesis, adversarial verification, smallest fix, regression test',
@@ -14,6 +11,44 @@ export const meta = {
14
11
  ],
15
12
  }
16
13
 
14
+ const WORKFLOW_INPUT = args && typeof args === 'object' ? args : {}
15
+ const WORKFLOW_ROUTING = WORKFLOW_INPUT.routing && typeof WORKFLOW_INPUT.routing === 'object'
16
+ ? WORKFLOW_INPUT.routing
17
+ : {}
18
+ const WORKFLOW_DEFAULT_MODEL = typeof WORKFLOW_INPUT.model === 'string'
19
+ ? WORKFLOW_INPUT.model.trim()
20
+ : Array.isArray(WORKFLOW_INPUT.models) && typeof WORKFLOW_INPUT.models[0] === 'string'
21
+ ? WORKFLOW_INPUT.models[0].trim()
22
+ : ''
23
+ const routeModel = (risk) => {
24
+ const candidate = WORKFLOW_ROUTING[risk] || WORKFLOW_ROUTING.default || WORKFLOW_DEFAULT_MODEL
25
+ return typeof candidate === 'string' ? candidate.trim() : ''
26
+ }
27
+ if (!routeModel('medium') || !routeModel('high')) {
28
+ return {
29
+ status: 'blocked',
30
+ reason: 'No explicit configured Bizar model routing was supplied. Read the global Bizar model router and retry with args.routing; provider defaults are prohibited.',
31
+ }
32
+ }
33
+ let WORKFLOW_DISPATCH_SEQUENCE = 0
34
+ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
35
+ const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
36
+ const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
37
+ const agentOptions = {
38
+ model: routeModel(opts.risk || 'medium'),
39
+ effort: opts.risk === 'high' ? 'high' : 'medium',
40
+ }
41
+ if (opts.schema) agentOptions.schema = opts.schema
42
+ if (opts.isolation) agentOptions.isolation = opts.isolation
43
+ if (opts.disallowedTools) agentOptions.disallowedTools = opts.disallowedTools
44
+ return agentFn(`${prefix}\n${prompt}`, agentOptions)
45
+ }
46
+ const barrierRef = ({ phase, label, summary, payload }) => {
47
+ let evidence = ''
48
+ try { evidence = JSON.stringify(payload ?? '').slice(0, 12000) } catch { evidence = '<unserializable>' }
49
+ return { promptBlock: `Prior phase: ${phase}; label: ${label}; summary: ${summary || ''}\nBounded evidence: ${evidence}` }
50
+ }
51
+
17
52
  const BUG_ID = typeof args === 'string'
18
53
  ? args
19
54
  : (args && typeof args.bug_id === 'string')
@@ -22,9 +57,7 @@ const BUG_ID = typeof args === 'string'
22
57
  ? args.topic
23
58
  : JSON.stringify(args || {})
24
59
 
25
- // Phase B (v10.21.0) artifact-on-disk barriers: one runId per workflow
26
- // invocation. Used by every writeArtifact() + barrierRef() in this script.
27
- const RUN_ID = randomUUID()
60
+ const RUN_ID = 'bizar-debug'
28
61
 
29
62
  const HYPOTHESIS = {
30
63
  type: 'object',
@@ -48,8 +81,6 @@ const iterations = []
48
81
  phase('Hypothesis')
49
82
  const initial = await dispatchAgent(agent, 'rca-hypothesis', `Root-cause bug ${BUG_ID} with the cheapest discriminating experiment. Return {cause, experiment, predictedOutcome}. Do not propose a fix yet.`, { role: 'research-analyst', risk: 'medium', capabilities: ['structured-output', 'reasoning'], label: 'hypothesis:initial', phase: 'Hypothesis', schema: HYPOTHESIS })
50
83
  iterations.push(initial)
51
- // Phase B: persist initial hypothesis artifact.
52
- writeArtifact({ runId: RUN_ID, phase: 'Hypothesis', label: 'hypothesis:initial', payload: initial, summary: initial?.cause ? initial.cause.slice(0, 200) : 'initial hypothesis', role: 'research-analyst' })
53
84
 
54
85
  let accepted = null
55
86
  for (let i = 0; i < MAX_ITERATIONS; i++) {
@@ -68,8 +99,6 @@ for (let i = 0; i < MAX_ITERATIONS; i++) {
68
99
  phase('Loop')
69
100
  const refined = await dispatchAgent(agent, 'rca-refiner', `The previous RCA hypothesis for bug ${BUG_ID} was not confirmed. Produce a refined hypothesis with a new cheapest discriminating experiment.\n${barrierRef({ runId: RUN_ID, phase: 'Hypothesis', label: priorLabel, summary: prior?.cause ? prior.cause.slice(0, 200) : `prior iter ${i + 1}` }).promptBlock}\n${barrierRef({ runId: RUN_ID, phase: 'AdversarialVerify', label: `verify:${i + 1}`, summary: verdict?.reason ? verdict.reason.slice(0, 200) : 'no confirmation' }).promptBlock}`, { role: 'research-analyst', risk: 'medium', capabilities: ['structured-output', 'reasoning'], label: `refine:${i + 1}`, phase: 'Loop', schema: HYPOTHESIS })
70
101
  iterations.push(refined)
71
- // Phase B: persist refined hypothesis artifact.
72
- writeArtifact({ runId: RUN_ID, phase: 'Hypothesis', label: `refine:${i + 1}`, payload: refined, summary: refined?.cause ? refined.cause.slice(0, 200) : `refined iter ${i + 1}`, role: 'research-analyst' });
73
102
  }
74
103
 
75
104
  if (!accepted) {
@@ -83,7 +112,6 @@ if (!accepted) {
83
112
 
84
113
  phase('Fix')
85
114
  const fix = await dispatchAgent(agent, 'fix-author', `Implement the smallest fix + regression test for bug ${BUG_ID} based on the accepted hypothesis. Edit and test in your isolated worktree. Do not commit, push, publish, or deploy.\n${barrierRef({ runId: RUN_ID, phase: 'Hypothesis', label: 'hypothesis:initial', summary: accepted.hypothesis?.cause ? accepted.hypothesis.cause.slice(0, 200) : 'accepted hypothesis' }).promptBlock}`, { role: 'implementer', risk: 'medium', capabilities: ['structured-output', 'reasoning'], label: 'fix', phase: 'Fix', isolation: 'worktree' })
86
- writeArtifact({ runId: RUN_ID, phase: 'Fix', label: 'fix', payload: fix, summary: typeof fix === 'string' ? fix.slice(0, 200) : 'fix proposed', role: 'implementer' })
87
115
 
88
116
  phase('Verify')
89
117
  const verify = await dispatchAgent(agent, 'fix-verifier', `Re-check the proposed fix for bug ${BUG_ID} against the regression test and adjacent paths. Reject the fix if it is unbounded, out of scope, or already covered.\n${barrierRef({ runId: RUN_ID, phase: 'Fix', label: 'fix', summary: typeof fix === 'string' ? fix.slice(0, 200) : 'fix artifact' }).promptBlock}`, { role: 'adversarial', risk: 'high', capabilities: ['structured-output', 'reasoning'], label: 'fix-verify', phase: 'Verify' })
@@ -1,5 +1,3 @@
1
- import { dispatchAgent } from './lib/dispatch.js'
2
-
3
1
  export const meta = {
4
2
  name: 'bizar-implement',
5
3
  description: 'Implement one bounded change in one worktree, or run explicitly supplied disjoint lanes concurrently',
@@ -9,6 +7,44 @@ export const meta = {
9
7
  ],
10
8
  }
11
9
 
10
+ const WORKFLOW_INPUT = args && typeof args === 'object' ? args : {}
11
+ const WORKFLOW_ROUTING = WORKFLOW_INPUT.routing && typeof WORKFLOW_INPUT.routing === 'object'
12
+ ? WORKFLOW_INPUT.routing
13
+ : {}
14
+ const WORKFLOW_DEFAULT_MODEL = typeof WORKFLOW_INPUT.model === 'string'
15
+ ? WORKFLOW_INPUT.model.trim()
16
+ : Array.isArray(WORKFLOW_INPUT.models) && typeof WORKFLOW_INPUT.models[0] === 'string'
17
+ ? WORKFLOW_INPUT.models[0].trim()
18
+ : ''
19
+ const routeModel = (risk) => {
20
+ const candidate = WORKFLOW_ROUTING[risk] || WORKFLOW_ROUTING.default || WORKFLOW_DEFAULT_MODEL
21
+ return typeof candidate === 'string' ? candidate.trim() : ''
22
+ }
23
+ if (!routeModel('medium') || !routeModel('high')) {
24
+ return {
25
+ status: 'blocked',
26
+ reason: 'No explicit configured Bizar model routing was supplied. Read the global Bizar model router and retry with args.routing; provider defaults are prohibited.',
27
+ }
28
+ }
29
+ let WORKFLOW_DISPATCH_SEQUENCE = 0
30
+ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
31
+ const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
32
+ const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
33
+ const agentOptions = {
34
+ model: routeModel(opts.risk || 'medium'),
35
+ effort: opts.risk === 'high' ? 'high' : 'medium',
36
+ }
37
+ if (opts.schema) agentOptions.schema = opts.schema
38
+ if (opts.isolation) agentOptions.isolation = opts.isolation
39
+ if (opts.disallowedTools) agentOptions.disallowedTools = opts.disallowedTools
40
+ return agentFn(`${prefix}\n${prompt}`, agentOptions)
41
+ }
42
+ const barrierRef = ({ phase, label, summary, payload }) => {
43
+ let evidence = ''
44
+ try { evidence = JSON.stringify(payload ?? '').slice(0, 12000) } catch { evidence = '<unserializable>' }
45
+ return { promptBlock: `Prior phase: ${phase}; label: ${label}; summary: ${summary || ''}\nBounded evidence: ${evidence}` }
46
+ }
47
+
12
48
  const TOPIC = typeof args === 'string'
13
49
  ? args
14
50
  : (args && typeof args.topic === 'string')
@@ -1,6 +1,3 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { dispatchAgent, writeArtifact, barrierRef } from './lib/dispatch.js'
3
-
4
1
  export const meta = {
5
2
  name: 'bizar-research',
6
3
  description: 'Research, plan, audit, and implement a bounded task across disjoint lanes with sequential pipeline verification',
@@ -14,16 +11,51 @@ export const meta = {
14
11
  ],
15
12
  }
16
13
 
14
+ const WORKFLOW_INPUT = args && typeof args === 'object' ? args : {}
15
+ const WORKFLOW_ROUTING = WORKFLOW_INPUT.routing && typeof WORKFLOW_INPUT.routing === 'object'
16
+ ? WORKFLOW_INPUT.routing
17
+ : {}
18
+ const WORKFLOW_DEFAULT_MODEL = typeof WORKFLOW_INPUT.model === 'string'
19
+ ? WORKFLOW_INPUT.model.trim()
20
+ : Array.isArray(WORKFLOW_INPUT.models) && typeof WORKFLOW_INPUT.models[0] === 'string'
21
+ ? WORKFLOW_INPUT.models[0].trim()
22
+ : ''
23
+ const routeModel = (risk) => {
24
+ const candidate = WORKFLOW_ROUTING[risk] || WORKFLOW_ROUTING.default || WORKFLOW_DEFAULT_MODEL
25
+ return typeof candidate === 'string' ? candidate.trim() : ''
26
+ }
27
+ if (!routeModel('medium') || !routeModel('high')) {
28
+ return {
29
+ status: 'blocked',
30
+ reason: 'No explicit configured Bizar model routing was supplied. Read the global Bizar model router and retry with args.routing; provider defaults are prohibited.',
31
+ }
32
+ }
33
+ let WORKFLOW_DISPATCH_SEQUENCE = 0
34
+ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
35
+ const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
36
+ const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
37
+ const agentOptions = {
38
+ model: routeModel(opts.risk || 'medium'),
39
+ effort: opts.risk === 'high' ? 'high' : 'medium',
40
+ }
41
+ if (opts.schema) agentOptions.schema = opts.schema
42
+ if (opts.isolation) agentOptions.isolation = opts.isolation
43
+ if (opts.disallowedTools) agentOptions.disallowedTools = opts.disallowedTools
44
+ return agentFn(`${prefix}\n${prompt}`, agentOptions)
45
+ }
46
+ const barrierRef = ({ phase, label, summary, payload }) => {
47
+ let evidence = ''
48
+ try { evidence = JSON.stringify(payload ?? '').slice(0, 12000) } catch { evidence = '<unserializable>' }
49
+ return { promptBlock: `Prior phase: ${phase}; label: ${label}; summary: ${summary || ''}\nBounded evidence: ${evidence}` }
50
+ }
51
+
17
52
  const TOPIC = typeof args === 'string'
18
53
  ? args
19
54
  : (args && typeof args.topic === 'string')
20
55
  ? args.topic
21
56
  : JSON.stringify(args || {})
22
57
 
23
- // Phase B (v10.21.0) artifact-on-disk barriers: one runId per workflow
24
- // invocation. Used by every writeArtifact() + barrierRef() in this script
25
- // so the on-disk store + the 3-line barrier block stay paired.
26
- const RUN_ID = randomUUID()
58
+ const RUN_ID = 'bizar-research'
27
59
 
28
60
  const BRIEF = {
29
61
  type: 'object',
@@ -65,9 +97,7 @@ const research = (await parallel([
65
97
 
66
98
  if (research.length === 0) return { status: 'blocked', reason: 'No research agent completed successfully.' }
67
99
 
68
- // Phase B: persist the research artifact for the next barrier agent.
69
100
  const researchSummary = `research lanes: ${research.map((r) => (r && r.summary) ? r.summary.slice(0, 80) : '<lane>').join(' | ')}`
70
- writeArtifact({ runId: RUN_ID, phase: 'Research', label: 'barrier', payload: research, summary: researchSummary, role: 'research-analyst' })
71
101
 
72
102
  phase('Plan')
73
103
  const plan = await dispatchAgent(agent, 'plan-author', `Design one reversible implementation for: ${TOPIC}\n${barrierRef({ runId: RUN_ID, phase: 'Research', label: 'barrier', summary: researchSummary }).promptBlock}\nReturn disjoint edit lanes. Shared root/config/lock files must have one owner. Include bounded tests and stop conditions.`, { role: 'architect', risk: 'medium', capabilities: ['structured-output', 'reasoning', 'architecture'], label: 'plan', phase: 'Plan', schema: PLAN })
@@ -75,9 +105,7 @@ if (!plan || !Array.isArray(plan.lanes) || plan.lanes.length === 0) {
75
105
  return { status: 'blocked', reason: 'Planning produced no implementation lanes.', research }
76
106
  }
77
107
 
78
- // Phase B: persist the plan artifact for the next barrier agent.
79
108
  const planSummary = `plan lanes: ${plan.lanes.map((l) => l.name).join(', ')}`
80
- writeArtifact({ runId: RUN_ID, phase: 'Plan', label: 'barrier', payload: plan, summary: planSummary, role: 'architect' })
81
109
 
82
110
  phase('Audit')
83
111
  const audit = await dispatchAgent(agent, 'plan-auditor', `Adversarially review this plan for correctness, security, conflicting file ownership, missing regression tests, and unbounded retry loops. Return a corrected plan, not commentary. Topic: ${TOPIC}\n${barrierRef({ runId: RUN_ID, phase: 'Plan', label: 'barrier', summary: planSummary }).promptBlock}`, { role: 'adversarial', risk: 'high', capabilities: ['structured-output', 'reasoning', 'architecture', 'security'], label: 'plan-audit', phase: 'Audit', schema: PLAN })
@@ -101,14 +129,6 @@ if (completed.length === 0) {
101
129
  return { status: 'blocked', reason: 'No implementation lane completed successfully.', plan: approved }
102
130
  }
103
131
 
104
- // Phase B: persist each lane's artifact for the next barrier agent.
105
- for (let i = 0; i < completed.length; i++) {
106
- const lane = lanes[i];
107
- const label = `implement:${i + 1}:${lane.name}`;
108
- const summary = `lane ${lane.name} files: ${(completed[i]?.files || []).slice(0, 5).join(', ')}`;
109
- writeArtifact({ runId: RUN_ID, phase: 'Implement', label, payload: completed[i], summary, role: 'implementer' });
110
- }
111
-
112
132
  phase('Verify')
113
133
  const reviews = await pipeline(
114
134
  completed,
@@ -119,11 +139,7 @@ const reviews = await pipeline(
119
139
  { role: 'adversarial', risk: 'high', capabilities: ['structured-output', 'reasoning'], label: `review:${index + 1}`, phase: 'Verify' },
120
140
  ),
121
141
  )
122
- // Phase B: persist review artifacts.
123
142
  const verifiedReviews = reviews.filter(Boolean);
124
- for (let i = 0; i < verifiedReviews.length; i++) {
125
- writeArtifact({ runId: RUN_ID, phase: 'Verify', label: `review:${i + 1}`, payload: verifiedReviews[i], summary: typeof verifiedReviews[i] === 'string' ? verifiedReviews[i].slice(0, 200) : `review ${i + 1}`, role: 'adversarial' });
126
- }
127
143
  const final = await dispatchAgent(agent, 'final-verifier', `Synthesize a bounded integration and verification report for topic "${TOPIC}". Do not claim success without fresh command evidence. Identify conflicts between worktrees, exact integration order, remaining gates, and any required human approvals.\n${barrierRef({ runId: RUN_ID, phase: 'Plan', label: 'barrier', summary: `approved plan with ${approved.lanes.length} lanes` }).promptBlock}\n${barrierRef({ runId: RUN_ID, phase: 'Implement', label: 'implement:summary', summary: `${completed.length} lanes complete` }).promptBlock}\n${barrierRef({ runId: RUN_ID, phase: 'Verify', label: 'review:summary', summary: `${verifiedReviews.length} reviews complete` }).promptBlock}`, { role: 'implementer', risk: 'medium', capabilities: ['structured-output', 'reasoning'], label: 'final-verification', phase: 'Verify' })
128
144
 
129
- return { status: 'ready-for-integration', topic: TOPIC, research, plan: approved, implementation: completed, reviews: reviews.filter(Boolean), final }
145
+ return { status: 'ready-for-integration', topic: TOPIC, research, plan: approved, implementation: completed, reviews: reviews.filter(Boolean), final }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * config/workflows/lib/dispatch.js — Workflow-side dispatch wrapper (F-189 / IMP-014).
2
+ * config/workflows/lib/dispatch.js — host-side dispatch and evidence utilities.
3
3
  *
4
4
  * Closes the IMP-014 P0 gap from `IMPROVEMENTS.md` line 867: every
5
5
  * `agent(...)` call emitted by native workflow scripts (`config/workflows/*.js`)
@@ -7,7 +7,7 @@
7
7
  * ride on every dispatch. The acceptance gate is verbatim: "Captured nested
8
8
  * Agent payloads contain expected models."
9
9
  *
10
- * This module is the single entry point for workflow-side dispatch. It:
10
+ * This retained host-side module:
11
11
  *
12
12
  * 1. Reads the operator's `userSelected.profiles`, the provider-health
13
13
  * snapshot, and the budget from the canonical config paths.
@@ -21,10 +21,11 @@
21
21
  * 4. Supports `dryRun: true` so the capture test can introspect
22
22
  * payloads without invoking a real agent.
23
23
  *
24
- * IMPORTANT: every workflow script in `config/workflows/*.js` MUST import
25
- * `dispatchAgent` from this module. The drift guard
26
- * `scripts/__tests__/autonomy-contract-workflow.test.mjs` fails CI when a
27
- * bare `agent(` call is reintroduced without a documented bypass.
24
+ * Claude's native workflow VM rejects static and dynamic imports. Shipped
25
+ * workflow entrypoints therefore contain a small self-contained wrapper and
26
+ * receive explicit configured model IDs through `args.routing`. This module
27
+ * remains the canonical host/test implementation for selector and evidence
28
+ * behavior outside that VM boundary.
28
29
  */
29
30
 
30
31
  import { randomUUID, createHash } from 'node:crypto';
@@ -885,10 +886,6 @@ export async function dispatchAgent(agentFn, agentName, prompt, opts = {}, conte
885
886
  return { __dispatchDecision: decision, payload: augmented };
886
887
  }
887
888
 
888
- if (opts.forceModel && typeof opts.forceModel === 'string') {
889
- augmented.model = opts.forceModel;
890
- }
891
-
892
889
  const startMs = Date.now();
893
890
  try {
894
891
  const result = await agentFn(prompt, augmented);
@@ -0,0 +1,96 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+
4
+ const META_PREFIX = 'export const meta =';
5
+
6
+ function firstStatement(source) {
7
+ return String(source).replace(/^\uFEFF/, '').trimStart();
8
+ }
9
+
10
+ function workflowParts(source) {
11
+ const normalized = firstStatement(source);
12
+ if (!normalized.startsWith(META_PREFIX)) {
13
+ throw new Error('`export const meta = { name, description, phases }` must be the FIRST statement in the script');
14
+ }
15
+
16
+ const open = normalized.indexOf('{', META_PREFIX.length);
17
+ if (open < 0) throw new Error('meta must be a pure object literal');
18
+
19
+ let depth = 0;
20
+ let quote = '';
21
+ let escaped = false;
22
+ for (let index = open; index < normalized.length; index += 1) {
23
+ const character = normalized[index];
24
+ if (escaped) {
25
+ escaped = false;
26
+ continue;
27
+ }
28
+ if (quote) {
29
+ if (character === '\\') escaped = true;
30
+ else if (character === quote) quote = '';
31
+ continue;
32
+ }
33
+ if (character === '"' || character === "'" || character === '`') {
34
+ quote = character;
35
+ continue;
36
+ }
37
+ if (character === '{') depth += 1;
38
+ if (character === '}') {
39
+ depth -= 1;
40
+ if (depth === 0) {
41
+ return {
42
+ block: normalized.slice(open, index + 1),
43
+ body: normalized.slice(index + 1).replace(/^\s*;?/, ''),
44
+ };
45
+ }
46
+ }
47
+ }
48
+ throw new Error('meta object literal is not balanced');
49
+ }
50
+
51
+ export function inspectNativeWorkflowSource(source, { expectedName } = {}) {
52
+ const { block, body } = workflowParts(source);
53
+ const name = block.match(/\bname\s*:\s*(['"])([^'"\r\n]+)\1/)?.[2];
54
+ if (!name) throw new Error('meta.name must be a non-empty string literal');
55
+ if (!/\bdescription\s*:\s*(['"])[^'"\r\n]+\1/.test(block)) {
56
+ throw new Error('meta.description must be a non-empty string literal');
57
+ }
58
+ if (!/\bphases\s*:\s*\[/.test(block)) throw new Error('meta.phases must be an array literal');
59
+ if (expectedName && name !== expectedName) {
60
+ throw new Error(`meta.name ${JSON.stringify(name)} does not match filename ${JSON.stringify(expectedName)}`);
61
+ }
62
+ if (/^\s*import\s/m.test(body) || /\bimport\s*\(/.test(body)) {
63
+ throw new Error('workflow body must be self-contained; imports are unavailable');
64
+ }
65
+ try {
66
+ Function(`return (async () => { 'use strict';\n${body}\n});`);
67
+ } catch (error) {
68
+ throw new Error(`workflow body does not compile: ${error.message}`);
69
+ }
70
+ return { name };
71
+ }
72
+
73
+ export function validateNativeWorkflowFile(path) {
74
+ const expectedName = basename(path, '.js');
75
+ try {
76
+ return inspectNativeWorkflowSource(readFileSync(path, 'utf8'), { expectedName });
77
+ } catch (error) {
78
+ throw new Error(`${path}: ${error.message}`);
79
+ }
80
+ }
81
+
82
+ export function validateNativeWorkflowDirectory(directory) {
83
+ const files = readdirSync(directory, { withFileTypes: true })
84
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.js'))
85
+ .map((entry) => join(directory, entry.name))
86
+ .sort();
87
+ if (files.length === 0) throw new Error(`${directory}: no workflow scripts found`);
88
+
89
+ const names = new Set();
90
+ for (const file of files) {
91
+ const { name } = validateNativeWorkflowFile(file);
92
+ if (names.has(name)) throw new Error(`${file}: duplicate workflow name ${JSON.stringify(name)}`);
93
+ names.add(name);
94
+ }
95
+ return { count: files.length, names: [...names] };
96
+ }
@@ -1,6 +1,3 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { dispatchAgent, writeArtifact, barrierRef } from './lib/dispatch.js'
3
-
4
1
  export const meta = {
5
2
  name: 'ultracode-research',
6
3
  description: 'Research a technical question with independent repository, documentation, architecture, and adversarial passes',
@@ -12,11 +9,47 @@ export const meta = {
12
9
  ],
13
10
  }
14
11
 
12
+ const WORKFLOW_INPUT = args && typeof args === 'object' ? args : {}
13
+ const WORKFLOW_ROUTING = WORKFLOW_INPUT.routing && typeof WORKFLOW_INPUT.routing === 'object'
14
+ ? WORKFLOW_INPUT.routing
15
+ : {}
16
+ const WORKFLOW_DEFAULT_MODEL = typeof WORKFLOW_INPUT.model === 'string'
17
+ ? WORKFLOW_INPUT.model.trim()
18
+ : Array.isArray(WORKFLOW_INPUT.models) && typeof WORKFLOW_INPUT.models[0] === 'string'
19
+ ? WORKFLOW_INPUT.models[0].trim()
20
+ : ''
21
+ const routeModel = (risk) => {
22
+ const candidate = WORKFLOW_ROUTING[risk] || WORKFLOW_ROUTING.default || WORKFLOW_DEFAULT_MODEL
23
+ return typeof candidate === 'string' ? candidate.trim() : ''
24
+ }
25
+ if (!routeModel('medium') || !routeModel('high')) {
26
+ return {
27
+ status: 'blocked',
28
+ reason: 'No explicit configured Bizar model routing was supplied. Read the global Bizar model router and retry with args.routing; provider defaults are prohibited.',
29
+ }
30
+ }
31
+ let WORKFLOW_DISPATCH_SEQUENCE = 0
32
+ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
33
+ const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
34
+ const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
35
+ const agentOptions = {
36
+ model: routeModel(opts.risk || 'medium'),
37
+ effort: opts.risk === 'high' ? 'high' : 'medium',
38
+ }
39
+ if (opts.schema) agentOptions.schema = opts.schema
40
+ if (opts.isolation) agentOptions.isolation = opts.isolation
41
+ if (opts.disallowedTools) agentOptions.disallowedTools = opts.disallowedTools
42
+ return agentFn(`${prefix}\n${prompt}`, agentOptions)
43
+ }
44
+ const barrierRef = ({ phase, label, summary, payload }) => {
45
+ let evidence = ''
46
+ try { evidence = JSON.stringify(payload ?? '').slice(0, 12000) } catch { evidence = '<unserializable>' }
47
+ return { promptBlock: `Prior phase: ${phase}; label: ${label}; summary: ${summary || ''}\nBounded evidence: ${evidence}` }
48
+ }
49
+
15
50
  const QUESTION = typeof args === 'string' ? args : args?.question || JSON.stringify(args || {})
16
51
 
17
- // Phase B (v10.21.0) artifact-on-disk barriers: one runId per workflow
18
- // invocation. Used by every writeArtifact() + barrierRef() in this script.
19
- const RUN_ID = randomUUID()
52
+ const RUN_ID = 'ultracode-research'
20
53
  const EVIDENCE = {
21
54
  type: 'object',
22
55
  required: ['claims', 'gaps'],
@@ -43,12 +76,10 @@ if (evidence.length === 0) return { status: 'blocked', reason: 'No research pass
43
76
 
44
77
  // Phase B: persist evidence artifact for the next barrier agent.
45
78
  const evidenceSummary = `${evidence.length} evidence passes with ${evidence.reduce((n, e) => n + (Array.isArray(e?.claims) ? e.claims.length : 0), 0)} total claims`
46
- writeArtifact({ runId: RUN_ID, phase: 'Research', label: 'barrier', payload: evidence, summary: evidenceSummary, role: 'research-analyst' })
47
79
 
48
80
  phase('Critique')
49
81
  const critique = await dispatchAgent(agent, 'completeness-critic', `Challenge these research claims. Identify contradictions, unread sources, outdated assumptions, and claims lacking reproducible evidence.\nQuestion: ${QUESTION}\n${barrierRef({ runId: RUN_ID, phase: 'Research', label: 'barrier', summary: evidenceSummary }).promptBlock}`, { role: 'adversarial', risk: 'high', capabilities: ['structured-output', 'reasoning'], label: 'completeness-critic', phase: 'Critique', schema: EVIDENCE })
50
- writeArtifact({ runId: RUN_ID, phase: 'Critique', label: 'barrier', payload: critique, summary: typeof critique === 'string' ? critique.slice(0, 200) : 'critique complete', role: 'adversarial' })
51
82
 
52
83
  phase('Synthesize')
53
84
  const synthesis = await dispatchAgent(agent, 'synthesis-author', `Produce a concise sourced decision brief for: ${QUESTION}. Separate verified facts, repository-specific implications, recommendation, risks, and unresolved gaps. Do not invent consensus or hide evidence gaps.\n${barrierRef({ runId: RUN_ID, phase: 'Research', label: 'barrier', summary: evidenceSummary }).promptBlock}\n${barrierRef({ runId: RUN_ID, phase: 'Critique', label: 'barrier', summary: typeof critique === 'string' ? critique.slice(0, 200) : 'critique complete' }).promptBlock}`, { role: 'architect', risk: 'medium', capabilities: ['structured-output', 'reasoning'], label: 'synthesis', phase: 'Synthesize' })
54
- return { question: QUESTION, evidence, critique, synthesis }
85
+ return { question: QUESTION, evidence, critique, synthesis }
@@ -1,6 +1,3 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { dispatchAgent, writeArtifact, barrierRef } from './lib/dispatch.js'
3
-
4
1
  export const meta = {
5
2
  name: 'ultracode-review',
6
3
  description: 'Review a change across independent dimensions and adversarially verify every finding',
@@ -11,11 +8,47 @@ export const meta = {
11
8
  ],
12
9
  }
13
10
 
11
+ const WORKFLOW_INPUT = args && typeof args === 'object' ? args : {}
12
+ const WORKFLOW_ROUTING = WORKFLOW_INPUT.routing && typeof WORKFLOW_INPUT.routing === 'object'
13
+ ? WORKFLOW_INPUT.routing
14
+ : {}
15
+ const WORKFLOW_DEFAULT_MODEL = typeof WORKFLOW_INPUT.model === 'string'
16
+ ? WORKFLOW_INPUT.model.trim()
17
+ : Array.isArray(WORKFLOW_INPUT.models) && typeof WORKFLOW_INPUT.models[0] === 'string'
18
+ ? WORKFLOW_INPUT.models[0].trim()
19
+ : ''
20
+ const routeModel = (risk) => {
21
+ const candidate = WORKFLOW_ROUTING[risk] || WORKFLOW_ROUTING.default || WORKFLOW_DEFAULT_MODEL
22
+ return typeof candidate === 'string' ? candidate.trim() : ''
23
+ }
24
+ if (!routeModel('medium') || !routeModel('high')) {
25
+ return {
26
+ status: 'blocked',
27
+ reason: 'No explicit configured Bizar model routing was supplied. Read the global Bizar model router and retry with args.routing; provider defaults are prohibited.',
28
+ }
29
+ }
30
+ let WORKFLOW_DISPATCH_SEQUENCE = 0
31
+ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
32
+ const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
33
+ const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
34
+ const agentOptions = {
35
+ model: routeModel(opts.risk || 'medium'),
36
+ effort: opts.risk === 'high' ? 'high' : 'medium',
37
+ }
38
+ if (opts.schema) agentOptions.schema = opts.schema
39
+ if (opts.isolation) agentOptions.isolation = opts.isolation
40
+ if (opts.disallowedTools) agentOptions.disallowedTools = opts.disallowedTools
41
+ return agentFn(`${prefix}\n${prompt}`, agentOptions)
42
+ }
43
+ const barrierRef = ({ phase, label, summary, payload }) => {
44
+ let evidence = ''
45
+ try { evidence = JSON.stringify(payload ?? '').slice(0, 12000) } catch { evidence = '<unserializable>' }
46
+ return { promptBlock: `Prior phase: ${phase}; label: ${label}; summary: ${summary || ''}\nBounded evidence: ${evidence}` }
47
+ }
48
+
14
49
  const TARGET = typeof args === 'string' ? args : args?.target || 'the current working diff'
15
50
 
16
- // Phase B (v10.21.0) artifact-on-disk barriers: one runId per workflow
17
- // invocation. Used by every writeArtifact() + barrierRef() in this script.
18
- const RUN_ID = randomUUID()
51
+ const RUN_ID = 'ultracode-review'
19
52
  const FINDINGS = {
20
53
  type: 'object',
21
54
  required: ['findings'],
@@ -1,6 +1,3 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { dispatchAgent, writeArtifact, barrierRef } from './lib/dispatch.js'
3
-
4
1
  export const meta = {
5
2
  name: 'ultracode',
6
3
  description: 'Research, design, implement, review, and verify a substantial engineering task with bounded multi-agent orchestration',
@@ -13,11 +10,47 @@ export const meta = {
13
10
  ],
14
11
  }
15
12
 
13
+ const WORKFLOW_INPUT = args && typeof args === 'object' ? args : {}
14
+ const WORKFLOW_ROUTING = WORKFLOW_INPUT.routing && typeof WORKFLOW_INPUT.routing === 'object'
15
+ ? WORKFLOW_INPUT.routing
16
+ : {}
17
+ const WORKFLOW_DEFAULT_MODEL = typeof WORKFLOW_INPUT.model === 'string'
18
+ ? WORKFLOW_INPUT.model.trim()
19
+ : Array.isArray(WORKFLOW_INPUT.models) && typeof WORKFLOW_INPUT.models[0] === 'string'
20
+ ? WORKFLOW_INPUT.models[0].trim()
21
+ : ''
22
+ const routeModel = (risk) => {
23
+ const candidate = WORKFLOW_ROUTING[risk] || WORKFLOW_ROUTING.default || WORKFLOW_DEFAULT_MODEL
24
+ return typeof candidate === 'string' ? candidate.trim() : ''
25
+ }
26
+ if (!routeModel('medium') || !routeModel('high')) {
27
+ return {
28
+ status: 'blocked',
29
+ reason: 'No explicit configured Bizar model routing was supplied. Read the global Bizar model router and retry with args.routing; provider defaults are prohibited.',
30
+ }
31
+ }
32
+ let WORKFLOW_DISPATCH_SEQUENCE = 0
33
+ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
34
+ const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
35
+ const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
36
+ const agentOptions = {
37
+ model: routeModel(opts.risk || 'medium'),
38
+ effort: opts.risk === 'high' ? 'high' : 'medium',
39
+ }
40
+ if (opts.schema) agentOptions.schema = opts.schema
41
+ if (opts.isolation) agentOptions.isolation = opts.isolation
42
+ if (opts.disallowedTools) agentOptions.disallowedTools = opts.disallowedTools
43
+ return agentFn(`${prefix}\n${prompt}`, agentOptions)
44
+ }
45
+ const barrierRef = ({ phase, label, summary, payload }) => {
46
+ let evidence = ''
47
+ try { evidence = JSON.stringify(payload ?? '').slice(0, 12000) } catch { evidence = '<unserializable>' }
48
+ return { promptBlock: `Prior phase: ${phase}; label: ${label}; summary: ${summary || ''}\nBounded evidence: ${evidence}` }
49
+ }
50
+
16
51
  const TASK = typeof args === 'string' ? args : args?.task || JSON.stringify(args || {})
17
52
 
18
- // Phase B (v10.21.0) artifact-on-disk barriers: one runId per workflow
19
- // invocation. Used by every writeArtifact() + barrierRef() in this script.
20
- const RUN_ID = randomUUID()
53
+ const RUN_ID = 'ultracode'
21
54
  const BRIEF = {
22
55
  type: 'object',
23
56
  required: ['summary', 'files', 'risks', 'verification'],
@@ -59,7 +92,6 @@ if (research.length === 0) return { status: 'blocked', reason: 'No research agen
59
92
 
60
93
  // Phase B: persist research artifact for the next barrier agent.
61
94
  const researchSummary = `research lanes: ${research.map((r) => (r && r.summary) ? r.summary.slice(0, 80) : '<lane>').join(' | ')}`
62
- writeArtifact({ runId: RUN_ID, phase: 'Research', label: 'barrier', payload: research, summary: researchSummary, role: 'research-analyst' })
63
95
 
64
96
  phase('Design')
65
97
  const plan = await dispatchAgent(agent, 'plan-author', `Design one reversible implementation for: ${TASK}\n${barrierRef({ runId: RUN_ID, phase: 'Research', label: 'barrier', summary: researchSummary }).promptBlock}\nReturn disjoint edit lanes. Shared root/config/lock files must have one owner. Include bounded tests and stop conditions.`, { role: 'architect', risk: 'medium', capabilities: ['structured-output', 'reasoning', 'architecture'], label: 'plan', phase: 'Design', schema: PLAN })
@@ -67,7 +99,6 @@ if (!plan || !Array.isArray(plan.lanes) || plan.lanes.length === 0) return { sta
67
99
 
68
100
  // Phase B: persist plan artifact for the next barrier agent.
69
101
  const planSummary = `plan lanes: ${plan.lanes.map((l) => l.name).join(', ')}`
70
- writeArtifact({ runId: RUN_ID, phase: 'Design', label: 'barrier', payload: plan, summary: planSummary, role: 'architect' })
71
102
 
72
103
  const audit = await dispatchAgent(agent, 'plan-auditor', `Adversarially review this plan for correctness, security, conflicting file ownership, missing regression tests, and unbounded retry loops. Return a corrected plan, not commentary. Task: ${TASK}\n${barrierRef({ runId: RUN_ID, phase: 'Design', label: 'barrier', summary: planSummary }).promptBlock}`, { role: 'adversarial', risk: 'high', capabilities: ['structured-output', 'reasoning', 'architecture', 'security'], label: 'plan-audit', phase: 'Design', schema: PLAN })
73
104
  const approved = audit || plan
@@ -79,20 +110,10 @@ const implementation = await parallel(lanes.map((lane, index) => () => dispatchA
79
110
  const completed = implementation.filter(Boolean)
80
111
  if (completed.length === 0) return { status: 'blocked', reason: 'No implementation lane completed successfully.', plan: approved }
81
112
  // Phase B: persist each implementation artifact.
82
- for (let i = 0; i < completed.length; i++) {
83
- const lane = lanes[i];
84
- const label = `implement:${i + 1}:${lane.name}`;
85
- const summary = `lane ${lane.name} files: ${(completed[i]?.files || []).slice(0, 5).join(', ')}`;
86
- writeArtifact({ runId: RUN_ID, phase: 'Implement', label, payload: completed[i], summary, role: 'implementer' });
87
- }
88
-
89
113
  phase('Verify')
90
114
  const reviews = await parallel(completed.map((result, index) => () => dispatchAgent(agent, `reviewer-${index + 1}`, `Try to refute this implementation result for task "${TASK}". Check correctness, security, scope, test evidence, and integration assumptions. Return only verified findings and required checks.\n${barrierRef({ runId: RUN_ID, phase: 'Implement', label: `implement:${index + 1}:${lanes[index]?.name || ''}`, summary: `review of lane ${lanes[index]?.name || index + 1}` }).promptBlock}`, { role: 'adversarial', risk: 'high', capabilities: ['structured-output', 'reasoning'], label: `review:${index + 1}`, phase: 'Verify' })))
91
115
  // Phase B: persist review artifacts.
92
116
  const verifiedReviews = reviews.filter(Boolean);
93
- for (let i = 0; i < verifiedReviews.length; i++) {
94
- writeArtifact({ runId: RUN_ID, phase: 'Verify', label: `review:${i + 1}`, payload: verifiedReviews[i], summary: typeof verifiedReviews[i] === 'string' ? verifiedReviews[i].slice(0, 200) : `review ${i + 1}`, role: 'adversarial' });
95
- }
96
117
  const final = await dispatchAgent(agent, 'final-verifier', `Synthesize a bounded integration and verification report for task "${TASK}". Do not claim success without fresh command evidence. Identify conflicts between worktrees, exact integration order, remaining gates, and any required human approvals.\n${barrierRef({ runId: RUN_ID, phase: 'Design', label: 'barrier', summary: `approved plan with ${approved.lanes.length} lanes` }).promptBlock}\n${barrierRef({ runId: RUN_ID, phase: 'Implement', label: 'implement:summary', summary: `${completed.length} lanes complete` }).promptBlock}\n${barrierRef({ runId: RUN_ID, phase: 'Verify', label: 'review:summary', summary: `${verifiedReviews.length} reviews complete` }).promptBlock}`, { role: 'implementer', risk: 'medium', capabilities: ['structured-output', 'reasoning'], label: 'final-verification', phase: 'Verify' })
97
118
 
98
119
  return { status: 'ready-for-integration', task: TASK, research, plan: approved, implementation: completed, reviews: reviews.filter(Boolean), final }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "10.23.7",
3
+ "version": "10.23.8",
4
4
  "description": "Autonomous, human-in-the-loop multi-agent harness for Claude Code with guarded workflows, typed SDK primitives, and MCP tools.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * SDK version constant. Keep synchronized with the workspace package versions.
3
3
  */
4
- export declare const SDK_VERSION: "10.23.7";
4
+ export declare const SDK_VERSION: "10.23.8";
5
5
  //# sourceMappingURL=version.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * SDK version constant. Keep synchronized with the workspace package versions.
3
3
  */
4
- export const SDK_VERSION = "10.23.7";
4
+ export const SDK_VERSION = "10.23.8";
5
5
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar-sdk",
3
- "version": "10.23.7",
3
+ "version": "10.23.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",