"Publish npm release shipping bin.cs for the rename"
TASK-IMP-135: Publish npm release shipping bin.cs for the rename
Summary
Cut and publish the first @cyberskill/cyberos npm release whose bin field is cs (not cyberos), via this repo's existing OIDC release.yml pipeline — closing the plan-level gap TASK-IMP-133's audit ISS-005 named, so the Homebrew tap follow-up has a real registry artifact to pin.
Problem
TASK-IMP-130's acceptance criteria prove a scratch build's package.json declares bin.cs. They do not require that build to reach the npm registry. TASK-IMP-133's entire premise — bump Formula/cyberos-cli.rb's url/sha256 and assert bin/"cs" — and TASK-IMP-134's manual release-time checklist both assume a published release with bin.cs already exists. As of 2026-07-23 the live registry still serves @cyberskill/cyberos@1.0.9 with bin.cyberos, while the rename code lives only on the open PR branch. TASK-IMP-133's own audit (ISS-005) and Edge cases section named this as an unowned gap in the five-task batch; leaving it unowned means IMP-133 stays permanently blocked with no task whose done criterion is "the registry artifact exists."
Proposed Solution
Own the operational cut through the audited release path already documented in docs/deploy/RELEASE.md and implemented by .github/workflows/release.yml:
- Land the rename code (TASK-IMP-130 and siblings) on
mainvia the normal PR merge — never a direct push tomain. - Let
version.yml/scripts/cyberos-version.mjsproduce the next platformVERSION(projected1.1.0minor from the renamefeatcommits), or apply an explicitRelease-As:/--setif the operator chooses a different SemVer; promote the[Unreleased]Breaking rename entry into that dated CHANGELOG section. - Tag
v$(cat VERSION)at the bump commit and push the tag sorelease.ymlfires natively (TASK-IMP-071). - Wait for the
npmjob (OIDC trusted publishing,id-token: write, noNPM_TOKEN) to publish@cyberskill/cyberos@<version>. - Verify live via
npm viewthat the published version'sbincontainscsand does not containcyberos.
That live registry fact is the sole publish evidence this task accepts — a local npm pack / scratch tarball does not satisfy it.
Alternatives Considered
- Leave the gap as an Edge case note on TASK-IMP-133 and wait for an ad-hoc operator release. Rejected: Stephen's 2026-07-23 judgment explicitly directed that the gap must not stay unowned; IMP-133 and IMP-134's manual checklist otherwise have no status-gating owner for their shared precondition.
- Publish from a developer laptop with
npm login+npm publish. Rejected:docs/deploy/RELEASE.mddocuments that the package uses trusted publishing (OIDC) pinned torelease.yml; a laptop publish either requires a token the docs say to revoke, or fights the "Require 2FA and disallow tokens" lock. Not the audited path. - Fold publish verification into TASK-IMP-130 by amending its ACs. Rejected: IMP-130 is already
donewith scratch-build ACs; reopening it conflates "code correct in tree" with "artifact on registry," and would invalidate the TRACE-004 evidence already recorded for its offline tests. - Cut the release from the open PR branch without merging to
main. Rejected:release.ymlchecks out the tag ref; tags on a non-main tip that never merges leavemainwithout the rename while the registry claims it shipped — the continuous-delivery and version-bump contracts both assume the bump commit lives onmain.
Success Metrics
- Primary: within one release cycle after TASK-IMP-130's code is on
main,npm view @cyberskill/cyberos versionreturns a version strictly newer than1.0.9whosebinobject has keycsand lacks keycyberos. Baseline today:1.0.9/bin.cyberos. - Guardrail: that version was published by the
release.ymlnpmjob for tagv<version>(OIDC), not by a localnpm publish— confirmed by a successful Actions run for that tag, not by trusting a developer machine's npm auth state.
Scope
In scope: merging the rename PR to main (or confirming it is already merged), the VERSION/CHANGELOG cut for the release that carries bin.cs, pushing the v* tag that fires release.yml, and verifying the live npm package's bin field.
Out of scope / Non-Goals
- Any further change to
tools/install/build.sh'sbinfield or CLI dispatch — that is TASK-IMP-130 (already done). - Updating
Formula/cyberos-cli.rbinhomebrew-tap— that is TASK-IMP-133, which this task unblocks. - Running TASK-IMP-134's manual clean-machine checklist — still a release-process step owned by that spec's Edge cases, not by this task's status transition.
- Publishing desktop/mobile native installers beyond whatever
release.ymlalready does for the same tag — incidental; this task's acceptance criteria are npm-bin only. - A local
npm publishfrom a developer laptop, inventing anNPM_TOKEN, or renamingrelease.yml(the filename is part of the OIDC trust pin).
Dependencies
Depends on TASK-IMP-130 — the code that emits bin.cs must exist in the tree that the release tag points at. Soft coordination with TASK-IMP-131/132/134: those are already done on the same PR branch and should land with 130; this task does not list them in depends_on because the publish contract is specifically "the built payload's bin is cs," which 130 alone defines.
Blocks TASK-IMP-133 — that task MUST NOT merge a Formula pin to a cs-bin tarball until this task's live npm view evidence exists.
Relationship to TASK-IMP-069 / TASK-IMP-071. IMP-069 added the payload release assets and the npm job shape; IMP-071 made git push origin vX.Y.Z fire release.yml natively (no [skip ci] brake). This task consumes both — it does not re-implement them.
AI Authorship Disclosure
- Tools used: Composer (Cursor agent) continuing the CyberOS
task-authordiscipline after Stephen's 2026-07-23 judgment call. - Scope: every
source_pagesline was re-read or re-queried in this session (livenpm view,cyberos-version.mjs --check,release.ymlnpm job, IMP-133 ISS-005) rather than carried forward from a prior agent's notes alone. - Human review: scope authorized by Stephen's explicit "do as your judgment" on the npm-release halt; PLAN-style approval treated as given for this single gap-closing task under that instruction.
1. Description (normative)
- 1.1 The rename code from TASK-IMP-130 MUST be present on
mainat the commit the release tag points to — landed via PR merge, never a direct push tomain. - 1.2 Platform
VERSIONMUST equal the SemVer that will be tagged and published, andCHANGELOG.mdMUST contain a dated## [<VERSION>]section that includes the Breakingcyberos→csrename entry (promoted out of[Unreleased]). - 1.3 A git tag
v$(cat VERSION)MUST exist and MUST point at the bump commit that carries that VERSION; pushing that tag MUST be what triggers.github/workflows/release.yml(nativepush: tagspath per TASK-IMP-071). - 1.4 The
release.ymlnpmjob for that tag MUST concludesuccess, publishing@cyberskill/cyberos@<VERSION>via OIDC trusted publishing (id-token: write) — not via a long-lived npm token and not via a laptopnpm publish. - 1.5 After the job succeeds, a live
npm view @cyberskill/cyberos@<VERSION> binMUST show acskey and MUST NOT show acyberoskey. A local scratch build ornpm packartifact MUST NOT be accepted as substitute evidence for this clause. - 1.6 This task MUST NOT mark itself
doneuntil clause 1.5's live registry evidence exists. It MUST NOT mark TASK-IMP-133done.
2. Acceptance criteria
- [x] AC 1 (traces_to: #1.1) -
git merge-base --is-ancestor <IMP-130-landing-commit> origin/mainexits 0, andgit show origin/main:tools/install/build.shcontains the literal"cs": "cli/bin/cli.mjs"bin entry - test: shell:git fetch origin main && git merge-base --is-ancestor "$(git log origin/main --grep='TASK-IMP-130' --format=%H | head -1)" origin/mainexits 0 ANDgit show origin/main:tools/install/build.sh | grep -F '"cs": "cli/bin/cli.mjs"'exits 0 - [x] AC 2 (traces_to: #1.2) - for
V=$(git show origin/main:VERSION | tr -d '[:space:]'),CHANGELOG.mdonorigin/maincontains a heading line matching## [$V]and that section (until the next##heading) contains both the substringcyberosand the substring `csin the rename Breaking bullet - test: shell: extract the dated section for$Vandgrep -Ffor the rename markers; fail if the bullet remains only under## [Unreleased]` - [x] AC 3 (traces_to: #1.3, #1.4) - tag
v$Vexists onorigin,git rev-list -n1 v$Vequals the commit whereVERSIONbecame$V, and the GitHub Actions run of workflowrelease.ymlfor that tag has jobnpmwithconclusion=success- test:gh api "/repos/cyberskill-official/cyberos/actions/runs?event=push&per_page=20" --jq ...(orgh run list --workflow=release.yml --branch "v$V") filters to that tag's run and asserts the npm job success - [x] AC 4 (traces_to: #1.5) -
npm view @cyberskill/cyberos@"$V" versionprints$V, andnpm view @cyberskill/cyberos@"$V" binJSON-parses to an object with keycsand without keycyberos- test:node -e 'const b=JSON.parse(require("child_process").execSync("npm view @cyberskill/cyberos@'"$V"' bin --json","utf8")); if(!b.cs||b.cyberos) process.exit(1)' - [x] AC 5 (traces_to: #1.5, #1.6) - AC 4's evidence is recorded against the live registry (command output from
npm view, not from a localdist/or.tgz), and this task's frontmatterstatusremains non-doneuntil that output is captured; TASK-IMP-133's frontmatterstatusis unchanged by this task - test: verify: the ship-tasks / final-acceptance note for this task pastes thenpm viewstdout, andgit difffor this task's landing commit does not flip TASK-IMP-133's status
3. Edge cases
- If PR #109 (or its successor) is not yet mergeable (failing gates, review block, merge conflict), this task HALTs at clause 1.1 — authoring and prep are done; the operator must unblock the merge. Do not force-merge past red gates.
- If
version.yml's Deploy Key / ruleset bypass is missing, the auto-bump may compute1.1.0in the run summary without pushing — then the operator (or this task's implementer with an explicit, reviewed bump commit on a PR) must landVERSION/CHANGELOGvia PR before tagging. Still no direct push tomain. - If
@cyberskill/cyberos@$Vis already on the registry when the npm job runs,release.ymlintentionally no-ops the publish (already published — nothing to do). That satisfies clause 1.4's job-success requirement only if AC 4 still showsbin.csfor that version — a previously published wrong-bin version under the same SemVer is a hard failure requiring a new SemVer, not a re-tag. - If OIDC trusted-publishing config on npmjs.com drifts (wrong workflow filename, missing
id-token: write, Node too old), the npm job fails withENEEDAUTH/ similar — this task HALTs and surfaces the exact Actions log; do not fall back to a token publish. - Security-class: this task publishes a public npm package. The audited path uses short-lived OIDC; introducing a long-lived
NPM_TOKENto "just get it out" is explicitly forbidden by this task's non-goals and bydocs/deploy/RELEASE.md.
Final-acceptance evidence (2026-07-23)
Operator session blanket approval applies. TRACE-004 / AC evidence:
- AC1: PR #109 merged to
main(340d4dc5);origin/maintools/install/build.shcontains"cs": "cli/bin/cli.mjs". - AC2:
VERSIONonmainis1.1.0;CHANGELOG.mdhas## [1.1.0] - 2026-07-22with the Breaking rename bullet (auto-bump commit81cf3f07). - AC3: tag
v1.1.0→81cf3f07; release.yml run29952703561jobnpmconclusion=success. - AC4/AC5: live
npm view @cyberskill/cyberos@1.1.0 bin --json→{ "cs": "cli/bin/cli.mjs" }(nocyberoskey). TASK-IMP-133 status was not flipped by this task's publish commit.
End of TASK-IMP-135.
Audit
§1 — Verdict summary
Six §1 clauses, five ACs, five edge cases including one security-class row. Closes the batch gap TASK-IMP-133 ISS-005 named: an owned, status-gating task whose done criterion is live registry evidence of bin.cs, not a scratch build.
§2 — Findings (all resolved)
ISS-001 — AC 3 lacked a concrete, re-runnable success filter
The first draft pointed at gh run view prose without requiring conclusion=success on the specific npm job for the cut tag. An implementer could paste a green overall workflow while the npm job was skipped or failed. Resolved: AC 3 now requires the release.yml run for v$V to show job npm with conclusion=success.
ISS-002 — AC 2 would pass on an Unreleased bullet alone
"CHANGELOG mentions the rename" is true today under ## [Unreleased] without any release cut. Resolved: AC 2 requires a dated ## [$V] heading for the published VERSION, with the rename markers inside that section (not only under Unreleased).
ISS-003 — clause 1.5's "must not fake publish" was prose-only
Without an AC that forbids substituting a local tarball, an implementer could npm pack a scratch build and call the task done. Resolved: AC 4 requires live npm view; AC 5 requires the recorded evidence be that npm view stdout and forbids flipping IMP-133 in the same commit.
ISS-004 — laptop npm publish was only discouraged in Alternatives, not a non-goal
A hurried implementer reading only Scope could still try a token publish when OIDC failed. Resolved: added an explicit Out of scope bullet forbidding local npm publish, inventing NPM_TOKEN, or renaming release.yml.
ISS-005 — reciprocity with TASK-IMP-133 was one-sided in the first draft body
The draft said this task blocks IMP-133, but IMP-133's frontmatter still listed only depends_on: [TASK-IMP-130] and its Scope still said "Publishing … is TASK-IMP-130's job." Resolved: IMP-133 depends_on gains TASK-IMP-135; Scope/Dependencies prose updated to name this task as the publish owner; IMP-130's blocks list gains TASK-IMP-135.
ISS-006 — projected 1.1.0 risked becoming a hard-coded false requirement
Pinning AC text to the literal string 1.1.0 would fail a legitimate Release-As: 1.2.0 cut that still ships bin.cs. Resolved: normative target is "first published version whose bin is cs"; ACs bind to V=$(cat VERSION) / npm view @…@$V, with 1.1.0 retained only as the projected default from cyberos-version.mjs --check.
§3 — TRACE-006 semantic sufficiency (per clause)
| Clause | Verb demand | Cited test asserts | Verdict |
|---|---|---|---|
| 1.1 MUST land on main via PR | ancestor check + bin string on origin/main | AC 1 | sufficient |
| 1.2 MUST cut VERSION + dated CHANGELOG | dated section contains rename | AC 2 (revised) | sufficient after ISS-002 |
| 1.3 MUST tag v$V at bump commit | tag points at VERSION commit | AC 3 | sufficient |
| 1.4 MUST publish via release.yml npm/OIDC | npm job conclusion=success | AC 3 (revised) | sufficient after ISS-001 |
| 1.5 MUST prove live bin.cs | npm view bin keys | AC 4 + AC 5 | sufficient after ISS-003 |
| 1.6 MUST NOT done without 1.5; MUST NOT close 133 | status discipline | AC 5 | sufficient |
§4 — Resolution
Six findings resolved in the audited revision. Score = 10/10.
Status transition draft -> ready_to_implement is authorised by this verdict per STATUS-REFERENCE.md §1.1. Execution still requires a green PR merge to main and a successful OIDC publish — operator HALT conditions remain if either is blocked.
End of TASK-IMP-135 audit.