{"record":{"id":"1ff38251ff593aa9","repo":"pbakaus/impeccable","slug":"invalid-manual-apply-result","errorCode":"invalid_manual_apply_result","errorMessage":"invalid_manual_apply_result","messagePattern":"invalid_manual_apply_result","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"skill/scripts/live-server.mjs","lineNumber":1281,"sourceCode":"    }\n    if (msg.token !== state.token) {\n      res.writeHead(401, { 'Content-Type': 'application/json' });\n      res.end(JSON.stringify({ error: 'Unauthorized' }));\n      return;\n    }\n    const pendingApplyDeferred = manualApply.getDeferred(msg.id);\n    if (pendingApplyDeferred) {\n      const validation = manualApply.validateResultMessage(msg, pendingApplyDeferred);\n      if (!validation.ok) {\n        recordManualEditActivity('manual_edit_apply_reply_invalid', {\n          id: msg.id,\n          pageUrl: pendingApplyDeferred.pageUrl,\n          chunk: pendingApplyDeferred.event?.chunk || null,\n          repair: pendingApplyDeferred.event?.repair || null,\n          reason: validation.body?.reason || validation.body?.error || 'invalid_manual_apply_result',\n          status: msg.data?.status || null,\n        });\n        res.writeHead(400, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify(validation.body));\n        return;\n      }\n      recordManualEditActivity('manual_edit_apply_reply_received', {\n        id: msg.id,\n        pageUrl: pendingApplyDeferred.pageUrl,\n        chunk: pendingApplyDeferred.event?.chunk || null,\n        repair: pendingApplyDeferred.event?.repair || null,\n        status: validation.result.status,\n        appliedCount: validation.result.appliedEntryIds.length,\n        failed: summarizeManualApplyFailures(validation.result.failed),\n        fileCount: validation.result.files.length,\n        noteCount: validation.result.notes.length,\n      });\n      manualApply.resolveDeferred(msg.id, validation.result);\n      acknowledgePendingEvent(msg.id);\n      flushPendingPolls();\n      res.writeHead(200, { 'Content-Type': 'application/json' });","sourceCodeStart":1263,"sourceCodeEnd":1299,"githubUrl":"https://github.com/pbakaus/impeccable/blob/f88b2837a7d7c3182e46307bbbb091a1ed547571/skill/scripts/live-server.mjs#L1263-L1299","documentation":"The live server tracks each user-approved manual-edit apply as a pending deferred keyed by event id. When the agent replies (live-poll.mjs --reply <id> done --data '{...}'), validateResultMessage checks the payload shape: status must be 'done'|'partial'|'error', appliedEntryIds must be an array of strings, and failed/files/notes must be arrays; 'partial' with zero applied and zero failed, or 'error' with applied entries, are rejected. On failure the server records manual_edit_apply_reply_invalid and answers HTTP 400 with the validation body, so the apply never completes for that id.","triggerScenarios":"Replying to a pending apply event with malformed --data JSON: missing status, appliedEntryIds not an array or containing non-strings, contradictory status ('error' while listing appliedEntryIds, 'partial' with empty applied+failed), or replying with a different message type for the same id.","commonSituations":"Hand-editing the --data JSON string on the command line (unescaped quotes break the shell/JSON); an agent omitting appliedEntryIds because nothing applied instead of using status:'error'; version skew where an older applier replies without the notes/files fields.","solutions":["Read validation.body in the 400 response — it names the exact rule that failed (e.g. appliedEntryIds_must_contain_strings)","Re-send with the documented shape: --data '{\"status\":\"done\",\"appliedEntryIds\":[\"ENTRY_ID\"],\"failed\":[],\"files\":[\"src/page.html\"],\"notes\":[]}'","Use status 'partial' (some applied + failed[] populated) or 'error' (zero applied) instead of 'done' when not everything applied","If the id no longer resolves, poll again — the server re-issues the apply event with a fresh id"],"exampleFix":"// before\nlive-poll.mjs --reply evt_123 done --data '{\"status\":\"done\"}'\n// after\nlive-poll.mjs --reply evt_123 done --data '{\"status\":\"done\",\"appliedEntryIds\":[\"evt_123#0\"],\"failed\":[],\"files\":[\"src/page.html\"],\"notes\":[]}'","handlingStrategy":"validation","validationCode":"// Validate the reply shape before sending --reply <id> done --data\nconst isValidApplyResult = (d) =>\n  d && typeof d === 'object'\n  && ['done', 'partial', 'error'].includes(d.status)\n  && Array.isArray(d.appliedEntryIds) && d.appliedEntryIds.every((x) => typeof x === 'string')\n  && Array.isArray(d.failed) && Array.isArray(d.files) && Array.isArray(d.notes)\n  && !(d.status === 'error' && d.appliedEntryIds.length > 0)\n  && !(d.status === 'partial' && d.appliedEntryIds.length === 0 && d.failed.length === 0);","typeGuard":"function isApplyResult(m) { return isValidApplyResult(m); } // narrows to { status: 'done'|'partial'|'error'; appliedEntryIds: string[]; failed: unknown[]; files: string[]; notes: unknown[] }","tryCatchPattern":null,"preventionTips":["Always generate the --data JSON with JSON.stringify, never hand-concatenated strings","Copy the exact template from the event instructions: status/appliedEntryIds/failed/files/notes","Map status to outcome strictly: all applied -> done, some -> partial with failed[] filled, none -> error with empty appliedEntryIds"],"tags":["manual-edit-apply","schema-validation","live-server","http-400"],"backgroundTag":"schema-validation-failed","analyzedSha":"f88b2837a7d7c3182e46307bbbb091a1ed547571","analyzedAt":"2026-08-18T04:58:36.608Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}