Repository navigation
fix(env): the automatic mux sweep keeps files configuration reads (HACK-2181) - #804
Conversation
…rotation advice (HACK-2165) Audit F1 and lead item 2: - A private write (.hack/.env files, the project key, the encrypted store and its key) staged its complete contents in a `.<name>.<uuid>.tmp` sibling that no ignore entry covers, so a crash could leave a copy git would stage. Staging now happens in `.hack-staging/` next to the target: a private (0700) directory whose `.gitignore` of `*` exists before any staging file, so git never lists a leftover. The next write there sweeps leftovers older than ten minutes, and staging siblings from earlier versions of the same target. A linked or shared staging directory is refused. Store-internal writers (background commands, shared-host claims, the .internal compose override) keep sibling staging. - doctor's weak-key remedy said `mv .hack.secret.key .hack.secret.key.old` (a name git does not ignore) and to re-add secrets as argv values. It now keeps a 0600 copy of the old key outside the worktree until every secret is re-added, and re-adds with --value-stdin or the hidden prompt. Co-Authored-By: Claude <noreply@anthropic.com>
… read (HACK-2165) Audit S1 and lead item 1: a machine-local layer that predates private creation, or was made by hand, kept mode 0644 through writes, and its staging copy took that mode before the contents were written. A machine-local write now drops group and other bits from an existing layer, before the staging copy holds any content, and a write that changes nothing tightens the file in place. Shared layers keep their mode. Commands that read env layers (env, up/run paths, session) warn once per file on stderr about a loose machine-local file, after a successful read so a refusal stays one line. HACK-1680's keep-mode test now asserts the tightening. Co-Authored-By: Claude <noreply@anthropic.com>
…K-2165) Lead item 3: values staged in $TMPDIR/hack-mux-env-*/env.json stayed there when the pane never ran its command. A payload now expires after ten minutes: the pane refuses and removes an expired one, the staging process removes its payload when the limit passes (if it still runs), and the next staging sweeps this user's older payloads. Tests stage in a private directory, never the host's temporary directory. Co-Authored-By: Claude <noreply@anthropic.com>
Lead items 2 and 5 and the migration path: `hack doctor` reports, for the project and the global Hack home, secret-bearing files (local layers, .hack/.env files, project keys, encrypted store and key) that are loose, linked, owned by another user, tracked, or unignored; staging copies left by interrupted writes; terminal-session payloads that never ran; and "store present, key missing, cannot be read". `hack doctor --fix` tightens modes, adds missing ignore entries and removes Hack's own leftovers, printing each change; it never deletes a store, a key or a user's file, and never untracks. A doctor hint no longer suggests a secret value in argv. Docs: the env guide, the threat model, and an "after upgrading" step in the v5 upgrade guide. Co-Authored-By: Claude <noreply@anthropic.com>
…ACK-2165) Keeps writeTextFileAtomicIfChanged under the complexity limit after tightenExisting; no behavior change. Co-Authored-By: Claude <noreply@anthropic.com>
…e layer staging (HACK-2165) Own-reviewer round on d74187c: - An existing `.hack-staging/.gitignore` (for example one a repository committed, checked out 0700 under umask 077) was trusted unread. It is now accepted only as a regular file of the user's holding exactly `*`; anything else refuses the write, so no staging copy can be visible to git. - The rotation remedy's `install` into a fixed name overwrote an earlier retired key. It now copies to a unique mktemp name and removes the key only once the copy is written. - Machine-local layer writes (writeTextFileAtomicIfChanged with tightenExisting) still staged as `.<name>.<uuid>.tmp` siblings, which only Hack's managed block ignores. They now stage in the private directory too; shared layers keep sibling staging. - Committed dotenv templates (.env.example, .sample, .template, .dist) are no longer reported as secret-bearing. - Tightening an unchanged layer is best effort (another user's file keeps working), and a missing store key is a warning on macOS while the keychain may still hold it. Co-Authored-By: Claude <noreply@anthropic.com>
…oject stores (HACK-2165) Bot review on d8719bd: - Concurrent first private writes in one directory could read the new `.hack-staging/.gitignore` before its creator wrote `*` and refuse. The ignore file is now staged complete and linked into place, so it never exists partially and a crash cannot leave an empty one. - An encrypted store or key configured inside the project checkout skipped the git checks. Such files are now checked like the project's own secrets (tracked is an error; unignored gets an anchored root .gitignore entry); files outside it stay exempt. Co-Authored-By: Claude <noreply@anthropic.com>
…ene refuses a linked .hack (HACK-2165) Bot review on 9e060a3: - On ExFAT/FAT, chmod cannot make the staging directory 0700, so every private write there was refused, regressing the documented first secret on such a drive. A staging directory of the user's own is now made 0700, and accepted at the mount-wide mode when the filesystem ignores chmod; a link or another user's directory is still refused, and the identity is re-checked before publishing. - doctor's secrets hygiene followed a `.hack` linked outside the project, so --fix could chmod or delete files there. A linked or non-directory `.hack` is now reported and not inspected, and every fix carries the identity of the directory it changes, checked again before the change (an ignore entry is never appended through a directory swapped after inspection). Co-Authored-By: Claude <noreply@anthropic.com>
…ct (HACK-2165) Bot security review on 9e060a3: a configured store or key under an in-project directory whose name begins with `..` (for example `..secrets/`) gave a relative path starting with "..", which was read as outside the project, so its git checks were skipped. Outside now means a `..` path segment or an absolute path. Co-Authored-By: Claude <noreply@anthropic.com>
…out or Hack home (HACK-2165) Bot security review on 9ff7fb0: a cloned repository's committed hack.config.json can point encryptedFile.path/keyPath at any file of the user's, and `hack doctor --fix` would then chmod it or remove old `.<name>.<uuid>.tmp` files beside it. Paths that only the project's configuration chose are now fixed, and their leftovers swept, only when their directory resolves (links followed) inside the checkout or the Hack home; elsewhere the finding is reported without a fix. The global configuration's paths are the user's own and keep their fixes. inspectSecretsHygiene is split into per-part helpers (no behavior change) to stay under the complexity limit. Co-Authored-By: Claude <noreply@anthropic.com>
…165) Bot security review on 553731f: a project-configured store or key inside the checkout but spelled through an alias (a link to the checkout, or another case on a case-insensitive disk) gave a relative path leaving the root, so its git checks were skipped. Containment now compares the resolved file directory with the resolved root, and git is asked about the resolved spelling. Co-Authored-By: Claude <noreply@anthropic.com>
…ectory (HACK-2165) Own-reviewer round on f00c17c: on a case-insensitive disk a store or key named `Store.key` in the configuration while git tracks `store.key` kept the configured case, so git's exact-case tracked check missed it. A regular file is now resolved itself (realpath of the file), so git is asked about its stored name; a link at the final component is still not followed. Co-Authored-By: Claude <noreply@anthropic.com>
… opens (HACK-2165) Bot review on 40d6f8e: - A configured in-project path with gitignore metacharacters (for example `secrets[prod]/store.key`) produced an entry git reads as a pattern, so the file stayed unignored while --fix reported it repaired. Entries now escape `\ * ? [ ]`, a leading `#` or `!` and a trailing space, and an ignore fix counts as applied only when git then ignores the file. - A FIFO in place of `.hack-staging/.gitignore` made the read-only no-follow open block, hanging every private write there. That open (and the two other no-follow opens this PR adds) now uses O_NONBLOCK; the regular-file check then refuses it. Co-Authored-By: Claude <noreply@anthropic.com>
…HACK-2165) Own-reviewer round on 41b0727: the ignore-file writer trims entries, which drops an escaped trailing space and would leave a pattern that never matches. Such a store or key is now reported with an "add it by hand" note and no fix. The message also names the ignore file by its absolute path when the project root was reached through a link. Co-Authored-By: Claude <noreply@anthropic.com>
Bot review on 9861948: under a restrictive umask the staged `.hack-staging/.gitignore` could be created without owner read, so the read-back check refused every private write in that directory. Its mode is now set to 0600 through the open handle before it is written, as the other private files already are. Co-Authored-By: Claude <noreply@anthropic.com>
… (HACK-2165) Bot security review on e0cbce3: a committed configuration could name an in-project store path containing a newline and `!`, so --fix wrote a second, negating ignore line that un-ignored a real key (verification then failed but the line stayed). A name with any control character now gets no ignore fix, only an "add it by hand" report, so an entry is always exactly one literal pattern line. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
computer pr review-status reported an overlapping base file. Co-Authored-By: Claude <noreply@anthropic.com>
…e control characters (HACK-2165) Bot security review on bc1b493: the read-time loose-layer warning suggested `chmod 600 .hack/<name>` with the name unquoted, so a force-added name such as `hack.env.;cmd;.local.yaml` turned the remedy into command execution when copied. The warning now recommends only `hack doctor --fix`. Every path doctor's secrets hygiene and the warning print has control and formatting characters shown as `\u{..}`, and the one remaining command (`git rm --cached`) shell- quotes the name, or is omitted when the name holds control characters. (Also closes the own-reviewer note about terminal sequences in configured paths.) Co-Authored-By: Claude <noreply@anthropic.com>
Own-reviewer round on 1fdf767: for a store configured through an alias of the checkout, the untrack command used the alias spelling relative to the resolved root (`../alias/...`), which git rejects. It now uses the resolved path git was asked about. Co-Authored-By: Claude <noreply@anthropic.com>
…use (HACK-2165) Bot security review on bcc9648: a repository can configure the encrypted store and key at in-checkout paths that do not exist yet, and the hygiene check only looked at existing files, so doctor reported clean and the first secret write created both files unignored. A configured store or key path inside the checkout now gets the git checks even before the file exists (resolved through its nearest existing ancestor), so `hack doctor --fix` adds the ignore entry ahead of first use. Protecting the writer itself belongs to the encrypted store backend work (HACK-2169). Co-Authored-By: Claude <noreply@anthropic.com>
…it will be created (HACK-2165) Own-reviewer round on c08593c: fixAllowed resolved the store file's directory with realpath, which fails for a directory that does not exist yet, so a project-configured in-checkout store lost its ignore fix with a false "outside the project" note. It now resolves through the nearest existing ancestor, like the in-checkout scoping. Co-Authored-By: Claude <noreply@anthropic.com>
next (021b16b) conflicts in docs/security/env-secrets-threat-model.md: #760 (HACK-2167) edited the same three rows. Both edits are kept: the staging and hygiene text from this branch, and #760's "mutation paths" and gap-18 notes plus its Compose wording. The attacker model for values at rest (accidental collection by git and other OS users; a same-user process out of scope) is added to the Scope section, as the lead asked once #760 merged. Co-Authored-By: Claude <noreply@anthropic.com>
…HACK-2165) Own-reviewer round on 6e636a9: the threat model's key-provisioning row still said the key is staged as a sibling (it is staged in `.hack-staging/`), and the `.hack/.gitignore` fix step's doc comment sat above maybeFixSecretsHygiene instead of its own function. Co-Authored-By: Claude <noreply@anthropic.com>
next edf23ae (the squash of #762, plus #767 and #769) conflicts in src/lib/fs.ts and src/lib/project-env-config.ts. Both sides are kept: - writeTextFileAtomicIfChanged has #762's noFollowTarget (destination never resolved through a link) and this branch's tightenExisting and private staging; machine-local writes pass all of createMode 0600, noFollowTarget and tightenExisting, with #762's link recheck before the rename. The in-place tighten for an unchanged write honours noFollowTarget too. - #769's live-input guard and #767's value-free listing are untouched (auto-merged). docs/env.md no longer says an existing layer keeps its mode. Co-Authored-By: Claude <noreply@anthropic.com>
tests/env-backend-command.test.ts parses `env list --json` from a run that shares one terminal stream for stdout and stderr; its fixture wrote a 0644 plaintext `.hack/.env.qa`, which env commands now warn about on stderr. The fixture writes it 0600, as Hack does. Co-Authored-By: Claude <noreply@anthropic.com>
…ds; fail closed on inspection (HACK-2165) Second-family review of 66e28c5 (1 blocker, 4 should-fix): - Blocker: the leftover scan could offer a configured, live store key for removal when its name looked like a staging sibling (for example `..hack.secret.key.<uuid>.tmp` chosen during recovery). Every configured store and key, the project key and the shared key, as given and resolved, is now excluded from removal. Writers no longer sweep legacy siblings beside a target at all (they cannot know what is configured); they sweep only their own staging copies inside `.hack-staging/`, and legacy siblings are left to doctor. - F1 commands: the rotation command checks, when it runs, that `~/.hack/retired-keys` is not in a git work tree and stops before copying if it is; the symlink repair no longer uses a `.new` intermediate (resolve, remove the link, copy in under umask 077). Tests run both commands in sandboxes with git controls. - Ignore repair refuses an existing ignore file it cannot read, instead of rewriting it with one entry. - Inspection errors other than "absent" (an unsearchable directory, an unlistable `.hack`) are error findings without fixes, never a clean result; a key that cannot be inspected is not reported missing. - The tmux backend tests stage in a private temporary directory, with a control showing an unrelated stale payload outside it is untouched. Co-Authored-By: Claude <noreply@anthropic.com>
…not a repository"; ignore repair merges its own read (HACK-2165) Second-family re-review of 9e97246 (1 blocker, 2 should-fix): - Blocker: the writer's staging sweep matched by name prefix, so a write of `.env` could delete a live key kept at `.hack-staging/.env.key.<uuid>.tmp`. Writers now remove nothing: they cannot tell a leftover from a file configured at a staging-shaped path. `hack doctor --fix` removes leftovers, excluding every configured store and key. - The rotation command copied whenever `git rev-parse` failed. It now copies only when git (LC_ALL=C) answers "not a git repository"; inside a work tree, with git missing, or on any other failure it stops before copying. - The ignore repair re-read the file through a reader that turns a failure into "absent". It now merges into the bytes it read through a no-follow handle and writes with a compare-and-swap against them, so a failed read can never replace the user's rules. Regressions: a live `.env.key.<uuid>.tmp` survives staging for `.env`; a git that fails otherwise stops the rotation; a failing path read during the merge leaves the ignore rules intact. All three fail on 9e97246. Docs updated: writes no longer sweep. Co-Authored-By: Claude <noreply@anthropic.com>
…r never writes through a link (HACK-2165) The printed rotation command now runs its "is this a checkout" probe with GIT_DIR, GIT_WORK_TREE, GIT_COMMON_DIR and GIT_CEILING_DIRECTORIES removed and discovery across file systems on, so a stale GIT_DIR or a ceiling in the shell can no longer produce the "not a git repository" answer for a directory inside a home checkout. Ignore repair publishes with noFollowTarget, so a link put at the admitted .gitignore after its read (by another user, in a shared directory) is refused instead of written through. Regressions: a home checkout with a stale GIT_DIR, a discovery ceiling or a stray GIT_WORK_TREE in the shell; a link swapped in between admission and publication. The first two and the swap fail without the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ore its checkout probe (HACK-2165) The probe now unsets the set git itself clears when it enters another repository (git rev-parse --local-env-vars: GIT_DIR, GIT_OBJECT_DIRECTORY, GIT_WORK_TREE, GIT_COMMON_DIR, GIT_INDEX_FILE, config parameters and the rest) plus GIT_CEILING_DIRECTORIES, instead of a hand-picked four. A stale GIT_OBJECT_DIRECTORY no longer turns a home checkout into "not a git repository". If git cannot list the variables, the command stops. Regressions: a stale GIT_OBJECT_DIRECTORY, alone and with a stale GIT_DIR; both fail with the previous command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ictly (HACK-2165)
The scope doctor resolves again before a removal tolerated what it could
not read: discovery and shared-key errors became missing entries, a config
parseError fell back to the default store paths, and a config file that
existed but could not be read was an empty layer. Any of these could make
an unknown configuration look like one naming nothing, and approve the
unlink of a file it now names.
readControlPlaneConfig and resolveFileSecretStorePaths take an opt-in
strict mode (only a missing file is an empty layer; a read or parse
failure rejects), and resolveSecretsHygieneScope({ strict: true }) lets
every discovery, read and parse failure reject. doctor's removal check
uses it; a rejection refuses the removal. The report keeps its tolerant
resolution.
Regressions (tests/secrets-hygiene-scope.test.ts, through the production
resolver): a malformed and an unreadable global configuration reject in
strict mode; an absent one resolves; --fix removes nothing while the
configuration is unreadable, and removes the leftover once it is readable
and unchanged. Three fail without the change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…git probes read paths literally (HACK-2165) Project discovery decided whether a present Compose entry supplies a project with the tolerant pathExists, so a stat failure (EACCES, EIO) dropped the project instead of raising, against inspectProjectInputsAtRoot's own rule that only ENOENT means absence. A missing target (a dangling link) is still absent; any other failure is now a project input access fault, so a strict hygiene scope rejects and no removal proceeds. projectEnvLiveInputPaths throws when a .git marker shows a checkout but git cannot name its shared location, so the live set is unknown rather than missing the shared and primary-worktree inputs; a project outside git is unaffected. The tracked and ignored probes pass a ./-prefixed path (ls-files also with --literal-pathspecs), so a configured name such as ':(literal)legacy.key' is never pathspec magic: check-ignore no longer exits 128 for it and ls-files matches the file itself, not a glob. The production-scope test now points TMPDIR at its fixture and asserts no finding lies outside it. Regressions: an uninspectable Compose target rejects discovery and the strict scope, while a dangling one stays absent; a checkout whose git cannot be read removes no leftover; a pathspec-magic name gets ignore fixes and is reported when tracked. Each fails without the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iling closed (HACK-2165) projectEnvLiveInputPaths checked git first and then let the collector look the family up again; a later git failure silently dropped the shared key and the primary worktree's inputs. The collector now takes the family from a single lookup (liveInputGitFamily): the shared key location, and the primary worktree derived from the same common directory, as resolveGitPrimaryWorktreeRoot derives it. With strictGit (the hygiene path) a checkout whose family git cannot name throws. Legacy cleanup's callers keep their behaviour. Regression: with git answering its first two calls and failing every later one, the set still names the shared key (or the call rejects). It fails with the previous collector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict in docs/security/env-secrets-threat-model.md gaps 16-19 only: next's wording for gaps 17 (#766) and 19 (#768, HACK-2171) is kept; gap 18 takes next's #762 text plus this branch's mode tightening, read warning, ACL/umask limits and markerless-work-tree note; gap 16 keeps the doctor reference, and the two table rows that pointed at gaps 17/18 say "narrowed". #768's cleanup receipts merge cleanly with this branch's strictGit live-input collector (its new caller keeps the non-strict path). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tracked store is a warning (HACK-2165) A configured store or key outside the project root but inside another git work tree (a home directory kept in git, say) got no git check, so doctor reported it clean. Hygiene now finds the containing work tree (the fail-closed marker walk, then git rev-parse from the nearest existing directory) and asks git there; an unignored file is reported for the user to ignore by hand (hygiene never edits another repository), a tracked one as tracked, and a checkout git cannot name is an error. A path inside a git directory itself is in no work tree. A tracked encrypted store is now a warning rather than an error: it holds ciphertext only, the key's own check stays an error, and projects may commit the store (next's HACK-1715 cleanup test tracks one; with the error, doctor --migrate-env-config exited 1 there after the merge). Regressions: a store and key in another work tree (unignored, ignored by hand, tracked, unnameable); a store inside a bare git directory; the '..secrets' test now expects an error for the tracked key and a warning for the tracked store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd use the nearest work tree (HACK-2165) - toGitRelativePath rewrites only the platform separator: on POSIX a backslash is part of a name, and turning it into "/" asked git about a different (possibly ignored) file. - The nearest-directory walks (resolveExisting, nearestExistingDirectory) climb only past a directory confirmed missing; any other realpath failure is unknown, so a containing repository whose root cannot be resolved is reported, not skipped. - The printed untrack command is git --literal-pathspecs rm --cached, so a name that looks like a pathspec untracks only that file. - In another work tree an existing regular key is checked under its stored case (realpath of the file), as in the project. - A store or key in a repository nested inside the project is checked against that repository (report only); a project inside a larger repository keeps its own ignore repair. - Discovery's Compose target check goes through Bun.file(...).stat() as pathExists did, so callers that observe file access (the adoption probes) see the same access; only ENOENT/ENOTDIR are absence. Regressions: a tracked key\prod.key with an ignored slash-spelled decoy; realpath EIO at the containing repository root; the printed command run against a ':(glob)*.key' name next to another tracked key; a nested repository; a monorepo project; a case-folding check that runs on case-insensitive filesystems. Each new one fails without the change (the case-folding one needs such a filesystem). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CK-2181) The sweep of stale terminal-session payloads before each staging now applies the same live-input check as `hack doctor --fix`, through one shared implementation (src/lib/live-inputs.ts): it never removes a payload directory that is, or holds, a path current resolution reads, and removes nothing when that configuration cannot be read. Regression with real files: a store key configured at a stale payload's env.json survives the sweep while an ordinary stale payload is removed; an unreadable configuration removes nothing. Both fail against the previous sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#764 landed with a stricter guard than the branch was cut from (a path that cannot be resolved for a reason other than absence counts as live, and retired stores are never removed). Took next's versions of the files #764 owns and moved that final code into src/lib/live-inputs.ts unchanged: realpathOrMissing, inRetiredStore, livePaths, isLive, plus one decision, removalAllowed, that both `hack doctor --fix` (removalStillSafe) and the automatic payload sweep call before each removal. For a payload the targets are its env.json and its directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@codex review |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…HACK-2181) Review follow-ups: two tests run the sweep with no injected scope from a project directory (a key named by the project's own configuration is kept; an unparsable project configuration removes nothing), which fail if the default stops resolving strictly or from the current directory. The threat model and the sweep comment now name the exact targets (env.json and the directory) instead of anything inside the directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…nputs # Conflicts: # src/commands/doctor.ts
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Second-family review: Claude Opus CLEAN at 6e767f5. Parent verification: 59 focused tests pass, including private tmux with an isolated short socket path; reviewer ran 104 affected tests. Fresh CLI typecheck/check, build, docs/privacy/lint/diff checks pass. The global-configuration regression failed before the fix and now preserves the configured file while removing an ordinary stale payload. No local Rust build or owner runtime changes. Exact-head Codex and Linux CI remain pending; not Ready for lead yet. |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
Ready for lead: 6e767f5 Automatic stale mux-payload cleanup now shares doctor's live-input guard and preserves the global configuration itself, including a configuration stored at a stale payload's env.json. Unknown configuration, live inputs and retired stores prevent removal.
No merge performed. Task scratch cleaned; handing this head to the lead. |
The next.17 section omitted the tmux breaking-change warning that already existed at the page tail. Move it into the release section and put its v4 stop procedure before the v5 trial. The env breaking entry was already correct; keep its scoped single-key/terminal/stdin migration guidance. Audit all **71 commits** from `v5.0.0-next.16` (`b21c3b1898`) through candidate **`5002cf87bb50686b17ceae8a4c835f524ee1b3a5`** against merged code and PR text. Add missing upgrade-assistant, activity, observe/plan/version, interactive exec, recovery, background-command and host/secret-custody notes. Preserve the complete native env-file behavior table and mark the four new JSON contracts **shape may change before 5.0.0**, with the planned HACK-2238 envelope migration. Correct stale native interactive-exec/network/metadata/Ctrl-C claims and the short upgrade assistant wording. Credit the publisher-shutdown fix to #719, already in next.16; keep next.17 lifecycle-command retirement separate. Retake a private stopped-state backup after v4 stops zellij, keeping the first copy too. Keep next.16 warnings scoped to that bundle. Keep original-key backups before older Doctor migrations and distinguish file rollback from runtime/data recovery. Only eight Markdown files change. Runtime, tests, workflows, generated CLI reference, diagrams, immutable snapshots and the site pin are unchanged. No diagram changed, so a new visual would add nothing. The site re-pin remains a separate release follow-up. Merge timing: respect the lead's candidate freeze; intended for the batch immediately after next.17 publication and the site re-pin. The moved tmux breaking-change notice is called out for the lead's pre-cut decision. This PR does not merge or publish anything. Validation at **`d95cfc554fabc4a0d70e3ba6caeb6746f63a198c`**: - Bun 1.4.4, frozen dependencies. Docs prepare/check/typecheck/build and site tests (2 passed, 25 assertions) pass. Production-mode HTTP smoke: 97 docs, 82 SVGs, 217 local links/anchors; task server stopped. - All 50 Hack commands named anywhere in the notes exist in the current generated CLI reference; 19 option contracts checked. CLI-reference regeneration has no diff. - All 71 commits have one row below, including the test commit and merge for #786. All 70 source evidence paths exist at the fixed candidate. Source release-note local links/anchors pass. - Privacy and complete reachable-history Gitleaks pass (1,330 commits, no findings). Persistent Claude Opus second-family review is CLEAN at this exact head. Hosted run 43585059152 passed docs site, secret scan and every planned Linux unit shard (4–8), plus the required test fan-in. Typecheck/privacy/quality ran on shard 8. Focus had no selected tests. Compiler, native core, runtime images/models, process-lifetime and Docker execution were out of scope; a green Docker fan-in is not Docker execution evidence. All bot comments were read; CLEAR at d95cfc5, zero unresolved threads/change requests. Codex code/security reviews found no issues. - No real install, migration, daemon/DNS/trust change, Docker or native VM run was performed for this documentation audit. Native acceptance and the candidate's hosted release qualification remain separate. ## Candidate commit coverage `entry present` means the behavior already had an entry within the next.17 section (some now have fuller detail). `added` means missing behavior or migration guidance was added there; #824 moves the existing warning into that section. `not user-visible` means no shipped CLI/runtime change: documentation, test/CI/repository housekeeping or the internal desktop preview. Source links pin the candidate, rather than a moving branch. | Commit / PR | Coverage | Entry or reason | Audited source | | --- | --- | --- | --- | | [70d328e](70d328e) [#743](#743) | added | Host setup: privileged/NSS safeguards | [src/lib/linux-trust-setup.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/linux-trust-setup.ts) | | [51f15c8](51f15c8) [#746](#746) | not user-visible | Docs-only upgrade checklist and site generation; no installed behavior | [scripts/docs/prepare.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/scripts/docs/prepare.ts) | | [c14f3f5](c14f3f5) [#744](#744) | added | Native setup JSON accounting | [packages/runtime-core/src/provider/setup.rs](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/packages/runtime-core/src/provider/setup.rs) | | [b0c6bcd](b0c6bcd) [#705](#705) | added | Retained background exec/run and cursor tools | [src/commands/background-command-start.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/background-command-start.ts) | | [c89f4fd](c89f4fd) [#745](#745) | added | Linux daemon query/home refusals | [src/commands/daemon-systemd.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/daemon-systemd.ts) | | [cce4b59](cce4b59) [#752](#752) | not user-visible | Site pin/snapshots and editorial pages only | [scripts/docs/versions.json](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/scripts/docs/versions.json) | | [56676fc](56676fc) [#754](#754) | added | Setup reuse/offline accounting; pin-watch changes are internal | [packages/runtime-core/src/provider/setup.rs](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/packages/runtime-core/src/provider/setup.rs) | | [7a496f1](7a496f1) [#755](#755) | added | MCP registered selectors and background tool guidance | [src/mcp/server.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/mcp/server.ts) | | [e7a7039](e7a7039) [#747](#747) | entry present | Native follow text shape; added session ends/restart recovery | [src/backends/native-log-follow.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/backends/native-log-follow.ts) | | [87a6df5](87a6df5) [#758](#758) | added | Compose short one-shot startup | [src/lib/compose-startup-state.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/compose-startup-state.ts) | | [2afb5c4](2afb5c4) [#761](#761) | added | Native Hack networks and inert generated labels | [src/lib/native-hack-compose-conventions.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/native-hack-compose-conventions.ts) | | [342a585](342a585) [#771](#771) | not user-visible | Docs-only next.16 trial record | [docs/versions/v5/guides/upgrade-from-v4.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/docs/versions/v5/guides/upgrade-from-v4.md) | | [6f0acf1](6f0acf1) [#769](#769) | entry present | Migration live-key/alias preservation | [src/lib/project-env-config.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/project-env-config.ts) | | [c96c444](c96c444) [#756](#756) | added | Child key filtering, fixed diagnostics, legacy ignore/YAML API refusals | [src/lib/child-environment.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/child-environment.ts) | | [e2c73c6](e2c73c6) [#748](#748) | not user-visible | Contributor CI selection/coalescing, outside product release notes | [scripts/ci-change-scope.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/scripts/ci-change-scope.ts) | | [ebf794e](ebf794e) [#760](#760) | added | Agent/output custody warning; runtime unchanged | [src/agents/instruction-source.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/agents/instruction-source.ts) | | [021b16b](021b16b) [#763](#763) | added | One-pass background redaction scan; no public benchmark claim | [src/lib/background-command/output-scanner.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/background-command/output-scanner.ts) | | [8564ec4](8564ec4) [#765](#765) | added | Shared preflight/start admission and detached failure reporting | [src/backends/native-project-review.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/backends/native-project-review.ts) | | [0c0fba2](0c0fba2) [#767](#767) | entry present | Breaking env exposure/protect/stdin/scope/API guidance | [src/commands/env.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/env.ts) | | [9435da9](9435da9) [#762](#762) | added | Local Git/link refusals and no-follow dotenv | [src/lib/project-env-config.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/project-env-config.ts) | | [edf23ae](edf23ae) [#753](#753) | added | Daemon stop proves the live Hack home | [src/commands/daemon-systemd.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/daemon-systemd.ts) | | [573ceec](573ceec) [#766](#766) | entry present | Doctor layer/integrity error; added store/key write refusals | [src/lib/secret-store.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/secret-store.ts) | | [a0e9760](a0e9760) [#757](#757) | added | Orphan output truncation and final-sync evidence | [src/lib/background-command/recovery.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/background-command/recovery.ts) | | [10e1958](10e1958) [#759](#759) | added | Private lifecycle logs/FIFO and expiring mux payloads | [src/lib/private-file.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/private-file.ts) | | [bd899ee](bd899ee) [#775](#775) | not user-visible | Portable test fixtures only | [tests/single-file-tar.test.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/tests/single-file-tar.test.ts) | | [80dae3d](80dae3d) [#776](#776) | added | Candidate missing-daemon diagnosis is optional/healthy | [src/commands/doctor.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/doctor.ts) | | [5b2a2d0](5b2a2d0) [#772](#772) | added | Bounded attaching-network retry; strict saved reads remain | [src/lib/native-compose-ownership.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/native-compose-ownership.ts) | | [ab29d49](ab29d49) [#777](#777) | not user-visible | CI scheduling only | [docs/reference/ci-test-selection.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/docs/reference/ci-test-selection.md) | | [31339c7](31339c7) [#770](#770) | entry present | Native inferred readiness, 240 s, HTTP/service_healthy limits | [packages/runtime-core/src/project/compose/service.rs](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/packages/runtime-core/src/project/compose/service.rs) | | [156ee7f](156ee7f) [#773](#773) | entry present | Loopback proxy port/Doctor/open checks | [src/lib/proxy-loopback.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/proxy-loopback.ts) | | [a7ec3c7](a7ec3c7) [#768](#768) | entry present | Cleanup receipts/partial outcomes and tracked-file wording | [src/lib/project-env-config.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/project-env-config.ts) | | [ceb3e23](ceb3e23) [#778](#778) | entry present | Native bridge socket readiness | [packages/runtime-core/src/provider/bridge.rs](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/packages/runtime-core/src/provider/bridge.rs) | | [af91049](af91049) [#783](#783) | entry present | Pointer reconciliation after partial cleanup | [src/commands/doctor.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/doctor.ts) | | [0a0bfdb](0a0bfdb) [#780](#780) | not user-visible | Site-only Linux guide publication; no CLI behavior | [scripts/docs/versions.json](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/scripts/docs/versions.json) | | [d0cf767](d0cf767) [#786](#786) | not user-visible | Consent test wording only (both commits) | [tests/env-migration-tracked-consent.test.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/tests/env-migration-tracked-consent.test.ts) | | [7e2e630](7e2e630) [#786](#786) | not user-visible | Consent test wording only (both commits) | [tests/env-migration-tracked-consent.test.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/tests/env-migration-tracked-consent.test.ts) | | [91c311e](91c311e) [#779](#779) | entry present | Restore or reversible retire; missing store keys scoped to readers | [src/lib/secret-store-retire.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/secret-store-retire.ts) | | [6ab1ace](6ab1ace) [#785](#785) | not user-visible | Docs-only release/upgrade/env notes | [docs/guides/v5-release-notes.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/docs/guides/v5-release-notes.md) | | [25bd63c](25bd63c) [#764](#764) | entry present | Doctor hygiene, local modes, ignored staging and live-input protection | [src/lib/secrets-hygiene.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/secrets-hygiene.ts) | | [d9df57c](d9df57c) [#784](#784) | not user-visible | Resolver/proxy and consent test corrections only | [tests/project-open-loopback.test.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/tests/project-open-loopback.test.ts) | | [2c1f665](2c1f665) [#791](#791) | added | Detached failure names, open DNS hint, init placeholder; prune guidance | [src/commands/project.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/project.ts) | | [f6fe55f](f6fe55f) [#787](#787) | not user-visible | Docs-only retirement/hygiene follow-up | [docs/guides/v5-release-notes.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/docs/guides/v5-release-notes.md) | | [7f9548c](7f9548c) [#792](#792) | not user-visible | Start-here docs, navigation and contributor rule only | [docs/versions/v5/install.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/docs/versions/v5/install.md) | | [a0cfc73](a0cfc73) [#793](#793) | added | Verified updater publication, backups and reversible candidate removal | [src/lib/self-update.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/self-update.ts) | | [292d312](292d312) [#782](#782) | added | Lock-free native ps and proved failed-start down | [src/backends/native-project-observe.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/backends/native-project-observe.ts) | | [b1e123b](b1e123b) [#796](#796) | added | Doctor reader count/worktree grouping in store recovery | [src/lib/doctor-secret-integrity.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/doctor-secret-integrity.ts) | | [286d00f](286d00f) [#789](#789) | added | Foreground command-before-wrapper retirement | [src/lib/project-lifecycle-processes.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/project-lifecycle-processes.ts) | | [bb6a7d1](bb6a7d1) [#788](#788) | not user-visible | Linux qualification runbook only | [docs/guides/linux-host-qualification.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/docs/guides/linux-host-qualification.md) | | [a262d42](a262d42) [#800](#800) | not user-visible | Internal desktop design; outside supported CLI release scope | [apps/desktop/DESIGN.md](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/apps/desktop/DESIGN.md) | | [6b161ba](6b161ba) [#774](#774) | added | Native interactive exec, finite fallback and receipt recovery | [src/backends/native-project-exec.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/backends/native-project-exec.ts) | | [1249272](1249272) [#799](#799) | not user-visible | Portable output-contract CI selection only | [scripts/test-shard-plan.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/scripts/test-shard-plan.ts) | | [fa3f03d](fa3f03d) [#790](#790) | added | Read-only upgrade check, snapshot and rollback | [src/commands/upgrade.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/upgrade.ts) | | [c3b79bc](c3b79bc) [#807](#807) | added | Observe-only global status and unknown evidence | [src/commands/global-status-observe.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/global-status-observe.ts) | | [29be043](29be043) [#812](#812) | added | Read-only local CA trust diagnosis | [src/lib/ca-trust-status.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/ca-trust-status.ts) | | [f0102fd](f0102fd) [#814](#814) | not user-visible | Prepared-base verifier test fixture only | [tests/qualify-provider-pin.test.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/tests/qualify-provider-pin.test.ts) | | [b4ebfec](b4ebfec) [#815](#815) | added | Compact upgrade plan, snapshot-backed apply/resume | [src/lib/upgrade-apply.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/upgrade-apply.ts) | | [c44b266](c44b266) [#801](#801) | not user-visible | Internal optional desktop preview; not shipped CLI behavior | [apps/desktop/Cargo.toml](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/apps/desktop/Cargo.toml) | | [56d1d0f](56d1d0f) [#804](#804) | added | Automatic mux sweep preserves live/retired config inputs | [src/lib/live-inputs.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/live-inputs.ts) | | [1958363](1958363) [#805](#805) | added | MCP timedOut result and Codex installer timeout | [src/mcp/server.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/mcp/server.ts) | | [e6207e8](e6207e8) [#810](#810) | added | Lifecycle activity writes including handler refusals; activity JSON | [src/commands/lifecycle-activity.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/lifecycle-activity.ts) | | [23fe08f](23fe08f) [#825](#825) | added | Read-only batch review and permission-only key repair | [src/lib/upgrade-review.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/upgrade-review.ts) | | [cab596c](cab596c) [#781](#781) | entry present | Full native env-file behavior table; added support/precedence summary | [src/backends/native-compose-environment.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/backends/native-compose-environment.ts) | | [1e84684](1e84684) [#798](#798) | not user-visible | Rust test process isolation/nextest; internal qualification only | [scripts/test-runtime-core.sh](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/scripts/test-runtime-core.sh) | | [518af29](518af29) [#817](#817) | not user-visible | Remove accidental empty checkout files; no installed CLI change | [.gitignore](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/.gitignore) | | [07192f7](07192f7) [#824](#824) | added | BREAKING warning moved from page tail into next.17; v4 stop procedure retained | [src/mux/mux-config.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/mux/mux-config.ts) | | [fe09a5b](fe09a5b) [#816](#816) | added | Read-only install/trust admin plans and exits | [src/commands/global.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/global.ts) | | [8977676](8977676) [#827](#827) | added | Registry-preserving project observe, runtime opt-in | [src/commands/projects.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/commands/projects.ts) | | [5965b23](5965b23) [#828](#828) | added | Running CLI version identity JSON | [src/lib/version-report.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/version-report.ts) | | [2922210](2922210) [#830](#830) | added | Upgrade safe/admin/manual action kinds | [src/lib/upgrade-action-kind.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/upgrade-action-kind.ts) | | [51b55c0](51b55c0) [#820](#820) | not user-visible | Archive/plan/shared-helper verification fixtures only | [tests/python-free-download.test.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/tests/python-free-download.test.ts) | | [5002cf8](5002cf8) [#809](#809) | added | Retirement read retry, second Ctrl-C and native cancellation cleanup | [src/lib/project-lifecycle-processes.ts](https://github.com/hack-dance/hack/blob/5002cf87bb50686b17ceae8a4c835f524ee1b3a5/src/lib/project-lifecycle-processes.ts) | --------- Co-authored-by: Kit <344842+roodboi@users.noreply.github.com>
The automatic stale terminal-session payload sweep now uses the same live-input removal guard as
hack doctor --fix. Before each removal it resolves current configuration strictly and keeps payloads whoseenv.jsonor directory is live, retired, or cannot be resolved. Unreadable configuration prevents cleanup.src/lib/live-inputs.tsshares path/realpath and retired-store decisions between both cleanup paths. The live set includes configured stores/keys, shared and project environment inputs, and the global configuration file itself—even whenHACK_GLOBAL_CONFIG_PATHpoints to a stale payload'senv.json. Doctor fixes retain both inspected and current inputs; automatic sweeps re-resolve current inputs before each candidate. Existing ownership, file-type, single-link and age checks remain in place.Validation at
6e767f5a513ea43d6028c3fb627f40839635e1c6:HACK-2181. Persistent second-family review, exact-head Codex and hosted CI remain pending.