From 58f57f87e799dcee13f3863a488755bebd5bf7df Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Feb 2026 05:08:26 +0000 Subject: [PATCH 1/2] fix: correct bugs found during deep codebase review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - inspect.ts: Fix case-sensitive string match on lowercased variable (`vi.message.includes` → `msg.includes`) in mapValidationToInspectReason, which could miss capitalized "At least" validation messages - programmaticFill.ts: Fix patchesApplied in parallel path onTurnComplete callback reporting attempted count instead of actual applied count - programmaticFill.ts: Use structured executionId format (`eid:serial:o0`) in serial fillForm path instead of ad-hoc `'0-serial'` string - programmaticFill.ts: Apply maxIssuesPerTurn cap in parallel runMultiTurnForItems (was accepted but ignored via `_` prefix) - fillRecordCollector.ts: Remove dead findTurnKeyForExecutionId method - summaries.ts, inspect.ts: Remove pointless `checkboxField = field` aliases (TypeScript already narrows via case guards) Co-Authored-By: Claude https://claude.ai/code/session_01DRb2BYXTsYqsEfZUoU4v52 --- packages/markform/src/engine/inspect.ts | 12 +++++------- packages/markform/src/engine/summaries.ts | 5 ++--- .../markform/src/harness/fillRecordCollector.ts | 16 ---------------- .../markform/src/harness/programmaticFill.ts | 17 ++++++++++------- 4 files changed, 17 insertions(+), 33 deletions(-) diff --git a/packages/markform/src/engine/inspect.ts b/packages/markform/src/engine/inspect.ts index e777d7a2..f490616d 100644 --- a/packages/markform/src/engine/inspect.ts +++ b/packages/markform/src/engine/inspect.ts @@ -187,7 +187,7 @@ function mapValidationToInspectReason(vi: ValidationIssue): IssueReason { if ( vi.code === 'MULTI_SELECT_TOO_FEW' || vi.code === 'STRING_LIST_MIN_ITEMS' || - vi.message.includes('at least') + msg.includes('at least') ) { return 'min_items_not_met'; } @@ -419,7 +419,6 @@ export function isCheckboxComplete(form: ParsedForm, fieldId: Id): boolean { return true; // Non-checkbox fields are not blocking } - const checkboxField = field; const response = form.responsesByFieldId[fieldId]; // If no response or not answered, checkbox is not complete @@ -433,13 +432,13 @@ export function isCheckboxComplete(form: ParsedForm, fieldId: Id): boolean { } const values = value.values; - const optionIds = checkboxField.options.map((o) => o.id); - const mode = checkboxField.checkboxMode; + const optionIds = field.options.map((o) => o.id); + const mode = field.checkboxMode; if (mode === 'multi') { // Multi mode: all options must be done or na (not todo, incomplete, or active) // If minDone is set, at least that many must be done - const minDone = checkboxField.minDone; + const minDone = field.minDone; if (minDone !== undefined) { const doneCount = optionIds.filter((id) => values[id] === 'done').length; return doneCount >= minDone; @@ -488,8 +487,7 @@ export function findBlockingCheckpoint(form: ParsedForm): BlockingCheckpointResu continue; } - const checkboxField = field; - if (checkboxField.approvalMode === 'blocking' && !isCheckboxComplete(form, fieldId)) { + if (field.approvalMode === 'blocking' && !isCheckboxComplete(form, fieldId)) { return { index: i, fieldId }; } } diff --git a/packages/markform/src/engine/summaries.ts b/packages/markform/src/engine/summaries.ts index 760dbb8f..a86b57ae 100644 --- a/packages/markform/src/engine/summaries.ts +++ b/packages/markform/src/engine/summaries.ts @@ -157,11 +157,10 @@ function isFieldSubmitted(field: Field, value: FieldValue | undefined): boolean } case 'checkboxes': { const v = value as CheckboxesValue; - const checkboxField = field; // For checkboxes, check if any option has been changed from default - const mode = checkboxField.checkboxMode ?? 'multi'; + const mode = field.checkboxMode ?? 'multi'; - for (const opt of checkboxField.options) { + for (const opt of field.options) { const state = v.values[opt.id]; if (mode === 'explicit') { // In explicit mode, "unfilled" is default, anything else is submitted diff --git a/packages/markform/src/harness/fillRecordCollector.ts b/packages/markform/src/harness/fillRecordCollector.ts index 270b0f56..0b0420b8 100644 --- a/packages/markform/src/harness/fillRecordCollector.ts +++ b/packages/markform/src/harness/fillRecordCollector.ts @@ -571,22 +571,6 @@ export class FillRecordCollector implements FillCallbacks { ); } - private findTurnKeyForExecutionId( - executionId: string, - turnStartEvents: Map, - ): string | undefined { - // Find the most recent turn for this executionId - let latestKey: string | undefined; - let latestTime = ''; - for (const [key, event] of turnStartEvents) { - if (event.executionId === executionId && event.timestamp > latestTime) { - latestKey = key; - latestTime = event.timestamp; - } - } - return latestKey; - } - private normalizeInput(input: unknown): Record { if (input && typeof input === 'object' && !Array.isArray(input)) { return input as Record; diff --git a/packages/markform/src/harness/programmaticFill.ts b/packages/markform/src/harness/programmaticFill.ts index ff88053c..b28c5f51 100644 --- a/packages/markform/src/harness/programmaticFill.ts +++ b/packages/markform/src/harness/programmaticFill.ts @@ -638,10 +638,8 @@ export async function fillForm(options: FillOptions): Promise { mergedCallbacks.onTurnStart({ turnNumber: turnCount + 1, issuesCount: stepResult.issues.length, - // Default to order 0, serial execution for non-parallel fills - // Parallel harness will override these values order: 0, - executionId: '0-serial', + executionId: serialExecutionId(0), }); } catch { // Ignore callback errors @@ -801,7 +799,7 @@ export async function fillForm(options: FillOptions): Promise { patches, rejectedPatches: stepResult.rejectedPatches ?? [], coercionWarnings: stepResult.coercionWarnings, - executionId: '0-serial', + executionId: serialExecutionId(0), }); } catch { // Ignore callback errors @@ -1164,7 +1162,7 @@ async function runMultiTurnForItems( items: ExecutionPlanItem[], targetRoles: string[], maxPatchesPerTurn: number, - _maxIssuesPerTurn: number, + maxIssuesPerTurn: number, maxTurnsTotal: number, startTurn: number, options: FillOptions, @@ -1210,6 +1208,9 @@ async function runMultiTurnForItems( return true; }); + // Cap issues shown to agent per turn (consistent with serial path) + scopedIssues = scopedIssues.slice(0, maxIssuesPerTurn); + if (scopedIssues.length === 0) break; // All items filled // Fire turn callback @@ -1276,9 +1277,11 @@ async function runMultiTurnForItems( // Apply patches let lastCoercionWarnings: PatchWarning[] | undefined; + let turnPatchesApplied = 0; if (response.patches.length > 0) { const applyResult = applyPatches(form, response.patches); - patchesApplied += applyResult.appliedPatches.length; + turnPatchesApplied = applyResult.appliedPatches.length; + patchesApplied += turnPatchesApplied; previousRejections = applyResult.rejectedPatches; lastCoercionWarnings = applyResult.warnings; } else { @@ -1294,7 +1297,7 @@ async function runMultiTurnForItems( mergedCallbacks?.onTurnComplete?.({ turnNumber: startTurn + turnsUsed, issuesShown: scopedIssues.length, - patchesApplied: response.patches.length, + patchesApplied: turnPatchesApplied, requiredIssuesRemaining: requiredIssues.length, isComplete: postInspect.isComplete, stats: response.stats, From ee778f429a1540a02afcb47c6f5d1e429b5d142f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 25 Feb 2026 00:55:54 +0000 Subject: [PATCH 2/2] fix: pass executionId to LiveAgent in serial fill path The serial fillForm path used serialExecutionId(0) in onTurnStart and onTurnComplete callbacks but didn't pass it to createLiveAgent, which defaulted to '0-serial'. This mismatch caused FillRecordCollector's buildTimeline() to fail to correlate LLM/tool events with their turns, resulting in zero tokens and missing tool call records in serial runs. Co-Authored-By: Claude --- packages/markform/src/harness/programmaticFill.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/markform/src/harness/programmaticFill.ts b/packages/markform/src/harness/programmaticFill.ts index bc19cb7b..1f440d72 100644 --- a/packages/markform/src/harness/programmaticFill.ts +++ b/packages/markform/src/harness/programmaticFill.ts @@ -581,6 +581,7 @@ export async function fillForm(options: FillOptions): Promise { additionalTools: options.additionalTools, callbacks: mergedCallbacks, maxStepsPerTurn: options.maxStepsPerTurn, + executionId: serialExecutionId(0), toolChoice: options.toolChoice, signal: options.signal, maxRetries: options.maxRetries,