Skip to content

fix(env): refuse machine-local writes git could collect; no-follow legacy dotenv reads (HACK-2165) - #762

Merged
roodboi merged 9 commits into
nextfrom
claude/hack-2165-env-local-write-guards
Nov 13, 2026
Merged

roodboi merged 9 commits into
nextfrom
claude/hack-2165-env-local-write-guards

Conversation

@roodboi

@roodboi roodboi commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

First of two PRs for HACK-2165 (umbrella HACK-2163): audit findings F4, F5 and F3. F1, S1 and the doctor "secrets hygiene" section follow in a second PR.

F4: a symlinked machine-local layer could write into a tracked file

hack env hydrate checked whether git tracks the local layer by the link's own name, which is untracked. The writer then resolved the link and replaced its target, which could be the tracked hack.env.default.yaml. Every machine-local write (set, add and unset with --local, and hydrate) now refuses a symlinked destination. The check runs before the write and again immediately before the commit rename. Shared layers can still be links.

F5: local writes continued when git privacy was not established

Plain local writes only warned when .hack/.gitignore could not be updated, and env set --env qa --local had no tracked-file check. Before every machine-local write, writeProjectEnvConfigFile now refuses, with the same fail-closed semantics as the migration's checks:

  • a layer git tracks;
  • a layer whose tracking git cannot report inside a checkout;
  • a layer that no effective rule ignores, after one attempt to restore the managed .hack/.gitignore block.

Outside a git checkout nothing can commit the layer, so these checks pass.

flowchart TD
  W[machine-local write] --> L{destination is a symlink?}
  L -- yes --> R1[refuse: linked]
  L -- no --> T{git tracks it?}
  T -- yes --> R2[refuse: tracked]
  T -- unknown, in checkout --> R3[refuse: tracking unknown]
  T -- no / no checkout --> I{effectively ignored?}
  I -- no --> F[repair .hack/.gitignore once] --> I2{ignored now?}
  I2 -- no --> R4[refuse: not ignored]
  I -- yes --> OK[write; link re-checked before rename]
  I2 -- yes --> OK
Loading

The HACK-1363 tests encoded the old "warn and continue" behaviour. They now cover two cases: a read-only .hack/.gitignore while another rule ignores the layer (warns and writes), and while nothing ignores it (refuses). The symlink-boundary test that expected hydrate and unset to follow an in-project local-layer link now expects a refusal.

F3: legacy env list followed a dotenv symlink out of the project

Legacy resolution read .hack/.env and the selected overlay with a reader that follows symlinks, so a plain_env value from a file outside the project was printed. All eight legacy dotenv reads in src/lib/hack-env.ts now use the private no-follow reader the mutations already used, and refuse a redirected file before reading it.

Merge ordering

The other session on HACK-2163 (codex/hack-2163-env-secret-leaks) edits the env parse errors in src/lib/project-env-config.ts. This PR touches that file only in writeProjectEnvConfigFile, plus three new functions placed right after it: assertMachineLocalLayerWriteDestination, machineLocalLayerGitProblem and assertMachineLocalLayerNotLinked. It does not touch any parse or error-reporting function, src/lib/shell.ts, src/templates.ts or src/daemon/routes/env.ts.

Validation

  • New tests: a local layer linked to the tracked default layer (the audit's scenario); a local-layer link to an in-project file (hydrate and unset); a tracked hack.env.qa.local.yaml with --env qa --local; a read-only .hack/.gitignore with nothing else ignoring the layer; legacy .hack/.env and overlay links outside the project. All six fail on the base commit.
  • Local, pinned Bun 1.4.4: typecheck, lint, privacy:check, docs:check, gitleaks. 45 env-related test files: 918 pass. One suite failed because it needs the compiled config compiler, which this host cannot build; hosted CI runs it.
  • Tests use temporary directories and sentinel values only.

🤖 Generated with Claude Code


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

roodboi and others added 2 commits November 13, 2026 06:17
Two audit findings on machine-local env layers:
- F4: a local layer that is a symlink (for example to the tracked
  default layer) passed the tracking check, which tests the link's
  name, while the writer replaced the file it resolves to. Every
  machine-local write now refuses a symlinked destination, checked
  again immediately before the commit rename.
- F5: local writes only warned when the ignore rule could not be
  established, and `env set --env <name> --local` could write into an
  already tracked local overlay. Before every machine-local write
  (set, add, unset, hydrate) Hack now refuses a tracked layer, an
  unknown tracking status inside a checkout, and a layer no effective
  rule ignores after one repair attempt, with the migration's
  fail-closed semantics.

The HACK-1363 tests now cover a read-only .hack/.gitignore while
another rule ignores the layer (warns and writes) and while nothing
does (refuses). docs/env.md describes the checks and the legacy read
change in the next commit.

Co-Authored-By: Claude <noreply@anthropic.com>
Audit F3: legacy env resolution, including `hack env list`, read
`.hack/.env` and the selected overlay with a reader that follows
symlinks, so a link to a dotenv file outside the project was parsed
and a plain_env value printed. Every legacy dotenv read in
hack-env.ts now uses the private no-follow reader the mutations
already used, and refuses a redirected file before reading it.

Co-Authored-By: Claude <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 11:05am UTC

Request Review

@linear-code

linear-code Bot commented Nov 13, 2026

Copy link
Copy Markdown

HACK-2165

…r and migration checks (HACK-2165)

Own-reviewer round on bee7837:
- On a case-insensitive filesystem a write to hack.env.qa.local.yaml
  replaces a tracked Hack.env.qa.local.yaml, which the exact ls-files
  check missed. The machine-local guard also asks git with an
  icase,literal pathspec and refuses a tracked case variant.
- findUnmigratedLegacyDotenv and doctor's legacy DEV_HOST read still
  followed a .hack/.env link. Both now use the private no-follow reader;
  a linked file is reported unreadable and never read.
- The guard's ignore repair is silent, so a read-only .hack/.gitignore
  warns once, not twice.

Co-Authored-By: Claude <noreply@anthropic.com>
@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-13T11:07:58.920621Z 44a47a2 Manual request
🔒 Security Review ✅ Completed 2026-11-13T11:10:23.840051Z 44a47a2 Manual request
ℹ️ 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

Copy link
Copy Markdown

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

Reviewed commit: a19c030a7e

ℹ️ 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: a19c030a7e

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.

roodboi and others added 3 commits November 13, 2026 08:34
…itive disks (HACK-2165)

Hosted shard 1 runs on macOS, where hack.env.qa.local.yaml and the
tracked Hack.env.qa.local.yaml are one file, so the "new file does not
exist" assertion cannot hold there; the refusal itself passed. The test
now checks that only the tracked spelling is listed, which holds on
both kinds of filesystem.

Co-Authored-By: Claude <noreply@anthropic.com>
…acy and alias gaps (HACK-2165)

Second-family review of a19c030 (1 blocker, 4 should-fix), under the
stated contract: protect against accidental collection by git and
other OS users; a same-user racer is out of scope.
- Blocker: a machine-local write resolved its destination with
  realpath, so a link swapped in after admission could redirect the
  write to the tracked default layer. Machine-local writes now use
  noFollowTarget: a link at the destination is refused and the commit
  renames onto the admitted path itself, which a later link cannot
  redirect.
- Legacy `env set`/`unset` (a project without env layers) wrote
  `.hack/.env` without the git gate. After the info/exclude repair,
  assertMachineLocalFileGitPrivate now refuses a tracked, unknown or
  unignored file before any value is written.
- Checkout detection walked only the lexical parents of the project
  path, so an alias outside a repository to a project inside one was
  "outside every checkout" when git refused. It now walks the
  resolved path's parents too and fails closed on an unresolvable
  path or an uninspectable `.git` entry.
- A private read could be satisfied from a parent directory swapped
  to another directory and restored. After reading, the pinned
  directories are re-checked and the path must still name the file
  opened; otherwise the read retries, then refuses.
Staging-file ignore protection (the fifth item) is closed by #764,
which stages machine-local writes in the self-ignoring .hack-staging/.
docs/env.md states the contract and the new checks.

Co-Authored-By: Claude <noreply@anthropic.com>
Keeps writeTextFileAtomicIfChanged under the complexity limit after
noFollowTarget; no behavior change.

Co-Authored-By: Claude <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. Another round soon, please!

Reviewed commit: 73b242a2c9

ℹ️ 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: 73b242a2c9

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.

roodboi and others added 3 commits November 13, 2026 09:31
…ixture env (HACK-2165)

Hosted shard 7: tests/fixture-hermeticity.test.ts requires every
fixture git spawn to use fixtureGitEnv. The new legacy git-privacy
test's helper built its own environment.

Co-Authored-By: Claude <noreply@anthropic.com>
next (c96c444) conflicts in src/lib/hack-env.ts: #756 (HACK-2163)
added envParseDiagnostic; this branch reads every legacy dotenv file
through the no-follow readLegacyDotenvText, so readTextFile is no longer
imported. Both are kept.

Co-Authored-By: Claude <noreply@anthropic.com>
… environment (HACK-2165)

Second-family review of the merge with next (723ee60): #756 filters
Hack key material out of the git probes' environment, but this
branch's extra `ls-files :(icase,literal)` probe was added after that
base and auto-merged without it, so git inherited
HACK_ENV_SECRET_KEY and the other credentials. It now passes
childEnvironment(). Test: a local `env set` with a key sentinel in
Hack's environment records the probe's child environment through the
spawn recorder; it is explicit and holds no sentinel.

Co-Authored-By: Claude <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: 44a47a2d65

ℹ️ 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: 44a47a2d65

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.

@roodboi

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Ready for lead: HACK-2165 first PR (F3, F4, F5 and the second-family review's five findings), merged with next c96c444.

  • Second-family reviewer, same thread: clean at this head. Final report: /home/hack/scratch/review-codex-762-44a47a2d.md. Earlier rounds: review-codex-762-a19c030a.md (1 blocker and 4 should-fix, all addressed), -73b242a2, -c5c9c0e5, -723ee60c (merge review; one should-fix, the case-insensitive git probe's child environment, fixed in 44a47a2).
  • Merge with next: the only conflict was the src/lib/hack-env.ts import block. fix(env): contain child keys and env diagnostics (HACK-2163) #756's envParseDiagnostic and child-key containment are kept, and every legacy dotenv read still goes through the no-follow reader. next has since moved to 021b16b, which merges cleanly.
  • Contract (stated in docs/env.md and the PR body): protection against accidental collection by git and against other OS users; a process running as the same user is out of scope.
  • Item 2 (staging-file ignore protection) is closed by fix(env): secrets at rest: ignored staging, tightened modes, expiring mux payloads, doctor secrets hygiene (HACK-2165) #764, which must merge in the same batch with this PR's no-follow destination and reader identity checks preserved.
  • Bots: Codex review and Codex security review at 44a47a2 found no issues. computer pr review-status: CLEAR at 44a47a2. No unresolved threads.
  • Hosted CI: all Linux jobs pass (unit shards, test focus, docs site, secret scan, change scope). Skipped by the light PR checks: macOS candidate core, config compiler, Docker e2e shards, process lifetime, runtime images and state models, proof. Nothing pending.
  • Follow-ups, not in this PR:
    • src/lib/doctor-native-settings.ts:233: the same ..-prefix "outside" test.
    • The tracked-layer refusal prints an unquoted git rm --cached -- <path>. The path comes from the project location and the user's own --env, not from repository contents.
    • The daemon env route (HACK-2163 area) does not run the legacy git gate.

#762 44a47a2

@roodboi
roodboi merged commit 9435da9 into next Nov 13, 2026
21 checks passed
@roodboi
roodboi deleted the claude/hack-2165-env-local-write-guards branch November 13, 2026 12:57
roodboi added a commit that referenced this pull request Nov 13, 2026
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>
roodboi added a commit that referenced this pull request Nov 13, 2026
…ent; close threat-model gaps 17 and 18

isKeyFileTrackedByGit (from #766) spawned git with the full environment,
so HACK_ENV_SECRET_KEY and other Hack key material reached the child.
It now passes childEnvironment(), as #756 requires for tool children.
Regression: a fresh process with a recording git first on PATH checks a
key inside a work tree with a sentinel project key set; git sees no Hack
key variable. It fails without the change.

The threat model's gaps 17 (HACK-2169, closed by #766) and 18 (HACK-2165,
closed by #762 and this branch) now describe the implemented guards and
their remaining limits, and the rows that pointed at them as open agree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi added a commit that referenced this pull request Nov 13, 2026
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>
roodboi added a commit that referenced this pull request Nov 13, 2026
… mux payloads, doctor secrets hygiene (HACK-2165) (#764)

Second of two PRs for HACK-2165 (umbrella HACK-2163): audit findings F1
and S1, lead items 1, 2, 3 and 5, and the `hack doctor` "secrets
hygiene" section as the migration path. The first PR is #762 (F3, F4,
F5); please merge this one after it. Item 4 (encrypted-file backend
links and key cache) moved to HACK-2169 and is not here.

## F1 and item 2: no committable copies of keys or env files

- **Staging leftovers.** Private writes (`.hack/.env` files,
`.hack.secret.key`, the encrypted store and its key) staged their
contents in a `.<name>.<uuid>.tmp` sibling that no ignore entry matched,
so a crash could leave a copy for `git add -A`. They now stage in
`.hack-staging/` next to the target. That directory is private (0700),
and it holds a `.gitignore` of `*` that exists before any staging file,
so git never lists anything in it, without changing any project ignore
file. The rename stays on the same filesystem. The next write there
removes leftovers older than ten minutes, plus older versions' siblings
for the same target. A linked or shared staging directory is refused.
Writers whose directories must hold only their own files (background
commands, shared-host claims, the `.internal` compose override) keep
sibling staging. `.hack-staging/` stays in the directory afterwards,
holding only its `.gitignore`.
- **Weak-key remedy.** It no longer says `mv .hack.secret.key
.hack.secret.key.old` (an unignored name). It now says to keep a 0600
copy outside the worktree (`~/.hack/retired-keys/`) until every secret
is re-added, and to re-add with `--value-stdin` or the hidden prompt,
never as argv. One other doctor hint that suggested `--secret KEY VALUE`
is fixed too.

## S1 and item 1: loose machine-local layers

- A machine-local write drops group and other bits from an existing
layer. The staging copy gets the tightened mode before any content is
written, and a write that changes nothing tightens the file in place.
Shared layers keep their mode. HACK-1680 had kept an existing layer's
mode; its test now asserts the tightening.
- Commands that read env layers (`env`, the up and run paths, `session`)
warn once per file on stderr about a loose machine-local file. The
warning names the file and its mode, and is printed after a successful
read, so refusals stay one line.

## Item 3: terminal-session env payloads

Payloads in `$TMPDIR/hack-mux-env-*/env.json` now expire after ten
minutes. The pane refuses and removes an expired one. The staging
process removes its payload when the time limit passes, if it is still
running, and the next staging sweeps this user's older payloads.

## Item 5 and the migration path: `hack doctor` "secrets hygiene"

This section covers the project and the global Hack home, and names
files, never values. It reports:
- secret-bearing files that are loose, links, owned by another user,
tracked, or unignored;
- staging leftovers;
- stale terminal payloads;
- a store that is present while its key file is missing: "store present,
key missing, cannot be read".

`hack doctor --fix` tightens modes (through a no-follow handle, only
while the file is still the one found), adds missing ignore entries, and
removes Hack's own leftovers. It prints each change. It never deletes a
store, a key or a user's file, and it never untracks a file.
`docs/guides/upgrade-to-v5.md` gains an "after upgrading, check secrets
at rest" step; `docs/env.md` and the threat model describe the
behaviour.

## Merge ordering and overlap

- **#762:** both PRs touch `writeProjectEnvConfigFile` in
`src/lib/project-env-config.ts`. #762 adds the destination guard and
this PR adds `tightenExisting`. The conflict is a small merge in one
object literal; after #762 lands I will merge `next` into this branch.
- **The HACK-2163 session:** none of its functions are touched here
(`src/lib/shell.ts`, the env parse errors, `src/templates.ts`,
`src/daemon/routes/env.ts`).
- **HACK-2169:** `src/lib/secret-store.ts` only gains a config-only
export at the end of the file (`resolveFileSecretStorePaths`).

## Validation

- **New tests, all with temp directories and sentinel values:**
- the staging directory, including a git control showing an interrupted
key publication leaves nothing to stage;
  - the sweep, and refusal of linked or shared staging directories;
  - tightening on write and on unchanged writes;
  - the read-time warning;
  - payload expiry and sweep;
- each hygiene finding and fix, including that `--fix` never deletes a
store.
- **Local, pinned Bun 1.4.4:** typecheck, lint, `privacy:check`,
`docs:check` and gitleaks pass. 162 test files that import the changed
modules ran.
- The failures are all in native and adoption files. They fail the same
way without these changes on this host: they need the compiled config
compiler, or hit native-state refusals here.
  - Hosted CI runs the full suite.
- **Not run:** a real macOS keychain, a real store or key, or a reboot.

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

<!-- codesmith:footer -->
---
<a
href="https://backend.blacksmith.sh/track/run-proof?expires=1797144836&installation_model_id=17053&pr_number=764&ref=codesmith_pr_footer&repository=hack-dance%2Fhack&return_to=https%3A%2F%2Fgithub.com%2Fhack-dance%2Fhack%2Fpull%2F764&variant=prove_it_works&signature=d73e9462a4d99d7d8864b40dd7ee3d13bb27ccfd101c77774057f7ba2cbf3ce0"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-prove-it-works-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-prove-it-works-light.svg"><img
alt="Prove it works"
src="https://pr-comments-assets.blacksmith.sh/codesmith/proof-prove-it-works-dark-3x.png"
width="129" height="28"></picture></a>
<sup><a
href="https://app.blacksmith.sh/hack-dance/codesmith/hack/pr/764?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>
Co-authored-by: Claude <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