Repository navigation
Conversation
… docs
A dangling or non-directory `.hack/.internal` beside an env layer made
`hack env set`/`unset` refuse with one line that had no stable code (and
`--json` reported E_UNEXPECTED). EnvLayerLockRefusedError is now a coded
E_PATH_CONFLICT refusal, the code `hack env hydrate` already gives the same
entry.
docs/env.md said hydrate reports E_PATH_CONFLICT for a `.hack/.internal`
that is a file; it reports E_CONFIG_INVALID ("Cannot inspect Hack project
inputs ...") from its earlier adoption-state check, which refuses ENOTDIR
rather than treating it as absent. The doc now says so, and a CLI test pins
it. The storage carrier reference and the probe-budget test now state the
decided reason for a hung helper start since #437: storage-budget, never
probe-timeout.
Refs HACK-1459
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixture `git` inherited the runner's environment: a caller-exported GIT_DIR made tests/native-env-metadata.test.ts commit, branch and register a worktree in another repository, and a contributor's global core.hooksPath (read through XDG_CONFIG_HOME even under a private HOME) refused fixture commits. tests/helpers/fixture-git.ts now builds every fixture git environment from PATH/TMPDIR plus the caller's values, removes every GIT_* variable, DOCKER_HOST/DOCKER_CONTEXT and the runner's HOME/XDG_CONFIG_HOME, and sets GIT_CONFIG_GLOBAL=/dev/null, GIT_CONFIG_NOSYSTEM=1 and a fixed identity. All fixture git spawns in tests/ go through it; the e2e harness gains an exact-env option for them and the checkout write guard drops caller repository variables. tests/fixture-hermeticity.test.ts proves an exported GIT_DIR, work tree, index and hostile global hooks leave a scratch repository byte-for-byte unchanged, and refuses any new unrouted fixture git spawn. Budgets sized for the work under load, assertions unchanged: - native-env-metadata: the template beforeAll gets a named 60 s budget (24/36 runs exceeded 5 s under I/O load) and afterAll waits for a timed-out build, so template directories no longer leak. - native-compose-file-owner (HACK-1460): one named 60 s per-case budget replaces the 5 s/15 s/30 s mix, and afterEach waits for a timed-out body before teardown so it cannot change the next case's store. - native-compose-storage-witness-command: `down --recover` ran under invoke's 1 s HACK_COMPOSE_STARTUP_TIMEOUT_MS and reported private-uncertain once load stretched the stop; recovery now gets a 30 s deadline, CLI processes a 45 s kill bound, the carrier the product default, and the case runs again with an ambient DOCKER_HOST at a missing socket. Refs HACK-1459 Refs HACK-1460 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 30-run soak caught the GIT_DIR hermeticity case failing 2/30: `commit` starts a detached `git maintenance run --auto` that takes objects/maintenance.lock after the command returns, so the fixture repository changed under the comparison. Fixture git now sets maintenance.auto=false and gc.auto=0 through GIT_CONFIG_COUNT, which also keeps an unowned background git from outliving any fixture; a test pins both settings. native-env-metadata cases and the generation linked-worktree case each run the product's Git worktree verification and took more than 5 s at load average above 100; they get named 30 s budgets. Assertions unchanged. Refs HACK-1459 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 30-run soak at load average 120 still failed the recovery cases: the 30 s recovery deadline and the 45 s kill bound both expired mid-stop (exit 137). Recovery now runs with DEFAULT_COMPOSE_STARTUP_TIMEOUT_MS, as on a host that sets no budget, CLI processes get a kill bound a minute above it, and cases an 8 minute budget. Assertions unchanged; in CI the file still takes about 5 s. Refs HACK-1459 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second 30-run soak passed 29/30 at load average 200-300; the one failure was the unknown-enrolled case, whose `ps` and `down` still ran under invoke's 1 s HACK_COMPOSE_STARTUP_TIMEOUT_MS. Every source CLI process in the file now runs with the product default budget, the same one recovery already uses. Assertions unchanged. Refs HACK-1459 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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
Follow-ups from HACK-1459 (items 1-6 plus its comment) and the HACK-1460 flake. Test fixtures no longer read the caller's Git or Docker environment, load-sensitive cases get named budgets sized for their work, a timed-out case can no longer break the next one, and one env refusal gets a stable code.
Root causes
gitcalls in about 50 test files inherited the runner's environment.GIT_DIR/GIT_WORK_TREEsent the fixture'sinit/commit/worktree addinto that repository.tests/native-env-metadata.test.tscleared those variables inbeforeEach, but its fixture runs inbeforeAll, before that.$XDG_CONFIG_HOME/git/config, whosecore.hooksPathguard refused fixture commits. That is the actual cause of the local-only failure ofnative-compose-generation"real linked worktree ...". The fixture already set its identity with-c user.*; it fails on the hook, not the identity.down --recoverstorage-witness cases fail locally.DOCKER_HOSTdid not cause this. The cases fail the same way with and without it, and with a private or a real HOME.invoke()defaultsHACK_COMPOSE_STARTUP_TIMEOUT_MSto 1 s, anddown --recoverbounds its owned stop by that budget. On a loaded host (load average 60-150 here) the stop's synthetic Docker processes take longer than 1 s, so the CLI reportsnative_compose_effect_refusal {effect-execution, private-uncertain}instead ofE_COMPOSE_FAILED. With a 60 s budget the same run gaveE_COMPOSE_FAILEDat load average 108. Two more wall-clock bounds expired under load: the in-process carrier deadline (Date.now() + 30_000, which gave "witness missing ... uncertain") andinvoke's 15 s kill (exit 137).afterAllas soon asbeforeAlltimes out, while the build keeps creatingnative-env-metadata-template-*directories. Probe: a 200 ms hook budget with a 300 ms body showed afterAll running while the body was still in progress.upcancellation case had an explicit 5 s budget. Other cases had 15 or 30 s. When a case times out, Bun runsafterEachwhile the body keeps running, and that body reads the module-levelstore/root, which the nextbeforeEachreplaces. A late body can therefore act on the next case's store. That is how one timeout becomes "state unsafe" in the next case.storage-budget. That is the right reason, because the remedy is the budget variable that the summary names. The code, the probe-budget test and the reference already agreed; the doc and the test now state it explicitly.hack env hydratewith a.hack/.internalfile is refused by the earlier legacy adoption-state check (ENOTDIR counts as an input access failure, never as absence), so it reportsE_CONFIG_INVALID. docs/env.md saidE_PATH_CONFLICT.EnvLayerLockRefusedErrorwas an uncodedClosedCliError, so the CLI printed it without anE_*prefix and--jsonreportedE_UNEXPECTED.Fix
tests/helpers/fixture-git.ts:fixtureGitEnv(),fixtureGitShellEnv()(forsrc/lib/shell.ts),runFixtureGit[Sync]().PATH/TMPDIRplus the caller's values.GIT_*variable,DOCKER_HOST/DOCKER_CONTEXTand the runner'sHOME/XDG_CONFIG_HOME.GIT_CONFIG_GLOBAL=/dev/null,GIT_CONFIG_NOSYSTEM=1, a fixed author and committer identity, andGIT_TERMINAL_PROMPT=0.gitEnvlets a test set a Git variable on purpose (used forGIT_INDEX_FILEandGIT_CEILING_DIRECTORIES).tests/goes through it (about 50 files). The e2erunCommandgets anexactEnvoption. The checkout write guard drops a caller's repository variables but keeps the contributor's config.tests/fixture-hermeticity.test.ts:GIT_DIR, the work tree, the index, the object directory, the common dir, a hostileGIT_CONFIG_GLOBALand a hostile identity, then runs every fixture route. The scratch repository stays byte-for-byte unchanged, and no hostile hook runs..gitconfigcannot add hooks, signing or identity.DOCKER_HOST/DOCKER_CONTEXTnever reaches fixture git or the private CLI env.native-compose-storage-witness-command: namedRECOVERY_DEADLINE_MS(30 s),CLI_COMMAND_TIMEOUT_MS(45 s) and a 3 min case budget. The carrier deadline uses the product default. The source-unavailable recovery case also runs with an ambientDOCKER_HOSTat a missing socket plus aDOCKER_CONTEXT.native-env-metadata: namedTEMPLATE_BUDGET_MS(60 s) on both hooks.afterAllwaits for the build to settle, and a build that outlives that wait removes its own directory.native-compose-file-owner: one namedFILE_OWNER_CASE_BUDGET_MS(60 s) viasetDefaultTimeoutreplaces the per-case 5/15/30 s budgets.afterEachwaits for the running body before teardown.EnvLayerLockRefusedErroris now a codedE_PATH_CONFLICTHackCliError. docs/env.md is aligned on hydrate's.internalfile and the lock refusal. The storage-carrier reference namesstorage-budgetas the reason for a hung start.Assertions in the flaky cases are unchanged. Only budgets, deadlines and teardown order changed.
Evidence
In progress: local 30-run soaks under load and hosted CI at this head; this section is updated when they finish.
Gaps
Listed when the evidence above is complete.
Refs HACK-1459
Refs HACK-1460
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.