Repository navigation
Conversation
`hack project target` let the shared project input guards escape as detail-less errors. A project directory the caller cannot search, or an unparseable legacy adoption receipt, printed a detail-less E_CONFIG_INVALID envelope, which the MCP adapter turned into data: null. The inspector now maps those guard refusals wherever they are raised, including inside config reads: native or conflicting inputs become project_inputs_unsupported, an unsafe or uncheckable adoption state becomes the new provisional adoption_state_unsafe refusal, and an input access failure becomes input_unreadable. The adoption guard error now carries its kind so callers classify it without parsing its message. Paired CLI/MCP fixtures cover each case, and the MCP argv builder is pinned literally so a dropped selector is caught independently of the paired runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The evaluator computed a window mean with built-in sum(), which rounds after every addition and changed its algorithm in Python 3.12. Ten idle samples of 0.01 averaged to 0.009999999999999998 on the macOS system Python 3.9, so a mean exactly at the exclusive 1% idle ceiling passed there and failed on 3.12+. Use math.fsum so every interpreter judges the same correctly rounded mean, and add a regression test that fails with plain sum() on both 3.9 and 3.12+. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The protocol page says every number in a protocol or summary must be finite and within +/-(2^53 - 1), but the checker only bounded the fields it judged with _number. A count read with _count (p95_min_samples, expected) and any field the checker never reads accepted 2^53, 10^400, NaN or Infinity: a protocol with p95_min_samples = 2^53 validated, and a summary with expected = 10^400 was judged inconclusive instead of being refused. Scan every number in the whole document iteratively and add the closed codes protocol_number_out_of_range and summary_number_out_of_range, reported alongside any field-specific code. The page now names both codes and says the bound covers unread fields. Refs: HACK-1333, HACK-1169 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nspector The shared project input guard checks each candidate input with lstat and treats only ENOENT as absence. Any other fault escaped as a plain Error, so a `.dev` file, or a `.dev` directory the caller cannot search, beside a valid `.hack` made `hack project target` print a detail-less E_UNEXPECTED envelope (and the MCP adapter returned data: null). The guard now throws a typed, value-free ProjectInputAccessError that classifies the fault as permission (EACCES, EPERM) or unreadable without keeping the OS message or its path. The target inspector maps it to the closed input_permission_denied / input_unreadable refusals. Other commands now see E_CONFIG_INVALID with the same value-free message as the adoption guard's input-access refusal, instead of E_UNEXPECTED. Paired CLI/MCP fixtures and domain tests cover both cases; each was proven by mutation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rain late The foreground graph owner drained its child's stdout and stderr for only 250 ms after exit. Under heavy oversubscription the pipe readers could be scheduled after that window, so the closed native error code (or readiness line) was dropped and a generic startup failure was reported. Replace the window with the named, documented NATIVE_EXIT_DRAIN_BOUND_MS (5 s), still measured from the observed exit. EOF still ends a normal drain at once; the bound only ends a drain that a descendant holding the pipes keeps open. Tests: a writer that emits the code only after the stand-in is reaped is kept (fails with the 250 ms window), and a held descendant still settles within the bound while it is alive (fails with a widened drain). Refs HACK-1350 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve LegacyComposeAdoptionSelectionError: keep this branch's public kind field, typed over next's message map (adoption, input-access, permission). target-inspect now maps next's new permission kind (#402, unsearchable .hack) to input_permission_denied instead of adoption_state_unsafe; the unsearchable-directory test expects it, and fails if the mapping is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HACK-1375. In a git checkout without a key, a failed root .gitignore write (read-only file, read-only mount, full disk) refused `hack env set --secret` but left `.hack.secret.key` behind. The retry the refusal suggested then found the key, skipped the ignore step and succeeded, so `git status` offered the key for commit. - Write the root .gitignore entry before a new checkout-local key; a failed entry write refuses with no key on disk. - Before `--secret` set or hydrate uses an existing checkout-local key that git does not ignore, add the entry; if that fails, or a later rule re-includes the key, refuse closed and encrypt nothing. Keys git already ignores, and checkouts outside git, cause no write. - `hack doctor` reports a present, un-ignored checkout-local key as `env-key-unignored` (warn) with a value-free remedy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ep every key Two concurrent `hack env set` commands with different keys on the same layer both exited 0 and one key was lost: each read the layer, changed it in memory and renamed a new file over it with no lock, so the last rename won (HACK-1376). Every env layer writer now holds a per-layer cooperative lock at `<layer dir>/.internal/<layer name>.lock` for its whole read, change and write: `hack env set`/`add`/`unset`, `hack env hydrate`, and the legacy `.hack/.env` set and remove. The lock reuses the projects-registry owner protocol (exclusive hard-link receipt, dead-owner recovery only on ESRCH), waits at most 10 seconds and then refuses closed, naming only the layer, with nothing written. set and unset also compare-and-swap against the bytes they read, so a writer that takes no lock (an editor, Hack v4) turns into a retryable AtomicWriteConflictError instead of a silent overwrite. A symlinked `.internal` is refused; a layer whose directory is not writable runs unlocked, since no writer can replace it. Refs HACK-1376 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.
Integration gate for merge batch b49: origin/next + #396 (HACK-1350) + #377 (HACK-1333) + #379 (HACK-1332) + #433 (HACK-1375) + #435 (HACK-1376). Tree acf4fbf0. Closed after the batch squashes.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.