Skip to content

fix(native): wait only for active services at native up - #314

Merged
roodboi merged 8 commits into
nextfrom
wf/native-inactive-readiness-b10
Oct 12, 2026
Merged

roodboi merged 8 commits into
nextfrom
wf/native-inactive-readiness-b10

Conversation

@roodboi

@roodboi roodboi commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A HACK-1163 acceptance run on the M3 (next at ccf526c) found that native up refuses with graph_readiness when the compose file has a service in a profile that is not selected.

Cause: readiness() in src/backends/native-project-start.ts emitted --ready for every compose service. src/backends/native-project-input.ts does not filter by profile, which is correct, because the review needs the whole file. The runtime then skips inactive services in Graph::from_plan (packages/runtime-core/src/project/execution.rs) and refuses with "Readiness includes inactive or unknown services."

Change

  • readiness() is now the exported nativeReadinessArgs({ specs, plan, initializers, routed }). It emits goals only for services that the reviewed plan (review.report.plan.services[name].active === true) marks active.
  • depends_on edges declared by inactive services are ignored. Before, an inactive service with depends_on: { web: { condition: service_healthy } } could raise an active web to healthy. That fails the same way when web has no healthcheck.
  • The runtime refusal is unchanged. It is a correct guard against goals the plan does not schedule.

Bridge pool capacity (native-project-routing.ts): kept as is

nativeBridgeCapacity counts routed services in inactive profiles on purpose. I left that unchanged and added a comment that gives the reason:

  • It runs before runtime up and before the profile-aware review, so profile activation has not been decided yet. Duplicating compose profile and dependency activation in TypeScript would create a second source of truth.
  • reviewedNativeRoutes refuses when the number of active routes is more than the capacity. Counting every declared route guarantees the floor is never too small, whatever the selected profiles are.
  • The cost of over-counting is a few idle bridge sockets in the pool (capped at 32). That is harmless, unlike an extra readiness goal, which the runtime refuses.

Validation

  • bun test tests/native-project-start.test.ts tests/native-project-routing.test.ts tests/integration-ownership.test.ts: 74 pass, 0 fail.
    • New unit test: inactive services (routed and unrouted) produce no goals, active ones do, services missing from the plan are excluded, and a missing plan produces no goals.
    • New startup tests through startNativeProject with injected runners (no Docker, OrbStack or real HOME): without a profile, only web=started is emitted, there is no admin=auto route slot, and bridge capacity is still 1. With profiles: ["debug"], the review receives debug and the goals become admin=healthy, web=healthy and worker=started, with the admin=auto route slot.
    • Negative control: forcing the active check to true (the old behaviour) makes the unit test and the no-profile startup test fail.
  • Rust: the new execution_dependency_compiler_refuses_readiness_for_inactive_or_unknown_services contract test proves Graph::from_plan still refuses goals for an inactive profile service (tools) and for an unknown service (ghost) with graph_readiness, and accepts the active-only set.
  • RUSTUP_TOOLCHAIN=1.97.1 bun run check:local (fmt and clippy -D warnings) and bun run test:local: all green (985 pass in the main suite, 0 failures).
  • bun run --cwd packages/cli typecheck: 0 errors. bun run --cwd packages/cli check: 0 errors, and the existing warnings are not in the changed files.

Gaps

  • I did not re-run native up on the M3 against a real project with an unused profile. This PR proves the argument construction and the runtime guard in isolation, not end-to-end runtime behaviour.
  • I did not run the full bun run test or hosted CI locally.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

roodboi and others added 4 commits October 12, 2026 13:52
…mands

A warm, image-only, unrouted `up` or `restart` with no hooks now runs
four directory-xattr storage proofs instead of six. Each proof is three
fresh helper containers.

- The warm enrollment tail (no new volume) reuses the effect-entry proof
  of the same generation under the same lease. It uses the existing
  adjacentProof record, which needs a byte-identical material binding,
  so any receipt write in between forces a full proof.
- Finalization skips the second proof pass when there is no route,
  after hook, or new or saved file material. In that case the
  completion step only reads.

Proof reuse stays within one command and one lease. Nothing is
persisted, so every command still opens with a full proof.

Each performed verification proof now emits a passive `storage.proof`
phase-trace span. The hosted native-config-compose scenario uses it to
assert four proofs on each of two consecutive warm ups.

Refs: HACK-1123

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sed file

The HACK-1211 experiment produced two iterations per Bun version: one
focused pass and one full `bun test` that started on a 600 s estimate and
overran the 30-minute budget, because the full suite takes 30-35 minutes
on hosted macOS.

- Never start an iteration whose estimate exceeds the remaining budget.
  The estimate is a conservative prior (focused 120 s, full 2400 s) until a
  run completes, then the longest completed run times 1.2. A command that
  does not fit is recorded once in summary.json `skippedForBudget` and
  leaves the rotation. Deadline stops remain a safety net (`overrun`).
- Add `--mode=focused|full|alternate` (default focused: only
  tests/native-https-owner.test.ts) and `--role=baseline|candidate`.
- The workflow gains a validated `mode` dispatch input; the label-triggered
  matched pair uses focused with baseline/candidate roles. Full mode needs a
  budget of at least 40 minutes. The job timeout stays budget + 15.
- summary.json gains per-command counts (iterations, passes, crashes, other
  failures, overruns, skipped-for-budget) and a one-line verdict such as
  "baseline 0/97 focused crashes".
- The shared classifier also treats exit 134 (SIGABRT) and 138 (SIGBUS) as
  crash exits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native `up` emitted a `--ready` goal for every compose service, including
services in profiles that were not selected. The runtime does not schedule
inactive services and refuses readiness for them ("Readiness includes
inactive or unknown services."), so any project with an unused profile
failed with graph_readiness.

Build readiness goals only for services the reviewed plan marks active,
and ignore dependency edges declared by inactive services so they cannot
raise an active service's goal. The runtime refusal stays as a guard; a
contract test now proves it still rejects inactive and unknown services.

Bridge pool capacity keeps counting inactive routed services: it is sized
before the profile-aware review and must remain an upper bound.

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

This comment has been minimized.

roodboi and others added 3 commits October 12, 2026 16:04
Add a source CLI regression for a first up with new file material and no
saved file material: finalization keeps its completion step, the second
finalize pass, and the post-completion file-mount readiness check. The
test fails when the retirement condition keeps only the saved-files arm.

Native-compose CLI harnesses now give the child a private HOME under the
fixture root instead of the contributor's HOME.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicted files: none. The plain merge applied cleanly; no generated
files needed hand resolution, and the generation and reference tests
pass on the merged tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicted files: none. The plain merge resolved automatically. This
PR's change set (native readiness goals, the bridge capacity comment and
the project_contract.rs guard test) does not overlap witness reuse or
merge train 2. Regenerated plugins and docs/reference/cli.md were
byte-equal to the merged tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi
roodboi changed the base branch from next to wf/witness-reuse-b8 October 12, 2026 20:50
@roodboi
roodboi marked this pull request as ready for review October 12, 2026 21:10
Base automatically changed from wf/witness-reuse-b8 to next October 12, 2026 21:50
Re-parents onto next after the previous stacked PR squashed in; next's tree equals a889387, which this branch already contains, so the branch tree is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi
roodboi merged commit cbaa698 into next Oct 12, 2026
30 of 31 checks passed
@roodboi
roodboi deleted the wf/native-inactive-readiness-b10 branch October 12, 2026 21:53
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