fix(release): publish the signed release's npm tarball, not a rebuild - #175
Merged
Merged
Conversation
…bution Record the 2026-09-16 platform decisions as dated amendments beside the original text: - distribution spec: Intel Mac (x64) and Linux .deb (x64) in scope, with EARS criteria; AppImage, a Windows desktop app, and non-macOS self-update stay out. Fix the stale npm package name. - distribution design: new §11 (one release platform table, per-platform install locations and CLI channel, Linux .deb specifics, split release jobs). - control-plane: npm publishes before Homebrew; the CLI dispatcher moves into the engine; the npm package ships the policy fixtures; milestone 7 starts with the Windows read path. - channels and site specs: native Intel Mac and Linux downloads, npm CLI as the no-app route. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
CRLF: every frontmatter reader tested startsWith("---\n"), so a file saved
with Windows line endings parsed as having no frontmatter and silently lost
its type, title, and dates; past the fence, the last field kept a trailing
\r and was dropped too. okf-local, files, layer-files, and promote now go
through one splitFrontmatter helper, and frontmatter writes keep the file's
line endings.
Paths: new platform-paths.mjs answers where config, data, and cache live on
macOS, Linux (XDG), and Windows, per control-plane spec §5.12, with
CONTEXTCAKE_* overrides. The app-bundled CLI (always the macOS path before)
and the npm CLI (~/.config on Windows before) both read their default
manifest from it.
Promotion bindings move to the data directory. Their macOS home was a
"Local State" folder inside Electron's userData, where Chromium keeps a file
of that name, so staging a promotion failed with ENOTDIR on any Mac that had
run the app. Bindings staged in the old location are still read and removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
The SQLite store reuses postings by file fingerprint (size, mtime), which a parser change never touches. Without a format bump, an unchanged CRLF note indexed before the frontmatter fix would keep postings and a `type` column from its old frontmatter-less parse until someone edited it. Bump FORMAT_VERSION to 6, test that an older-format store re-analyzes every document, and record the rule in CLAUDE.md. Also note in the manifest trust gotcha that CONTEXTCAKE_MANIFEST and CONTEXTCAKE_CONFIG_DIR select the CLI's default manifest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
npm-publish.yml rebuilt the package on the publish runner, so the registry copy could differ from the tarball listed in the release's SHA256SUMS. It now downloads that tarball, checks its checksum, name, version, entries, and lifecycle scripts (scripts/verify-npm-tarball.mjs, unit-tested), and runs `npm publish <tgz>`. Also: - upgrade to npm 11 and assert npm >= 11.5.1 and Node >= 22.14.0, which trusted publishing requires; Node 22 bundles npm 10 - a re-run for a version npm already has succeeds only when the published dist.integrity matches the release tarball, and fails loudly otherwise - optional input to publish the context-cake pointer package, which no workflow published before - document the one-time prerequisites (placeholder 0.0.0, trusted publisher) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
siracusa5
changed the base branch from
c/multi-platform-downloads-ca2eb1
to
main
September 17, 2026 03:34
…and profiles Wave A slices (a) and (b) of the control-plane spec. - packages/core/src/cli.mjs + cli/: one command table that routes argv and generates `help --json`. Table commands answer with the versioned JSON envelope, typed ControlErrors, the §5.2 exit codes, and redaction of tokenEnv values, provider token shapes, and Authorization headers. `mcp` accepts only its serving flags. `--timeout` is refused on mutating commands before they start. - The app CLI and the npm bin become thin wrappers. The app passes a wrapSpawn hook so mcp and doctor still run through its observability launchers. - `init` creates a v2 manifest and never migrates or overwrites. - `profile` family over control/profiles.mjs: the original six commands keep their text; show, rename, clone, and purge-state are new. Delete retires sidecar state; purge-state --confirm removes it. Mutations accept --expect-revision, checked under the manifest lock. - `account status` reports a typed disabled state. - The npm package ships the capture and context policy fixtures. The distribution test asserts the exact packed file list, and a new e2e test installs the packed tarball and runs help, init, and MCP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
9 tasks
MCP's list_concepts, get_links, read_file markdown, and rule merge, plus the service's /api/resolve disposition decoration, move into concept-queries.mjs so the CLI query family answers from the same code. Behavior is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
concept list|search|read|links and file list|read answer through the resolver, the retained search store, and layer-files.mjs, with coverage naming every source a read could not reach and --require-complete exiting 6. Parity tests pin the answers to /api/resolve, /api/search, /api/files, /api/file, and MCP list_concepts and get_links. doctor runs in-process: manifest and quarantined layers, the selected profile, source reachability, config/data/cache writability, and every contextcake on PATH with its version. A failed check exits 8. The app's wrapSpawn hook still adds the observability check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
Source operations take { profileId, expectRevision }; null is default, the
only profile the HTTP service reads, so its routes behave as before.
- addSource stages a fresh clone and promotes it under the manifest lock
after the revision and name checks.
- removeSources can keep unreferenced clones (retainClones), and
pruneClones deletes one only when nothing references it and git reports
no changes, stash, or unpushed commits.
- syncSource moves out of service.mjs so the CLI runs the same code.
- listSources, configurePendingSource, withSourceSession, testSources.
- settingsView reports which tier each value came from; patchSettings
takes expectRevision.
- repairContextManifest takes profileId and a precondition;
syncPackAssignmentLevel takes a profileId.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
`contextcake source list|show|add|update|level|reorder|remove|test|sync| prune|pending-list|pending-configure|pending-dismiss` and `contextcake settings list|get|set|reset`, both experimental, over the shared control operations. Parity tests drive the CLI and the HTTP service over the same fixtures. The packed-tarball e2e now adds its folder with `source add` and checks MCP list_concepts serves it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
…and profile state - Spawned entrypoints refuse dispatcher flags they do not implement. `write --json --dry-run` used to read --dry-run as the value of --json and write for real. --timeout on a spawned write exits 2 (TIMEOUT_REFUSED); doctor keeps its own --json. - ctx.signal aborts on --timeout and interrupts. A read is abandoned and the process exits; a write always runs to the end, so an answer never says INTERRUPTED over a mutation that lands. A second SIGINT exits. - Redaction honors toJSON, keeps Error message and code, and only marks true cycles as circular. - migrateManifestToV2 allocates the profile id under its lock and throws PROFILE_EXISTS and PROJECT_MAPPED instead of plain errors. The test harness now fails any answer whose code the command does not declare. - Writes report the revision they wrote (ctx.noteManifestWrite); reads answer from the manifest the envelope read. - profile delete writes the manifest before retiring state and reports a failed retire as a warning. purge-state checks and deletes under the manifest lock. - New table trait requireComplete for commands that must exit 6 on partial coverage. The e2e test runs the Windows .cmd shim through a shell. - Site CLI reference documents the new profile commands and the envelope and exit-code change for `contextcake profile`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
…cli-wave-a-query Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
Query and doctor operations take the manifest the envelope read instead of reading the file again, stop on --timeout and interrupts through ctx.signal, and report the revision they read. Site docs describe the concept, file, and doctor commands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
Adapts the source and settings families to the foundation fixes: - every manifest write reports the revision it wrote under the lock (MANIFEST_REVISION on the operation result, ctx.noteManifestWrite); - `source test` uses the requireComplete trait instead of throwing; - adds and pending configures check ctx.signal before the locked write, and `source test` aborts its reads on it; - the tolerant manifest read computes the file's revision itself now that currentManifestRevision is gone. Also documents the two families in the site CLI reference. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
`source test` and `source sync` register their session close with ctx.onClose when the dispatcher offers it, so an MCP child cannot outlive a read cut off by --timeout. Clones and pulls take ctx.signal; an aborted clone removes its partial directory and answers with the abort reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
/api/resolve hashed raw section text while MCP read_file and the Discrepancy Center record fingerprint() (JSON-quoted), so a section acknowledged in the Discrepancy Center read "reopened" over HTTP. The shared decorator now uses fingerprint(), and MCP calls it instead of its own copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
…tings - ctx.onClose(fn) registers async cleanups. The dispatcher runs them newest first, 1.5 s in total, after run() settles and before it answers a timeout or interrupt; an abandoned read waits 1 s before exiting. Read sessions register their close there and also close on ctx.signal, so a timed-out query no longer orphans an MCP child that ignores SIGTERM. - Aggregate queries list each source once and read from that listing, so coverage describes the data; a read failure after listing marks the source partial. - doctor never runs what it finds on PATH. control/installs.mjs skips relative entries, counts one install per folder (PATHEXT names only on Windows), dedupes by realpath, caps the scan, and reads the version from package.json or Info.plist. - doctor lists MCP and keychain-credential sources as notProbed without failing --require-complete, uses the query coverage vocabulary, and refuses a data folder under a regular file. - file read answers INVALID_INPUT for an empty path segment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
7 tasks
…firmed host Review findings on the source family: - prune: cloneState runs git with user config off and counts ignored files, hidden index flags, in-progress merges or rebases, and reflog-only commits as work. Prune and the app's remove rename a clone into .cache/repos/.trash under the lock and check and delete it after releasing it; a clone that changed goes back. - pending-configure refuses to attach a token to a GitHub source whose apiBase is not the default unless --api-base restates it, and the pending listing always shows apiBase. The probe override never gets a token unless a test opts in. - add re-checks a reused clone under the lock and refuses a slot holding a clone of another repository; sync refuses a non-clone folder in the slot; a failed clone only removes a directory it created. - nested scrub markers count as missing; testSources drops its abort listener. - HTTP tests for clone add, sync, remove, SYNC_FAILED, and the new 409. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
Stack the source/settings families on the query/doctor families so the Wave A PRs merge in a straight line (#176 → #178 → #180) instead of conflicting. Conflict resolution: - cli/families/index.mjs: register source, settings, concept, and file. - CLAUDE.md: take #178's dispatcher row and ctx.onClose gotcha, and #180's control-operations row and clone/token gotchas. - reference/cli.md: one description naming every family. Follow-ups the merge made possible: - source.mjs calls ctx.onClose directly now that the dispatcher provides it. - cli-doctor test: the "no source family" case builds a table without it, and the "with source family" case uses the real registry instead of a stub. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
Stack the npm publish hardening on the Wave A CLI PRs so every open PR merges in one straight line. The only conflict was scripts/test.mjs, where both sides added a release suite next to each other; both suites are kept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
10 tasks
The Wave A CLI PRs landed as squash commits; the only conflict was the test registry, where this branch adds the npm-tarball suite beside them. Verified no suite from main was dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: John Siracusa <siracusa5@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part of plan step 4 (first npm publication), stacked on #173. The npm workflow rebuilt the package on the publish runner, so what reached the registry could differ from the tarball in the signed release's
SHA256SUMS. It now publishes that exact tarball after checking it. It also upgrades npm to a version trusted publishing supports.Changes
scripts/verify-npm-tarball.mjs(new, unit-tested) checks the release tarball:SHA256SUMSlinepackage/, with nonode_modulesIt prints the registry-style
sha512-integrity..github/workflows/npm-publish.yml:gh release downloadthe tarball andSHA256SUMS, verify, thennpm publish "$TGZ" --provenance --access public. No rebuild.npm@11, asserting npm ≥ 11.5.1 and Node ≥ 22.14.0 (npm docs). Node 22 bundles npm 10, which cannot use trusted publishing.dist.integritymatches the release tarball; otherwise fail.publish_pointerinput for thecontext-cakepointer package, which no workflow published.scripts/tests/release-workflow.test.mjspins the new shape; newnpm-tarballsuite registered in the release group.Test Plan
node scripts/test.mjs release: 4/4 suitesSHA256SUMSline, a lifecycle script, and a wrong versionNotes
npm publish <tarball>. It is standard npm CLI usage; the first real run confirms it.workflow_dispatch), after the signed release exists.Open questions for John
contextcake@0.0.0(and trycontext-cake@0.0.0) with 2FA, then attach the trusted publisher (repoContextCake/context-cake, workflownpm-publish.yml, environmentnpm-publish) and disable token publishing.🤖 Generated with Claude Code