LearnNewsExamplesServices
Frontmatter
id15303
titlecheck-whitespace is merge-blind too: the dev-merge is still blocked after #15300
stateClosed
labels
bugdeveloper-experienceai
assigneesneo-opus-grace
createdAtJul 16, 2026, 10:11 PM
updatedAtJul 16, 2026, 10:48 PM
githubUrlhttps://github.com/neomjs/neo/issues/15303
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 16, 2026, 10:48 PM

check-whitespace is merge-blind too: the dev-merge is still blocked after #15300

Closed Backlog/active-chunk-6 bugdeveloper-experienceai
neo-opus-grace
neo-opus-grace commented on Jul 16, 2026, 10:11 PM

Problem

#15300 did not finish the job, and I want that on the record before anyone relies on its claim. It made check-chore-sync.mjs merge-aware, and that guard now correctly passes a dev-merge — verified live on grace/15272-throttle-producer immediately after #15300 merged (38a7285973): the sync-data guard no longer appears in the failure set.

The merge is still refused. check-whitespace.mjs has the identical merge-blindness:

Trailing whitespace found in resources/content/discussions/chunk-1/discussion-15173.md:24
… (and :25 :26 :27 :28 :184 :185 :479)

That file is pipeline-authored and arrived with the merge. The branch never touched it. Trailing whitespace is normal in GitHub discussion bodies — it is how markdown encodes a hard line break — so the sync pipeline legitimately produces content this hook legitimately rejects.

The evidence is in the pipeline's own comment

.github/workflows/data-sync-pipeline.yml:69:

<h1 class="neo-h1" data-record-id="4">Commit (neo repo). --no-verify: generated data fails whitespace hooks. NEO_SKIP_TICKET_ARCHAEOLOGY:</h1>

<h1 class="neo-h1" data-record-id="5">the explicit archaeology-gate exemption for the generated-data class (declares intent + future-proofs;</h1>

<h1 class="neo-h1" data-record-id="6">co-exists with --no-verify, which these commits still need for whitespace).</h1>

NEO_SKIP_TICKET_ARCHAEOLOGY=1 git commit --no-verify -m "chore(data): Hourly data sync pipeline update [skip ci]"

The pipeline knows its data fails the whitespace hook and escapes with --no-verify. It can: it is a trusted CI job committing only generated data. A maintainer merging dev cannot — --no-verify is banned here and also skips the AiConfig test-mutation safety lint.

So the pipeline's escape hatch does not extend to anyone who later inherits its commits through a merge, which is everyone.

Scope: this is a hook-layer property, not two isolated bugs

package.json lint-staged runs check-whitespace.mjs on the * glob — every staged file, of every type. A merge stages everything it brings in. So any hook on a broad glob inherits the same defect; check-chore-sync and check-whitespace are the two that actually fire on resources/content/** today.

The *.mjs hooks (check-shorthand, check-jsdoc-types, check-ticket-archaeology, check-block-alignment, check-parse) are the same shape but currently latent for sync data — they don't match .md/.json. They are not latent for a dev-merge that carries someone else's .mjs drift, which is how I hit check-block-alignment on revalidationSweep.mjs earlier today (a file I never touched).

Acceptance Criteria

  • A dev-merge on a name-prefixed maintainer branch commits successfully with the pipeline's content staged — verified by actually performing it, not by unit assertion. This is the AC #15299 could not discharge and #15300 only half-delivered.
  • check-whitespace.mjs does not fail a file whose staged content is inherited from MERGE_HEAD — reuse #15300's discriminator semantics (git diff --cached --name-only MERGE_HEAD ⇒ diverged ⇒ authored ⇒ still checked). A file hand-edited during the merge is still authoring and still fails.
  • Fail-closed on the same terms: absent MERGE_HEAD or a failed diff ⇒ treat every staged file as authored.
  • Decide the shape once, not per-hook. #15300 put the discriminator inline in one script; a second copy is a duplicated primitive (ADR-0019 C2) that will drift. Extract a shared inheritedFromMerge() helper both consume, or state why two copies are correct.
  • Red-then-green: the whitespace spec fails against the current script for a merge-inherited file and passes after; a hand-edited file still fails in both.

Out of scope

  • The *.mjs hooks — latent for sync data, and I'd rather fix them when a real merge trips one than speculatively. check-block-alignment on revalidationSweep.mjs is a live instance, but it arose from an entangled duplicate-SHA merge that no longer applies post-#15300; re-verify before widening.
  • Loosening check-whitespace for resources/content/** generally. That would stop catching drift a maintainer authors there. The merge-inheritance distinction is the correct axis, not the path.

Why this is filed rather than folded into #15300

#15300 is merged and its diff is correct as far as it goes. Re-opening it to widen scope would be worse than a narrow successor: its ACs and evidence are about the sync guard, and this is a different script with its own fixtures. The honest cost is that #15300's PR body claims it "unblocks dev-refresh for every feature branch" — that claim is wrong, and this ticket is the correction. #15281's roster join is still blocked behind it.

Evidence

  • Live reproduction: git merge origin/dev on grace/15272-throttle-producer post-#15300 → sync guard passes, check-whitespace fails on 8 lines of pipeline-authored discussion-15173.md.
  • .github/workflows/data-sync-pipeline.yml:69 — the pipeline's own --no-verify for exactly this hook.
  • package.json lint-staged — check-whitespace is on the * glob.
  • #15300 (38a7285973) — the discriminator to reuse.

Related: #15299 (the first half) · #15281 (still blocked)

tobiu closed this issue on Jul 16, 2026, 10:48 PM