{"record":{"id":"8bb1346a6efdc797","repo":"nexu-io/open-design","slug":"generated-chatspec-ts-has-an-unexpected-shape","errorCode":null,"errorMessage":"Generated chatSpec.ts has an unexpected shape","messagePattern":"Generated chatSpec\\.ts has an unexpected shape","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/chat-motion-overlay/scripts/run_test_matrix.py","lineNumber":34,"sourceCode":"    parser.add_argument(\"--node-modules\", required=True, help=\"Resolved node_modules directory with remotion/react/typescript\")\n    parser.add_argument(\"--render\", action=\"store_true\", help=\"Render representative still frames with Remotion\")\n    return parser.parse_args()\n\n\ndef write_json(path: Path, payload: dict) -> None:\n    path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + \"\\n\", encoding=\"utf-8\")\n\n\ndef run(cmd: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:\n    return subprocess.run(cmd, cwd=str(cwd), text=True, capture_output=True)\n\n\ndef read_generated_chat_spec(path: Path) -> dict:\n    content = path.read_text(encoding=\"utf-8\").strip()\n    prefix = \"export const chatSpec = \"\n    suffix = \" as const;\"\n    if not content.startswith(prefix) or not content.endswith(suffix):\n        raise ValueError(\"Generated chatSpec.ts has an unexpected shape\")\n    return json.loads(content[len(prefix) : -len(suffix)])\n\n\ndef participant(side: str, preset: str, upload_path: str | None = None) -> dict:\n    value = {\"side\": side, \"preset\": preset}\n    if upload_path:\n        value[\"uploadPath\"] = upload_path\n    return value\n\n\ndef base_config(\n    *,\n    container: str = \"wechat\",\n    avatar_mode: str = \"preset\",\n    device_frame: str = \"iphone-dynamic-island\",\n    nickname_mode: str = \"hidden\",\n    delivery_format: str = \"mov\",\n    show_timestamp: bool = True,","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/chat-motion-overlay/scripts/run_test_matrix.py#L16-L52","documentation":"Thrown by read_generated_chat_spec() in run_test_matrix.py when the generated src/chatSpec.ts file does not start with 'export const chatSpec = ' or does not end with ' as const;'. The test harness strips those wrappers to recover the JSON object, so any deviation (manual edit, different template, partial write) makes parsing impossible.","triggerScenarios":"run_test_matrix.py reads <bundle>/src/chatSpec.ts after prepare_chat_overlay_bundle.py produced it, but the file's shape changed — e.g. the template was hand-edited, write_chat_spec_ts was modified to emit a different wrapper, or a stale/foreign chatSpec.ts is present.","commonSituations":"Editing the remotion template's chatSpec.ts by hand; changing the wrapper format in write_chat_spec_ts without updating the reader; pointing the test matrix at a bundle produced by an older or forked template.","solutions":["Regenerate the bundle with prepare_chat_overlay_bundle.py so chatSpec.ts is written by write_chat_spec_ts in the canonical shape.","If you intentionally changed the wrapper, update read_generated_chat_spec's prefix/suffix constants to match.","Ensure run_test_matrix.py reads from a bundle this tool generated, not a manually authored file."],"exampleFix":"// before (chatSpec.ts after a hand edit)\nexport const chatSpec: ChatSpec = {...};\n// after (regenerated)\nexport const chatSpec = { ... } as const;","handlingStrategy":"type-guard","validationCode":"PREFIX = \"export const chatSpec = \"\nSUFFIX = \" as const;\"\ndef safe_read_chat_spec(path):\n    content = path.read_text(encoding=\"utf-8\").strip()\n    if not content.startswith(PREFIX) or not content.endswith(SUFFIX):\n        raise ValueError(\"chatSpec.ts shape mismatch; regenerate the bundle\")\n    import json\n    return json.loads(content[len(PREFIX):-len(SUFFIX)])","typeGuard":"def is_canonical_chat_spec_ts(content: str) -> bool:\n    c = content.strip()\n    return c.startswith(\"export const chatSpec = \") and c.endswith(\" as const;\")","tryCatchPattern":null,"preventionTips":["Never hand-edit generated chatSpec.ts; regenerate from the builder.","If you change the wrapper in write_chat_spec_ts, update the reader in lockstep."],"tags":["test-harness","chat-motion-overlay","spec-shape"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}