Skip to content

feat(secrets): retire a store whose key is gone, and fail doctor only where the store is read (HACK-2177) - #779

Merged
roodboi merged 24 commits into
nextfrom
claude/hack-2177-retire-store
Nov 13, 2026
Merged

roodboi merged 24 commits into
nextfrom
claude/hack-2177-retire-store

Conversation

@roodboi

@roodboi roodboi commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes HACK-2177. A machine can have an old global encrypted store (~/.hack/secrets.enc.json) whose key file is gone. Since #766, hack doctor then failed in every project, saying not to provision a new key, but offering no other way forward.

What changes

  • hack secrets retire-store: a deliberate, reversible way forward when the key is gone for good.
    • It acts on the store this project selects (--path or the current directory), or the global store outside a project.

    • It moves the store into a new directory <store>.retired-<UTC time>/ beside it, keeping its name. Under the store's lock:

      • it creates that directory exclusively (0700; refused if the name exists);
      • it hard-links the store into it (a link never replaces a file);
      • it renames whatever is at the store path into the same directory under an unpredictable, checked-free name, and removes that extra name only when it is the archived file.

      A replacement put at the store path meanwhile is kept beside the archive. Every rename target and the only name ever removed are inside the new directory, and failure cleanup is rmdir, which refuses unless the directory is empty.

    • It only retires a file that holds a Hack encrypted-store envelope (the configured path is project input), never the key file itself (same device and inode), and refuses when the config that selects the store cannot be read or does not parse. It classifies the store again under the lock, and the prompt names the resolved store path.

    • It never deletes, reads or prints an entry, and leaves the key file alone.

    • It prints what moved and the mv command to put it back.

    • Without a terminal it is refused with E_INTERACTIVE_REQUIRED unless --yes is passed, as hack secrets delete is.

    • It refuses a store that opens with the key in use, an absent store, and a store unreadable for another reason (a refused key, an unreadable or linked store); for those, doctor says what to fix instead.

    • It refuses a store that another registered checkout's key file or the OS keychain still opens. It also refuses, unless --allow-unchecked is passed, when coverage is incomplete: an unreadable registry, an unavailable checkout, or an unreadable key. HACK_SECRETS_FILE_KEY overrides key files and the keychain at runtime and may be set in another project's shell, which the command cannot check, so in practice retiring needs --allow-unchecked as the explicit confirmation that none does. Doctor's recovery text says so.

  • Doctor's secret integrity finding for a store with a missing or wrong key:
    • Error only in a project whose runtime reads the store: its legacy env contract declares a store-backed (source: "keychain") value, or plaintext is kept in the backend. This is the same condition the runtime uses before it opens the store; an unreadable contract counts as reading it. In every other project it is a warning, so a machine-wide store no longer fails unrelated projects.
    • It says the store's entries cannot be counted without the key. The envelope is a single encrypted payload, so there is no count to read; I kept that honest rather than estimating.
    • It names the registered projects that read it (names only, from configuration; no store is opened for that). Store files are compared by device and inode, so a reader reaching the store through a symlinked directory is found. When the registry or a project's config cannot be read, it says the list may be incomplete instead of claiming no project reads it.
    • A key file that exists but cannot be read is reported as such (fix its permissions), not as missing, and is not retirable.
    • It gives the two choices: restore the key, or retire the store. "Do not provision a new key" stays.
  • Retired files are listed as information (the check stays ok), with an mv command using absolute paths. A quoted ~ would not expand.
  • Recovery workflow: a new "Choose one" step names both choices whenever that finding is present.
  • Docs: the env guide, a short upgrade-guide section "An old global secret store whose key is gone", and the regenerated CLI reference.
flowchart TD
  D[hack doctor in a project] --> L{global/project store: key opens it?}
  L -- yes --> OK[ok]
  L -- missing or wrong key --> U{does this project's runtime read the store?}
  U -- yes --> E[error + users + two choices]
  U -- no --> W[warning + users + two choices]
  E --> C{choose}
  W --> C
  C -- restore the key --> OK
  C -- hack secrets retire-store --> R[store renamed .retired-UTC, nothing deleted]
  R --> I[doctor: retired file listed as info, check ok]
  I -- mv it back --> L
Loading

On the severity rule (c)

I agree with the rule. The predicate is the runtime's own condition for opening the store, not merely "the configured backend is encrypted_file". A global backend setting is inherited by every project, so that would fail them all again.

Tests (fake homes only)

tests/secret-store-retire.test.ts sets HOME, HACK_HOME and the global config inside a temp root, and uses two registered projects sharing the global store: app-a reads it (a legacy contract with a store-backed value), and app-b does not.

  • Store present, key missing: app-a gets an error, app-b a warning. The warning names app-a, says the entries cannot be counted, gives both choices, and contains no value or key. The recovery block shows "Choose one" with both.
  • Retire through the real CLI (a subprocess with a private env):
    • Non-interactive without --yes: E_INTERACTIVE_REQUIRED, and the store is byte-identical.
    • Without --allow-unchecked: refused, naming HACK_SECRETS_FILE_KEY; the store is byte-identical.
    • With --yes --allow-unchecked: the store is gone from its name, and exactly one .retired-* file holds identical bytes. The output names both paths and the mv back, and contains no value.
  • Doctor afterwards: one info finding in both projects, with the absolute mv.
  • Restore by moving the file back: the bytes are identical, the warning is back, and with the key restored the store opens and the synthetic value reads back. Nothing was lost.
  • Refusals: a store that opens, a refused (0644) key, and an absent store. Nothing moves in any of them.
  • tests/missing-project-error-code.test.ts pins secrets retire-store as a command that works without a project (it uses the global store).

Local gates (Bun 1.4.4):

  • bun run typecheck is clean.
  • bun run check: 43 warnings, the baseline on next; none in the touched files.
  • privacy:check and gitleaks are clean.
  • 256 tests pass across the new file, doctor secret integrity, secret store, doctor --fix, missing-project and the recovery/CLI suites.
  • bun run docs:check could not run locally (the docs site's dependencies are not installed here); the hosted docs job covers it.

No real store, key, keychain or registry was touched on any host.

🤖 Generated with Claude Code


Proof this PR
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

… where the store is read (HACK-2177)

A machine with an old global encrypted store whose key file is gone made
hack doctor fail in every project, with no way forward but restoring the
key.

- hack secrets retire-store moves the store aside to
  <store>.retired-<UTC time> in the same directory (a hard link, then the
  old name is removed, under the store lock). It never deletes, reads or
  prints an entry, leaves the key file alone, prints how to put the store
  back, and needs --yes when it cannot prompt. It refuses a store that
  opens, is absent, or is unreadable for another reason.
- The secret integrity finding for a store with a missing or wrong key is
  an error only in a project whose runtime reads the store (a store-backed
  legacy contract value, or plaintext kept in the backend), and a warning
  elsewhere. It says the entries cannot be counted without the key, names
  the registered projects that read the store, and gives both choices.
- Retired files are reported as information (the check stays ok), with an
  mv command using absolute paths.
- The recovery workflow gets a "Choose one" step naming both choices.
- Docs: env guide, upgrade guide section, CLI reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
hack-docs Ready Ready Preview Nov 13, 2026 6:10pm UTC

Request Review

@linear-code

linear-code Bot commented Nov 13, 2026

Copy link
Copy Markdown

HACK-2177

@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-13T17:58:53.830913Z 48bc0a6 Manual request
🔒 Security Review ✅ Completed 2026-11-13T18:03:54.333576Z 48bc0a6 Manual request

Security findings

Advisory findings (7)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 871debc233

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

Comment thread src/lib/secret-store-retire.ts Resolved
Comment thread src/commands/recovery-guidance.ts Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 871debc233

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/lib/doctor-secret-integrity.ts Resolved
…ope the retire command to the diagnosed project (HACK-2177)

- retireEncryptedStoreFile takes a recheck run under the store lock just
  before the move; retireSecretStore classifies the store again there, so
  a store whose key was restored, or that was replaced, is not retired.
- The doctor finding names hack secrets retire-store --path <project>, the
  recovery block carries that exact command, and scoped guidance adds
  --path to an unscoped one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bc26d7ccc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

Comment thread src/lib/secret-store-retire.ts Resolved
Comment thread src/commands/secrets.ts Resolved
…ACK-2177)

A configured store path is project input and may hold a quote; the mv
restore commands doctor and retire-store print, and the --path in the
retire command, now use quoteShellWord instead of bare single quotes.
The guidance extracts the backticked command whole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: f913a591d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

…store is not retired (HACK-2177)

The listing classified every key-resolution failure other than a refused
key as missing, so retire-store accepted a store whose key file exists
but is unreadable (mode 000). It is now key_unreadable: doctor says to
fix the key's owner and permissions, and retire refuses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 580e91d69a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

Comment thread src/lib/secret-store.ts Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 580e91d69a

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/lib/secret-store-retire.ts Resolved
Comment thread src/lib/doctor-secret-integrity.ts Resolved
…key aliases and unsure config, and report incomplete reader scans (HACK-2177)

Second-family review of #779:
- The move never unlinks: the archive name is created exclusively as an
  empty placeholder, checked to still be that empty file, and the store
  is renamed over it. A replacement put at the store path by a writer
  outside the lock is archived, not deleted; an existing archive name is
  refused, never replaced.
- A store that is its own key file (same device and inode, so aliases and
  hard links too) is refused.
- Retire refuses, before and under the lock, when the global or project
  config that selects the store exists but cannot be read or does not
  parse, instead of falling back to another store.
- The reader scan reports an unreadable registry and projects it could
  not check, compares store files by device and inode, and doctor says
  when the list may be incomplete instead of "no registered project
  reads it".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 938212ec41

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 938212ec41

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…nd store findings are errors only where the store is read (HACK-2177)

With no key file, a keychain that is locked, denies access, times out or
is in its failure cooldown left the key's existence unknown, yet the
listing called it key_missing and retire-store accepted it. Keychain
failures now carry SecretStoreKeyAccessError and classify as
key_access_failed (as does any unexpected failure); retire refuses it and
doctor says to unlock the keychain. Missing now means: no key file, and
the keychain answered no item or is turned off.

Every store-level finding (not only the missing key) is now an error only
in a project whose runtime reads the store, a warning elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 177610c50d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 177610c50d

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…mod guidance is quoted (HACK-2177)

- The configured store path is project input: retire-store now refuses,
  before and under the lock, a file that does not hold the encrypted
  store envelope, and doctor never offers to retire such a file. The
  confirmation prompt names the resolved store path.
- The chmod and git rm guidance in the store and key findings uses
  absolute, shell-quoted paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: f4bc8140e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: f4bc8140e7

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…t is replaced or removed, and keep scan uncertainty (HACK-2177)

Second-family review of #779 at 177610c:
- Retire creates <store>.retired-<stamp>/ exclusively (0700), hard-links
  the store into it (a link never replaces a file), then renames whatever
  is at the store path into that directory under an unpredictable,
  checked-free name, removing it only when it is the archived file. A
  replacement written meanwhile is kept beside the archive. Every rename
  target and the only name ever removed are inside the new directory;
  failure cleanup is rmdir, which refuses unless empty.
- The reader scan reads the registry strictly (an invalid registry is
  unknown, not empty) and treats a store identity it cannot establish as
  unchecked, never "different".
- Docs and the CLI reference describe the retired directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4af05a108d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

Comment thread src/lib/projects-registry.ts Resolved
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 354f950e98

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

…read (HACK-2177)

The best-effort parser also drops invalid worktree entries inside a valid
project; readProjectsRegistryStrict now counts them as rejected, so the
reader scan reports them as unchecked and retire needs --allow-unchecked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 47afaee94c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

…ed worktrees field as unchecked (HACK-2177)

- An lstat failure on the configured store path other than absent now
  refuses (whether it is a symlink is unknown) instead of passing.
- A registry project whose worktrees field is present but not an array
  counts as one rejected entry in the strict read, so the reader scan is
  incomplete and retire needs --allow-unchecked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: a49048ffe7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: a49048ffe7

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/lib/secret-store-retire.ts Resolved
…nother shell (HACK-2177)

HACK_SECRETS_FILE_KEY overrides key files and the keychain at runtime and
may be set in the shell of any project that selects the store. A key file
or keychain that does not open the store is no proof that project cannot,
so retire-store now refuses unless --allow-unchecked confirms none does.
Doctor's recovery text and the docs say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 48bc0a6b13

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 48bc0a6b13

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…ecovery (HACK-2177)

The CLI help, the generated reference and the doctor recovery choice now
say that retiring needs --allow-unchecked: it confirms that no shell for a
project that selects the store sets HACK_SECRETS_FILE_KEY, which Hack
cannot inspect. A key known to open the store still refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Ready for lead: head e1fe59b.

  • Persistent reviewer: CLEAN at e1fe59b, with no must-change items. The remaining follow-ups (native filesystem and concurrency qualification, full CLI recovery qualification, secondary key-probe hardening, the documented private-directory ownership limit) were already accepted as out of scope.
  • Codex security P2 ("Treat environment-only store keys as unchecked") was fixed in 48bc0a6 and its thread is resolved. HACK_SECRETS_FILE_KEY overrides key files and the keychain, so any project that selects the store with no key that opens it may still use it from another shell. Retiring now needs --allow-unchecked as the explicit acknowledgement. A key file or keychain entry known to open the store still refuses, even with the flag. e1fe59b says so in the CLI help, the generated reference and the doctor recovery choice.
  • Codex code review and Codex security review: both clean at 48bc0a6. e1fe59b only changes wording.
  • Bot threads: CLEAR at e1fe59b.
  • CI at e1fe59b: success. 14 checks pass; 7 matrix and Docker jobs are skipped by the workflow's change scope. No hosted jobs are pending.
  • Local gates: 132 tests pass across the 7 related files; typecheck clean; check at the 44-warning baseline (none in touched files); privacy and gitleaks clean.

Not merged.

@roodboi
roodboi merged commit 91c311e into next Nov 13, 2026
21 checks passed
@roodboi
roodboi deleted the claude/hack-2177-retire-store branch November 13, 2026 18:48
roodboi added a commit that referenced this pull request Nov 13, 2026
Add next.17 “what changed and what to do” notes to the release summary,
both upgrade guides and the env guide. Users get concrete script
migrations for hidden env values and native followed logs, the next.16
key-backup warning, and recovery guidance for partial cleanup.

These describe merged source changes intended for next.17; they do not
claim a release is published. The docs site remains pinned to next.16.
#764 and #779 remain explicitly pending, and the recorded next.16 trials
keep their original limits.

| Subject | Merged contract checked at base `0a0bfdb8` |
| --- | --- |
| #747: native plain followed logs, streams, timestamps and structured
session results |
[native-log-follow.ts](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/backends/native-log-follow.ts),
[followed-service-log
contract](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/docs/reference/native-authored-commands.md#followed-service-logs)
|
| #767: metadata/`has_value`, scoped plaintext reads, terminal-only
reveal, stdin secret writes, `env protect`, global-only lifecycle
hooks/processes and YAML daemon API | [canonical prerelease
wording](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/scripts/prerelease-plan.ts#L158),
[env
command](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/commands/env.ts),
[env
routes](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/daemon/routes/env.ts),
[env
contract](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/docs/env.md)
|
| #769 / HACK-2172 and #768/#783: protect live keys/config/YAML
including filesystem aliases; cleanup receipts, partial removal and
config-pointer reconciliation |
[project-env-config.ts](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/lib/project-env-config.ts),
[Doctor
cleanup](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/commands/doctor.ts),
[canonical cleanup
note](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/scripts/prerelease-plan.ts#L164)
|
| #766: Doctor refuses unusable env layers |
[doctor-secret-integrity.ts](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/lib/doctor-secret-integrity.ts)
|
| #773: loopback proxy ports, diagnosis without automatic repair |
[proxy-loopback.ts](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/lib/proxy-loopback.ts),
[Doctor](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/commands/doctor.ts)
|
| #770: inferred route readiness, 240-second grace, HTTP 200–499 limits
and `service_healthy` exclusion |
[http_probe.rs](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/packages/runtime-core/src/provider/http_probe.rs),
[probe
contract](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/docs/guides/native-candidate.md#native-http-readiness-probe)
|
| #778: bridge connection readiness after pool restart |
[bridge.rs](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/packages/runtime-core/src/provider/bridge.rs)
|
| #758 and #776: merged startup fix and newer optional-daemon behavior,
kept separate from next.16 | [project
command](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/commands/project.ts),
[Doctor](https://github.com/hack-dance/hack/blob/0a0bfdb8d1ee818d89bb49a2109bd88b0a9fea1b/src/commands/doctor.ts)
|

Validation at `f1d0d72fede5db6ce46f56039ba22a47911aa308`:
- Persistent reviewer: clean after round 2.
- Pinned Bun 1.4.4: docs prepare/check/typecheck and production site
build pass.
- Production-mode HTTP smoke: 95 docs, isolated searches/exports,
redirects/refusals, 37 diagram blocks / 74 SVGs and 183 recipe
links/anchors pass. The rendered upgrade guide returns 200 and contains
the new guidance.
- Privacy check, diff check and full-history Gitleaks pass (1,295
commits, no leaks).
- Source citations and documented command forms checked against the
merged base.

Documentation only: no runtime, workflow, site pin, snapshot or global
client changes. No live migration, keys, daemon, Docker or native VM
tests were run. Lead owns batch integration and deployment.

<!-- codesmith:footer -->
---
<a
href="https://backend.blacksmith.sh/track/run-proof?expires=1797183263&installation_model_id=17053&pr_number=785&ref=codesmith_pr_footer&repository=hack-dance%2Fhack&return_to=https%3A%2F%2Fgithub.com%2Fhack-dance%2Fhack%2Fpull%2F785&variant=run_proof_on_this_pr&signature=b04dd421a08341f70e804e671fd68678f102d1c4152146bdea18adf6de909981"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-run-proof-on-this-pr-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-run-proof-on-this-pr-light.svg"><img
alt="Run Proof on this PR"
src="https://pr-comments-assets.blacksmith.sh/codesmith/proof-run-proof-on-this-pr-dark-3x.png"
width="168" height="28"></picture></a>
<sup><a
href="https://app.blacksmith.sh/hack-dance/codesmith/hack/pr/785?autoLogin=true&ref=codesmith_pr_footer">Open
in [code]smith</a> · Need help on this PR? Tag
<code>@codesmith-bot</code> with what you need.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

---------

Co-authored-by: Kit <344842+roodboi@users.noreply.github.com>
roodboi added a commit that referenced this pull request Nov 13, 2026
#779 (HACK-2177, retiring a store whose key is gone) and this branch each
added resolveFileSecretStorePaths to src/lib/secret-store.ts; git merged
both without a textual conflict, leaving a duplicate declaration. One
definition remains: #779's, keeping its name and { active, storePath,
keyPath } result for secret-store-retire.ts, now with this branch's
opt-in strict mode (an unreadable or malformed configuration rejects
instead of defaulting), which hack doctor's removal check uses. Doctor
takes only storePath and keyPath from it for its hygiene scope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi added a commit that referenced this pull request Nov 13, 2026
…2165, HACK-2177)

A retired store (<store>.retired-<stamp>/, from #779) may hold the only
copy of values its key still opens. Hygiene's detectors never look inside
one, but the removal check now refuses any target whose path, as given or
resolved, has a retired-store component, both when findings are built and
just before the unlink, with or without a scope.

Regression: a store retired with retireEncryptedStoreFile survives
inspection and --fix, and a guarded removal offered for its archived file
(with and without a scope) is refused. It fails without the check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi added a commit that referenced this pull request Nov 14, 2026
…(HACK-2189) (#790)

## Summary

HACK-2189 is the first unit of the upgrade assistant (HACK-2188) and the
foundation the later units build on. The goal: moving a machine from v4
to v5 becomes one guided, resumable and reversible flow, replacing a
hand-written rollback sheet. This PR adds a read-only check, a snapshot
and rollback library, and three commands on top of them.

```mermaid
flowchart LR
  C["hack upgrade check<br/>(read-only)"] -->|blockers| F[fix the named blocker] --> C
  C -->|plan| S["hack upgrade snapshot"] --> A["plan steps<br/>(later: hack upgrade apply)"]
  A -->|undo| R["hack upgrade rollback<br/>--rollback-manifest"]
```

### `hack upgrade check [--json]`

It checks the machine and every registered project.

**Machine:**
- The running version and channel (stable or next; candidate launcher).
- The `hack` on PATH: its version and install kind.
- hackd: whether it runs, who runs it, and whether a login service is
registered (systemd or launchd).
- The **projects registry**. Dead entries (the checkout, its `.hack/`,
or all of `hack.config.json`, `hack.toml` and `docker-compose.yml` are
gone) are counted **apart from** live entries that cannot be read. Dead
ones point at `hack projects prune --dry-run`.
- Hack's global proxy and DNS (installed and running), the #773 loopback
**proxy ports** check, and the Linux DNS route.
- The **global secret store and key**. It reuses the #779 code, and
reads the key file directly, so it never prompts the keychain.

**Per live checkout** (each project's primary checkout and each
registered linked worktree):
- **Env format:** YAML layers, legacy JSON, legacy dotenv, or a
conflict.
- **Env key:** present, private and untracked (doctor's key audit).
- **Tracked machine-local or sensitive files**, and the committed
`.hack/.gitignore`.
- **Agent integration files** (`hack setup` state).
- On Apple Silicon, the **native preflight** verdict. It runs in-process
only and is never a blocker, because native is optional.

**Output:** an ordered plan and the single next command. Blockers come
first, then `hack upgrade snapshot`, then machine steps, then project
steps. The command exits 0 when nothing blocks. Otherwise it exits 1 and
names the blockers. A blocker is something that would make the upgrade
fail or lose data:
- an env layer conflict;
- a tracked secret key, or an env key error;
- an unreadable or invalid registry;
- a global store whose key is gone while a registered project reads it.

**Changes nothing:** no file, registry, service, container or key is
written, started or provisioned, and nothing prompts. A test compares
the whole fixture tree (paths, modes, digests) before and after. Doctor
functions with side effects are avoided:
- `globalStatus`/`ensureDockerRunning` can start Docker;
- `hack projects` touches the registry;
- the native runtime review spawns the binary and writes a temporary
directory.

`collectProjectNativePreflight` is split out of the doctor command so
the report can be collected without printing it or running the runtime
review.

**Fast:** dead entries cost one `stat`. Live projects are checked eight
at a time, with an 8-second limit per check and a shared 24-second
budget. When the budget runs out, the remaining checks report "not
checked" rather than running. Host probes have time limits too. An
80-entry fake registry (76 dead) finished its checks in about 130 ms.

**Hermetic:**
- Every host probe is injected: Docker, the resolver, service managers,
the PATH `hack` and native preflight.
- `HACK_HOST_PROBES_DISABLE=1` refuses them all and is set by the test
preload, like `HACK_SYSTEMD_DISABLE`.
- A refused or failed probe reports "not checked", never a pass.

### Snapshot and rollback library: `src/lib/upgrade-snapshot.ts`

The API is posted in a comment for HACK-2191:
#790 (comment). It
is modelled on the `hack setup migrate` backup manifest, with these
differences:
- **No content in the manifest.** It holds only path, label, kind
(`file` | `absent`), size, mode and SHA-256, before and after. Bytes
live in content-addressed `blobs/<sha256>`, because env files hold
secrets.
- **Private storage outside every working tree.** Directories are 0700
and files 0600, created with no-follow exclusive opens and owner-checked
on every read. A snapshot root inside a Git working tree is refused.
Manifest writes are atomic: temporary file, fsync, rename.
- **All or nothing.** A link, directory, unreadable or oversized target
refuses the whole snapshot before anything is written.
- **Restore puts back exact bytes and modes.** It writes a no-follow
temporary file and renames it over the target.
- **It refuses a target that changed since the step recorded it**
(`recordUpgradeSnapshotAfter`) unless told to overwrite. A target with
no recorded after state also needs `overwrite`.
- **It never deletes.** What it replaces or removes is kept in the
snapshot and named in `keptAs`.
- **It is idempotent**, and reports every target it could not restore
with a closed reason.

### Commands

- `hack upgrade snapshot [--json]` keeps:
- the global config, the registry, and the secret store and key (when a
file store is used);
- per live checkout: the env migration's write set, which is its
`.hack/` config, env contract, Compose file, dotenv files, YAML layers
(the base, machine-local and per-overlay layers it would create,
recorded as absent when they don't exist yet), `.hack/.gitignore` and
in-checkout env key files.

It skips links. Printed commands run the CLI that produced them: during
the prerelease, the `hack-next` launcher, with
`HACK_RUNTIME_BACKEND=compose` for a Compose run.
- `hack upgrade rollback --rollback-manifest <manifest> [--overwrite]
[--json]` restores the files. Without a manifest it lists the snapshots
and changes nothing, because non-interactive mode never guesses.

### Docs

- The upgrade guide gains a "Check first" section.
- `docs/cli.md` lists the commands.
- `docs/reference/cli.md` is regenerated.

## Validation

- **New tests** in `tests/upgrade-snapshot.test.ts` and
`tests/upgrade-check.test.ts` (19, all passing), using a temporary HOME.
No Docker, no daemon autostart, no real home or registry. They cover:
- snapshot, then mutate, delete and create, then rollback: byte-for-byte
equal with exact modes; the created file moved aside and kept;
idempotent;
- refusal on a changed target; overwrite keeps the edit; a target with
no after state; a race during restore;
  - unsafe targets and roots; a non-private manifest; a damaged blob;
- sentinel values absent from every manifest, result and report, with
storage modes 0700/0600;
- dead vs unreadable registry entries: 79 entries, 77 dead; a TOML-only
or Compose-only project counts as live;
- plan ordering; ready with an empty plan; secret-store blocker only
when a project reads the store;
- the check changes nothing (whole-tree digest); disabled or failing
probes report not checked; the time budget;
- tracked files, ignore rules and a tracked secret key in a real
temporary Git repo;
- CLI: `check` exits 1 naming blockers, and snapshot → mutate → rollback
refuses without `--overwrite` and restores with it.
- **Related suites:** 143 pass across 11 files. The one failure is
"recovers a confirmed dead registry owner", which times out on this host
on `next` as well, plus the unhandled error it leaves behind.
- **Gates:** typecheck clean; `check` at the 44-warning baseline (none
in touched files); `docs:prepare` then `docs:check` pass; privacy ok;
gitleaks clean.


### Review round 2 (head 3118605): what changed

- **Readiness:** `ready` needs no blockers **and** no `incomplete`
checks. A required check that fails or times out, disabled host probes,
a key only the keychain could confirm, or a registry cut short by the
deadline are all incomplete. The exit code is 1 when not ready. A live
checkout whose config cannot be read blocks.
- **Commands vs actions:** commands are built from the running CLI's
prefix plus separately quoted arguments, never by rewriting text, so a
path containing " hack " survives. Plan steps carry a `command` or a
manual `action`. `next_command` is null when the first step is manual,
and `next_action` then says what to do. The snapshot step now comes
first.
- **Global key:** read-only `inspectStoreKeyState` follows the runtime's
order and refusals (env key, then the key file through the runtime's
reader: link, mode, Git tracking). It never asks the keychain. An absent
store is not a lost key.
- **Snapshot coverage:** it includes the migration's own overlay
selection, so layers for overlays only the secret store holds are
recorded as absent. When those can only be listed through the keychain,
the snapshot reports the gap.
- **Restore never loses an edit:** the current entry is displaced by one
rename to an invocation-owned sibling and its exact bytes are kept.
Publication uses a hard link that refuses an occupied target, and a late
edit is put back. Unknown restore paths are rejected. The manifest
directory is fsynced, and the single-writer rule is documented.
- **One deadline, no stray children:** the 25 s budget covers the
registry read, classification, probes and checks. Config inputs must be
regular files (no-follow, non-blocking, bounded). A new
`withProcessDeadline` (AsyncLocalStorage) caps `exec` and the Git
probes, so every child a check starts ends with that check. Previously
unbounded Git calls in shared helpers (`readGitPath`, the
generated-files `ls-files`, three ignore/tracking probes) now have 10 s
limits, which helps doctor too.
- **Tests:** 32 upgrade tests, including late-replacement races,
store-only overlays, key audits, a FIFO config, hanging Git with
child-death checks, and quoting through a real shell split. A 64-file
related slice has only the failures that also occur on a clean `next`
checkout on this host (adopted-project doctor tests, registry lock
timing).

## Limits and follow-ups

- The daemon finding reports the owner and registration. Handover is the
next unit.
- `hack upgrade snapshot` does not cover the daemon's unit or plist or
Docker state; the guide says so.
- Native preflight is the in-process part only.
- The check is not timed on a real 80-entry machine; the fixture
measurement is above.
- Env-key remedies are relative to the project root, so the plan step is
`hack doctor --path <root>` and the remedy is in the finding text.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Kit <344842+roodboi@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
roodboi added a commit that referenced this pull request Nov 14, 2026
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>
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