Skip to content

fix(hygiene): spell the marketplace script's licence header the way the gate reads it - #1354

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-1352-publish-marketplace-licence-header
Aug 27, 2026
Merged

fix(hygiene): spell the marketplace script's licence header the way the gate reads it#1354
os-steve merged 1 commit into
mainfrom
claude/issue-1352-publish-marketplace-licence-header

Conversation

@claude

@claude claude Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #1352

scripts/publish-marketplace.mjs opened with

// Copyright (c) 2026 ObjectStack contributors. Licensed under the Apache-2.0 license.

while COPYRIGHT_HEADER in scripts/check-source-hygiene.mjs:346 is anchored

/^\/\/ Copyright \(c\) \d{4} ObjectStack\./

The period is required directly after ObjectStack, so the file carried a
licence header to every human reader and none at all to the gate. This
normalises line 2 to the canonical spelling the regex already defines. One line,
one file.

Nothing was red, and nothing turns green — that is the point

scanHeaderPosition is handed allTs (scripts/check-source-hygiene.mjs:565),
so no .mjs is judged at all. The gate's own banner on this branch says so:

markers, copyright header : 329 .ts file(s) — 326 under src, test, e2e, scripts plus 3 root .ts file(s)
  ✓ copyright header at the top of every .ts file

What the fix removes is a latent trap: widening the header check to .mjs
would have gone red on a file whose header a reviewer had just read and
approved — the failure mode that makes people doubt a working gate.

The premise was re-measured, and the falsifiable half was checked

The card's ruling hangs on "contributors" being a one-file deviation rather than
a deliberate attribution this file requires. Both halves were verified before
editing:

  • Sweep for ObjectStack contributors across the tree (excluding .git,
    node_modules, build output): exactly one first-party source file
    this one. The only other hit is .changeset/header-position-prose-integers.md,
    which describes this defect rather than mandating the spelling.
  • Control term proving the sweep reached the tree: ObjectStack. Licensed under returns 340 occurrences across 335 files. The sweep is live, and the
    deviation is 1 file against 335.
  • No ruling or licence document requires it. LICENSE is the stock
    Apache-2.0 text with its Copyright [yyyy] [name of copyright owner]
    placeholder unfilled. And the script propagates license from package.json
    (scripts/publish-marketplace.mjs:208, mp.license ?? pkg.license), never a
    copyright holder — line 2 never reaches the published marketplace payload. So
    this is a typo corrected, not a licence statement changed.

No fork to report.

Verified with the gate's own regex, not by eye

Reading the header by eye is precisely what let it pass for as long as it did, so
the measurement harness lifts the regex literally out of the gate source and
evaluates it — it is never retyped:

lifted COPYRIGHT_HEADER = /^\/\/ Copyright \(c\) \d{4} ObjectStack\./
lifted INDENTED_COPYRIGHT_HEADER = /^\s+\/\/ Copyright \(c\) \d{4} ObjectStack\./

Applied to the eight .mjs under SCANNED, before and after:

file before after
scripts/check-lint-i18n-gate.mjs header @ 2 header @ 2
scripts/check-source-hygiene.mjs header @ 2 header @ 2
scripts/check-source-token-ratchet.mjs header @ 2 header @ 2
scripts/check-stackblitz-lock.mjs headerless headerless
scripts/lib/main-module.mjs header @ 1 header @ 1
scripts/lib/source-hygiene-surface.mjs header @ 1 header @ 1
scripts/publish-marketplace.mjs headerless (reader saw one) header @ 2
scripts/sync-docs-screenshots.mjs headerless headerless

The header lands on line 2, which is the position scanHeaderPosition wants
for this file (#! preamble ⇒ want = 2), so a future widening finds it correct
in position as well as in spelling.

The deliverable: the two counts converge

before:  3 of 8 headerless by the gate's rule · 2 of 8 by `grep Copyright`
after:   2 of 8 by both

"How many .mjs lack a header" had two defensible answers and this file was the
entire disagreement. It is now one answer. The two files still headerless are
headerless to reader and regex alike, which is what makes the remaining figure
mean something.

Read-coupling: #1351 rewrites the scanHeaderPosition docstring prose that
states this count, and consumes the 2 of 8 left behind here. That card stays
open and is untouched by this PR — no prose stating the count was edited, and
scripts/check-source-hygiene.mjs is not in this diff.

What was deliberately NOT done

  • COPYRIGHT_HEADER was not widened to accept "contributors". Loosening a
    gate's predicate so a non-conforming file passes is gate weakening, and it is
    the worse answer here: it would preserve one deviant spelling at the cost of
    permanently admitting a whole class.
  • INDENTED_COPYRIGHT_HEADER and the scanned surface are unchanged.
  • ⛔ The scanner was not widened to .mjs. That is a real and defensible
    question, but a scope change that would light up the other .mjs at once —
    this card is its precondition, not the change itself. Worth filing; two files
    would need headers first.

Verification

pnpm verify green on 162ac24 — the commit this PR is opened at, re-run after
the final commit so the figure below belongs to this exact tree:

✓ Validation passed (1213ms)
✓ i18n lint gate: 0 `i18n/missing-*` issues
✓ source hygiene clean          (incl. ✓ copyright header at the top of every .ts file)
✓ source token ratchet clean    (business ~82,489 / interaction ~37,431 / total ~133,854)
✓ Build complete (1215ms)
  Test Files  142 passed (142)
       Tests  3004 passed | 1 skipped (3005)

Run through the shared verify lock: VERDICT command-exit 0 · held the lock 127s · waited 0s. Exit status captured before any pipe.

Changeset has empty frontmatter — the sanctioned "this PR releases nothing"
declaration changeset-check.yml documents. No src/ metadata changed: no
object, field, view, label, flow or hook. The skip-changeset label was not
applied.

Generated by Claude Code


Generated by Claude Code

…he gate reads it

`scripts/publish-marketplace.mjs` opened with

    // Copyright (c) 2026 ObjectStack contributors. Licensed under the Apache-2.0 license.

while `COPYRIGHT_HEADER` in `scripts/check-source-hygiene.mjs` is anchored

    /^\/\/ Copyright \(c\) \d{4} ObjectStack\./

which requires the period directly after `ObjectStack`. The file therefore
carried a licence header to every human reader and none at all to the gate.
Normalised to the canonical spelling the regex already defines.

Nothing is red today: `scanHeaderPosition` reads `allTs`, so no `.mjs` is
judged. What the fix removes is a trap — widening the header check to `.mjs`
would have gone red on a file whose header a reviewer had just read and
approved, which is the failure mode that makes people doubt a working gate.

It also collapses a measurement that had two defensible answers. Measured with
the gate's own regex over the eight `.mjs` under `SCANNED`: headerless was 3 of
8 by the gate and 2 of 8 by `grep Copyright`; it is now 2 of 8 by both.

The deviation was one file against 335 carrying the canonical form, and it was
not a deliberate attribution: LICENSE is the stock Apache-2.0 text with its
placeholder unfilled, and the script propagates `license` from `package.json`
rather than any copyright holder, so line 2 never reaches the published payload.

`COPYRIGHT_HEADER` is unchanged and the scanned surface is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMzCeNC4SZcPNBpE2zCVCg
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 27, 2026 8:43am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline

Projects

None yet

2 participants