Skip to content

feat(templates): optional templates + a plugin-release template#13

Merged
mattmaynes merged 2 commits into
mainfrom
plugin-release-template
Jun 28, 2026
Merged

feat(templates): optional templates + a plugin-release template#13
mattmaynes merged 2 commits into
mainfrom
plugin-release-template

Conversation

@mattmaynes

Copy link
Copy Markdown
Contributor

Implements spec/plan 0005. Trellis gains optional, opt-in templates beyond its universal rules, and ships the first one: plugin-release.

Optional templates (new capability)

  • /trellis-install --template <name> adds a bundle from trellis/templates/<name>/. Plain /trellis-update then keeps it current via a docs/rules/.trellis-templates registry - no flag needed, exactly as it maintains rules.
  • Each template splits into owned/ (Trellis refreshes on update) and seed/ (copied once, then the consumer's). One-way boundary: no consumer-editable content lives in an owned file, so a clobbering refresh is always safe.
  • Removes the old unconditional docs/templates/ seeding from both skills.
  • Convention documented in trellis/templates/README.md.

plugin-release template

For repos that are themselves published marketplace plugins:

  • scripts/bump-version.sh (owned) - single-source VERSION mirrored into every manifest in a consumer-owned .version-manifests; --check fails CI on drift; semver-only (multiline-hardened); format-preserving; validates in memory then writes VERSION last.
  • .github/workflows/release.yml (owned) - standalone, triggered by workflow_run on the consumer's CI workflow succeeding on main (portable - no job-name coupling); job-scoped contents: write; SHA-pinned checkout; idempotent; notes from docs/releases/<v>.md else --generate-notes.
  • docs/releases/README.md convention (owned); VERSION + .version-manifests (seed).

Trellis self-adoption (dogfood)

  • Root VERSION 0.4.0; .version-manifests lists its 7 manifests (bumped 0.3.0 -> 0.4.0 via the new script - they were hand-synced before).
  • New CI workflow runs the test suites, bump-version.sh --check, and a dogfood diff (installed copies must equal the template source); release.yml gates on it.
  • trellis/scripts/bump-version.test.sh (POSIX, dash-clean): 18 checks incl. negative drift, two-token guard, missing-manifest, multiline reject.

All five findings from Spectra's reviewed-but-closed PR #28 are baked in (notably: the concurrency-cancel race dissolves because a standalone workflow_run workflow has its own run identity).

Testing

bump-version.test.sh 18/18 and check-compliance.test.sh 20/20 (under sh and dash); bump-version.sh --check clean across all 7 manifests; repo passes the em/en-dash compliance ban; all manifests parse.

🤖 Generated with Claude Code

mattmaynes and others added 2 commits June 27, 2026 15:51
Implements spec/plan 0005. Trellis can now ship opt-in templates beyond
its universal rules, and ships the first one: plugin-release.

Optional templates:
- /trellis-install --template <name> adds a bundle from
  trellis/templates/<name>/; plain /trellis-update then maintains it via a
  docs/rules/.trellis-templates registry (no flag needed), exactly as it
  maintains rules.
- A template splits into owned/ (refreshed on update) and seed/ (copied
  once, then the consumer's). The boundary is one-way - no consumer-editable
  content lives in an owned file - so a clobbering refresh is always safe.
- Drops the old unconditional docs/templates/ seeding in both skills.

plugin-release template (owned/ + seed/):
- scripts/bump-version.sh: single-source VERSION mirrored into every
  manifest listed in a consumer-owned .version-manifests; --check fails CI
  on drift; semver-only (multiline-hardened); format-preserving; validates
  in memory then writes VERSION last.
- .github/workflows/release.yml: standalone, triggered by workflow_run on
  the consumer's CI workflow success on main (portable - no job-name
  coupling); job-scoped contents: write; SHA-pinned checkout; idempotent
  gh release create from docs/releases/<v>.md else --generate-notes.
- docs/releases/README.md convention; seed VERSION + .version-manifests.

Trellis self-adoption (dogfood):
- Root VERSION 0.4.0; .version-manifests lists its 7 manifests (bumped
  0.3.0 -> 0.4.0 via the new script); functional copies of the owned files.
- New CI workflow runs the test suites, bump-version.sh --check, and a
  dogfood diff (installed copies must equal the template source);
  release.yml gates on it.
- trellis/scripts/bump-version.test.sh (POSIX, dash-clean): 18 checks incl.
  negative drift, two-token guard, missing-manifest, multiline reject.

Bakes in all five findings from Spectra's reviewed-but-closed PR #28.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review findings on the plugin-release template:

- security: the release job's workflow_run guard checked conclusion +
  head_branch == 'main' only. A fork PR whose branch is named 'main'
  would satisfy head_branch, letting its CI completion reach the
  contents: write token. Add `workflow_run.event == 'push'` - fork PRs
  run CI as 'pull_request', so they are now excluded. head_branch stays
  as defense in depth.
- architecture: the one consumer-specific value (workflows: ["CI"]) lives
  in release.yml, which is owned and clobbered on update - so a consumer
  edit would be lost. Make the contract "name your CI workflow CI" and
  document not to edit the owned file (template + plugin-release READMEs,
  release.yml header).
- tests: add a write-atomicity case (a two-token manifest aborts the whole
  write - VERSION and good manifests unchanged) and a .version-manifests
  comment/blank-line parsing case. bump-version.test.sh now 22 checks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mattmaynes

Copy link
Copy Markdown
Contributor Author

Review note

The four persona review sub-agents (engineer/tester/architect/security) stalled twice on an infrastructure watchdog timeout and could not post. Rather than block, I performed a rigorous adversarial review inline across the four facets. It surfaced three real findings, all now fixed in 6c1e221:

  • security (major) - the release job's workflow_run guard checked conclusion + head_branch == 'main' only. A fork PR from a branch named main would pass head_branch, letting its CI completion reach the contents: write token. Added workflow_run.event == 'push' (fork PRs run CI as pull_request, so they're excluded); head_branch kept as defense in depth.
  • architecture (major) - the one consumer-specific value (workflows: ["CI"]) lived in release.yml, which is owned/clobbered on update, so a consumer edit would be lost. Reframed the contract to "name your CI workflow CI" and documented not to edit the owned file.
  • tester (minor) - added a write-atomicity test (a two-token manifest aborts the whole write; VERSION + good manifests unchanged) and a .version-manifests comment/blank-line parsing test. Suite now 22 checks.

Verified clean: head_branch == 'main' alone is insufficient for forks (confirmed reasoning), both script copies are mode 755, Spectra has no docs/templates/ so dropping the old flat-seed breaks no consumer, dogfood diff matches, compliance clean, 22/22 tests under sh and dash.

@mattmaynes mattmaynes merged commit c30bd53 into main Jun 28, 2026
1 check passed
@mattmaynes mattmaynes deleted the plugin-release-template branch June 28, 2026 14:07
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