CodeIssuesPull RequestsActionsSecurityInsights
✨ AI
More
Settings

fix(post-receive): committed to repos it could not identify, and silently skipped seven features on the rest #5493

MergedXSccantynz wants to mergefix/post-receive-repaired-repos-it-could-not-identifymainopened 20d ago
ccantynzcommented 20d ago

What happened

Two repos being handed to a buyer today — bookaride.co.nz and bookaridenz.com — each arrived carrying an unreviewed gluecron[bot] "fix: auto-repair by gluecron" commit on main. Nobody asked for them. Found while diagnosing why GitHub mirroring looked dead.

Root cause

One expression, repeated eight times in src/hooks/post-receive.ts:

eq(users.username, owner), eq(repositories.name, repo)

It is case-sensitive. The sibling resolver in src/middleware/repo-access.ts:161,174 uses lower() = lower(), so a push to .../Bookaridenz.git is authorised against the stored row ccantynz/bookaridenz — and then every lookup in this file misses on the same name. Confirmed against the live row.

The miss lands two different ways, both wrong:

1. The gates disagreed about what null means.

gate form fail direction writes?
auto-issues automationSettings && … CLOSED no
doc-drift automationSettings && … CLOSED no
auto-repair !automationSettings || … OPEN yes — commits to default branch

The only gate that writes is the only one that fails open, against autoRepairMode's documented default of off. The defaults table existed and said "off"; the call site overrode it by accident.

2. The other seven lookups each return; on no match.

Semantic indexing, preview builds, doc drift, server-target deploys, dependency scanning, repo onboarding and the vapron deploy trigger have been silently doing nothing for every repo whose push URL case differs from its stored name — no error, no log, just absence. Fixing only the settings lookup would have left that whole class alive and invisible.

The fix

  • All eight resolution sites use lower() = lower().
  • Unresolvable settings fall back to AUTOMATION_DEFAULTS, never null — the defaults table becomes the single authority.
  • All three gates read isAutomationOn(...) uniformly.

Every mode the push path gates on defaults to off, so this only ever narrows what runs on an unconfigured repo.

Verification

  • Regression test asserts the invariant, not strings: no gate may use the fail-open form, settings may never resolve to null, no site may use the exact-match expression. Comments are stripped before asserting so the file can still quote the old bug in prose.
  • Verified it FAILS 5/8 against the pre-fix code and passes 8/8 after.
  • tsc --noEmit clean.
  • 82 pass across the 8 test files exercising post-receive; 32 pass across automation-settings + autorepair-tree-integrity.

Related

Same "two sources of truth" family as #196 (merge chain resolved disk path from the request slug, not the row's diskPath) and the pending per-route lowercase-slug sweep.

The mirroring investigation that surfaced this is separate and still open: repo_mirrors has zero rows because the importer never creates them (upsertMirror has exactly one caller — the manual form), and outbound Gluecron→GitHub push is #207, still unbuilt.

ccantynzAI Reviewcommented 20d ago

AI Triage

(no summary)

Priority: medium Risk area: mixed

Suggested labels: (no label suggestions) Suggested reviewers: (no reviewer suggestions)

Suggestions only — nothing has been applied. The PR author stays in control.

gluecron[bot]🤖 botAI Reviewcommented 20d ago

AI review unavailable

The platform's AI balance is exhausted, so AI generation is temporarily unavailable. Nothing was lost. You can queue this as a repair for the internal agent from the repository's Health page, or try again once the balance is restored. The PR is otherwise unchanged.

Cross-repo impact

See what breaks downstream if this PR merges.

Analyze →
⮌ Merged

This pull request was merged into main.

c comment · e edit title · m merge · a approve · r request changes · ? shortcuts