Skip to content

fix(bin): harden GitLab merge request verification - #10

Merged
eyevanovich merged 4 commits into
mainfrom
fm/firstmate-gitlab-mr-create-verification
Jul 23, 2026
Merged

eyevanovich merged 4 commits into
mainfrom
fm/firstmate-gitlab-mr-create-verification

Conversation

@eyevanovich

Copy link
Copy Markdown
Owner

Intent

Autofix the remaining live GitLab merge-request creation verification bug exposed by KissCut issue 146. Identify and handle the exact GitLab API response semantics: create/update requests use remove_source_branch, while read responses distinguish the MR-specific should_remove_source_branch intent from force_remove_source_branch project policy and remove_source_branch_after_merge project defaults. Accept successful create or idempotent reuse only when title, exact description intent (allowing only null/empty normalization), draft state, source, target, head SHA, authenticated authorship, and requested source-branch deletion semantics truly match. Preserve ambiguous-success no-retry safety and never create a duplicate; conflicting existing state must refuse unless an explicitly safe guarded convergence path exists. Diagnostics must disclose only mismatched field names, never credentials or full untrusted MR bodies. Keep all fixes focused on GitLab MR create/reuse response semantics and field-only diagnostics, and add a live-shape regression fixture. The captain explicitly authorized per-task AUTOFIX via --yes for ordinary findings, but not destructive, irreversible, credential, or security-sensitive choices.

What Changed

  • Verify GitLab merge-request creation and reuse against MR-specific source-branch removal intent, exact description semantics, draft state, head SHA, and authenticated authorship.
  • Fail closed on missing or conflicting response fields without retrying creation, and report only mismatched field names without exposing response data or usernames.
  • Expand GitLab live-response fixtures and regression coverage for project policy/default distinctions, malformed descriptions, authorship mismatches, and duplicate-prevention behavior.

Risk Assessment

⚠️ Medium: Captain, the required verification behavior is now correct, but the unused project-default parser adds avoidable coupling and failure surface that is safe to remove mechanically.

Testing

The previously successful full baseline was supplemented by the focused GitLab forge suite and an end-to-end fake-API CLI run; exact live-shape reuse avoided duplicate creation, conflicting MR deletion intent refused with field-only diagnostics, and new draft creation preserved its body and deletion request, with a clean worktree afterward.

Evidence: GitLab MR create/reuse end-to-end transcript
LIVE-SHAPE IDEMPOTENT REUSE (force=false, should=true, project-default=false)
/Users/ipiesh/.no-mistakes/worktrees/2814d4e34fac/01KY6J004VW1DFDW8RPC50CCPR/bin/fm-forge.sh mr-create <test-worktree>/repo --title 'Ship fix' --source fm/fix --remove-source-branch
out='{"forge":"gitlab","host":"gitlab.com","project":"kisscut-museum/kisscut-platform","mr":{"iid":5,"title":"Ship fix","state":"opened","url":"https://gitlab.com/kisscut-museum/kisscut-platform/-/merge_requests/5","source_branch":"fm/fix","target_branch":"main","draft":false,"merge_status":"can_be_merged","detailed_merge_status":"mergeable","sha":"e33d09c7d53e53dc466e7169c616c8c184001cf4","merge_commit_sha":null,"labels":["backend"],"author":"mate","assignees":[],"pipeline":{"id":9,"status":"success","sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","url":"https://gitlab.com/p/9"},"already":true}}'
assert_no_grep '--method POST' <test-worktree>/glab.log 'live-shape reuse created another merge request'

CONFLICTING MR-SPECIFIC DELETION INTENT
run_adapter mr-create <test-worktree>/repo --title 'Ship fix' --source fm/fix --remove-source-branch
error: matching merge request does not match requested head and metadata mismatch (should_remove_source_branch)
assert_no_grep '--method POST' <test-worktree>/glab.log 'conflicting remove-source retry created another merge request'

NEW DRAFT MR CREATION WITH EXACT BODY AND DELETION REQUEST
/Users/ipiesh/.no-mistakes/worktrees/2814d4e34fac/01KY6J004VW1DFDW8RPC50CCPR/bin/fm-forge.sh mr-create <test-worktree>/repo --title 'Detailed fix' --source fm/fix --body-file <test-worktree>/repo/mr-create-body.md --draft --remove-source-branch
out='{"forge":"gitlab","host":"gitlab.com","project":"kisscut-museum/kisscut-platform","mr":{"iid":5,"title":"Draft: Detailed fix","state":"opened","url":"https://gitlab.com/kisscut-museum/kisscut-platform/-/merge_requests/5","source_branch":"fm/fix","target_branch":"main","draft":true,"merge_status":"can_be_merged","detailed_merge_status":"mergeable","sha":"e33d09c7d53e53dc466e7169c616c8c184001cf4","merge_commit_sha":null,"labels":["backend"],"author":"mate","assignees":[],"pipeline":{"id":9,"status":"success","sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","url":"https://gitlab.com/p/9"},"already":false}}'
assert_grep '"description":"Requested body\n\n"' <test-worktree>/glab.log 'merge-request POST did not preserve trailing body newlines'

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 warning
  • 🚨 bin/fm-forge.sh:672 - The required distinction between MR intent and project policy is violated: when should_remove_source_branch is unavailable, this fallback infers MR intent by comparing force_remove_source_branch with remove_source_branch_after_merge. Those are independent project-policy/default fields, so their inequality cannot prove the MR-specific choice and may falsely accept conflicting state. Require a boolean should_remove_source_branch; otherwise fail closed.
  • 🚨 bin/fm-forge.sh:680 - The required “exact description intent (allowing only null/empty normalization)” is not enforced. jq's // also converts boolean false and a missing member to &#34;&#34;, allowing a malformed response to match an empty requested description. Require description to be a string or explicit null, and normalize only null.
  • 🚨 bin/fm-forge.sh:1350 - The forbidden diagnostic behavior remains: authorship mismatch messages interpolate the authenticated username instead of disclosing only mismatched field names. Report fields such as author.id,author.username without including their values; the same issue also affects created-MR verification at line 1382.

🔧 Fix: Harden GitLab merge-request response verification
1 warning still open:

  • ⚠️ bin/fm-forge.sh:167 - FM_GITLAB_PROJECT_REMOVE_SOURCE_DEFAULT has no remaining consumer after verification switched exclusively to should_remove_source_branch. Parsing it adds dead state and can make every load_trusted_project caller fail on an irrelevant malformed project-default field. Remove this parsing block and the global; the fixture can retain the field as part of the live response shape.
✅ **Test** - passed

✅ No issues found.

  • command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"
  • Baseline (already successful): command -v tmux &gt;/dev/null || { echo &#34;tmux is required for e2e tests&#34; &gt;&amp;2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo &#34;== $t ==&#34;; bash &#34;$t&#34; || rc=1; done; exit &#34;$rc&#34;
  • Focused regression suite: bash tests/fm-forge.test.sh
  • Evidence run: PS4=&#39;+${LINENO}: &#39; bash -x tests/fm-forge.test.sh
  • Reviewed the evidence transcript for live-shape idempotent reuse, conflicting deletion-intent refusal without POST retry, field-only diagnostics, and successful draft MR creation preserving the exact body and deletion request
  • Verified the worktree remained clean with git status --short
✅ **Document** - passed

✅ No issues found.

🔧 **Lint** - 1 issue found → auto-fixed ✅
  • ⚠️ linter found issues (exit code 1)

🔧 Fix: Remove unused GitLab project deletion default
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

@eyevanovich
eyevanovich merged commit 9845ea3 into main Jul 23, 2026
@eyevanovich
eyevanovich deleted the fm/firstmate-gitlab-mr-create-verification branch July 23, 2026 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant