fix: retain canonical evidence for merge-conflicted source files - #125
Conversation
There was a problem hiding this comment.
Validator approval after policy checks for exact head 4a345a72247c76f7a70f934799bf50ab741b5874.
Ticket: ticket-104
Correlation ID: local-autogrammar-todo2code-pr-125-ticket-104
Model: openrouter/z-ai/glm-5.3-flash
Reviewed diff chunks: 2
Advisory LLM verdict: REQUEST_CHANGES
Advisory summary: Reviewed all 2 diff chunk(s). Chunk 1 adds an additive merge-conflict marker extractor hooked into code2dsl, producing observed source-bound merge_conflict_fact records with exact ranges, block hashes, and strict fail-closed grammar parsing. Implementation is clean: malformed/nested blocks emit warnings only, binary/null-byte and size boundaries are respected, ignore matcher applied, and no merge-side selection or Git index claims. A new regression test covers the core case. However, the reported 'governance / enforce' check is FAIL, which prevents approval regardless of the required-check list. | This chunk adds thorough tests for merge-conflict extraction: diff3/CRLF handling, malformed/nested block rejection, ignore/binary/size boundaries, and excerpt binding via block hashes. Test coverage is strong and behavior assertions (e.g., excerpt truncation at 2000 bytes still yielding distinct IDs via blockSha256) are well designed. However, test_results_data contains a failed check (governance / enforce: FAIL), so approval is not permitted under validation policy.
Advisory findings: Reported check 'governance / enforce' is FAIL; approval is not permitted while any reported check fails, and the PR does not explain or resolve this failure.; Reported check 'governance / enforce' shows FAIL in test_results_data; a failing reported check prevents approval regardless of required-check classification.
The LLM output above is advisory and was not used as the approval trust root.
Semantic review prerequisite: not_required; policy 676cb4516bbfed2a000e40b9b1b6e4a430ecc761ec546aeb53d721a1905cfdd7.
Actual PR impact radar
Exact range: 1c82ce8e7a7c2d5310302bc5c69a093702560dfc...4a345a72247c76f7a70f934799bf50ab741b5874
Change digest: 9a974bd293445ce1ba4f7e01bd6a775c265a5a8f0b332470b2e04ef07bf543ae
Score: 56/100 (L), estimated 64 min, split recommended: true
Affected services/components: repository-wide/unclassified
Machine-readable radar JSONL and SVG
{"actual_change":{"additions":283,"base_sha":"1c82ce8e7a7c2d5310302bc5c69a093702560dfc","binary_files":0,"categories":{"code":2,"configuration":1,"docs":1,"tests":1},"change_digest":"9a974bd293445ce1ba4f7e01bd6a775c265a5a8f0b332470b2e04ef07bf543ae","comparison":"1c82ce8e7a7c2d5310302bc5c69a093702560dfc...4a345a72247c76f7a70f934799bf50ab741b5874","deletions":0,"file_count":5,"files":["project/ticket-104/README.md","project/ticket-104/intent.json","src/extractors/ast.ts","src/extractors/merge-conflicts.ts","test/ast-merge-conflicts.test.ts"],"head_sha":"4a345a72247c76f7a70f934799bf50ab741b5874","service_count":0,"services":[]},"assessment_mode":"observed-pr","axes":{"coupling":5,"delivery":2,"scope":3,"uncertainty":3,"validation":1},"complexity":"L","confidence":0.9,"diagnostics":["RADAR-ACCEPTANCE-MISSING","RADAR-BUDGET-EXCEEDED"],"estimate":{"budget_minutes":30,"minutes":64,"within_budget":false},"impact":{"components":["diff3","malformed","project","src/extractors","test"],"files":["diff3/CRLF","malformed/nested","project/ticket-104/README.md","project/ticket-104/intent.json","src/extractors/ast.ts","src/extractors/merge-conflicts.ts","test/ast-merge-conflicts.test.ts"],"public_interfaces":[],"runtime_dependencies":0},"schema":"subactor.ticket-radar/v1","score":56,"split":{"parts":[{"estimated_minutes":11,"name":"Implement diff3","scope":["diff3"]},{"estimated_minutes":11,"name":"Implement malformed","scope":["malformed"]},{"estimated_minutes":11,"name":"Implement project","scope":["project"]},{"estimated_minutes":11,"name":"Implement src/extractors","scope":["src/extractors"]},{"estimated_minutes":11,"name":"Implement test","scope":["test"]},{"estimated_minutes":15,"name":"Validate and project to trackers","scope":["tests","planfile","github/gitlab/jira projections"]}],"reason":"estimated_minutes_exceed_budget","recommended":true},"standards":[{"id":"wellmanifest/dsl","revision":"6c60fc4e0dd1f1bb74f46a7745e28019908d1203","version":"0.1.0-dev"},{"id":"wellmanifest/ticket-lifecycle","revision":"5bf581907a87b46a13a73e6c033d3abe4d9a306f","version":"0.1.0-dev"},{"id":"wellmanifest/git-lifecycle","revision":"7d77d4b7af57e69bc75c3a0290b3a4805c5c4438","version":"0.2.0-dev"},{"id":"wellmanifest/logs","revision":"48c284ef7a069055c0bcb6b900147ce5e65f8b43","version":"0.3.0"}],"ticket_ref":"ticket-104"}<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img"><title>ticket-104: fix: retain canonical evidence for merge-conflicted source files</title><rect width="128" height="128" rx="12" fill="#f8fafc"/><g stroke-width="1"><polygon points="64,55 72,61 69,71 59,71 56,61" fill="none" stroke="#d7dde5"/><polygon points="64,47 80,59 74,78 54,78 48,59" fill="none" stroke="#d7dde5"/><polygon points="64,38 89,56 79,85 49,85 39,56" fill="none" stroke="#d7dde5"/><polygon points="64,30 97,53 84,92 44,92 31,53" fill="none" stroke="#d7dde5"/><polygon points="64,21 105,51 89,99 39,99 23,51" fill="none" stroke="#d7dde5"/><line x1="64" y1="64" x2="64" y2="21" stroke="#aab4c0"/><line x1="64" y1="64" x2="105" y2="51" stroke="#aab4c0"/><line x1="64" y1="64" x2="89" y2="99" stroke="#aab4c0"/><line x1="64" y1="64" x2="39" y2="99" stroke="#aab4c0"/><line x1="64" y1="64" x2="23" y2="51" stroke="#aab4c0"/></g><polygon points="64,38 105,51 79,85 59,71 48,59" fill="#fb923c" fill-opacity="0.45" stroke="#c2410c" stroke-width="2"/><circle cx="64" cy="64" r="3" fill="#c2410c"/><g font-family="sans-serif" font-size="7" fill="#334155"><text x="64" y="11" text-anchor="middle">SCO</text><text x="114" y="48" text-anchor="middle">COU</text><text x="95" y="107" text-anchor="middle">UNC</text><text x="33" y="107" text-anchor="middle">VAL</text><text x="14" y="48" text-anchor="middle">DEL</text></g><text x="64" y="124" text-anchor="middle" font-family="sans-serif" font-size="8" fill="#0f172a">L · 64m</text></svg>DECISION D-104-9620
TICKET ticket-104
HEAD_SHA 4a345a72247c76f7a70f934799bf50ab741b5874
CORRELATION_ID local-autogrammar-todo2code-pr-125-ticket-104
ACTOR agent:ifuri-validator-agent[bot]
APPLIED_RULE P-CORE-015
INPUT author_login = "tom-sapletta-com"
INPUT observed_checks = ["Live OpenRouter contract (opt-in)=SKIPPING","governance=SKIPPING","verify=PASS","Java adapter (JDK 17 required)=PASS","governance / remote lifecycle=PASS","governance / enforce=FAIL","koru / code-review=PASS","Java adapter (JDK 17 required)=PASS","verify=PASS"]
INPUT required_checks = ["verify","Java adapter (JDK 17 required)","koru / code-review"]
INPUT required_checks_source = "protected registry + GitHub applied rules (env/request)"
INPUT reviewer_login = "ifuri-validator-agent[bot]"
INPUT semantic_review_assessment = {"schema":"subactor.validator/semantic-review-assessment/v1","subject":{"repository":"autogrammar/todo2code","pull_request":125,"head_sha":"4a345a72247c76f7a70f934799bf50ab741b5874","base_sha":"1c82ce8e7a7c2d5310302bc5c69a093702560dfc","diff_sha256":"3a8260a3bef5e7d8d465a063b27611dd74cee404e6d1436c68c62cf9f04e8213"},"policy":{"policy_schema":"subactor.validator/semantic-review-policy/v1","policy_version":1,"policy_sha256":"676cb4516bbfed2a000e40b9b1b6e4a430ecc761ec546aeb53d721a1905cfdd7","required":false,"critical_paths":[],"observed_paths":["project/ticket-104/README.md","project/ticket-104/intent.json","src/extractors/ast.ts","src/extractors/merge-conflicts.ts","test/ast-merge-conflicts.test.ts"]},"grounding":"full-diff-not-per-finding-proof","execution_authority":false,"status":"not_required","reason":null,"review_sha256":null,"unresolved":[]}
INPUT superseded_checks = []
INPUT ticket_radar_receipt = {"schema":"subactor.ticket-radar/v1","base_sha":"1c82ce8e7a7c2d5310302bc5c69a093702560dfc","head_sha":"4a345a72247c76f7a70f934799bf50ab741b5874","change_digest":"9a974bd293445ce1ba4f7e01bd6a775c265a5a8f0b332470b2e04ef07bf543ae","score":56,"complexity":"L","estimated_minutes":64,"split_recommended":true,"services":[],"authority":"ADVISORY","promotion":"FORBIDDEN"}
VERDICT APPROVE AUTHORITY DETERMINISTIC
REJECTED REQUEST_CHANGES BECAUSE NO_UNSAFE_CHANGE_REASON_FOUND
ADVISORY llm_verdict = "REQUEST_CHANGES" MODEL "openrouter/z-ai/glm-5.3-flash"
ASSERT VERDICT_AUTHORITY != "ADVISORY"
Python merge markers make AST parsing fail, so code2dsl returns no evidence for the conflicting file and bounded SubLLM editing cannot resolve it. Supplement code2dsl with observed, canonical conflict-marker records that bind exact line ranges and the complete block hash. Preserve AST warnings; reject malformed/nested blocks and retain ignore, binary and size boundaries. The extractor never chooses a merge side or asserts Git index state.
Validation: 455 tests passed, one optional Java test skipped; five new regressions cover grammar, diff3/CRLF, identity and boundaries. Real SubLLM extraction applied a source-bound fixture edit and rejected a stale-source replay without LLM calls. Offline smoke passed; Docker smoke and protected exact-head gates are recorded separately.
Ticket: ticket-104.