Skip to content

feat(native): load Compose environment files privately (HACK-2062) - #781

Merged
roodboi merged 18 commits into
nextfrom
codex/hack-2062-native-env-files
Nov 14, 2026
Merged

roodboi merged 18 commits into
nextfrom
codex/hack-2062-native-env-files

Conversation

@roodboi

@roodboi roodboi commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Native Compose projects can use service env_file and the Compose directory's .env without putting acquired values into plans or command arguments. Start and doctor --native-preflight share guarded reads, interpolation, precedence and private-delivery admission. The Compose backend keeps its behavior.

Implements HACK-2062. The approved scope and environment-file contract describe supported forms and limits.

Environment input boundaries

  • Supports string/list/long-form files, required: false, omitted/empty default format, raw, ordered duplicates and Compose dotenv operators/quotes. Optional absent files are skipped; unsafe or unreadable active inputs refuse.
  • Container precedence is Hack-managed values, explicit service environment, ordered files, then image defaults. Interpolation uses managed globals, allowed shell values, then .env beside Compose. Dotenv alone injects nothing.
  • Values stay in memory and use bounded private stdin/guest allocations, outside stored env fingerprints. Normalized declarations contain names. Source presence can change plan identity; private value edits do not add value digests.
  • Original env-file paths remain excluded from snapshots and live source, including inactive and absent optional inputs. Case/Unicode aliases match the runtime fold; ambiguous aliases refuse.
  • Unresolved readable bare service keys explicitly remove image defaults by name; older runtimes must echo those removals or refuse. Reserved bare names remain runtime/adapter-owned. Runtime activation must agree with preparation.
  • Fresh exec resolves current file values privately for the selected service. File-value edits need an explicit restart for an existing run; private delivery retains the automatic-restart-policy restriction.
  • Diagnostics identify the destination key, source and physical assignment line where available. They omit excerpts, authored error words and dollar-reference tokens extracted from private values. Detached owners discard stderr; no owner stderr log file is written. Detached starts do not show per-key “unresolved input” notices; inspect them with foreground start or preflight. Only validated shell names and the fixed root-dotenv notice cross IPC.

Behavior changes for earlier v5 prereleases

Change Operator action
.hack/.env is read on every native start and preflight, including when managed env exists. Symlinks, other-owner or group/other-writable files and project ancestors refuse. COMPOSE_*/HACK_* keys and dotted/dashed identifiers refuse. Use an owned regular file, deliberately correct permissions, remove control entries and rename unsupported keys, or move values to managed env. A sibling .env.state.json refuses possible materialized output; deliberately retire obsolete output after moving to current managed layers.
Service environment defaults now take allowed shell values before authored fallbacks. Shell presence can change normalized declarations and plan identity. Unset the exported name to retain the fallback. Values remain private.
An unresolved bare service-environment key is now unset instead of empty, including without env files. It removes an image default; an older runtime that cannot confirm unset names refuses review. Reserved runtime/adapter-owned names keep their behavior. Use a matching current CLI/runtime bundle. Write KEY: "" when you intend explicit empty rather than removing the key.
Fresh native hack exec now delivers allowed caller-shell and selected-service file values privately. Shell delivery also affects projects without env files; exec values can differ from startup values. Run exec from the intended shell/environment selection. Restart when changed inputs should also replace the service's startup environment; exec alone does not update it.
Projects with env_file or adjacent .env refuse public-field references to ambient values, even with defaults. Authored fallbacks for newly file-backed names are frozen before private ownership; runtime-bound compounds refuse. Unset the ambient source or write a public literal. With neither file source, public defaults retain their previous behavior, including ${PORT:-3000} when the caller exports PORT.
The dotenv_without_managed_env refusal is removed. A project-root .env outside the Compose directory is ignored, with one names-only notice. Move needed variables beside Compose (normally .hack/.env) or into managed env.
Preflight now reads allowed caller variables and decrypts managed env. A missing project key becomes a structured managed_env_unreadable blocker; the report remains usable. Run from the intended shell and recover the existing key when instructed; do not generate a replacement for existing ciphertext.
Native contexts, Dockerfiles and Dockerfile-specific ignore inputs may not overlap declared env-file or automatic dotenv locations, including optional/inactive paths, or a present ignored project-root .env. Use disjoint build inputs, a prebuilt image or Compose. Authored ignore rules do not lift this refusal.

Adjacent interpolation .env refuses every COMPOSE_* key, broader than Compose's recognized control set. Native reserves the whole prefix so future controls cannot silently become ordinary interpolation inputs and project/file/profile selection remains explicit. Rename application-specific variables without this prefix; this is not a prefix-wide ban on literal service env_file entries.

Approved compatibility exception: even with no env_file or adjacent .env, a present project-root .env now refuses an overlapping native context or Dockerfile input. Root dotenv remains ignored for interpolation; Buildx reads raw contexts, so COPY . could otherwise embed it. The check uses presence only and never reads its values. Use disjoint inputs or a prebuilt image. Selected-service exec and builds without overlapping dotenv keep their admission.

The build-argument guard keeps its previous inputs and policy: exact equality with any nonempty managed value explicitly marked secret: true refuses, with no minimum length. Plain file and shell values do not widen it. The regression builds a fixture with env-file NODE_ENV=production, shell PORT=3000 and matching literal build args. Build args enter image history; a plain file is not a safe way to pass a credential as a build arg.

The short v5 native start/preflight pages link to the rules and next.17 migration notes. These changes are intended for next.17, not a claim that it is published.

Validation and remaining gates

The four lead-requested corrections at 20cf8d4b have 259 focused Bun passes, seven existing skips and no failures across ten files. Typecheck, lint/privacy, CLI build, both Rust Clippy modes, Rust tests (default: 1,246 pass/40 ignored; all features: 1,548 pass/50 ignored), eight compiled CLI checks, CLI-reference regeneration, production docs build/check and HTTP smoke passed. Reachable-history Gitleaks passed. Exact-head results and remaining hosted gates are recorded in progress comments; no final whole local Bun-suite pass is claimed.

The additional approved root-dotenv build refusal has focused start/preflight and disjoint-build/exec regressions; final per-head results are in the progress comments.

The lead accepted the static implementation delta and requested the completed documentation follow-up. Subsequent real-VM acceptance found a cold-start blocker: Engine retains a requested bare removal marker in inspected Config.Env, while verification required every inspected entry to contain =. The current narrow fix retains explicit compiled removal names beside expected image/public values and accepts bare inspected entries only for those names. Extra, changed, restored, duplicate and malformed entries still refuse. No preflight or lifecycle policy change is included.

The regression fixture records real Docker Engine 29.9.2/API 1.56 inspection for removing an image-default name and a name absent from the public Python image. It contains public image defaults and synthetic inputs only. Coverage reaches the actual Compose create/inspect gate from a compiled plan and runs the other caller through fake-engine cold start; negative cases never start. A secret-scan exception is limited to the fixture path and exact public Python GPG signing-key fingerprint line; the rule remains enabled elsewhere.

Latest local gates: Rust default 1,248 pass/40 ignored; all features 1,551 pass/50 ignored; rustfmt, both Clippy modes and root lint/privacy passed. The same persistent Opus reviewer cleared this fix with no blockers or required changes. New-head hosted checks and bot reviews are recorded in progress comments.

Mac acceptance remains lead-owned: rerun all four real-VM env-file variants on the fixed head, with the bare key absent from both first process and fresh exec and every other image default retained. Prior batch checks established file delivery/isolation and persistence but exposed the cold-start blocker; they do not qualify this new fix. No Mac VM acceptance or merge readiness is claimed here. Compose grammar edge parity and an explicit own-key-file path denylist remain follow-ups; external/symlinked files, override/include/extends, CLI --env-file, dotenv project selection and automatic permission repair remain out of scope.


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

@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 14, 2026 6:18am UTC

Request Review

@linear-code

linear-code Bot commented Nov 13, 2026

Copy link
Copy Markdown

HACK-2062

roodboi commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Design scope question (private implementation is proceeding): the brief forbids file values in argv, plans and plan identities. I am implementing .env interpolation inside service environment and env-file values, delivered privately. A file-defined name referenced by image/command/ports/labels/build/public fields will refuse before effects, including when it has a default; it will never silently choose a different value. Is broader public-field interpolation required in a later unit with an explicit public/non-secret declaration? The persistent design review is now clean, and this question does not hold the safe private path.

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Design revision after the first persistent review (before integration):

  • .env means the file beside the selected Compose file, normally .hack/.env. If .env.state.json is present, refuse that potentially stale materialized source instead of copying another selection. A root .env outside the Compose directory is not consumed. Managed global values outrank allowed shell values, which outrank dotenv; service-scoped managed values override the destination without becoming ambient sources.
  • Permit ordinary owner-readable 0644 files, but reject other-owner, multi-link, symlink/special, group/other-writable, changing, oversized or invalid UTF-8 input. No permission mutation. Bounds are 256 KiB/4096 lines per source, 64 references/1 MiB per preparation, plus existing private delivery limits.
  • I checked installed Docker Compose v5.6.1 (compose-go v2.16.1): unresolved bare names in a later env file keep prior values; an unresolved service environment key remains null and overrides the file binding. Native must implement that removal explicitly by name, rather than substitute empty or inherit an env-file value.
  • The Rust original-input path collector must accept optional/raw references and retain their exclusion even while absent; duplicate file references are processed in order and exclusions deduplicated. Tests will cover a file appearing after review, snapshots and live sharing.
  • Preflight will use the same permitted shell context, managed resolution and normalizer as start. Required-reference and mode failures will therefore agree. It may need the same decryption key. No value goes into the temporary review document.
  • Private env-file delivery inherits the existing automatic-restart-policy restriction. File-value-only edits require an explicit restart; no file-value digest will be persisted. Literal/default-only expressions that consult no private source retain the public path.

The public-field interpolation scope question in the Blocked comment remains the only lead decision. The independent parser/no-follow helper prototypes have 40 passing focused tests; they are not yet integrated or pushed as implementation.

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

The lead approved the first-unit scope: .env interpolation in service environment and env-file values, delivered privately. References in public fields refuse before effects. The scope question is resolved; implementation is continuing.

@roodboi
roodboi marked this pull request as ready for review November 13, 2026 19:07

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Implementation is ready for review at 1877d9b05f59d75bb48ab3fe9ae3de44b6edfa2e. The approved design limits .env interpolation to service environment and env-file values delivered privately; public-field references refuse before effects. Persistent Opus review is clean. The PR description records the local full-suite failure and the passing targeted reruns, rather than claiming a final full-suite pass. Hosted Linux CI on this head is the remaining suite gate; actual guest delivery and image-default removal remain separate Mac qualification.

@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-14T06:22:55.149072Z aab610e Manual request
🔒 Security Review ✅ Completed 2026-11-14T06:27:10.703073Z aab610e Manual request

Security findings

Advisory findings (3)

ℹ️ 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: 1877d9b05f

ℹ️ 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: 1877d9b05f

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.

Resolve authored service fallbacks before adding file-backed bindings,
including unselected branches and escaped dollar signs. Allow file-local
cloud names before refusing unresolved restricted shell lookups.

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Pushed bf5f281, fixing both inline findings; both threads now have fix explanations and are resolved. The persistent second-family reviewer is clean on this exact tree. Validation: 373 focused tests passed (7 existing skips), typecheck, lint/privacy, CLI build, all eight compiled native-plan checks, and reachable-head Gitleaks passed. A final full local Bun-suite pass is not established; hosted Linux CI on this head remains the full-suite gate. Real Mac/guest acceptance remains with the lead.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: bf5f281fcb

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

ℹ️ 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 commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the Linux shard 8 failure in 04ae59c (test-only). The random command ID contained env, triggering a substring assertion over all serialized metadata. The test now checks that raw persisted metadata equals returned metadata and has no env or environment property. Its legitimate caller test:env makes the old false positive deterministic. No production code changed.

Validation: all 66 tests in background-command-journal.test.ts passed; repository-configured lint, privacy check, normal commit hooks and reachable-head Gitleaks passed. The same persistent second-family reviewer reviewed this repair and is clean. Exact-head hosted Linux gates remain pending.

@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: 04ae59c916

ℹ️ 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: 04ae59c916

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 commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 39c57c7 to close the env-file path-alias finding. Exclusions use the runtime's existing Unicode/case collision rule, ambiguous aliases refuse, and ignore-rule aliases cannot become env inputs. The strengthened snapshot/live-source regression failed under the old exact-path comparison and passes with this fix. Persistent second-family review is clean; the finding has a fix reply and is resolved.

Final local Rust checks: default 1,187 pass/39 existing ignores; all features 1,481 pass/49 existing ignores; fmt and Clippy in both modes pass. Privacy, all eight compiled CLI plan checks and reachable-head Gitleaks pass. The earlier journal assertion repair has 66 passing focused Bun tests. A final full local Bun-suite pass is still not established; hosted Linux CI on this head remains that gate. Actual Mac filesystem/guest acceptance remains with the lead.

@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: 39c57c708c

ℹ️ 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: 39c57c708c

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 commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Pushed abc1776 to fix public executable defaults referencing newly file-backed names. The code freezes only authored fallback text from the project context; file values remain private, and compound runtime-bound sources refuse. The inline finding has a fix reply and is resolved. Persistent second-family review is clean.

Validation on this tree: 386 focused Bun passes, 7 existing skips, 0 failures across nine files; typecheck, lint/privacy, CLI build, all eight compiled native-plan checks and reachable-head Gitleaks passed. The nine new positive cases all fail when the fix is disabled. Rust is unchanged from the alias fix, whose default/all-feature suites and Clippy passed. A final full local Bun-suite pass is not established; exact-head hosted Linux remains the suite gate. Mac filesystem and actual guest acceptance remain with the lead.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: abc17763df

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

roodboi commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 434dc0c to fix the shard 5 catalog failure reported on 39c57c7. The alias-ambiguity error source_case_collision is now classified as a preflight project blocker, with a rename/correct-env_file fix. A source-CLI regression proves JSON and text report exit 1, ready false, stopped review and the blocker. Source inventory/capture behavior is unchanged.

The catalog and preflight CLI files pass all 31 tests. Typecheck, lint/privacy, CLI build, normal commit hooks and reachable-head Gitleaks passed. The same persistent second-family reviewer is clean. This is additive to the prior public-defaults fix and its 386 focused passes; hosted Linux gates on this exact head remain pending.

@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: 434dc0cd9f

ℹ️ 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 Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 20cf8d4be9

ℹ️ 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: 20cf8d4be9

ℹ️ 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 commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Approved root-dotenv build exception pushed: 08c031a242d1828575c47d674aa490c3c30b851d.

The ignored root .env is now protected by the same lexical native build guard as env-file sources, using only its presence. This deliberately adds a refusal for overlapping contexts/Dockerfile inputs even without env_file or adjacent .env, as approved by the owner. Root values remain unread; disjoint builds, source-free builds and selected-service exec retain admission. Migration notes, full rules, short native entry links and PR body document the exception. The security finding has a fix reply and is resolved.

Delta validation: 146 focused Bun tests passed; typecheck, lint/privacy, CLI build, eight compiled CLI checks, docs production build/check and the two new commits' Gitleaks passed. No Rust source changed after its passing default/all-features gates at 20cf8d4b; no new Mac or whole-suite pass is claimed. Exact-head hosted checks and bot review remain pending.

second-family review: pending, lead

The same persistent reviewer remains unavailable through the previously reported provider429 path. The lead's real Mac guest unset/image-default and real env-file-project acceptance remain outstanding. No merge or new Ready claim.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 08c031a242

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

roodboi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Hosted shard 5/8 on 20cf8d4b found one stale exact-string expectation in tests/native-compose-backend-features-cli.test.ts: the product correctly refused the managed secret, but the test still expected “privately delivered environment value” rather than the restored “hack env secret value”. Fixed test-only at 262e9dca9a22a5fea87bd5e631061f4fa20954d2.

All 24 tests in that complete CLI file pass, including the exact closed error line, omission of the synthetic secret, and zero runtime calls. Changed-file lint and delta Gitleaks pass. Repository search found no remaining instance of the obsolete phrase. No product or documentation change; previous root-dotenv and lead-required fixes remain intact. Exact-head hosted CI/review pending; second-family review and Mac acceptance remain lead-owned.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 262e9dca9a

ℹ️ 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: 262e9dca9a

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 commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Ready for lead: 262e9dca9a22a5fea87bd5e631061f4fa20954d2 — review and Mac-acceptance handoff.

second-family review: pending, lead

The four required corrections are implemented: private diagnostics name destination/file/line without value fragments; the build-arg guard retains explicit managed-secret-only equality with no minimum length and excludes file/shell values; no-file public defaults keep next's admission; root-dotenv ignored notices, missing-key-safe preflight and migration notes are present. The owner also approved the narrow root-dotenv build exception: overlapping contexts/Dockerfiles refuse without reading root dotenv. Disjoint builds and selected-service exec remain admitted. The final commit only fixes a stale exact-message CLI test assertion.

Normal merges carry next through 29be0434a, including #796. Exact-head CI43555051664 completed successfully: test fan-in and Linux shards4–8, focused tests, Linux candidate core, x64/arm64 config compiler, docs and secret scan. macOS, Docker execution, runtime models, runtime images and Linux process-lifetime checks were deferred/skipped in this tier; the Docker fan-in success does not prove Docker execution.

Codex code and automatic security reviews are clean at this head. All bot findings have fix replies and resolved threads. Review-status: CLEAR at 262e9dc: no unresolved bot threads, bot change requests, or overlapping base changes. The automated summary still displays an older advisory label for root dotenv; the actual review thread is resolved.

Local evidence: 259 focused passes/7 existing skips for the lead corrections; 146 passes for root-dotenv/build/preflight coverage; 24 passes for the corrected CLI file. Typecheck, lint/privacy, CLI/config compiler builds, eight compiled CLI checks, Rust default1246 passes/40 ignored and all-features1548/50 ignored, fmt/both Clippy modes, docs production build/check and HTTP smoke97 pages/216 links passed. History and subsequent delta Gitleaks passed. No final whole local Bun-suite or Mac guest result is claimed.

Persistent Opus retries after the account-reset notice still returned provider429; per the lead's instruction, that review is handed back without another provider. Remaining acceptance: second-family delta review from 8a537829 to this head; real Mac guest proof that a bare key removes an image default; and a real project with an env file. Detached owner stderr is discarded, not logged. No merge performed. Stopping the watch for lead-owned review/testing.

roodboi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Ready for lead: 9f173de7632e51c243932f137547e67458e70c69 — requested docs-only follow-up for the Mac batch.

Exactly one commit follows the accepted implementation head 262e9dca; only three Markdown files changed. The release-notes and PR behavior tables now explicitly cover both changes that also affect projects without env files: unresolved bare service keys are unset rather than empty (older runtimes unable to confirm unset names refuse), and fresh hack exec delivers current allowed shell and selected-service file values. Remedies explain explicit empty values, matching runtime bundles, intended caller context and restart when startup values should change.

The full rules and short preflight page explicitly state that detached starts omit per-key “unresolved input” notices because owner stdio is ignored. The docs retain and explain the broader .env ban on every COMPOSE_* key: reserving the prefix prevents future controls from silently becoming ordinary interpolation inputs and keeps selection explicit; literal service env_file entries are not universally subject to that rule.

Validation: docs preparation/contracts and production site build passed; git diff --check and the one-commit secret scan passed. No product code changed and no runtime tests were added or rerun for this prose-only change. Exact-head hosted checks and bot re-review for this new docs head are pending. Parent 262e9dca has green CI, clean automated reviews and the lead's accepted delta review with no blockers.

Mac acceptance remains with the lead as requested: tests/native-exec-stale-env.test.ts, APFS case-alias cases, and a real env-file project checking sentinel delivery only inside the container, bare unset removing an image default, root-context build refusal, and preflight with/without the key. No Mac acceptance or PR merge claimed.

Follow-up retained, not implemented here: JSON up should carry the build-overlap refusal reason that text mode shows.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 9f173de763

ℹ️ 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: 9f173de763

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 commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Ready for lead: 9f173de7632e51c243932f137547e67458e70c69 — final docs head, CI and automated reviews green.

Exact-head CI43558877184 completed successfully: test fan-in and Linux shards4–8, focus, candidate core, both compiler jobs, docs and secret scan. macOS, Docker execution, runtime models/images and Linux process-lifetime were deferred/skipped by this tier; Docker fan-in success does not establish execution.

Codex code and automatic security reviews are clean at this head. Review-status: CLEAR at 9f173de: no unresolved bot threads, bot change requests, or overlapping base changes. next advanced to c44b266d88 on unrelated files; no additional merge was added, preserving the requested single docs-only commit over the implementation the lead accepted at 262e9dca.

The missing no-env-file behavior rows, detached per-key notice limit and broader COMPOSE_* rationale are documented; no product code changed. Remaining acceptance is the lead's Mac batch already specified: native-exec-stale-env, APFS case aliases and the real env-file project checks. JSON up's build-overlap reason remains an explicit follow-up. Stopping the watch for owner testing; no merge or Mac acceptance claimed.

@roodboi

roodboi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Lead Mac acceptance at 9f173de763: one blocker, not merged.

Passed on an Apple Silicon Mac (batch tree: next c44b266 + this head): typecheck, 489 selected test files, Rust 1,803 passed, real-VM scenarios smoke, webdb and up-twice, and a new real-VM env-file scenario:

Check (real VM, values compared by SHA-256 computed in the container) Result
Service env_file value and a second file key are in the service's first process pass
Value interpolated from the adjacent .env arrives; the .env key itself is not injected pass
The other service has none of these keys pass
Both private values exist in exactly two files across the isolated home and every step log: the two authored files pass
No process argv or process environment on the host held either value (polled every 0.2 s) pass
After a file edit: fresh exec sees the new value, the running service keeps its start value, the next start uses the new value pass
Build context containing the env file: preflight exit 1 (environment_file_input), up -d exit 1, nothing listens pass
Bare unresolved key (environment: ["PYTHON_VERSION"], nothing exported) fail

Blocker. With a bare unresolved service-environment key, preflight reports ready: true (warning environment_notice), then cold up -d exits 1 after about 50 s with E_STARTUP_INCOMPLETE ... (graph_config_mismatch) ... Pinned image or container environment is malformed or differs; values omitted. It fails with or without env files, and also for a bare name the image does not define. The same fixture without the bare entry starts and passes every row above.

Likely cause, read from source and not yet proven against the engine: image_environment::verify parses the inspected Config.Env with a parser that refuses entries without =, while the engine keeps a bare user entry verbatim in the inspected configuration. Details and the required regression are with the owning session.

Next: fix, same reviewer, new ready note; I rerun the real-VM scenario on that head.

🤖 Generated with Claude Code

roodboi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Bare-unset cold-start fix pushed at aab610e2eea7e9b8562fb472a99d2d412480b9bc.

The supplied prerequisite real-engine capture confirms that a requested bare removal remains bare in inspect Config.Env, while absent from the first process. Verification now retains the explicit compiled removal names in memory alongside exact pinned-image/public values. Only those names may appear bare; unexpected names, duplicates, restored values, malformed fields and missing/extra/changed values still refuse. Image defaults still require assignments. No preflight or lifecycle policy change.

The public regression fixture semantically matches the actual Engine29.9.2/API1.56 capture for both an image-default key and a key absent from the image. It contains only public image defaults and synthetic inputs. The Compose regression compiles the removal plan through inputs/config and invokes the actual Session::verify_config create/inspect gate. The other caller runs through fake-engine cold start: valid captures reach Ready; malformed/unrequested/duplicate/restored/default-drift cases cannot start. The exact public Python signing-key fingerprint line has a fixture-path-scoped secret-scan exception; scanning stays enabled elsewhere.

The same persistent Opus reviewer is working again and cleared this round with no blockers or required changes. Local gates pass: Rust default1248 tests/40 ignored, all-features1551/50 ignored, rustfmt and both Clippy modes, root lint/privacy, and reachable-history secret scan1326 commits. No new VM/container was created for this round; real-engine prerequisite evidence was supplied before edits. The lead still owns all four real-VM env-file variants on this fixed head, including bare absence from both first process and fresh exec and retention of all other image defaults.

New-head CI and bot reviews are pending before the final Ready/head handoff. #794's pending integration state is unchanged.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: aab610e2ee

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

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 14, 2026

Copy link
Copy Markdown
Contributor Author

Ready for lead: aab610e2eea7e9b8562fb472a99d2d412480b9bc

Fixed the bare unresolved-key cold-start blocker. Docker preserves a requested bare removal name in inspected Config.Env; verification now accepts it only when the compiled configuration explicitly requested that removal. Unrequested bare names, duplicates, malformed fields, restored values and valued-entry drift still refuse.

The regression fixture uses actual public-engine captures for both an image-default name and a name absent from the image. Tests cover both cold-start callers: compiled Compose/input/config through Session::verify_config, and native execution::run through create/inspect verification.

Validation: default Rust suite 1,248 passed; all-feature suite 1,551 passed; rustfmt, both Clippy modes, lint/privacy and history secret scan passed. Exact-head CI passed all selected jobs, including five Linux shards, candidate core and x64/arm64 compilers. Mac and Docker execution were deferred/skipped by scope.

The persistent Opus reviewer is clean. Codex code and security reviews are clean at this head. Review-status: CLEAR at aab610e2ee, with no unresolved bot threads or change requests.

Remaining acceptance is the lead's four realVM env-file variants, including the bare key absent from both the first process and fresh exec while other image defaults remain. No new Mac runtime qualification is claimed.

@roodboi

roodboi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Lead real-VM acceptance at aab610e2ee: passed.

Bundle built from this head on an Apple Silicon Mac; isolated home; one native pool per run.

Variant Cold start Result
Env file + adjacent .env + bare PYTHON_VERSION ok all 30 checks pass: file value and interpolated value in the service's first process (compared by SHA-256 inside the container); .env key not injected; PYTHON_VERSION absent from the first process and from a fresh exec while PYTHON_SHA256, GPG_KEY, LANG and PATH remain; other service has none of the keys; both private values exist only in the two authored files across the home and all logs; no host process argv or environment held them (polled every 0.2 s); fresh exec sees an edited file, the running service keeps its start value, the next start uses the new one; build context containing the env file refuses in preflight and up -d
Bare PYTHON_VERSION only, no env files ok bare key absent from the first process and exec; row retained across down/up -d
Bare name the image does not define, with env files ok starts; image defaults untouched

The previous head failed all three at cold start with graph_config_mismatch.

Next: the combined-tree Mac batch (typecheck, selected tests, Rust suite, VM scenarios including this one), then merge.

🤖 Generated with Claude Code

@roodboi
roodboi merged commit cab596c into next Nov 14, 2026
22 checks passed
@roodboi
roodboi deleted the codex/hack-2062-native-env-files branch November 14, 2026 08:10
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>
roodboi added a commit that referenced this pull request Nov 15, 2026
…otenv comments (#849)

Two follow-ups accepted when #781 (native Compose env files, HACK-2062)
merged.

## 1. Hack key files are never environment sources

A service `env_file` or the automatic `.env` is refused with a closed
reason, *"Hack key and secret-store files cannot be environment
sources"*, before any inspection, cache reuse or read, when it is:

- named `.hack.secret.key` at any depth. This covers the checkout key,
the shared key under the git common directory and the primary checkout's
key;
- named `.hack-secrets.enc.json` or `.hack-secrets-file.key` (the legacy
project-relative encrypted store and key);
- under the Hack home (`HACK_HOME`, else `~/.hack`) when the Hack home
lies inside the project root. A Hack home equal to the project root
refuses every source.

Paths are compared after Unicode NFC normalization and full Unicode case
folding, so a case or normalization alias (`Ä` and `ä`, for example) is
refused too. An absent optional entry with such a name is refused rather
than skipped. The check sits in the shared `read` of
`native-compose-environment.ts`, which start, preflight, restart
preflight and fresh exec all reach through `prepareNativeProjectInput`.
The refusal names only the authored source path.

```mermaid
flowchart TD
  S["env_file entry or automatic .env"] --> L["Literal path admission (inside project root)"]
  L --> K{"Hack key or secret-store file?"}
  K -->|"yes"| R["Closed refusal, nothing read"]
  K -->|"no"| G["Guarded read: ownership, modes, no symlinks, single link"]
  G --> P["Parse and private delivery"]
  class R fail
```

**Decisions for the lead:**
- **The check uses paths only, with no file access.** My first version
also read the encrypted-store paths from the Hack config and matched
files by identity. Running the existing native env suites under strace
showed it would make about 19 in-process test files stat and read the
real `~/.hack` (the base reads nothing there). So the final check is
lexical. As a result:
- It does not cover encrypted stores configured at other paths. That
item was not on the accepted list; the default store and key live in the
Hack home.
  - It does not recognize a Hack home reachable only through a symlink.
  - Both limits are in the doc.
- **A project inside the Hack home keeps only the name rules.** Hack's
global service projects live there, so denying the whole Hack home would
refuse every source they have.

## 2. Compose dotenv grammar parity

`tests/native-compose-env-parity.test.ts` is table-driven: 86 cases,
each expectation recorded offline with Docker Compose v5.6.1, `docker
compose config --format json`, with an unreachable `DOCKER_HOST`. The
test itself does not use Docker. The cases cover quoting and escapes,
`export` prefixes, inline comments, multi-line quoted values, empty
versus unset, bare names, and the `${VAR:-x}`, `${VAR-x}`,
`${VAR:?err}`, `${VAR?err}`, `${VAR:+x}` and `${VAR+x}` operators.

- **Fixed: a tab before `#` started a comment in native.** Compose
starts an unquoted inline comment only at a space followed by `#`, so
`A=val\t# c` is `val\t# c` in Compose but was `val` in native. The
automatic `.env` follows the same rule in Compose (checked), so one fix
covers both.
- **Documented, not changed:** native refuses text after a closing quote
(`A="v"x`); Compose silently drops it. Native also refuses
non-identifier keys, as already documented. Both refusals fail closed
and are now listed in `docs/native/environment-files.md`.

Docs: `docs/native/environment-files.md` (the new rule, a parity section
and prerelease change notes), its diagram (`.mmd` updated and
re-rendered with `pele-render --theme light`, which reproduces the
previous SVG byte for byte), and the short v5 page
`docs/versions/v5/native/preflight.md`.

## Verification (Linux, pinned Bun 1.4.4)
- New denylist tests: 10 pass. With the guard disabled, 9 fail (the one
pass is the path-only unit test).
- Parity table: 86 pass. With the old comment rule restored, 3 fail.
- New CLI case: `doctor --native-preflight` reports the
`environment_file_input` blocker with the reason, and foreground `up`
exits 1 with only the runtime review call. The key file's content never
appears in output or argv.
- Every suite #781 touched, plus the new ones (13 files): 578 pass, 7
skip, 0 fail. Run with `umask 022`; under a `002` umask, 3 existing
tests fail on the base as well, because group-writable fixture
directories are refused.
- Root typecheck, Biome on changed files (only existing complexity
warnings), `docs:prepare` + `docs:check`: pass.

Not verified: macOS, the real-VM env-file scenario (the lead runs it),
and the full root suite.

🤖 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>
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