Skip to content

cut-rc.yml's release-file allowlist still restates the two literal blank template paths, and the precondition its comment names for reading them instead landed in #9648 #9830

Description

@os-steve

Found while working #9348 (reading how the version pass is validated). Out of that card's scope — it adds a --self-test to the rewriter and touches no workflow but lint.yml. Filed unassigned.

What was observed

cut-rc.yml's Build the single version commit step enforces a release-file allowlist. Its doc half is read from the declaration:

node --input-type=module \
-e 'import { SURFACES } from "./scripts/check-docs-image-tag.mjs"; for (const s of SURFACES) console.log(s.file);'

Its template half is restated, two literals, both hard-coding the template name blank:

git add -A -- \
... \
packages/create-objectstack/src/templates/blank/objectstack.config.ts \
packages/create-objectstack/src/templates/blank/objectstack.manifest.json \
...

with the same two paths repeated in the grep -vE re-check below it.

The comment above that block explains why, and its stated precondition is no longer true:

The manifest is named literally, like its sibling config.ts, rather than read the way the doc surfaces below are: sync-template-versions.mjs does declare its targets (a TEXT_STAMPS table plus a walk of src/templates/), but exports none of them and has no entry-point guard, so importing it would RUN the sync instead of reading its list. Give it those two things and this pathspec should read from it too.

PR #9648 (card #9554) gave it exactly those two things. scripts/sync-template-versions.mjs now exports stampedPaths() — documented in its own header as "the export cut-rc.yml's release-file allowlist is for" — plus TEXT_STAMPS, TEMPLATE_DIR, TEMPLATE_PKG_FILE and findTemplateDirs(), and carries the #9064 entry-point guard, so importing it rewrites nothing. Verified on 9ff11921a: the export resolves and packages/create-objectstack/src/template-version-stamps.test.ts pins that an import against a stale two-template checkout leaves it byte-identical.

Why it matters, and the honest severity

Today it is inert. The repo ships one template, so the two literals equal what stampedPaths() derives, and every cut passes.

The day a second template ships it is a failed release cut, not a soft failure. The allowlist is enforced in two directions: paths outside it are refused, and — the half that bites here — anything the version pass modified but left unstaged fails the step:

::error::tracked files were modified but fall outside the release file surface, so the version commit would be incomplete. Refusing to push.

sync-template-versions.mjs discovers templates by walking src/templates/, deliberately ("a template added tomorrow is covered on the day it lands"), so it will stamp the new template's three surfaces; the pathspec will stage none of them; the cut refuses to push. Nothing is red until someone attempts a release.

#9648's test already states this as an assertion, against a two-template fixture:

The finding, stated as an assertion: the pair cut-rc.yml spells literally is a STRICT SUBSET of what the version pass actually writes as soon as a second template exists.

So the failure is predicted and pinned; only the workflow half is outstanding.

Shape of a fix (not a ruling)

Mirror the doc half, on the terms the comment already sets out:

node --input-type=module \
-e 'import { stampedPaths } from "./scripts/sync-template-versions.mjs"; for (const p of stampedPaths()) console.log(p);'

then stage and re-check by whole-line exact match (grep -vxF -f), the way SURFACE_LIST already is. Two details worth carrying over from the doc half: an empty resolved list must be a hard failure rather than an empty allowlist (stampedPaths() already throws instead of returning [], so this is a resolution failure only), and stampedPaths() reports the per-template package.json too, which the existing *package.json pattern already covers — so the union is a superset of today's behaviour, never a narrowing. The two blank literals and their grep -vE alternatives come out in the same edit; leaving them in would re-seed the restatement.

Refs: #9554 / PR #9648 (landed the export and the guard) · #9348 (found here) · #9518 (the last time this allowlist refused every cut) · #9064 (the doc half's precedent).


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions