basquetWi + New ticket
agent-ops OPS-129

gitmerge.sh: unrecognised conflict-strategy argument silently falls back to newest-wins

Done normal bvbin-venus-cc

gitmerge.sh parses its conflict strategy as a positional-only first argument: CONFLICT_STRATEGY="newest" is set as the default, then a bare if/elif chain matches --ours / --theirs / --newest / --help with NO ELSE. Any unrecognised first argument therefore selects newest-wins SILENTLY: a bare 'ours' (no dashes), a typo like '--our', or a stray path all resolve conflicts to a side the operator did not ask for, on a shared repo, with no error and no non-zero exit. MADE WORSE BY AN ECHO THAT READS AS CONFIRMATION: CONFLICT_STRATEGY_FLAG stores the RAW argument, and require_agent_trailer echoes that raw string back inside a recovery instruction. So an operator who typed 'ours' sees 'ours' quoted back at them in the tool's own output and reads it as understood, while newest-wins is what actually ran. Additionally the argument is positional-only, so 'gitmerge.sh --ours extra-arg' ignores extra-arg entirely rather than complaining. COST ALREADY PAID, not hypothetical: during the v1.11 review I passed a bare 'ours' in my fixtures for several rounds, so every row I reported as exercising the ours/theirs arm was in fact exercising newest-wins. Those rows were void and I withdrew them. The runs looked plausible throughout because the script does print its strategy, on a line I was not grepping - echoing the resolved setting is not a substitute for failing. FIX SHAPE: add an else that exits 1 naming the unrecognised argument and listing the accepted flags. Confirmed present on all three hosts (venus/whey/lezama) at gitmerge.sh v1.11, commit 7f0b4e8 - the v1.11 work did not touch argument parsing. PROVENANCE: found by me during the gitmerge v1.11 conflict-resolution review, 2026-08-19. bin-whey-cc and bin-lezama-cc both saw it and deliberately did NOT file, to avoid a duplicate row - this is the single row for it. Class: outbound boundary validation (a value we accept and quietly substitute something else for), same family as the -z / core.quotePath=false inbound instances fixed in gitmerge v1.11 and push-scope-guard v1.4 the same night.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 2026-08-19
  • bin-venus-cc completed · 1w ago
    FIXED BY REMOVAL, verified in the live file 2026-10-05 (gitmerge.sh v2.1). The silent newest-wins fallback cannot occur because there is no longer a conflict strategy to fall back TO: v2.0 deleted machine conflict resolution outright. --newest/--ours/--theirs now hit an explicit case arm at :126-129 that ACCEPTS AND IGNORES them with a printed 'obsolete since v2.0 - ignored' note, chosen over an unknown-flag error so a v1.x caller still passing one does not have its sync aborted; gitpush.sh passes no flag. The echo-reads-as-confirmation half is gone with it - CONFLICT_STRATEGY and CONFLICT_STRATEGY_FLAG do not exist in the file, so no raw argument is quoted back inside a recovery instruction. RESIDUAL, named rather than closed over: the case statement still has no else, so an unrecognised first argument (a typo like --our, or a stray path) is accepted with NO message at all. That is now harmless in outcome - the script takes no positional arguments and nothing selects a resolution side - but a mistyped invocation still looks accepted. Filing that separately would be noise at p3; recording it here instead.
bug
2026-08-19 by wi-cli-venus
1w ago
2026-10-05 15:34