Skip to content

fix(native): compile host aliases like execution and name unbound aliases - #636

Closed
roodboi wants to merge 1 commit into
nextfrom
claude/hack-1938-alias-wrapper-diagnostics
Closed

roodboi wants to merge 1 commit into
nextfrom
claude/hack-1938-alias-wrapper-diagnostics

Conversation

@roodboi

@roodboi roodboi commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Native up now compiles service extra_hosts the way native execution does before it adds project-wide aliases. Alias refusals now name the service, the alias and where the alias came from, so they no longer surface one at a time after the VM boots. This PR also covers the HACK-1939 output polish.

HACK-1938: interpolated alias duplicated a project-wide alias

Before this change, extra_hosts: ["${UNSET_PREFIX:+x-}api.example.test:host-gateway"] together with a project-wide internal.extra_hosts {api.example.test: host-gateway} failed with invalid_extra_hosts about 146 s in, after the VM booted. There were two causes:

  • The CLI split list entries at the first : even inside ${...}, unlike native review.
  • The CLI compared names before interpolation, so it added the project-wide alias a second time.

Now:

  • src/backends/native-compose-interpolation.ts predicts what native execution compiles a Compose string to. It uses the same grammar as runtime-core project::inputs::Resolver: no ambient environment, :- selects its word, :+ selects nothing, $$ escapes, and at most four levels of nesting. Required references, Hack-managed names and COMPOSE_PROJECT_NAME (which is bound only after review) stay unknown, so review and execution still decide those.
  • List entries are split at the first : or = outside braces, matching review.
  • A project-wide alias that a declaration already compiles to is not added again. This holds for interpolated names and other spellings, in both list and map form.
  • A service whose declarations compile to one alias twice is refused before any runtime work with E_CONFIG_INVALID, naming the service and the alias. A project-wide alias mapped to another target is also refused with both named; the target value is omitted.

HACK-1939 item 1: the unbound-binding refusal names the service, alias and source

Native host alias has no reviewed host dependency binding; configuration was not dropped. is now followed by every unbound alias, the services that declare it, and where it comes from:

  • the service's Compose extra_hosts
  • internal.extra_hosts in project configuration
  • this checkout's .hack/.internal/extra-hosts.json
  • the main checkout's map, which a linked worktree inherits

The message then gives the "aliases" a binding needs in HACK_NATIVE_DEPENDENCIES, and how to remove the alias instead: hack internal extra-hosts unset <alias>, which hides an inherited entry in this worktree. Listener values are never printed. Example:

Native host alias has no reviewed host dependency binding; configuration was not dropped. Unbound: api.example.test for service worker (from the main checkout's .hack/.internal/extra-hosts.json, which this linked worktree inherits). Add a binding with "aliases": ["api.example.test"] to the HACK_NATIVE_DEPENDENCIES selection (a version 2 entry without "services" applies to every service that declares them), or run `hack internal extra-hosts unset api.example.test` in this worktree to stop inheriting it.

HACK-1939 item 3: output polish

  • Redaction: short managed values (1 to 3 characters) are no longer redacted inside version strings, decimals or hyphenated words. 8.0.46 stays 8.0.46, and DEBUG=0 and mode on. still redact.
  • Nothing created: a fresh startup whose graph owner exited before reserving any runtime state now says so. Inspection reports native_graph_unreserved, no mapping was attempted, and it is not a restore. The message is Native graph startup failed (<code>) before any runtime state was reserved; nothing was created and no cleanup is needed. instead of "exit cleanup is unconfirmed". Every other case keeps the unconfirmed report.
  • Ready and stop output: foreground image-project up prints the run id, each routed service's reviewed hostnames as HTTPS URLs (with the HTTPS port unless it is 443) and the published loopback ports once ready. Without the native HTTPS frontend it says routes are not served on this host. After Ctrl-C and confirmed cleanup it prints one line saying whether volumes and the stopped mapping were kept.

HACK-1939 item 2 (relative entrypoint and health executables in the dependency and environment wrappers) is a runtime change and comes in a separate PR.

Validation

  • Focused bun tests through the bounded runner, 252 pass and 0 fail across 14 files:
    • native-compose-interpolation, native-project-internal-hosts, internal-extra-hosts-worktree
    • native-startup-logs, native-project-start, native-project-restart, native-project-start-stop-hooks
    • native-refusal-cli, native-selection-refusals, native-runtime-metadata
    • native-up-env-fingerprint-wiring, native-up-runtime-metadata-selection
    • native-compose-backend-features-cli, native-gcloud-environment
  • New regression tests:
    • the interpolated alias with a literal control, in list and map forms
    • duplicates after interpolation, and a named target conflict
    • named unbound refusals for every source kind, including a real linked-worktree fixture for the inherited source
    • version-string redaction
    • the nothing-reserved path, with a negative control where a mapping was attempted
    • the ready summary and the confirmed-stop line
  • bun run --cwd packages/cli typecheck and bun run --cwd packages/cli check pass in a clean checkout outside /private/tmp.
  • Biome passes on the changed files. The one remaining complexity warning is lower than on next (22, was 23).
  • bun run docs:check passes. privacy:check reports ok. Gitleaks finds nothing, staged or in history.
  • The full bun suite was not run locally; CI runs it.

Host proof

Candidate bundle built from #638's head, which contains this commit unchanged. It ran with a fresh isolated HOME, HACK_HOME and native home, HACK_DAEMON_AUTOSTART_DISABLE=1, one VM at a time, and synthetic fixtures. The setup was the HACK-1938 repro: extra_hosts: ["${UNSET_PREFIX:+x-}api.example.test:host-gateway"] plus internal.extra_hosts {api.example.test: host-gateway}, with a version 2 binding to a host listener.

  • The project reached ready-observed in about 9 to 22 s. The same setup on next.10 failed with invalid_extra_hosts after about 146 s.
  • hack exec app -- wget http://api.example.test:8080/ returned the host page, and /etc/hosts holds one api.example.test entry.
  • up printed Native project ready (run <id>). Press Ctrl-C to stop; use hack ps and hack logs from another terminal.
  • Ctrl-C printed Native project stopped; owned cleanup confirmed. Named volumes and the stopped run mapping are kept for the next hack up. and exited 130.

Remaining gaps

Refs HACK-1938, HACK-1939

🤖 Generated with Claude Code

…ases

Native up matched service extra_hosts names before Compose interpolation,
and split list entries at the first ":" even inside "${...}". A service
alias such as "${UNSET_PREFIX:+x-}api.example.test" next to the same
project-wide internal.extra_hosts alias was therefore added twice, and the
runtime refused the run with invalid_extra_hosts after the VM booted.

- Predict the value native execution compiles each extra_hosts name and
  target to (same grammar as the runtime resolver: no ambient environment,
  :- selects its word, :+ selects nothing; managed names, required
  references and COMPOSE_PROJECT_NAME stay unknown), and split list entries
  outside braces like review. A project-wide alias that an interpolated or
  differently spelled declaration already compiles to is not added again.
- A service whose declarations compile to one alias twice refuses before
  any runtime work with E_CONFIG_INVALID, naming the service and alias. A
  conflicting target for a project-wide alias also names both.
- The unbound-alias refusal lists every unbound alias, the services that
  declare it and where it comes from (Compose, project config, this
  checkout's extra-hosts.json, or the main checkout's map a linked worktree
  inherits), the "aliases" a binding needs, and how to remove the alias.
- Short managed values are no longer redacted inside version strings,
  decimals or hyphenated words (8.0.46 stays 8.0.46).
- A fresh startup whose owner exited before reserving any runtime state
  reports the failure code and that nothing was created, not an
  unconfirmed cleanup.
- Foreground up prints the run, reviewed HTTPS route URLs and published
  loopback ports once ready, and a confirmed-stop line after Ctrl-C.

Refs HACK-1938, HACK-1939

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

roodboi commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@linear-code

linear-code Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

HACK-1939

HACK-1938

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 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-07T02:04:25.966912Z 4d25fa5 Manual request
🔒 Security Review ✅ Completed 2026-11-07T02:06:38.224648Z 4d25fa5 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: 4d25fa5a4d

ℹ️ 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: 4d25fa5a4d

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 added a commit that referenced this pull request Nov 7, 2026
## What this is

Merge train batch 7 into `next`: three real-project trial fixes. Each
component PR's reviewed head is merged with `git merge --no-ff`, in this
order (#638 is stacked on #636). Integration conflicts are resolved in
the batch's merge commits, described below. Hosted CI on this branch is
the full-suite gate for the combined result.

| PR | Title | Linear | Head included | Review at that head |
| --- | --- | --- | --- | --- |
| #636 | fix(native): compile host aliases like execution and name
unbound aliases | HACK-1939 | `4d25fa5a` | Codex code + security: no
findings; 0 open threads |
| #638 | fix(native): run stock image processes in wrappers like Docker,
naming refusals | HACK-1939 | `08f2d8c3` | Codex code + security: no
findings; 0 open threads |
| #637 | fix(native): reconcile edited graphs that have a dependency
cache | HACK-1937 | `592a9b1d` | Codex code + security: no findings (the
security review confirms the cache-poisoning fix); 0 open threads;
real-VM proof passed |

## Integration changes

- **#636 with batches 1–6:**
- The restart preflight and native `up` pass both #608's
`dependencyOwners` and #636's `internalExtraHostSources`.
- #615 and #636 both added a "nothing was reserved" startup report. They
are now one `finalInspectionFailure`: #615's check plus #636's guards (a
settled failure, not a restore, no mapping attempted), with the combined
message "nothing was created and no cleanup is needed" followed by
#615's diagnostic.
- #636 matched `native_graph_unreserved`, but image projects inspect
with `graph inspect`, which only returns `graph_unreserved`. #636's
check therefore could never fire, so its tests now use
`graph_unreserved`.
- #636's ready summary and confirmed-stop line ride #611's progress
events, so `up` prints one ready line, `Project is ready (SERVICES; run
RUN). …`, with HTTPS URLs and published ports under it. Ctrl-C prints
one stopped line, which states that data was retained when a run mapping
was published.
- **#638 with #615 / #622:** the error type keeps the fixed-message type
and adds #638's process-refusal types. The unified startup report
appends #638's named process refusals after the startup diagnostic.
Doctor keeps the amd64 emulation and settings checks next to #638's
process-wrapper check.
- **#637 with #615 / #638:** failures keep the fixed message, service
and process refusals, and add #637's `planChange`. Its guidance replaces
"inspect owned state before retrying". The cache-poisoning fix holds in
the merged code:
  - `dependency_cache.rs` is #637's file unchanged;
- `plan_reconcile::ordinary_layout` still routes caches with a writable
consumer through reconciliation;
- no batch change mounts a cache volume outside
`instance_scoped_volumes`.
- **Size fix (`dbcf2bcc`):** the three error additions together pushed
`CandidateError` past clippy's large-error bound. `cause_code` is now a
boxed `str`, and process refusals and the plan change share one boxed,
flat-serialized `RefusalDiagnostics`. A test pins the size and the JSON
shape.
- **#640** (credential-helper EPIPE race fix) was folded into batch 6,
so it is not listed here.

## Validation on this branch (`458e0a93`)

- Clean CLI typecheck: pass. Biome on changed files: no errors.
- `RUSTUP_TOOLCHAIN=1.97.1 bun run check:local`: pass. All-features
clippy `-D warnings` (Zig 0.15.2): pass.
- Cargo `--lib`: 1375 pass. `--features native-config-plan`
`provider::graph`: 688 pass. Contracts: `project_contract` 66,
`compose_fields_contract` 10, `interpolation_budget_contract` 1,
`local_contract` 14.
- `bun run test:models` (TLA+ 1.7.4): exit 0, 177 controls verified.
- Focused bun tests, bounded runner, the changed test files: 260 pass, 0
fail.
- `docs:prepare` + `docs:check`, `privacy:check`, gitleaks over
`origin/next..HEAD`: pass.

Not run locally: the full bun suite, the Linux cargo jobs, docker-e2e
and native host qualification. Hosted CI covers the first three.

After this merges, each component PR is closed with a link to the merge
commit.

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

<!-- codesmith:footer -->
---
<a
href="https://backend.blacksmith.sh/track/run-proof?expires=1796634294&installation_model_id=17053&pr_number=641&ref=codesmith_pr_footer&repository=hack-dance%2Fhack&return_to=https%3A%2F%2Fgithub.com%2Fhack-dance%2Fhack%2Fpull%2F641&variant=run_proof_on_this_pr&signature=daf0979b3cf0e8fb0d4344fa34460d87b7d75f22ce9fa22f3da3d8c483a575a9"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-run-proof-on-this-pr-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-run-proof-on-this-pr-light.svg"><img
alt="Run Proof on this PR"
src="https://pr-comments-assets.blacksmith.sh/codesmith/proof-run-proof-on-this-pr-dark-3x.png"
width="168" height="28"></picture></a>
<sup><a
href="https://app.blacksmith.sh/hack-dance/codesmith/hack/pr/641?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: Dimitri Kennedy <344842+roodboi@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Merged into next through the train batch PR #641 (squash commit 76460f8). This PR's reviewed head 4d25fa5a4d5808f8f1f9aa421dd21dd573ee99dc was included unchanged. #641 describes the integration resolutions. Closing as merged by batch; the branch is kept.

🤖 Addressed by Claude Code

@roodboi roodboi closed this Nov 7, 2026
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