rowStringSlice (internal/board/validate.go) is deliberately tolerant: any non-array/malformed dependson decodes to nil, which is indistinguishable from "no dependencies". The BT-007 dangling-reference cross-check is fed only from that helper, so a real reference stored as a string ("BT-019") never enters the cross-check. validate therefore exits 0 with zero findings on data the write path ([]string) can never emit.
I diagnosed the bug against the real coding-hermes/boardctl repo, reproduced it (RED), applied the ground-truth fix, and verified it end-to-end. The full self-contained write-up is at /workspace/SOLUTION.md.
validate silently tolerates a non-array depends_onrowStringSlice (internal/board/validate.go) is deliberately tolerant: any non-array/malformed depends_on decodes to nil, which is indistinguishable from "no dependencies". The BT-007 dangling-reference cross-check is fed only from that helper, so a real reference stored as a string ("BT-019") never enters the cross-check. validate therefore exits 0 with zero findings on data the write path ([]string) can never emit.
Add a shape check in validateTaskRow before the cross-check collection, plus two helpers:
if raw := row.Get("depends_on"); raw != nil {
if kind := jsonKindName(raw); kind != "" && kind != "array" {
rep.Add("warn", "tasks.jsonl line %d (task %s): depends_on is not an array (%s) — treated as no dependencies",
idx+1, id, kind)
if kind == "string" {
if s, ok := decodeJSONString(raw); ok && strings.TrimSpace(s) != "" {
rep.Add("warn", "tasks.jsonl line %d (task %s): depends_on value %q looks like a reference but is not an array element — it was not cross-checked against task ids",
idx+1, id, s)
}
}
}
}
jsonKindName classifies array/string/number/boolean/object/null (with UseNumber()); decodeJSONString extracts the string content. rowStringSlice is left untouched. The exact git apply-able patch (verified to apply cleanly to the pre-fix commit 3363874) and the full test file are embedded in SOLUTION.md.
got 0; findings: [] for every malformed shape and fails TestBT055MalformedRefSkipsCrossCheck.go test ./internal/board/ -run TestBT055 -v → all 3 pass (shape battery over array/absent/string/number/null/object/boolean, cross-check isolation, byte-preservation).go test ./... → all packages ok.validate on a 9-row board → EXIT=0, RESULT: OK (10 warning(s)), and tasks.jsonl sha256 byte-identical before/after. The dangerous row now reports:[warn] tasks.jsonl line 4 (task BT055-REF): depends_on is not an array (string) — treated as no dependencies
[warn] tasks.jsonl line 4 (task BT055-REF): depends_on value "BT-019" looks like a reference but is not an array element — it was not cross-checked against task ids
Design guarantees: no new errors, exit codes unchanged, read-only, and the malformed shape (being impossible from write.go) is correctly treated as legacy/hand-written drift.
# Evidence - Problem class: go-jsonl-rowstring-slice-silent-non-array-tolerance - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T12:22:16.530Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "PROBLEM: a board validator that extracts a row field with a tolerant rowStringSlice helper (documented: non-array/malformed values yield nil) silently treats a malformed shape as 'no dependencies' - the dangling-reference cross-check has nothing to test, so validate reports OK on data a writer can never emit (write side types the field as []string; the shape only exists on legacy/hand-written rows). The dangerous case: a REAL reference expressed as a string is never cross-checked. FIX: keep the read tolerance (do not error, do not change exit codes) but add a WARN-level finding when a row carries the key with a non-array value, naming file+line+task id and the JSON kind (string/number/boolean/object/null), plus a second warn when the string's decoded content is non-empty saying the reference was not cross-checked. Table tests over all shapes; assert validate never rewrites the file (byte-identical) and exit stays 0 with warnings.", "environment": "boardctl Go board validator over JSONL foreman boards (tasks.jsonl)", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-jsonl-rowstring-slice-silent-non-array-tolerance", "provider": "openrouter", "solved_at": "2026-09-24T12:22:16.531Z", "version": "main e50aea4"}I diagnosed the bug against the real coding-hermes/boardctl repo, reproduced it (RED), applied the ground-truth fix, and verified it end-to-end. The full self-contained write-up is at /workspace/SOLUTION.md.
validate silently tolerates a non-array depends_onrowStringSlice (internal/board/validate.go) is deliberately tolerant: any non-array/malformed depends_on decodes to nil, which is indistinguishable from "no dependencies". The BT-007 dangling-reference cross-check is fed only from that helper, so a real reference stored as a string ("BT-019") never enters the cross-check. validate therefore exits 0 with zero findings on data the write path ([]string) can never emit.
Add a shape check in validateTaskRow before the cross-check collection, plus two helpers:
if raw := row.Get("depends_on"); raw != nil {
if kind := jsonKindName(raw); kind != "" && kind != "array" {
rep.Add("warn", "tasks.jsonl line %d (task %s): depends_on is not an array (%s) — treated as no dependencies",
idx+1, id, kind)
if kind == "string" {
if s, ok := decodeJSONString(raw); ok && strings.TrimSpace(s) != "" {
rep.Add("warn", "tasks.jsonl line %d (task %s): depends_on value %q looks like a reference but is not an array element — it was not cross-checked against task ids",
idx+1, id, s)
}
}
}
}
jsonKindName classifies array/string/number/boolean/object/null (with UseNumber()); decodeJSONString extracts the string content. rowStringSlice is left untouched. The exact git apply-able patch (verified to apply cleanly to the pre-fix commit 3363874) and the full test file are embedded in SOLUTION.md.
got 0; findings: [] for every malformed shape and fails TestBT055MalformedRefSkipsCrossCheck.go test ./internal/board/ -run TestBT055 -v → all 3 pass (shape battery over array/absent/string/number/null/object/boolean, cross-check isolation, byte-preservation).go test ./... → all packages ok.validate on a 9-row board → EXIT=0, RESULT: OK (10 warning(s)), and tasks.jsonl sha256 byte-identical before/after. The dangerous row now reports:[warn] tasks.jsonl line 4 (task BT055-REF): depends_on is not an array (string) — treated as no dependencies
[warn] tasks.jsonl line 4 (task BT055-REF): depends_on value "BT-019" looks like a reference but is not an array element — it was not cross-checked against task ids
Design guarantees: no new errors, exit codes unchanged, read-only, and the malformed shape (being impossible from write.go) is correctly treated as legacy/hand-written drift.
# Evidence - Problem class: go-jsonl-rowstring-slice-silent-non-array-tolerance - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T12:22:16.530Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "PROBLEM: a board validator that extracts a row field with a tolerant rowStringSlice helper (documented: non-array/malformed values yield nil) silently treats a malformed shape as 'no dependencies' - the dangling-reference cross-check has nothing to test, so validate reports OK on data a writer can never emit (write side types the field as []string; the shape only exists on legacy/hand-written rows). The dangerous case: a REAL reference expressed as a string is never cross-checked. FIX: keep the read tolerance (do not error, do not change exit codes) but add a WARN-level finding when a row carries the key with a non-array value, naming file+line+task id and the JSON kind (string/number/boolean/object/null), plus a second warn when the string's decoded content is non-empty saying the reference was not cross-checked. Table tests over all shapes; assert validate never rewrites the file (byte-identical) and exit stays 0 with warnings.", "environment": "boardctl Go board validator over JSONL foreman boards (tasks.jsonl)", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-jsonl-rowstring-slice-silent-non-array-tolerance", "provider": "openrouter", "solved_at": "2026-09-24T12:22:16.531Z", "version": "main e50aea4"}