Skip to content

docs(openspec): propose nightly CLI builds - #116

Open
thecodedrift wants to merge 4 commits into
mainfrom
openspec/nightly-cli-builds
Open

docs(openspec): propose nightly CLI builds#116
thecodedrift wants to merge 4 commits into
mainfrom
openspec/nightly-cli-builds

Conversation

@thecodedrift

@thecodedrift thecodedrift commented Aug 19, 2026

Copy link
Copy Markdown
Member

Stack (root → tip):

Proposal only — no implementation. Artifacts for a nightly build of the CLI published from main as a separate package, plus a split of the release workflows so each carries one design.

What it proposes

  • @taskless/cli-nightly, published from main, version n.m.k-yyyymmddhhmmssx<sha>
  • Two gates: is .changeset/ empty (a directory listing, before any install), and is this SHA already built
  • A new npm-autopublish environment for flows that publish without a human click
  • Four workflow files replacing release.yml and vale-binaries.yml, each with one trust story

The design is argued in #111; design.md records the decisions with their reasoning rather than restating the conclusions.

Built from main, not from PRs

The original framing was "on PR update". That is what the title of #111 said until this proposal was written, and it does not survive contact with release.yml's security model: a PR-triggered publish would route contributor-authored changeset text into a credentialed job, inverting the split that file exists to maintain, and would publish unreviewed code under the @taskless scope.

Tying builds to changeset edits does not work either. Measured on the #71#106 stack, add-vale-rule-engine had 7 commits after its last changeset edit and agent-command-and-vale-authoring had 11 — a nightly stamped at changeset-edit time would omit all of them while looking current.

Delivery: stacked, merging forward, three PRs

  1. Split the release workflows — behavior-preserving, plus a correction to release.yml's header, which claims "No required reviewers … by design" while npm-production demonstrably has a required reviewer.
  2. The nightly — including creating npm-autopublish and registering trusted publishing.
  3. Move Vale to npm-autopublish — tip, archives the change.

The nightly deliberately precedes the Vale move. npm trusted-publisher bindings can be scoped to a GitHub environment. If the six @taskless/vale-* bindings are, moving Vale first invalidates all of them and the next publish fails the OIDC handshake with no stored token to fall back on — trading a working release path for a convenience. The nightly exercises the same environment, handshake, and binding model on a package where failure is free: nothing resolves @taskless/cli-nightly, so a failed first publish blocks nobody. Stated generally in design.md, because it outlives this change: prove a new credential path on something disposable before migrating something that works onto it.

Two prerequisites no implementer can satisfy

Called out in the proposal body rather than buried in tasks, because they gate merges:

  1. The npm-autopublish environment must be created in repo settings — no required reviewers, branch policy on main.
  2. @taskless/cli-nightly must be published once manually and a trusted-publisher binding registered. Trusted publishing is per-package, and the package does not exist yet.

Both sit as maintainer task groups before the nightly unit. A PR waits rather than merging with a dangling environment reference.

Gaps this surfaced in #111

Writing the design down found four things the discussion had left implicit, all captured as tasks:

  • changeset status returns an array; taking [0] breaks the day a second changesets-managed package appears. Filter by name.
  • npm view … versions lists versions regardless of tag, so a version that published but failed to get tagged reads as already-built and is skipped forever. The Vale workflow handles that case explicitly; nightly's was left implicit.
  • Renaming a workflow renames its check. main requires Validate; any branch-protection or breadcrumb reference to an old name silently stops reporting.
  • Whether Vale's trusted-publisher bindings are environment-scoped is unknown and unasked — the most likely way the Vale PR breaks.

openspec validate --all --strict: 24 passed, 0 failed.

No changeset — a proposal ships nothing.

Refs #111

thecodedrift and others added 2 commits August 18, 2026 23:25
Adds the nightly-cli-builds change: publish @taskless/cli-nightly from
main, and split release.yml into one workflow per release design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
Swaps the last two units of the nightly stack. The nightly now lands
before Vale moves to `npm-autopublish`, rather than after.

The reason is the trusted-publisher risk the proposal already names.
npm bindings can be scoped to a GitHub environment, and if the six
`@taskless/vale-*` bindings are, moving Vale first invalidates all of
them and the next publish fails the OIDC handshake with no stored token
to fall back on. That trades a working release path for a convenience.

The nightly exercises the same environment, the same handshake, and the
same trusted-publishing model on a package where failure is free:
`@taskless/cli-nightly` is new, nothing resolves it, and a failed first
publish blocks nobody. Whatever the binding model turns out to require
is learned there. By the time Vale moves, the destination is proven.

Stated generally, since it outlives this change: prove a new credential
path on something disposable before migrating something that works onto
it.

Both human-gated prerequisites move up accordingly — the environment and
the `@taskless/cli-nightly` binding are now needed before the nightly
unit rather than before the Vale one. The archive moves to the Vale PR,
which is now the tip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
Copilot AI lite review requested due to automatic review settings August 19, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an OpenSpec proposal describing how to publish nightly CLI builds from main as a separate npm package (@taskless/cli-nightly) and how to split existing release workflows so each workflow file documents a single trust boundary/release design.

Changes:

  • Adds a task plan for a 3-PR stacked delivery (workflow split → nightly publish → move Vale to autopublish environment).
  • Adds new/modified OpenSpec requirements for nightly publishing, workflow trust boundaries, and Vale platform package publishing.
  • Adds proposal/design documents plus .openspec.yaml metadata for the change.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
openspec/changes/nightly-cli-builds/tasks.md Step-by-step delivery plan, prerequisites, and acceptance checks for the nightly + workflow split.
openspec/changes/nightly-cli-builds/specs/vale-binary-packages/spec.md Updates requirements describing Vale platform-package publishing workflow and gating.
openspec/changes/nightly-cli-builds/specs/infrastructure/spec.md Adds infrastructure requirements for workflow separation, credential-free gates, environments, and concurrency.
openspec/changes/nightly-cli-builds/specs/cli-nightly-builds/spec.md Adds requirements defining nightly package identity, versioning, gating, and provenance expectations.
openspec/changes/nightly-cli-builds/proposal.md High-level proposal summary (what/why/impact) for nightly publishing and workflow split.
openspec/changes/nightly-cli-builds/design.md Detailed design rationale and trade-offs for the proposed workflows and security model.
openspec/changes/nightly-cli-builds/.openspec.yaml OpenSpec metadata for the new change proposal.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread openspec/changes/nightly-cli-builds/tasks.md Outdated
Comment thread openspec/changes/nightly-cli-builds/tasks.md Outdated
@thecodedrift thecodedrift added the skip-changeset PR intentionally ships no release note (bypasses the changeset requirement) label Aug 19, 2026
@thecodedrift
thecodedrift marked this pull request as draft August 19, 2026 15:30
@thecodedrift

Copy link
Copy Markdown
Member Author

@claude /review

@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @thecodedrift's task in 4m 8s —— View job


Review of #116 — propose nightly CLI builds

  • Gather context (diff, PR description, existing review comments)
  • Read all changed files
  • Cross-check design.md / proposal.md / specs / tasks.md for consistency
  • Check against CLAUDE.md conventions (changeset, stacked-PR rules)
  • Post inline comments for concrete issues
  • Post final top-level assessment

This is a proposal-only PR (528 lines, all new OpenSpec markdown/yaml, no workflow code yet), and the reasoning in design.md is unusually thorough — the D1–D10 decisions each carry a rejected-alternative section, and cross-references between proposal.md, design.md, tasks.md, and the delta specs are largely consistent (gate ordering, version scheme, concurrency narrowing, and the npm-autopublish environment story all agree across files).

Left 3 inline comments, roughly in order of severity:

  1. tasks.md:35 (task 4.2) / design.md:92 — The PR description's "Gaps this surfaced in Publish a nightly @taskless/cli-nightly build from main #111" section claims two fixes were "captured as tasks":

    • changeset status returns an array; taking [0] breaks once a second changesets-managed package exists — "Filter by name."
    • npm view … versions lists versions regardless of tag, so a version published-but-not-tagged reads as already-built forever; "nightly's was left implicit" (implying it's now explicit).

    Neither is actually reflected in tasks.md or design.md's D4 section — the task/design text as written would reproduce the exact [0]-indexing bug, and the tag-blindness gap isn't given its own mitigation or task. Worth tightening the task/design text so an implementer doesn't have to reconstruct these from the PR body alone.

  2. tasks.md:18 (task 1.8) — "Add the changeset for the whole change... grow it as [it lands]" conflicts with CLAUDE.md's stacking rule that a forward-merging stack's changeset should describe only the landing PR's own scope and grow per-unit, not describe the whole future change up front on the base branch.

  3. Two issues already flagged by the Copilot review on this PR remain unresolved in the current diff — noting them here since they're still open:

    • tasks.md:7 (task 0.3): "Confirm the currently published @taskless/cli-nightly name is unclaimed on npm" is self-contradictory (currently published vs. unclaimed).
    • tasks.md:18 (task 1.8): "grow it as groups 3 and 4 land" — group 3 is a human-gated prerequisite, not a PR; should be groups 4 and 5 (PR 2 and PR 3).

No correctness issues found in the delta specs' scenario/requirement structure against the existing base specs (openspec/specs/infrastructure, openspec/specs/vale-binary-packages) — the MODIFIED requirement title in vale-binary-packages/spec.md matches the base spec exactly, and the ADDED requirements in infrastructure/spec.md don't collide with existing titles. The stale release.yml header claim the proposal calls out ("No required reviewers... by design") is confirmed accurate against the current file, so that part of the premise checks out.

Branch: openspec/nightly-cli-builds

Comment thread openspec/changes/nightly-cli-builds/tasks.md Outdated
Comment thread openspec/changes/nightly-cli-builds/tasks.md Outdated
Comment thread openspec/changes/nightly-cli-builds/design.md Outdated
Two of these were claimed as fixed in the PR description and were not
actually in the artifacts — an implementer following the tasks would
have reproduced both bugs the description said were handled.

Select the proposed version by package name, never by index.
`changeset status` returns an array of every package it releases, and
`[0]` is the CLI only while the CLI is the sole managed package. The six
vale packages already sit in the changesets ignore list, so a second
managed package is not hypothetical. Taking `[0]` would stamp a nightly
with another package's version — a wrong version that publishes cleanly
and looks plausible. Now stated in the task, the design, and the spec,
with a scenario.

Record why there is no tag bookkeeping. `latest` is correct for every
version this package produces, so there is no channel to maintain and no
state to drift. The reviewer's case — a publish that lands while the tag
move does not — is accepted rather than mitigated: the window closes at
the next nightly, and gate 2 skipping a re-run of identical bytes is
right on its own terms. Written down, because "we thought about this"
and "we did not think about this" are indistinguishable from silence.

Scope the PR 1 changeset to PR 1. The stack merges forward, where
CLAUDE.md requires each unit to extend the changeset with what it landed
rather than the base promising everything up front.

Also: the group cross-reference in 1.8 was left pointing at the
pre-reorder numbering, and task 0.3 asked to confirm a "currently
published" name is "unclaimed".

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

Copy link
Copy Markdown
Member Author

Re: @claude[bot] — "Review of #116 — propose nightly CLI builds…"
#116 (comment)

All three addressed in 6f7c501; the five inline threads carry the detail and are resolved.

The first finding was the valuable one, and it was a defect in the PR description rather than the proposal: two gaps were described as "captured as tasks" when they existed only in the prose about the artifacts. An implementer following task 4.2 would have reproduced the [0]-indexing bug the description claimed was handled. Both are now in the task, the design, and the spec — the version is selected by name === "@taskless/cli", with a scenario for metadata describing more than one package.

The tag-blindness gap resolved as documentation rather than mechanism. latest is correct for every version this package produces, so there is no channel to maintain; the published-but-untagged window closes at the next nightly and repairs itself. That reasoning is now written down, since "we considered this" and "we missed this" are indistinguishable from silence.

— AI Coding Agent

@thecodedrift

Copy link
Copy Markdown
Member Author

@claude /review

@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @thecodedrift's task in 4m 37s —— View job


Review of #116 — propose nightly CLI builds

  • Gather context (diff, PR description, existing review history)
  • Read all changed files in full (post-6f7c501 state)
  • Re-check the items already flagged/fixed for regressions
  • Fresh pass for new issues (correctness, consistency, spec/design/task alignment)
  • Post inline comments for new concrete issues
  • Post final top-level assessment

This pass re-reviewed the proposal after 6f7c501 (which addressed all prior findings from Copilot and the earlier @claude review — the [0]-indexing task, the "groups 3 and 4" off-by-one, the tag-blindness documentation, and the changeset-scope wording all check out correctly now). I verified those fixes are actually reflected in the current tasks.md/design.md, and they are.

Found 3 new issues, all in artifacts this proposal is entirely responsible for getting right since there's no code yet — left as inline comments:

  1. design.md:71-75 (Gate 1) — the load-bearing correctness bug. Gate 1 is specified as a literal "directory listing" of .changeset/ being empty. I checked this repo's actual .changeset/ directory: README.md and config.json are permanent files that always exist there (created by changesets init, never removed). So .changeset/ is never literally empty — not even right after a Version Packages PR merge consumes every pending changeset, which is the exact case D4 claims is "self-handling" ("the directory is empty, no nightly is built"). As written, gate 1 would read "changesets pending" on every push, including release pushes. The repo already has the correct pattern one file over, in require-changeset.yml: .changeset/*.md excluding README.md. Worth propagating that exclusion into proposal.md/design.md D4/tasks.md 4.4 & 6.2.

  2. design.md:171 — stale task cross-reference. "(task 4.2)" is cited for the Vale-trusted-publisher-bindings check, but task 4.2 is the [0]-indexing/version-selection fix — unrelated. The actual bindings check is task 5.3. Same class of bug as the "groups 3 and 4" off-by-one already fixed twice in this PR (a leftover from renumbering when the nightly moved ahead of the Vale move), just missed in this spot.

  3. tasks.md:28 (task 3.1) — circular task ordering. Group 3 (a human-gated prerequisite the doc places before PR 2 merges) instructs publishing the first @taskless/cli-nightly tarball using "the script in 4.1" — but task 4.1 creates that pack script as part of PR 2, the PR group 3 is supposed to precede. Unlike the Vale precedent (where vale-prepare.cjs already exists on main), this script doesn't exist anywhere at the point group 3 is meant to run. The likely intent — open PR 2 first, use its unmerged branch's script for the one-time manual publish, then merge — is never stated.

Everything else re-verified clean: the npm-production-has-a-required-reviewer premise still matches the live release.yml header (confirmed against the actual file); the six @taskless/vale-* packages are indeed in the changesets ignore list; the MODIFIED requirement title in vale-binary-packages/spec.md matches the base spec exactly; no ADDED requirement title in infrastructure/spec.md or the new cli-nightly-builds capability collides with anything existing; packages/cli/package.json's name (@taskless/cli) matches what the spec's version-selection requirement keys on.
· Branch: openspec/nightly-cli-builds

Comment thread openspec/changes/nightly-cli-builds/design.md
Comment thread openspec/changes/nightly-cli-builds/design.md Outdated
Comment thread openspec/changes/nightly-cli-builds/tasks.md Outdated
…ds PR 2

Three findings from the re-review, the first two of which would have
shipped as bugs.

`.changeset/` is never empty. It permanently holds `README.md` and
`config.json` — `changesets init` writes both and nothing removes them —
so a literal emptiness test answers "pending" on every push, including
the Version Packages merge that D4 claims it self-handles. The gate is
"any `.changeset/*.md` other than `README.md`", which is exactly how
`require-changeset.yml` already counts them; reuse that rule rather than
inventing a second one.

Group 3 could not run where it was placed. It publishes the tarball from
the pack script in task 4.1, and 4.1 is part of PR 2 — the PR group 3 is
meant to precede. Introduced when the units were reordered to put the
nightly ahead of the Vale move: before that swap the binding step came
after the nightly PR, so the script existed. It now says what it always
meant — open PR 2, run its pack script from that branch, publish and
bind, then merge. Vale's equivalent reads as a clean prerequisite only
because `vale-prepare.cjs` was already on `main`.

Also the third stale cross-reference from the same renumbering: the
Vale-bindings check is task 5.3, not 4.2.

Found by the first review round that could read whole files rather than
diff hunks, after the review workflow was fixed to check out the PR ref.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset PR intentionally ships no release note (bypasses the changeset requirement)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants