Skip to content

fix(e2e): make hotfix-conflict scenario anchor deterministic - #309

Merged
joshua-temple merged 3 commits into
mainfrom
fix/hotfix-conflict-scenario-determinism
Jun 24, 2026
Merged

joshua-temple merged 3 commits into
mainfrom
fix/hotfix-conflict-scenario-determinism

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Problem

TestMultiStepScenarios/Hotfix_Conflict_Resolution flipped pass/fail run-to-run on the same commit (green on PR #277, red on the rc.4 orchestrate run). executeHotfixApply anchored env/<env> at the recorded state SHA and silently fell back to trunk HEAD when that SHA came back empty under a gitea state-propagation race. The fallback seeded the env branch at the just-patched tip, turning the scenario's engineered cherry-pick conflict into an empty clean apply, so the harness applied the cascade-hotfix label instead of cascade-hotfix-conflict and step 10's assertion matched nothing.

Fix

  • resolveEnvAnchor(env, baseRef): explicit base_ref (context-resolved, literal fallback) then the recorded state SHA, then a loud error - the silent trunk-HEAD fallback is removed, so an unresolvable anchor surfaces the race instead of fabricating the wrong outcome.
  • Step 10 pins base_ref: commit1 so the cherry-pick deterministically conflicts every run; the assertion + labels now match the genuine engineered conflict (the scenario's name and description always intended a conflict).
  • New unit tests TestResolveEnvAnchor (precedence + loud-error path) and TestParseHotfixApplyBaseRef.

Verification

cd e2e && go build ./... passes; go test ./harness/ -run TestResolveEnvAnchor|TestParseHotfixApplyBaseRef passes; golangci-lint run ./harness/ clean. Host-side git repro confirms anchor=commit1 conflicts 5/5 across every merge strategy. The full containerized scenario needs Docker (act+gitea) and runs in CI.

executeHotfixApply anchored env/<env> at the recorded state SHA and silently fell back to trunk HEAD when that SHA was momentarily empty under a gitea state-sync race. The fallback seeded the env branch at the just-patched tip, turning the scenario's engineered cherry-pick conflict into an empty clean apply and flipping the resulting PR label run-to-run. resolveEnvAnchor now resolves an explicit base_ref then the recorded SHA, and errors loudly instead of guessing; the conflict scenario pins base_ref and asserts the conflict label it actually produces.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
gitea's branches API resolves old_ref_name as a branch or tag name, not a raw commit SHA, so seeding env/<env> from an anchor SHA silently branched from HEAD instead. At the conflict scenario's apply step HEAD already carried the patched content, so the cherry-pick applied cleanly and never produced the engineered conflict. Seed the env branch with a git push of the anchor SHA from inside the act container, which resolves the object by hash precisely, so the anchor content is exactly what the SHA guarantees.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple marked this pull request as draft June 24, 2026 18:39
@joshua-temple

Copy link
Copy Markdown
Collaborator Author

Holding this as a draft. The two commits are sound, safe improvements to the hotfix e2e harness: env-branch anchor resolution no longer silently falls back to trunk HEAD, and env branches are seeded with git push <sha> so the anchor SHA is honored rather than dropped by gitea's branch API (which resolves old_ref_name as a branch/tag name, not a raw SHA). They pass build/vet/lint and regress none of the other hotfix scenarios (Clean_Apply, Stacked, Rollback_Runtime, Multi-Env_Clean_Chain, Multi-Env_Conflict_Halt, Containment_Guard all green in CI).

What remains: Hotfix_Conflict_Resolution still does not produce its engineered conflict in CI. With the anchor SHA now honored (seeded env/test at 0f33e27), the cherry-pick of the patch commit still reports conflict=false. The remaining question is the scenario's own commit content: the seeded anchor's tree is not diverging from the patch on the line the conflict depends on. This needs a local container iteration (act + gitea) to trace the actual commit blobs, rather than blind CI cycles.

Not blocking anything: this is e2e test scaffolding for the hotfix conflict path; it does not affect generated output or the product. Parking until it can be iterated locally.

…rses

execInRepo read the container exec output with a plain io.Copy, but Docker's exec stream is multiplexed with 8-byte per-frame headers. Those binary header bytes landed between newlines, so parseSentinel split lines that began with a frame header instead of CONFLICT_FILES=, the match never hit, and conflict was always parsed as false (wrong cascade-hotfix label on a genuine conflict). Read through the existing readDemuxedStream (stdcopy.StdCopy) helper instead. The cherry-pick was always conflicting correctly; the sentinel is now parseable. Verified locally: the conflict scenario passes deterministically with conflict=true across repeated runs, and the clean-apply path is unaffected.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple marked this pull request as ready for review June 24, 2026 22:42
@joshua-temple
joshua-temple merged commit 827ba13 into main Jun 24, 2026
13 checks passed
@joshua-temple
joshua-temple deleted the fix/hotfix-conflict-scenario-determinism branch June 24, 2026 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant