Skip to content

train b50 integration gate (do not merge) - #459

Closed
roodboi wants to merge 22 commits into
nextfrom
train/b50
Closed

roodboi wants to merge 22 commits into
nextfrom
train/b50

Conversation

@roodboi

@roodboi roodboi commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Integration gate for merge batch b50: origin/next + #404 (HACK-1337) + #446 (HACK-1385) + #430 (HACK-1373) + #439 (HACK-1378) + #421 (HACK-1361) + #448 (HACK-1391) + #452 (HACK-1390) + #405 (HACK-1327, HACK-1392). Tree 5f938772. Closed after the batch squashes.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

roodboi and others added 22 commits October 22, 2026 04:25
…ls, null platform and external volumes

The native Compose-backend classifier reserved a hand-written, incomplete
set of ownership label namespaces, treated `platform: null` as a foreign
platform, and did not classify external volumes, which the native runtime
refuses as `external_volume`.

- Add `HACK_OWNERSHIP_LABEL_NAMESPACES` as the single source of truth for
  the label namespaces Hack writes (io.hack-local, io.hack.native-config,
  io.hack.native-file, io.hack.storage-witness) and derive the reserved set
  and its refusal phrase from it. A test scans the TypeScript and Rust
  runtime-core sources so every io.hack label key is covered and no
  namespace is stale.
- Treat an absent or null `platform` as unset, as Compose and native review do.
- Classify `volumes.<v>.external` (true or the legacy {name} form) as the
  new closed feature `external_volume` in the refusal, doctor output and docs.
- Document that only `.hack/docker-compose.yml` and the explicit `platform`
  field are classified; overrides, `extends` and `include` are not followed.
- Move `checkComposeBackendFeatures` so `checkProjectEnvKey` keeps its JSDoc.

Refs HACK-1337

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every file in `bun test` shares one Bun process. A test that changed PATH,
HOME, HACK_HOME or cwd and then failed before its restore leaked that change
into every later file, which then failed with "Executable not found in
$PATH: git/bun" and hid the first failure (HACK-1327).

- Preload tests/helpers/global-state-guard-preload.ts from bunfig.toml. It
  snapshots the environment, cwd, umask, exit code, guarded global functions
  and process listeners around every test and at every file boundary, fails
  the leaking test or the run with a value-free message naming the file, and
  restores the environment, cwd, umask and exit code so later files run
  clean. Replaced functions and listeners are reported but kept, because
  they may still sandbox a timed-out test's unsettled work.
- Fix leaks it and a review found: fzf replaced the process.env object;
  the VM-file, retained-build and retained-source-bind helpers could fail
  setup after changing PATH/HOME with no restore registered; the retained
  routing and retained-file fixtures kept HACK_HOME/PATH on the fixture when
  their lifetime was unknown; the protected-files negative left a
  process.kill spy for later files.

Refs HACK-1327

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d exclude only carrier proofs

- A carrier boundary now keeps a refusal's own observed reason (an issued
  helper policy boundary or a classified command cause) even when it is caught
  after the carrier deadline. `storage-budget` is reported only for a
  deadline-shaped refusal (timeout, budget, lifetime mark or no observed cause)
  whose carrier deadline expired and was not cancelled.
- A recorded storage command that replaces an admission refusal with its fixed
  command refusal now carries the source's issued boundary, so a helper policy
  refusal observed during admission is no longer relabeled `storage-budget` at
  `storage-helper-start`.
- The start deadline's storage exclusion now wraps only each carrier proof
  (carrier creation and witness verification). The guard's freshness checks
  and saved-state reads consume the start budget.
- The exclusion-cap comment states that the uncapped sum is finite,
  (5B+3) x V x S, but can reach hours.

Refs HACK-1361

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chctl shim per test

The M3 full suite at the first guard commit failed 16 shared-host-lifecycle
cases (helpers reported as foreign listeners or never ready) and flagged
tests/e2e-launchd-guard.test.ts for a process exit listener.

- Root cause of the shared-host cascade: the engine-identity ambient-proxy
  test set HTTP(S)_PROXY and their lower-case forms, then restored them with
  a delete. Bun 1.4 copies assignments to proxy keys into the environment
  that fetch and Bun.spawn (without env) use, but never a delete, so every
  later loopback fetch in the process went through the dead proxy while
  process.env looked clean. restoreEnv now assigns "" before deleting.
- The guard now also compares Bun's native environment at each file
  boundary (one /usr/bin/env child per file), names the file, and turns
  proxies off natively with NO_PROXY=* because Bun cannot take a deleted key
  back. Restores of deleted keys assign "" first as well.
- The e2e harness's launchctl shim registered a process exit listener that
  lived for the whole Bun process; releaseLaunchctlGuard removes the shim and
  listener, and e2e-launchd-guard calls it after each test.

Refs HACK-1327

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs HACK-1337

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-catalog denylist

A dashed `git-<sub>--helper` binary (also by an absolute git-core path or
with `.exe`) and a `<sub>--helper` argument to `git` are now read as
`git <sub>`, so `git-bisect--helper --bisect-run cmd` meets the `bisect`
rule and `git-submodule--helper foreach cmd` the `foreach` rule. Only a
whole `<sub>--helper` word is rewritten, so no existing refusal is lost.

The reference now names file-loaded code (pickle, marshal, an evaluated
config file) as a known limit beside stdin SQL, and records that a
helper's own options, such as the old `rebase--helper
--add-exec-commands`, are not modeled.

Refs HACK-1373

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve bunfig.toml: keep both test preloads, this branch's global-state
guard (HACK-1327) and next's checkout-write guard (HACK-1343, #390).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ike .hack/.gitignore

`ensureHackGitInfoExclude` decided writability with `access(W_OK)` alone,
so root (or a user an ACL grants write) replaced a 0444 `info/exclude`,
while `.hack/.gitignore` already treats a file with no write permission
bit as read-only for every user. Check the mode bits first, then the
injectable access probe, and document the ACL consequence in docs/env.md.

docs/cli.md now describes the missing-project refusal as one closed
`E_PROJECT_NOT_FOUND: <message>` line and shows each logger's prefix
instead of only the console logger's `ERROR:` form.

The short-spelling parse test now also covers `up` from the #370
differential table.

Refs HACK-1378

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`hack env unset KEY` with a piped stdin and no HACK_NO_INTERACTIVE never
returned and burned a full CPU: `confirmSafe` only consulted the explicit
opt-out, so a TTY-less run entered the clack confirm, whose readline loop
cannot settle on a non-TTY stdin (HACK-1385).

- `confirmSafe` now gates on `canPrompt()` (TTY on both streams plus the
  opt-outs) and resolves every TTY-less run by its declared policy within
  milliseconds; `doctorConfirm` notes skipped destructive steps on the same
  condition.
- Raw clack prompts without a guard are routed through the convention:
  `secrets` (name, value, delete), the `session` picker and attached-workspace
  choice, `mcp` target selection, `update` (stdin as well as stdout), and the
  `agent` client picker.
- Regression tests: unit cases for each policy without a TTY, and an e2e
  spawn of `env unset` and `secrets delete` with piped stdin under a 5 s kill
  bound asserting exit, bounded CPU and wall time, and no value echo.
- docs/cli.md documents the non-TTY behavior.

Refs HACK-1385

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… pending adoption pins

After a refused `hack config adopt --recover --stop` (HACK-1318, journaled
switch), `hack doctor` told the user to run `hack down` and plain
`hack config adopt --rollback`, both of which that pending state refuses.
The compose-env-at-rest report now carries `adoptionPending`, and the
check and `--fix` note name `hack config adopt --recover --rollback` while
the adoption is pending; the settled wording is unchanged.

Deadline-guard follow-ups from #397 (HACK-1391):
- prove the keep-CHANGED branch of the `mutateRetainedContainers` catch
  with a drift reported after the caller deadline (mutation: dropping the
  exclusion fails only this test)
- prove `requireMutationDeadline`'s expiry branch directly through the
  callback revocation fence (mutation: issuing CHANGED fails only this test)
- correct the retained-routing helper comment (deadline expiry is
  E_LEGACY_ADOPTION_DEADLINE) and the HACK-1283 comment (HACK-1340)
- document that a second `--recover --stop` after the refusal gets the
  generic E_LEGACY_ADOPTION_BUSY refusal, and assert it in the HACK-1318 test

Refs HACK-1391

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`lokiTail` registered a `process.once("SIGINT")` handler and never removed
it, so every follow that ended by socket close (the normal path) left a
stale handler in the process. In the shared `bun test` process the next
`process.emit("SIGINT")`, in tests/native-authored-project-oneoff.test.ts,
consumed those `once` handlers and the global-state guard blamed that test
for a listener change it did not cause. `runAuthoredCommand` in
src/lib/native-authored-command.ts is symmetric; the one-off file passes on
its own.

The handler is now installed only while the tail is open and removed in a
`finally`, so a SIGINT-fired (self-removing) handler and a normal close both
leave the listener list as it was. Two regression tests cover both paths
and fail without the fix. The guard now reports listener counts
(`2 -> 0`) so a consumed handler from an earlier file is distinguishable
from one added by the test it names.

Refs HACK-1392, HACK-1327

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nput access refusals

Target inspector follow-ups after #379 (HACK-1390).

- Classify input presence faults through an exported seam,
  projectInputAccessFault(), so EPERM is covered by a test (a mode-000
  directory only produces EACCES).
- ProjectInputAccessError keeps a value-free class/code diagnostic (for
  example Error:ENOTDIR) as a non-enumerable property, read with
  projectInputAccessDiagnostic(). Envelopes, printed errors and JSON stay
  unchanged and carry no path, OS message or cause.
- input_permission_denied now names the permission fix first. The contract
  doc no longer says an unsearchable .hack or .hack/.internal is
  adoption_state_unsafe; it is input_permission_denied even on a project
  that never used adoption.

Refs HACK-1390

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi roodboi closed this Oct 24, 2026
@roodboi
roodboi deleted the train/b50 branch October 24, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant