Fix --version, the unbuildable service image, and a NUL leak in status - #31
Merged
Conversation
`crosscode --version` and `-V` both failed with USAGE_ERROR. The parse loop already handled commander's `commander.version` code, but `.version()` was never called, so that branch was unreachable -- on the one flag people try first after installing from npm. The version comes from the root manifest, substituted into the new apps/daemon/src/version.ts at bundle time, because an installed bundle has no package.json at a path it can rely on. Both shipped entrypoints read that one constant, which also fixes crosscode-mcp announcing a hand-written "0.1.0" as its MCP serverInfo version that nothing kept in step with the manifest. Run from source through tsx it reports 0.0.0-source rather than claiming to be whatever release it was last built from. Answered before commander sees it so it honours --json like every other command, and scoped to leading flags so a -V bound for a child process of `crosscode run --` is not intercepted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The image copied apps/vscode-extension/package.json, a directory deleted in the 2026-08-02 CLI-only pass. COPY of a missing path is a hard error, so the documented deployment path had not built since that merge and no job noticed. Removing that one line would fix today's break, but the hand-written manifest list is what let it rot, so the manifests are now discovered by a find in a throwaway stage: add or remove a workspace member and the image follows. Install-layer caching still holds, because that layer keys off the manifests alone rather than the whole tree. That stage copies the repo, and there was no .dockerignore -- node_modules, .git and any .env would have gone to the daemon and into a layer. Added one. CI now builds the image, which is the only thing that keeps any of this honest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`crosscode status --json` -- the first thing every agent reads -- carried headReflog, which joins a commit hash to its reflog subject with a raw NUL. It is an internal change-detection sentinel with no consumer outside the daemon process, and it reached the wire because status() spread the whole RepositoryState. status() now names its public fields instead, so this is a contract rather than an accident and the next field added to RepositoryState is private until someone decides otherwise. The MCP get_workspace_state tool forwards the same object and is fixed with it. The sentinel is still computed and still drives same-HEAD reset detection; only its publication changed. GitState is untouched, so an existing checkout sees no spurious transition on upgrade. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The variable outlived the dashboard: it names a site that no longer has one. It stays readable so setups made before the removal keep working, but now warns once per process and defers to CROSSCODE_WEB_URL whenever both are set. The notice goes to stderr and never stdout. README and AGENTS both promise that with --json stdout is one line of JSON and nothing else, so a notice printed above that line would break every agent parsing it -- a worse bug than the stale name being warned about. The precedence chain was written out twice, once in resolveWebUrl and once in the MCP server's bootstrap, which is how the two could have drifted. Both now call configuredWebUrl(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Billing enforcement was described as unwired in the status file and in BUILD_INSTRUCTIONS. assertPlanAllowsAutonomyTier and assertSeatCapAvailable are enforced in store.ts and answer 402. assertSemanticReviewCallAvailable really is unwired, but deliberately: review runs on the member's own MCP agent and never reaches the service, so metering it would mean adding a round-trip purely to bill for it. Recorded as a decision, not a gap. The status file is a snapshot of one merge, so it carries a dated correction rather than a rewrite. - architecture.md and onboarding-contracts.md both had WEB_URL falling back to "the production default". No such default exists; resolveWebUrl throws WEB_URL_REQUIRED. Corrected, and the deprecated fallback documented. - The verification baseline was stuck at 25 files / 231 tests. Re-measured, and reframed as a dated observation with the commands to reproduce it, since that is the failure mode of hand-copied counts. - The CORS tests named a dashboard that no longer exists; they cover the opt-in CROSSCODE_ALLOWED_ORIGINS case for any browser client. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
What this changes
Four defects found while verifying the npm-packaging, JSON-envelope, Supabase-onboarding and CI-test work already on
main:crosscode --versionand-Vfailed withUSAGE_ERROR(CLI, MCP server, build script);apps/service/Dockerfilecould not build at all because it copiedapps/vscode-extension/package.json, deleted in the 2026-08-02 CLI-only pass (coordination service, CI);status --jsonleakedheadReflog, an internal sentinel carrying a raw NUL byte, into the first payload every agent reads (daemon); and several docs asserted things the code contradicts (docs).Why
The image break meant the documented deployment path had not worked since that merge and no job noticed, so CI now builds it and the manifest list is discovered by a find instead of hand-written — the same staleness cannot recur. Likewise
--versionis the first thing anyone runs after installing from npm, and it also fixescrosscode-mcpreporting a hand-writtenserverInfo.versionthat nothing kept in step with the manifest.Testing
pnpm testgoes 296 → 301 (5 new tests covering the version flag including therun --passthrough case, the status projection, and the stderr-only deprecation). Verified end to end against a freshnpm pack+npm installoutside the repo, anddocker buildverified clean and with--no-cache.pnpm buildpassespnpm testpasses — 29 files, 301 passed / 19 skippedpnpm test:postgrespasses — 6 files, 19 tests, none skippedSecurity / trust-boundary impact
Net improvement, no weakening. The repo had no
.dockerignore, so the new manifest stage would have sent.gitand any.envinto the build context; one is added that keeps credentials out of image layers.status()now projects an explicit field list rather than spreading internal state, so the next internal field is private by default. TheCROSSCODE_DASHBOARD_URLdeprecation notice goes to stderr only, preserving the guarantee that--jsonstdout is exactly one parseable line. No change to auth, RLS, checkpoint/materialization safety, or excluded paths; reset detection andGitStateare untouched, so no spurious transition on upgrade.Related issues
None.