Skip to content

Commit 3d7aedc

Browse files
fix: bound oversized task-check rewrites to one correction
## Model GPT-6.1-Sol. ## Summary Refs #521/#522/#489; FEATURE_SPEC5.3/5.4/5.5 respecting3/7. Actual local-c21ee2959a02b0ef consumed600s during repeated oversized TASK proposals. Give this specific length failure its existing single validation correction immediately, rather than spending the general turn budget rewriting the same invalid contract. Still-invalid correction explicitly stops before coder/publication. Preserve all existing changed-Ask, scope, locked-model and other rewrite behavior. No parsing waiver, requirement truncation or increased budget. ## Verification New red20-turn regression confirmed prior repeated rewrite exhaustion. Final Node20 planner/acceptance/task/plan-mode/layout63/63pass,4.8s. Exactly two calls for unchanged invalid oversized draft, successful written and JSON corrections retained; existing Ask/protected-path/selected-model refusals unchanged. Last full trusted CI1720/1720green; new head requires CI. No private config, runtime deps/store, workflow/policy/vendor edit, dummy commit or invented evaluation. node --test --test-concurrency=4 tests\planner.acceptance.test.mjs tests\planner.test.mjs tests\planner-task.test.mjs tests\plan-mode.test.mjs tests\test-layout.test.mjs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> AI-Agent: r4yharris-agent-coder AI-Model: GPT-6.1-Sol
1 parent f208b8c commit 3d7aedc

3 files changed

Lines changed: 36 additions & 1 deletion

File tree

‎docs/MULTIAGENT.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ reject oversized checks explicitly rather than cutting off requirements.
4848
Reading existing task documents preserves complete checks, including legacy
4949
long checks, so source-validated acceptance continuation remains compatible.
5050
Model validation feedback requests concise complete wording within the existing
51-
turn budget. Title shortening is unchanged. This implements FEATURE_SPEC
51+
turn budget. Oversized written checks receive the single validation correction
52+
immediately, rather than repeating rewrites until the general budget expires.
53+
A still-invalid correction stops before coding and publication.
54+
Title shortening is unchanged. This implements FEATURE_SPEC
5255
sections 5.3-5.5; neither the original Ask nor its safety obligations are waived.
5356
Directory-document checks also recognize content phrasing such as "covering",
5457
"gives instructions" and "indexes"; operation-success assertions stay denied.

‎src/planner/stub.mjs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,16 @@ export async function planAsk(ask, {
429429
});
430430
} catch (error) {
431431
if (!(error instanceof Error)) throw error;
432+
if (!/^[{\[]/.test(plannerPlanText(message.content)) &&
433+
/^Acceptance check must be at most 240 characters/.test(error.message)) {
434+
if (validationRepairUsed) return fallback(error.message, turn);
435+
validationRepairUsed = true;
436+
messages.push({ role: 'user', content:
437+
`The written TASK.md is invalid (${redactSecrets(error.message, { env, apiKeyEnv: config.llm.api_key_env })}). ` +
438+
'Use this single validation correction to return the complete required JSON plan. ' +
439+
'Keep every requirement and the original Ask; the harness will write the validated artifacts.' });
440+
continue;
441+
}
432442
if (!/^[{\[]/.test(plannerPlanText(message.content)) && turn >= budget + Number(repairUsed)) {
433443
if (validationRepairUsed) return fallback(error.message, turn);
434444
if (!/^Acceptance checks must contain 1-8 entries/.test(error.message)) {

‎tests/planner.acceptance.test.mjs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,28 @@ test('an oversized written TASK stays invalid until a complete concise JSON corr
6464
assert.deepEqual(parseTaskDocument(result.task, { expectedAsk: ask }).acceptance_checks, [concise]);
6565
});
6666

67+
test('a repeatedly invalid written TASK stops after one correction even with a large general turn budget', async () => {
68+
const config = parseConfig(readFileSync(new URL('../roster.config.example.yml', import.meta.url), 'utf8')
69+
.replace('base_url: ""', 'base_url: "http://localhost:1234/v1"').replace('model: ""', 'model: test-model'));
70+
const valid = buildPlan(ask, { ...options, acceptanceChecks: ['The guide contains setup instructions'] }).task;
71+
const invalid = valid.replace('The guide contains setup instructions', check(241));
72+
let calls = 0;
73+
const result = await planAsk(ask, { config: { ...config, planner: { ...config.planner, turn_budget: 20 } }, env: {},
74+
tools: { write_file: async () => ({ ok: true }) },
75+
fetchImpl: async () => {
76+
calls += 1;
77+
return Response.json({ choices: [{ finish_reason: 'tool_calls', message: {
78+
role: 'assistant', tool_calls: [{ id: `invalid-${calls}`, type: 'function', function: {
79+
name: 'write_file', arguments: JSON.stringify({ path: 'TASK.md', content: invalid }),
80+
} }],
81+
} }] });
82+
} });
83+
assert.equal(calls, 2);
84+
assert.equal(result.mode, 'stub');
85+
assert.match(result.error, /240.*shorten/);
86+
assert.equal(parseTaskDocument(result.task, { expectedAsk: ask }).ask, ask);
87+
});
88+
6789
test('slice JSON correction retains the original Ask and does not accept a truncated requirement', async () => {
6890
const config = parseConfig(readFileSync(new URL('../roster.config.example.yml', import.meta.url), 'utf8')
6991
.replace('base_url: ""', 'base_url: "http://localhost:1234/v1"').replace('model: ""', 'model: test-model'));

0 commit comments

Comments
 (0)