Repository navigation
fix: forward security pins and fixes from main to next (HACK-1758) - #545
Merged
Merged
Conversation
Forward main's 69ed70e to next: every workflow including prerelease.yml and bun-crash-experiment.yml, .tool-versions, mise.toml, packageManager, the Dockerfiles, the candidate build guards, and the e2e and pattern fixture image tags. The pattern fixtures take the 1.4.4-alpine index digest from the registry; the same lookup reproduces the old 1.4.2 digest. mise.toml is a reviewed input of .hack/tasks.json, so its pin is updated. The raw-file method reproduced the unchanged pin first. The native file and storage helper stays on oven/bun:1.4.2-slim: its catalog pins image, Bun and libc bytes that need separate runtime qualification. Helper bytes still reproduce under Bun 1.4.4 and the helper entry reaches no Brotli, zlib or fetch path. CI pulls both images. The frozen benchmark workload pin also stays. b5d3212 needs no port: tests/helpers/env.ts already deletes unset vars. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(HACK-1758) Forward main's 739c4e5 and 75225e0 where next was below main: - zod 4.3.5 to 4.6.7 (GHSA-mrhx-c7xc-m3w5) - @modelcontextprotocol/sdk 1.26.0 to 1.32.0 - drizzle-orm ^0.44.5 to 0.45.2 in packages/db - next 16.2.6 to 16.4.1 in examples/next-app site/ already pins next 16.4.3 and zod 4.6.7. Both lockfiles are regenerated. examples/next-app/bun.lock had still resolved next 16.1.1. drizzle-orm 0.45 no longer auto-installs its prisma peer, so the prisma tree leaves the root lockfile, as it already has on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Forward main's 4c194a1. Native keychain access goes through one gated boundary (src/lib/keychain.ts), so HACK_SECRETS_DISABLE_KEYCHAIN_FALLBACK stops every read, write and delete, and tests/preload.ts replaces Bun.secrets with an in-memory backend for the whole `bun test` process. Adapted for next: the preload runs before the state guards, so their baseline holds the flag. The fake is not frozen because next's tests spy on it. Tests that use a stubbed keychain open the gate explicitly and restore it with restoreEnv. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…OME/HACK_HOME (HACK-1758) Forward main's 98ab4e7 (HACK-1357). An empty or relative HOME, or a relative HACK_HOME, made Hack resolve the registry, daemon socket and secret key paths under <cwd>/.hack. src/lib/home-dir.ts now normalizes the home, refuses a relative HACK_HOME and throws when no absolute home exists. Beyond main's call sites, next has the same bug in code that main lacks: MCP session capture, launchd access, the native AWS environment and setup migrate. Each now uses the helper, with regression tests for the MCP and launchd paths. MCP sessions keep resolving a relative HACK_HOME against the captured checkout: tests/mcp-session-context.test.ts pins that as next's contract. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Forward main's 83d71ac (HACK-1351). Runtime commands in a linked worktree created .hack/hack.branches.json or rewrote its last_used_at on every up, down, restart, run, exec and logs, which dirtied git status. Usage now lives in machine-local .hack/.internal/branch-usage.json. The tracked registry is written only by `hack branch add` and `remove`, and legacy last_used_at values are read as a fallback and left byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HACK-1758) Forward main's 65e4d97 (HACK-1346). Fixture names are recorded as owned in memory and in a per-PID journal. A Docker scenario that leaves an owned network now fails, the harness sweeps by exact ID and never prunes, run.ts sweeps on SIGINT, SIGTERM and SIGHUP and reclaims journals from dead runs, and downBestEffort uses the real `<name>--<branch>` project name. Adapted for next: run.ts keeps its launchd canary and import.meta.main guard, and the new test restores its env var for next's state guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Next already inherited primary local overlays in linked worktrees, but main's 736544e regression suite found these gaps, 24 of its 36 cases failing before this change: - verify the Git worktree family before reading a primary checkout, so a redirected .git file cannot pull in another checkout's local overlays - add the HACK_WORKTREE_INHERIT_LOCAL=0 opt-out, checked before any Git work - only unreadable or malformed project config blocks inheritance, not a field error elsewhere in a well-formed file - reject non-regular inherited files, and fail closed on unreadable or unparsable ones with a path-only error - route inherited-only worktrees to the YAML env commands - have doctor report a materialized .env whose inputs are all gone as obsolete Two of main's test expectations keep next's behaviour: the layer guard's refusal message for a symlinked default file, and the tombstone a local unset leaves so it can hide an inherited value. The graph runner keeps following only worktree.inherit_local, since its adoption proofs record that setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CK-1758) Forward main's 395351b and 541c611. Jobs on Blacksmith Ubuntu runners use useblacksmith/checkout@v1 with the same inputs; jobs on hosted and macOS runners keep actions/checkout, as on main. No permissions or secrets change. Main's 1c1cc98 runner sizing is left out: its measurements come from main's single docker-e2e job, and next shards that job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ACK-1758) Forward main's 2dd3853. The sample digest listed entity names and a contact address from a private knowledge base. This commit is outside the HACK-1758 list; it is carried because next is public and still had the names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runtime commands no longer write hack.branches.json, so the note in this test no longer describes what `up` does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bun 1.4.2 did not carry a process.env delete into its native environment, so a deleted proxy kept routing later fetch calls. Bun 1.4.4 does. The sentinel test that was written to fail on this upgrade now asserts the new behaviour, and fails if the old one returns. restoreEnv keeps its empty assignment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CK-1758) An inherited local overlay loop must keep next's closed, value-free refusal, so the layer guard runs before validatePrimaryLocalFile. The ported test now spawns the CLI with privateCliEnv, as next's isolation scan requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Next's test files share one process, so a path saved by an earlier file outlived a later HACK_E2E_OWNERSHIP_DIR and the journal went elsewhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Handing the release credential to a third-party action on a floating tag is a security decision of its own, so it isn't part of this port. Other checkout steps stay on Blacksmith. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
roodboi
added a commit
that referenced
this pull request
Nov 2, 2026
Fixes the cause of HACK-1777, which blocks merging #545. ## Problem MCP SDK 1.32 closes a stdio client's transport when its read buffer passes 10 MiB. Hack's 8 MiB capture budget bounds raw child output only. The result repeats that output as `content` text and as `structuredContent`, log results add `data.events`, and JSON escaping grows each copy. A large result could end the client's whole session. ## Change `boundMcpToolResult` (`src/mcp/result-budget.ts`) runs at the single exit of the `tools/call` dispatcher, so it covers every tool. It limits the serialized result to `MCP_TOOL_RESULT_LIMIT_BYTES` (9.5 MiB of UTF-8), which leaves 512 KiB under 10 MiB for the JSON-RPC envelope and the start of a following message in the client's last read. A result within the limit is returned unchanged. Over the limit, data goes in this order: 1. **`content` text.** It repeats `structuredContent` with extra escaping, so it buys the most and loses the least. It becomes a view of at most 256 KiB, in the same JSON shape, with its own `outputTruncated` and cutoff. It is not removed outright, because some clients read only this channel. 2. **Log tools: `stdout`, then whole events from the end.** `stdout` repeats the events. **Other tools: `data`, then the end of `stdout`.** Omitting `data` matches what a capture cut already does. 3. **The end of `stderr`.** Its first 16 KiB are protected from step 2. What a caller sees: - A cut never splits a code point. Sizes are measured with the real serializer, in bytes. - `structuredContent` that was cut has `outputTruncated: true` and a fixed note ending `stderr`. - `structuredContent` that fits after step 1 is complete and has no flag. Only the text view says it was cut. - When events are removed from a successful collection, `data.cutoff` is `{bound: "bytes", limit: 9961472}` and `data.count` is the number kept. `data.stop_reason` still says why collection stopped. - A backend failure stays a failure with a null cutoff. - `ok` and `exitCode` still describe the command. A command that exited 0 stays `ok` when its result is cut. Docs updated: `docs/reference/mcp-tools.md`, `docs/reference/mcp-logs.md`, `docs/guides/agent-first-setup.md`, `tests/e2e/README.md`, and the comments in `src/mcp/logs-contract.ts`. No agent guidance changed, so nothing was regenerated. ### Please check these choices - **Text-only clients get less.** A client that reads only `content` now sees 256 KiB of a large result, not all of it. `structuredContent` keeps the most because the 8 MiB capture must still fit, so the text cannot share the budget equally. Clients on `structuredContent` are unaffected. - **Two channels can differ.** On a cut result the text view has fewer events than `structuredContent`, and its cutoff limit is `262144`. Each channel is truthful about itself, but they are no longer the same JSON. - **The note can appear in `stderr` of an env tool** that normally has an empty one on success. The note is fixed text and carries no values. ## Verified Local runs on Bun 1.4.2 with a private temporary HOME and HACK_HOME and `HACK_DAEMON_AUTOSTART_DISABLE=1`, at this head. **New test, `tests/mcp-tool-result-size.test.ts`:** 13 pass. It drives the real server over stdio with a real SDK client on default limits, using a stub `HACK_MCP_COMMAND`. A `tee` records the bytes the client reads, so the size assertion holds on any SDK version. Coverage: - log snapshot and log tail above 8 MiB, with emoji and characters JSON escaping expands - a complete log stream that goes over the message limit - a non-log tool above the capture limit, and one under it that exits 0 but expands when escaped - a text-only cut, and a small result that must stay unchanged - the session staying usable after each result, with the SDK validating against `outputSchema` - unit cases: budget edges around surrogate pairs, an oversized first event, backend failure, the stderr floor, a result with no typed payload, and an unexpected oversized field **Client-only control**, with the server from unfixed `origin/next` on SDK 1.26 and only the client changing: | Client SDK | Server | Result | | --- | --- | --- | | 1.26 | unfixed | OK, 33.7 MB result | | 1.32 | unfixed | `MCP error -32000: Connection closed` | | 1.32 | fixed | OK, 9,961,406 bytes, 1012 events, cutoff `bytes` / 8388608 | **SDK 1.32 on client and server:** the #545 tree in a scratch copy, with this fix applied, passes 13 of 13. **Mutations:** | Mutation | SDK | Result | | --- | --- | --- | | Remove the bound from the dispatcher | 1.26 | 6 fail; the size assertion sees lines up to about 68 MB | | Remove the bound from the dispatcher | 1.32 | 6 fail; `Connection closed` in the output | | Measure string length instead of bytes | 1.26 | 6 fail | **`checkByteCutoff` in `tests/e2e/scenarios/mcp-logs-compose.ts`:** unchanged, including its 8 MiB limit assertion. Reparsed events can't outgrow the 8 MiB capture unless the source JSON uses unusual number spellings, so canonical events fit the budget and the capture limit stays the one reported. The new test makes the same checks with a stub: a complete ordered prefix of whole events, and no split code point. **Gates:** - `bun run --cwd packages/cli typecheck` passes. - Biome passes on changed code files. - Focused MCP tests pass: 73 across `mcp-tool-result-size`, `mcp-lifetime`, `mcp-logs-contract`, `mcp-inspect-tools`, `mcp` and `mcp-session-context`. - `bun run docs:check` passes. - gitleaks finds no leaks over `origin/next..HEAD`. ## Not verified - **`mcp-logs-compose` itself was not run locally,** because it needs Docker. Its logic was read and matched with a stub, not executed. The ticket's done condition requires it to pass on SDK 1.32 defaults, so it needs a `docker-e2e` run on the merged result with #545. It can't prove that on this branch alone, because this branch has SDK 1.26. - **Full suite and hosted CI:** the full suite was not run locally. See this PR's checks for CI. - **Real clients** other than the SDK (Claude Code, Codex and others) were not measured. - **Concurrent large results:** the 512 KiB margin is reasoned from how `ReadBuffer` drains complete lines, not tested with parallel calls. - **Published prerelease:** the ticket also requires this on `next` and in a prerelease, which happens after merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Dimitri Kennedy <344842+roodboi@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…-security-pins Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…K-1758) The server resolved these against the session cwd, but it passes the raw env to child commands, and they now refuse a relative HACK_HOME. Tool calls then failed after the server had read a different registry. A session now refuses both overrides with the CLI's error. A socket client with such a context loses only its own session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-1758) recordBranchUsage is called for up, down, restart, run, exec and branch add; nothing calls it for logs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…HACK-1758) Under Bun 1.4.4 no process.env route found leaves a hidden native change: set and delete, loadEnvFile, a Worker, libc setenv, a replaced env object. The fixture therefore presents a changed native environment through a small seam in the guard's reader. This tests the boundary check and its report, not a real Bun leak. Also reword tests/mcp-tool-result-size.test.ts: the pinned MCP SDK 1.32 has the 10 MiB read limit that its comments treated as optional. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Child commands run in the session cwd and accept a relative HACK_GLOBAL_CONFIG_PATH, so refusing it stopped the server in a setup where the CLI works. The session resolves it against the captured cwd again, which matches them. It still refuses a relative HACK_HOME, as children do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The seam stayed set for later files, which left the native check blind there. It now answers the guard's next read only, and a nested test checks that a later file gets the real reader back. The fixture name and the outer test now say that this covers detection and reporting, not the repair. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-security-pins Brings in #436. Conflict in the doctor env materialization check: keep its refusal for an unusable layer entry, and keep inspecting a legacy-format project that has recorded materialization state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-security-pins Brings in #543 and 9057298. secrets.ts and secret-store.ts conflicted because each side wrapped the native keychain. The shared keychain now gates first and then calls #543's deadline-bounded nativeKeychain, so every user gets both, and keychainKeyAccessError stays as #543 wrote it. A new test covers the order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps this branch's keychain-disabled check after key bootstrap, behind the shared guard. That refusal now throws SecretStoreKeyUnavailableError from PR 538, which the shell-value check depends on. With the check after bootstrap, a run with no key and no store is first use and creates a key file, keychain or not. PR 538's recovery test asserted that step stopped, which held only through the old early refusal, so that step is removed. Its afterEach now restores the preloaded keychain setting for the state guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi
added a commit
that referenced
this pull request
Nov 3, 2026
Keeps both import additions in the e2e fixture: this PR's fixture git environment and next's gitignore markers and Docker ownership record. Three git spawns that arrived on next (#545 and the HACK-1709/1711 follow-ups) built their own git environment, which this PR's scan refuses. They now use fixtureGitEnv. The legacy-scope test keeps its private HOME; its commits now carry the shared fixture identity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 3, 2026
roodboi
added a commit
that referenced
this pull request
Nov 3, 2026
… deadline (#499) **Update at 3e85b9b (second `next` merge, at c019124):** this brings in #557, #564, #562 and #563. The only conflict was at the end of `tests/native-compose-adoption-routing-generation.test.ts`. This PR adds live caller budget cases there, and #563 adds the HACK-1359 mutation deadline refusal; both are kept. Typecheck and Biome pass on Bun 1.4.4. The five adoption generation test files and the compose-env-at-rest tests pass (235/0). The file-owner tests need a built config compiler; once it was built with Rust 1.97.1, they passed on their own (29/0). **Update at 04abddb:** merged `next` at 1df527b (#545, #419, #337). The one conflict was in `tests/native-authored-project-start.test.ts`: #419 renamed the stand-in owner cap to `STAND_IN_CAP_MS`, sized from the case ceiling. Both sides are kept, and the failed-case test's Bun timeout now uses the named fixture margin, which is the same 5000 ms. Typecheck and Biome pass on Bun 1.4.4. - `tests/native-compose-adoption-generation.test.ts` run on its own passes: 123 pass, 0 fail. - In one earlier 4-file run at a host load average of about 7–9, two of its cases failed. Both pass when rerun on their own. One of them refused with `E_CONFIG_INVALID` for a cancellation, tracked as HACK-1810 and noted on HACK-1458. - HACK-1458's 10 loaded runs have not been done. > **Current state (bd55b86, merges `next` at e5d907d).** This head adds the local Oct 27 to 31 recovery work that was never pushed: live adoption transaction deadlines, retained routing fixture budgets, retiring proven pre-ready host processes after cancellation, a faster startup compiler stand-in, and a per-case failure cleanup for stand-in host groups. > > Checked at this head: `tsc` passes, and 233 tests pass with 0 failures across the adoption generation and routing files, authored project start, and the fixture helper tests. These ran on a host at load 8 to 10, once, with no added load generator. > > Not done: the ticket's 10 consecutive loaded runs of the affected files have not been run at this head, and the failed Oct 26 verification has not been re-run. An independent read-only review by `claude-hd` (the Codex route is limited until Nov 4) read the code at c520acc and did not run it. It recommended merging, with four low findings. The mux cleanup finding is fixed at bd55b86, proven by two mutations, and the delta after that review has not been reviewed. The others are in HACK-1801. It raised two decisions for the owner: pre-Ready teardown stops host processes without running the authored down hooks, and teardown runs the compiler again before checking for Removed, which can slow cancellation. The Evidence and Gaps sections below describe earlier heads. Refs HACK-1458 (unit G106). Product fix for the four product clocks that kept HACK-1367's loaded runs from passing. Test-side budgets stay with #419 (G52/G105); this PR adds new cases at the end of the two shared test files and edits none of #419's hunks. ## Root cause 1. **Public adoption dispatch.** `tryLegacyComposeAdoptedCommand` opened its adoption store without a deadline, so every store inspection phase (one probe owner) got the default 15 s aggregate clock, whatever `HACK_COMPOSE_STARTUP_TIMEOUT_MS` allowed. A slow engine expired it as `E_NATIVE_COMPOSE_PROBE_TIMEOUT`; `translate()` turned that into `E_LEGACY_ADOPTION_STATE`, and the command reported `E_CONFIG_INVALID` ("state is invalid, unsafe or changed"). The command's own deadline only started after `loadActive`, so inspection before it was never bound by the caller. 2. **Authored compiler calls.** `acquireNativeExecutionInputs` never passed a compiler timeout, so each compiler invocation (and the later `assertFresh`/value checks) kept the transport's fixed 10 s default, even with minutes of startup budget left. 3. **Source5 cancellation after readiness hang.** Reproduced: after `onReady` aborts, the cancel's `down.before` re-acquires inputs with the hard signal. That acquisition also kept the fixed 10 s compiler clock. When one compiler call took longer, `down.before` failed at `acquire` (no hook ran), `onStopFailure` fired, and the start retained the live owner and host processes, waiting for a hard cancel that the case never sends. With a scratch copy whose compiler sleeps 11 s after readiness, the case hung for its whole 420 s budget, with hook events `down.before:acquire` then `down.before:stop-operation` and no down hook, the same symptoms as the loaded report. 4. **Failed authored cases leave host process groups running.** This is a product path, two of them: - (i) Startup inputs check freshness under the startup signal. Once a cancel or the startup deadline (its timer aborts the same signal) ends startup after the host processes started and before reservation, `stopUnstartedHostProcesses` could never pass `hostProcesses.stop()`'s freshness check (it re-reads inputs with the aborted signal), so the groups stayed up. A loaded local run of a new case showed it directly: startup expired after `host-process-ready.json` was written, the start failed, and the mux window group was still alive afterwards. - (ii) `createNativeAuthoredHostProcesses().start()` had no failure path. A failure after the launch intent (cancel, deadline or refused check during the launch) left the mux session running behind an intent that recovery never converts into authority. Failures after reservation still retain host processes on purpose, with complete owner/ready/state records that `hack down` recovery stops (`retireNativeAuthoredHostProcesses`). Ending those in a test is test cleanup (#419). ## Fix - **Adoption** (`native-compose-adoption-generation.ts`, `native-compose-adoption-command.ts`): - The store takes an optional caller `deadline`. With one, each inspection phase gets `legacyAdoptionProbePhaseMs`: the remaining time, capped at `LEGACY_ADOPTION_PROBE_PHASE_MAX_MS` (60 s). An expired deadline refuses as `E_LEGACY_ADOPTION_DEADLINE`, and an unclassified failure after a probe timeout or past the deadline becomes `E_LEGACY_ADOPTION_DEADLINE`, not `E_LEGACY_ADOPTION_STATE`. A store without a deadline is unchanged. - The command starts one deadline (`HACK_COMPOSE_STARTUP_TIMEOUT_MS`) before it opens the store and uses it for inspection, the retained operation, the plain container runner and `ps` inspection. `E_LEGACY_ADOPTION_DEADLINE` maps to `E_STARTUP_TIMEOUT`. The message stays the store's fixed text, so the closed `legacy-deadline` diagnostic token is unchanged. - **Compiler** (`native-config-compiler.ts`, `native-project-validation.ts`, `native-execution-inputs.ts`): named `NATIVE_CONFIG_COMPILER_MIN_TIMEOUT_MS` (1) and `NATIVE_CONFIG_COMPILER_MAX_TIMEOUT_MS` (60 000), `nativeConfigCompilerTimeoutWithin(remainingMs)`, and an optional `compilerTimeoutMs` getter that every compiler invocation of a selection reads, including later freshness checks. - **Authored start** (`native-authored-project-start.ts`): - Startup compiler calls use the remaining startup deadline (floor 1 ms, ceiling 60 s). Calls outside startup (down-hook acquisition, checks after ready) have no startup deadline and take the 60 s ceiling, so a slow host no longer fails a graceful stop. - Before stopping unstarted host processes, teardown re-acquires inputs under the hard signal and refuses a changed selection, like the down hooks do. - **Host processes** (`native-authored-host-processes.ts`): a start that fails after its launch intent calls the controller's `abort()`, which ends the session through the ownership token it created. Owner bookkeeping is cleared only when every launched group was recorded and is proven absent within 5 s. Otherwise it stays retained. - Docs: `docs/reference/native-compose-adoption.md`, `docs/reference/native-authored-commands.md`. ## Tests New cases, each proven by mutation (product change reverted in a scratch copy): | Case | File | Clock | Mutation result | | --- | --- | --- | --- | | `HACK-1458: public dispatch inspection outlives the former fixed 15 s phase clock while the caller deadline remains` | adoption generation | injected `Date.now` jump of 16 s after the first engine probe, 120 s deadline | base: `E_CONFIG_INVALID` "state is invalid, unsafe or changed" | | `HACK-1458: an expired public dispatch deadline ends inspection as a startup timeout, not invalid config` | adoption generation | injected 6 s jump, 5 s deadline | base: returns `0` (inspection ignored the expired deadline and started the container) | | `HACK-1458: a startup compiler call may outlast the compiler's 10 s default while the startup deadline remains` | authored start | real: one `compile` sleeps 10.5 s | wiring reverted: `stage inputs (E_COMPILER_TIMEOUT)` after 11.4 s | | `HACK-1458: Source5 cancellation after readiness settles when its down acquisition compiler is slow` | authored start | real: first compiler call after readiness sleeps 10.5 s | pending (running) | | `HACK-1458: cancellation after host processes start and before reservation stops them` | authored start | real: second `frontend-plan` held until cancelled | teardown refresh removed: `process-order` is `started` only and the host group stays alive | | `HACK-1458: cancellation during the host process launch ends the launched group` | authored start | real: first `list-panes` delayed 1 s | `abandonLaunch` made a no-op: the owned mux session (`mux.json`) stays | ## Evidence In progress: 10-consecutive loaded runs of both whole files are running locally (M5, Bun 1.4.2, private HOME/HACK_HOME/DOCKER_CONFIG, keychain fallback disabled, unreachable DOCKER_HOST, added CPU load stopped in a trap), on this branch plus #419's test-side budgets. Hosted CI at the exact head is the full-suite evidence. This section will be updated with results. ## Gates - gitleaks 8.30.1 `dir` over a `git log -p origin/next..HEAD` export: no leaks. - Root `bun run check` (includes `privacy:check`): passed. `bun run --cwd packages/cli typecheck`: 0 errors. Biome on the changed files: no new warnings (the 8 existing complexity warnings in `native-compose-adoption-generation.ts` remain). - The CLI surface and consumer agent guidance did not change, so `docs:cli-reference` and plugin regeneration do not apply. - `origin/next` merged at 246f1b4. ## Gaps To be completed with the evidence. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/hack-dance/codesmith/hack/pr/499?autoLogin=true&ref=codesmith_pr_footer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v3.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png" width="162" height="28"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1795466280&installation_model_id=17053&pr_number=499&ref=codesmith_pr_footer&repository=hack-dance%2Fhack&return_to=https%3A%2F%2Fgithub.com%2Fhack-dance%2Fhack%2Fpull%2F499&signature=6606080a151b569805ac7899cdf5e25b54266947167b30eaa5ddade6721505a5"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png" width="190" height="28"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Dimitri Kennedy <344842+roodboi@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.
Update at 7bd4fa0: merged
nextwith #538 (conflicts indoctor.ts,project-env-config.ts,secret-store.tsand #538's shell-value test). The keychain-disabled refusal is now aSecretStoreKeyUnavailableError, which #538's check depends on. One behaviour change: with the keychain disabled, no key and no store file, a run counts as first use and creates a key file, as onmain(#423). Onnextbefore this merge it stopped with a refusal, so I removed the one step of #538's test that asserted that stop. The #538 test also restores a preloadedHACK_SECRETS_DISABLE_KEYCHAIN_FALLBACKinstead of deleting it. Focused env, secret-store and doctor tests pass on the pinned Bun 1.4.4. Details on HACK-1758.Update at ede96a8: merged
nextwith #543 (conflicts insecrets.tsandsecret-store.ts); the shared keychain now gates first and then uses #543's deadline-bounded calls, with a new test for that order.Update at 03a0c75: merged
nextwith #436 (one conflict, in doctor's env materialization check, kept both sides); focused env, doctor and MCP tests show no new failures on Bun 1.4.4.Update at b2ca3cd: review findings at 30f2b1b
This note replaces the older text below where they disagree.
nexthad not moved, so there was nothing new to merge.HACK_HOME. A relativeHACK_GLOBAL_CONFIG_PATHresolves against the captured cwd again, which matches child commands because they run in that cwd. The previous note's refusal of that setting was a mistake and is reverted: it made the server refuse what the CLI and its children accept.disableNativeProxies) works.Verified here (private HOME and HACK_HOME,
HACK_DAEMON_AUTOSTART_DISABLE=1):bun run typecheck,bun run check,bun run docs:check, gitleaks, and focused tests: 264 pass, 61 skip, 0 fail across 31 files, all*mcp*files included.HACK_GLOBAL_CONFIG_PATHagainHACK_GLOBAL_CONFIG_PATHagainst the process cwdHACK_HOMEagainst the session cwdNot rerun: the whole
bun run testsuite on this head; hosted CI is the check.Update at 30f2b1b: review findings at 676bf9b
This note replaces the older text below where they disagree. Merged current
next(f94b17ff, including #547) with a merge, not a rebase.ffc49b33RelativeHACK_HOME:captureMcpSessionContextnow refuses a relativeHACK_HOMEorHACK_GLOBAL_CONFIG_PATHwith the CLI's error wording. The earlier "MCP sessions still resolve it" note no longer applies. A socket client with such a context loses only its own session. The CLI itself still doesn't check a relativeHACK_GLOBAL_CONFIG_PATH; only MCP sessions refuse it.71975bb6: droppedlogsfrom the commands that record branch usage, indocs/cli.mdandsrc/lib/branch-usage.ts.30f2b1bfGuard positive case: this is a seam, not a real leak. Under Bun 1.4.4, these routes left no native change thatprocess.envhides: set and delete,loadEnvFile, a Worker, libcsetenv, and a replaced env object. So the fixture hands the guard's native reader a changed environment. It tests the boundary check and its report, not a real Bun leak.30f2b1bfSDK comment: reworded intests/mcp-tool-result-size.test.tsfor the pinned SDK 1.32.RELEASE_PAT: stays onactions/checkoutinrelease.ymlandrelease-prepare.yml(676bf9b7). The earlier note aboutuseblacksmith/checkoutreceiving it and the 9-step count no longer apply.Verified here (private HOME and HACK_HOME,
HACK_DAEMON_AUTOSTART_DISABLE=1):bun run typecheck,bun run check,bun run docs:checkand gitleaks overorigin/next..HEADpass.*mcp*files included.tests/mcp-tool-result-size.test.tspasses (13).hack mcp servestart with either relative override exits 1 with the error.Read, not run:
checkByteCutoffintests/e2e/scenarios/mcp-logs-compose.tsshould still hold. Events are parsed from the capture, so they total about 8 MiB and fit under the 9.5 MiB result limit. No event is dropped, so the reported cutoff stays{bytes, 8 MiB}, and #547 only trimsstdouton a code point boundary. Docker wasn't used; hosted CI runs this scenario.Not rerun: the whole
bun run testsuite on this head; hosted CI is the check.Current head:
676bf9b7. It mergesnextatddc73a0dand keepsRELEASE_PATonactions/checkout@v4inrelease.ymlandrelease-prepare.yml.mainpasses that credential touseblacksmith/checkout@v1, a third-party action on a floating tag. Doing the same onnextis a security decision of its own, so it isn't part of this port. All other checkout steps ported here stay on Blacksmith. This changes the "RELEASE_PATnow goes throughuseblacksmith/checkout" point below.Refs HACK-1758. Forwards the security pins and fixes from
maintonext, one topic per commit.mainis not merged. Each commit was checked by readingnextand, wheremainhas a regression suite, by running that suite against unmodifiednext.Classification
maincommit69ed70e9#4572c84aac3: all workflows includingprerelease.yml,.tool-versions,mise.toml,packageManager, Dockerfiles, candidate build guards, e2e and pattern fixture tagsb5d3212aHACK-1404undefinedtests/helpers/env.ts:12(restoreEnv, used by 109 test files). Noprocess.env.X = undefinedremains, and every restore of a value that may be undefined is guarded. Bun 1.4.4 did change proxy deletes, handled in2852393c395351bc#454bunecosystemf97ca462:.github/dependabot.yml, same asmain. GitHub reads this file from the default branch, so it has no effect untilnextlands739c4e57#456dd92fae0: MCP SDK 1.26.0 to 1.32.0, drizzle-orm ^0.44.5 to 0.45.2, next 16.2.6 to 16.4.1 inexamples/next-app;site/already has next 16.4.375225e00#513dd92fae0: zod 4.3.5 to 4.6.7;site/already has it4c194a12#42380abd5fc:src/lib/keychain.ts,tests/preload.ts,tests/keychain-guard.test.ts. The fake is not frozen, becausenext's tests spy on it3312658f#526src/lib/logs.ts:62,:91andsrc/ui/loki-logs.ts:442, from #417 and #478.main'stests/loki-tail.test.tspasses unchanged on unmodifiednext98ab4e70#527ac12697c:src/lib/home-dir.ts, plus the same bug innext-only code: MCP session capture, launchd access, native AWS environment, setup migrate65e4d97b#5288a79897a,32cd2edf:tests/e2e/docker-ownership.ts, harness andrun.ts736544ef#529nextalready inherited (resolvePrimaryLocalProjectDir). 24 of the 36 cases inmain's test failed on unmodifiednext.40d0df6f,e4837dcaadd worktree-family verification, the env opt-out, fail-closed reads, YAML routing and doctor's obsolete report83d71ac7#530eb30178f,d925c5ef:src/lib/branch-usage.ts;touchBranchUsageis gone541c611b#520useblacksmith/checkouton Blacksmith Ubuntu runnersf97ca462: 9 steps acrossci,release,release-prepare,release-node-runtime-image. Hosted and macOS jobs keepactions/checkout, as onmain1c1cc983#521main's singledocker-e2ejob, andnextshards that job. The original PR itself marks the sizes as unbenchmarked, and it adds cost08e0412a,2e8dc61d,91b14bf0,60f3c0b9,010880c7,5e03d47enexthas its own release line2dd3853b#51225dfed3e:nextis public and still carried these names. Drop this commit if you want it handled separatelyPins that stay on the old version
oven/bun:1.4.2-slim(src/lib/native-compose-vm-file-protocol.ts:6,src/lib/native-compose-storage-witness-docker-artifact.ts:19,:66). The catalog pins exact image, Bun and libc bytes per platform, andscripts/generate-native-storage-witness-helper.tssays changed artifacts need separate runtime qualification. Moving it is a product decision. Evidence: the helper bundle reproduces byte for byte under Bun 1.4.4, and its source has no Brotli, zlib or fetch use. Bun 1.4.2 is still in that image. CI now pulls both images, anddocs/reference/native-config-files.mdstill names 1.4.2-slim correctly.scripts/application-benchmark-protocol.jsonandscripts/check-application-benchmark-protocol.pyfreeze this pin as part of the protocol, andscripts/benchmark-native-compose.pydefaults to the same image. No Bun from these ships.src/agents/client-capabilities.ts:501, thedocs/versions/snapshots,scripts/test-shard-durations.json, and comments describing 1.4.2 behaviour. The capability record still says MCP SDK 1.26.0 and needs a new observation.1.4.2only as sample data.Behaviour to review
HACK_HOMEis now an error in the CLI. MCP sessions still resolve it against the captured checkout, becausetests/mcp-session-context.test.tspins that asnext's contract.HACK_WORKTREE_INHERIT_LOCAL=0applies to the Compose path. The graph runner follows onlyworktree.inherit_local, because its adoption proofs record that setting.docs/cli.mdsays so..gitlink that cannot be verified, such as a bare-repo worktree, now reads no primary overlays instead of failing the command.main'sproject-env-worktree-localtest keepnext's behaviour: the layer guard's refusal for a symlink leaving the project, and the tombstone fromenv unset --local, which hides an inherited value.tests/global-state-guard.test.tschanged because Bun 1.4.4 now carries aprocess.envdelete into its native environment. That test was written to fail on this upgrade. It now asserts the new behaviour and fails if the old one returns. The guard's native check has no reachable leak under 1.4.4 and stays as a regression check.useblacksmith/checkoutnow receivesRELEASE_PATinrelease-prepare.ymlandrelease.yml, as onmain. No permissions or secrets changed.25dfed3eremain in git history. This PR does not rewrite history.Verified
Local, on macOS arm64 with Bun 1.4.4 from mise. All tests ran with a private temporary HOME and HACK_HOME,
HACK_DAEMON_AUTOSTART_DISABLE=1, and aDOCKER_HOSTpointing at a socket that does not exist.bun install --frozen-lockfile: pass.bun run typecheck,bun run check,bun run docs:check: pass on the head.origin/next..HEAD: no leaks..hack/tasks.json: the method first reproduced the oldmise.tomlpin and all threepackage.jsonscript pins. Onlymise.tomlneeded re-pinning, andtests/project-task-catalog.test.tspasses.tests/integration-ownership.test.tspasses with no fingerprint change.tests/doctor-fix-noninteractive.test.tsand are described below.bun run testran once, before the last three fix commits and before rebasing onto #542: 8505 pass, 127 skip, 27 fail across 598 files.next?native-compose-ingress,native-compose-proxy-probeDOCKER_HOSTglobal-state-guardnextwith 1.4.4, pass with 1.4.22852393c, passingproject-env-local-inheritance,cli-spawn-env-isolation,e2e-docker-ownershipe4837dca,32cd2edf, passingnative-compose-route-claims(Unexpected end of JSON input)The 5
doctor-fix-noninteractivefailures appear only when that file runs alone under plainbun testwith this isolation. Unmodifiednextfails the same 5 the same way, and they passed in the full run.Not verified
docker-e2e. No Docker scenario ran locally, so the 1.4.4 and 24.22.1 image tags,docker-ownershipagainst a real engine, andworktree-parallel-upare unproven until CI runs.next, a published prerelease, and the published build reporting the new pin.🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.