Skip to content

fix(hook-base-ref): I-phase — pre-push base-ref via git stdin (F1-F4, ADAPT pre-commit precedent) - #288

Merged
artyhoo merged 1 commit into
stagingfrom
worktree-hook-base-ref-iphase
May 29, 2026
Merged

fix(hook-base-ref): I-phase — pre-push base-ref via git stdin (F1-F4, ADAPT pre-commit precedent)#288
artyhoo merged 1 commit into
stagingfrom
worktree-hook-base-ref-iphase

Conversation

@artyhoo

@artyhoo artyhoo commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

I-phase implementing the hook-base-ref-detection R-phase (PR #287 research patch). The pre-push hook guessed a static origin/staging base, which silently no-op'd on any consumer repo without a staging branch (install.sh ships the bash fallback, which had the same default) and mis-scoped prod-promotion pushes to main. The fix reads the base git already hands the hook on stdin (<local_ref> <local_sha> <remote_ref> <remote_sha>) — eliminating the guess entirely.

Verdict: ADAPT pre-commit's stdin-parsing primitive (SSOT #85). Read base for dev; env-override for CI.

What changed (F1–F4)

Finding Fix Where
core parsePushRefs() + Z40 + commitsNotOnRemotes() (pure, unit-tested) packages/core/hooks/utils/git.ts:44,64,89
core resolveBase() — precedence env > stdin remote_sha (Z40 → commits-not-on-remotes) > origin/staging default packages/core/hooks/pre-push.ts:95
F1 stranded origin/main...HEAD (3-dot) §6 guard → routed through resolver, reconciled to 2-dot packages/core/hooks/pre-push.ts:386-405
F2 TS silent-skip → visible warning in every base-scoped section (§6/§7/§1.7/§8) packages/core/hooks/pre-push.ts:123 (warnSkip)
F3 verified: .husky/pre-push uses exec, git forwards pre-push stdin natively; no husky npm dep
F4 SSOT #85 (pre-commit precedent, ADAPT, T16 match analysis + falsifier) docs/meta-factory/prior-art-evaluations.md:153
consumer pre-push.fallback.sh (the channel install.sh ships) gets the same resolver packages/core/hooks/pre-push.fallback.sh:28

Reserved fork resolved to Option A (stdin). The R-phase left "incremental vs full-PR dev-side scope" to the maintainer. Resolved to A: Option B reintroduces the trunk-guess the finding's root cause (#own-stack-blind-spot) condemns; stdin is a strict improvement given the per-commit trailer invariant + CI's full-PR backstop. B is documented as the SSOT #85 falsifier — a reversal callable later, not a live toss-up.

Verification

  • git unit 68/68 (6 new commitsNotOnRemotes, 9 new parsePushRefs/Z40)
  • prepush-upstream-ref.test.sh 8/8 (5 new stdin scenarios), prepush-fallback-base-ref.test.sh 5/5 (new) — RED-first paired-negatives (4/5/7/8 + FB1/FB2 confirmed failing pre-fix)
  • principles 153/153, typecheck clean, actionlint + zizmor clean, render drift clean, audit-ai-docs 191/191
  • pushed through the live pre-push hook — the new resolver's Z40 path ran on this branch's own first push and passed
  • adversarial cold-review run; 1 MAJOR + 1 MINOR + 1 NIT, all applied

§1.7 Forward-check applied

The change complies with every active discipline layer:

§1.7 Backward-check applied

Complete sweep of every base-resolving surface the R-phase enumerated — all four reconciled, none silently superseded:

  • packages/core/hooks/pre-push.ts:95upstreamRef() (old static default) → resolveBase().
  • packages/core/hooks/pre-push.fallback.sh:28 — old UPSTREAM_REF=…:-origin/stagingresolve_commits().
  • packages/core/hooks/pre-push.ts:386-405 — F1 stranded origin/main...HEAD 3-dot guard → resolver-routed 2-dot.
  • .github/workflows/audit-self.yml:312,316 — CI PREPUSH_UPSTREAM_REF: origin/${{ github.base_ref }} preserved unchanged.

Explicit non-supersession / out-of-scope notes:

  • R-phase PR research(hook-base-ref-detection): R-phase — pre-push base-ref via git stdin (ADAPT pre-commit precedent) #287 is NOT superseded — this PR is the implementation of its verdict (the research patch remains the authoritative design record).
  • Pre-existing failing test pre-push.test.ts > invokes the remaining audit-self self-tests by literal pathNOT touched. It asserts stale .test.sh literals left by the Wave 10.4 .ts migration; fails identically on pristine origin/staging; not in the CI gate (test:principles = principles/ only). Separate stale-assertion fix per CLAUDE.md PR strategyObservation 1.
  • install.sh ship-list NOT touched — the fix lands in both pre-push.fallback.sh (shipped) and pre-push.ts, so the consumer fallback now resolves the base from stdin and no longer silently no-ops. Shipping pre-push.ts to consumers for the substance arms is a separate enhancement → Observation 2.
  • CI loud-fail preserved, not regressedaudit-self.yml:307 still fails loudly when the PR base is unresolvable; the new dev-side warnSkip fires only when neither stdin nor env resolves (CI always supplies env, so it never reaches that path).

Observations (surfaced, not acted on — PR-scope discipline)

  1. Pre-existing pre-push.test.ts stale .test.sh assertion (above) — separate fix.
  2. install.sh ships only the fallback, not pre-push.ts — bug is closed (fallback now works), but full TS substance arms for consumers = separate enhancement umbrella.

🟢 Простыми словами

Что починили: pre-push-хук раньше угадывал, с какой веткой сравнивать пуш — жёстко origin/staging. На любом чужом репозитории без ветки staging проверка молча ничего не делала (а консьюмерам устанавливается именно bash-fallback с тем же дефолтом). Теперь хук читает базу, которую git сам передаёт ему на stdin при каждом пуше — работает на любом репозитории с любым именем транка, гадать больше не нужно. Новая ветка (Z40) → проверяются «коммиты, которых ещё нет ни на одном remote».

Чем CI остался прежним: CI как и раньше передаёт PREPUSH_UPSTREAM_REF=origin/<base_ref> и громко падает, если база не резолвится (audit-self.yml:307) — полный PR-scope. Изменился только dev-side: вместо тихого пропуска теперь либо stdin-база, либо видимое предупреждение. Приоритет: env (CI) > stdin (dev) > старый дефолт. Backstop в CI не тронут.


Base: staging. Closes the hook-base-ref-detection I-phase.

… ADAPT pre-commit precedent)

The pre-push hook guessed a static `origin/staging` base, which silently
no-op'd on any consumer repo without a `staging` branch (install.sh ships the
bash fallback, not pre-push.ts) and mis-scoped prod-promotion pushes to `main`.
Read the base git already hands the hook on stdin instead.

- git.ts: parsePushRefs() + Z40 + commitsNotOnRemotes() (pure, unit-tested).
- pre-push.ts: resolveBase() — precedence env > stdin remote_sha (Z40 →
  commits-not-on-remotes) > origin/staging default; visible warning, never a
  silent skip (F2). F1: route the stranded `origin/main...HEAD` §6 guard through
  the resolver, reconcile 3-dot → 2-dot.
- pre-push.fallback.sh: same resolver (consumer-facing channel; dual-impl §5).
- 19 paired-negative tests across git.test.ts (+6 commitsNotOnRemotes, +9
  parsePushRefs/Z40), prepush-upstream-ref.test.sh (+5), and the new
  prepush-fallback-base-ref.test.sh (5); CI registers the fallback test.

Implements the R-phase (#287) recommended design — Option A (read stdin, not a
trunk-detection guess). Maintainer's reserved fork (incremental vs full-PR
dev-side scope) resolved to A: stdin is a strict improvement given the
per-commit trailer invariant + CI full-PR backstop; Option B documented as the
falsifier in SSOT #85. F3 verified: dispatcher uses `exec`, git forwards
pre-push stdin natively (no husky npm dep).

Prior-art: prior-art-evaluations.md#85 (pre-push base-ref via git stdin; pre-commit precedent, verdict ADAPT — reused the stdin-parsing primitive, scope semantics differ per the row's T16 analysis).
Prior-art: pre-commit/pre-commit (DeepWiki 2026-05-29) — stdin remote_sha as diff base; Z40 new-branch via rev-list --remotes fallback. The mature reference this design ADAPTs.
Prior-art: research-patches/2026-05-29-hook-base-ref-detection.md (R-phase patch, PR #287) — the prior-art survey + recommended design this commit implements.
@artyhoo
artyhoo merged commit 83292b0 into staging May 29, 2026
26 checks passed
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