@@ -7,7 +7,7 @@ import { isAllowedFile, plannerToolDefinitions } from '../runtime/tools.mjs';
77import { plannerMemoryText , redactSecrets } from '../runtime/memory.mjs' ;
88import { applyFeedback } from './feedback.mjs' ;
99import { parsePlannerToolCalls } from './tool-calls.mjs' ;
10- import { allowedFile , checkedList , ensureAcceptanceChecks , ensureAllowedFiles , ensureOriginalAsk , ensureTitleHeading , isDirectoryDocumentAsk , oneLine , outputDirectoryFromAsk , parseTaskDocument , validateDirectoryDocumentPlan , validateDirectoryFiles } from './task.mjs' ;
10+ import { acceptanceCheck , allowedFile , checkedList , ensureAcceptanceChecks , ensureAllowedFiles , ensureOriginalAsk , ensureTitleHeading , isDirectoryDocumentAsk , oneLine , outputDirectoryFromAsk , parseTaskDocument , validateDirectoryDocumentPlan , validateDirectoryFiles } from './task.mjs' ;
1111import { selectReasoning } from '../llm/reasoning.mjs' ;
1212import { isLlmTimeout , localRequestTimeoutMs , resolveRequestTimeout } from '../llm/request.mjs' ;
1313import { issueWave } from '../lib/wave-labels.mjs' ;
@@ -89,7 +89,7 @@ export function buildPlan(ask, { reference, title, acceptanceChecks, filesAllowe
8989 const files = checkedList ( filesAllowed , 'Files allowed' , allowedFile , 32 ) ;
9090 const docsOnly = files . length > 0 && files . every ( ( file ) => file . endsWith ( '.md' ) ) ;
9191 const checks = checkedList ( acceptanceChecks , 'Acceptance checks' ,
92- ( check ) => oneLine ( check , 'Acceptance check' ) )
92+ acceptanceCheck )
9393 . filter ( ( check ) => ! ( docsOnly && / n o d e - - t e s t / . test ( check ) ) ) ;
9494 const requirements = scope ?? askRequirements ( cleanAsk ) ;
9595 if ( requirements . outputDirectory && ! checks . length ) {
@@ -145,6 +145,7 @@ export function planStub(ask, { reference = 'local:draft', title, metadata } = {
145145
146146export function planFromTask ( task , ask , { issueTitle, issueBody } = { } ) {
147147 const { title, acceptance_checks, files_allowed } = parseTaskDocument ( task , { expectedAsk : ask , issueTitle, issueBody } ) ;
148+ acceptance_checks . forEach ( acceptanceCheck ) ;
148149 checkAskScope ( files_allowed , askRequirements ( issueTitle ? `${ issueTitle } \n${ ask } ` : ask , { allowMissing : true } ) ) ;
149150 const { difficulty, estimate_min, task_class, model } = readTaskMetadata ( task ) ;
150151 return { title, acceptance_checks, files_allowed, difficulty, estimate_min, task_class, model, design : parseDesign ( task ) } ;
@@ -220,6 +221,7 @@ export async function planAsk(ask, {
220221 : 'You have no tools in this draft-only planning context. ' ) +
221222 'Plan one software task. JSON shape: {"title":"Task title","acceptance_checks":["check"],"files_allowed":["path"]}. ' +
222223 'Use 1-8 acceptance checks in JSON or TASK.md; group related checks without dropping requirements. ' +
224+ 'Each acceptance check must be at most 240 characters; shorten wording without dropping requirements. ' +
223225 'A code task needs acceptance_checks including node --test exits 0 and files_allowed. ' +
224226 'Optional fields: difficulty (1-5), estimate_min, task_class (feat|fix|docs|test), model, steps, notes. ' +
225227 ( lockedModel ? `Keep model ${ lockedModel } ; do not choose another model. ` : '' ) +
@@ -425,6 +427,16 @@ export async function planAsk(ask, {
425427 } ) ;
426428 } catch ( error ) {
427429 if ( ! ( error instanceof Error ) ) throw error ;
430+ if ( ! / ^ [ { \[ ] / . test ( plannerPlanText ( message . content ) ) &&
431+ / ^ A c c e p t a n c e c h e c k m u s t b e a t m o s t 2 4 0 c h a r a c t e r s / . test ( error . message ) ) {
432+ if ( validationRepairUsed ) return fallback ( error . message , turn ) ;
433+ validationRepairUsed = true ;
434+ messages . push ( { role : 'user' , content :
435+ `The written TASK.md is invalid (${ redactSecrets ( error . message , { env, apiKeyEnv : config . llm . api_key_env } ) } ). ` +
436+ 'Use this single validation correction to return the complete required JSON plan. ' +
437+ 'Keep every requirement and the original Ask; the harness will write the validated artifacts.' } ) ;
438+ continue ;
439+ }
428440 if ( ! / ^ [ { \[ ] / . test ( plannerPlanText ( message . content ) ) && turn >= budget + Number ( repairUsed ) ) {
429441 if ( validationRepairUsed ) return fallback ( error . message , turn ) ;
430442 if ( ! / ^ A c c e p t a n c e c h e c k s m u s t c o n t a i n 1 - 8 e n t r i e s / . test ( error . message ) ) {
0 commit comments