@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.
- package/cli/commands/models.mjs +23 -4
- package/cli/doctor.mjs +9 -0
- package/cli/provision.mjs +15 -5
- package/config/claude/agents/office-manager.md +17 -0
- package/config/claude/hooks/agent-model-guard.mjs +16 -30
- package/config/claude/hooks/sessionstart-model-sync.mjs +11 -1
- package/config/claude/hooks/workflow-route-guard.mjs +6 -2
- package/config/workflows/bizar-debug.js +39 -11
- package/config/workflows/bizar-implement.js +38 -2
- package/config/workflows/bizar-research.js +40 -24
- package/config/workflows/lib/dispatch.js +7 -10
- package/config/workflows/lib/native-contract.mjs +96 -0
- package/config/workflows/ultracode-research.js +40 -9
- package/config/workflows/ultracode-review.js +39 -6
- package/config/workflows/ultracode.js +39 -18
- package/package.json +1 -1
- package/packages/sdk/dist/version.d.ts +1 -1
- package/packages/sdk/dist/version.js +1 -1
- package/packages/sdk/package.json +1 -1
package/cli/commands/models.mjs
CHANGED
|
@@ -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
|
-
* -
|
|
951
|
-
*
|
|
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
|
-
|
|
1072
|
-
|
|
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 {
|
|
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 =
|
|
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
|
|
40
|
-
*
|
|
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
|
-
*
|
|
43
|
-
*
|
|
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-
|
|
94
|
-
* disabled list. Empty / missing prefix list is a no-op
|
|
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 &&
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 —
|
|
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
|
|
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
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `
|
|
27
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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