feat(mosaic): leaseEnforcementActivatable() capability probe (#869 C1) #870

Merged
Mos merged 4 commits from feat/869-c1-activation-probe into main 2026-07-23 17:14:58 +00:00
Owner

Part of #869 (Point-1 C1). See commit messages for details.

Part of #869 (Point-1 C1). See commit messages for details.
jason.woltje added 2 commits 2026-07-22 18:39:40 +00:00
Adds a real activation-capability probe so a downstream install-ordering
guard (C2, out of scope here) can refuse to wire lease-broker enforcement
(PreToolUse/Stop hooks) on a host that cannot actually activate it — the
root cause of #828's version-skew brick, where the published CLI tarball
lagged the enforcement reseed and every tool call denied with
GATE_UNAVAILABLE.

- LEASE_ACTIVATION_CAPABILITY {name, version}: versioned signal owned by
  the activation half (execLeaseGatedRuntime), independent of npm semver.
- Hidden `mosaic __lease-capability` subcommand: prints that capability
  from the actually-resolvable built CLI artifact, not source-tree
  presence.
- leaseEnforcementActivatable(): pure predicate, true iff a compatible
  capability is advertised AND the broker supervisor (launcher + daemon.py
  artifacts, socket path) resolves. Detection only — never starts the
  broker. Both inputs are injectable for testing.
- C-REGRESS: added a vitest spec that runs the two test-locked fail-closed
  gate cases in runtime_tools_unittest.py directly, proving the gate's
  fail-closed-on-absent-identity behavior is unchanged by this card.

Part of #869 (Point-1 C1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore(orchestrator): fix pre-existing Prettier drift in README.md
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
c64a04e7dd
Whitespace-only markdown table/spacing fix. Pre-existing on origin/main
(introduced by #868), unrelated to #869 C1 — fixed only because the repo's
pre-push hook runs a full-repo \`pnpm format:check\` and was blocking this
branch's push. No functional change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jason.woltje added 1 commit 2026-07-22 18:58:06 +00:00
Independent review of PR #870 found defaultCapabilityProbe() resolved the
installed CLI via req.resolve('@mosaicstack/mosaic/package.json') — a
subpath NOT present in package.json's `exports` map. Node throws
ERR_PACKAGE_PATH_NOT_EXPORTED for that on every real install, which the
catch-all silently turned into an always-null probe: leaseEnforcementActivatable()
could never return true anywhere, defeating the card's purpose.

Fix: resolve via the already-exported "." entry instead
(require.resolve('@mosaicstack/mosaic') -> dist/index.js), then take
cli.js as its sibling in the same built dist/ directory — matching
package.json's bin.mosaic mapping. No change to the exports map itself
(that would mask a separate, out-of-scope pre-existing issue in
resolveTool()/launch.ts per review guidance).

Adds a positive-path test that stages a realistic fake dist/index.js +
dist/cli.js at the package's real resolved location and asserts
defaultCapabilityProbe() returns the actual {name, version} capability
object with zero mocking of the resolver. Verified red against the prior
(reverted-and-restored) buggy resolution before landing the fix, and green
after — the previous only-unmocked test asserted null for the wrong
reason (masking this bug) and is left in place alongside the new one.

Re-verified: launch.spec.ts (30), fail-closed-regression.spec.ts (2), and
runtime_tools_unittest.py (25, including both C-REGRESS-locked fail-closed
cases) all still green. typecheck/lint/format:check pass via
`turbo run ... --filter=@mosaicstack/mosaic`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jason.woltje added 1 commit 2026-07-22 19:24:49 +00:00
fix(mosaic): DI-inject CLI-entry resolver so tests never touch real dist/ (#869 C1 review fix R3)
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
c5a2bcc516
Round-2 review found the positive-path test's writeFileSync() staged a
stub cli.js/index.js at the package's REAL resolved dist/ path, and
afterEach only removed files that had NOT pre-existed — never restoring
original CONTENT for files that had. On a host with a real pre-built
dist/cli.js (ordinary `pnpm build && pnpm test`, and CI: this package's
turbo.json overrides the `test` task to depend on `build`, so CI always
builds a real dist/cli.js before running vitest), the test would silently
overwrite the real ~26KB compiled CLI with an 87-byte stub and still
report PASS. Confirmed as the exact root cause of CI1972's red `test`
step: src/cli-smoke.spec.ts execs the real dist/cli.js in the same vitest
process/run, so the clobber surfaced there.

Fix (dependency injection, not snapshot/restore):
- defaultCapabilityProbe() now takes an injectable CapabilityProbeDeps
  ({ resolveCliEntry }), defaulting to the real defaultResolveCliEntry()
  in production — no change to the real-artifact-read guarantee.
- defaultResolveCliEntry() itself now takes an injectable ModuleResolver
  (defaults to the real require.resolve), so its resolution CHOICE (bare
  "@mosaicstack/mosaic" specifier vs the buggy "./package.json" subpath)
  can be tested in complete isolation from real package/build state.
- The positive-path test now stages its stub cli.js in an mkdtempSync()
  scratch directory and injects resolveCliEntry to point there — it never
  calls the default resolver, so it structurally cannot touch the real
  package's dist/. It also asserts the real dist/ path's existence is
  unchanged by the test.
- The "returns null when unbuilt" test now injects a resolver pointing at
  a path that cannot exist, instead of relying on this checkout happening
  to be unbuilt (deterministic regardless of ambient host build state).

Verified:
- Reintroduced the R1 bug in defaultResolveCliEntry() and confirmed the
  new resolver-choice test fails red against it; restored the fix (byte-
  identical diff against the pre-revert file) and confirmed green.
- Built a real dist/cli.js (~26KB) via `turbo run build`, ran the targeted
  specs against it, then ran the FULL `turbo run test --filter=@mosaicstack/mosaic`
  CI-parity path (which builds dist/ itself per this package's turbo.json
  override before vitest runs, exactly matching Woodpecker's `test` step):
  77/77 test files, 1451/1451 tests passed, including cli-smoke.spec.ts
  (22/22) and lease-activation-probe.spec.ts (15/15) in the SAME run.
  sha256 of dist/cli.js before and after that full run: identical
  (e61d8de7a2223b6578a2b733edd927707830e011f44b9d20901194c23c4a5272,
  26317 bytes) — the real build artifact is untouched byte-for-byte.
- python3 -m unittest runtime_tools_unittest: 25/25 pass, including both
  C-REGRESS-locked fail-closed cases.
- typecheck/lint/format:check all pass via turbo --filter=@mosaicstack/mosaic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mos approved these changes 2026-07-23 17:14:56 +00:00
Mos left a comment
First-time contributor

Gate-16 APPROVE -- stamped by Mos (id-11)

Registered under the distinct Mos bot (id-11) for cryptographic author!=reviewer separation on PR #870 (issue #869 Point-1 Card C1). PR author jason.woltje(id2) != approver Mos(id11). Registers the independent reviewer's finalized R3 verdict; MS-LEAD prepared/relayed, Mos stamps+merges.

VERDICT: APPROVE @ c5a2bcc516 (Part of #869 -- umbrella card, intentionally no Closes; #869 closes at Point-2 per Mos disposition).

Finding-0 (commit-author metadata): PR commits carry local git author ms-lead-reviewer reviewer@mosaic.local (shared-checkout config) which resolves to author:null/unsigned via the Gitea commit API -- inert metadata, NOT a credential, never the load-bearing signal. Load-bearing separations HOLD: PR-author jason.woltje(id2) != approver Mos(id11); process independence is real (distinct adversarial subagent, 3 rounds, which found the ERR_PACKAGE_PATH_NOT_EXPORTED probe-always-null bug AND a destructive test that overwrote real dist/cli.js). Durable fix (c) tracked.

Independent review, 3 rounds dual-gate converged: R1 REQUEST-CHANGES (probe resolved a non-exported subpath -> always-null, whole card defeated; tautological test masked it); R2 REQUEST-CHANGES (destructive test overwrote real dist/cli.js); R3 APPROVE (DI refactor, mkdtemp+injected resolver, real dist verified untouched -- rebuilt dist/cli.js, 77/77 files 1451/1451 passed CI-way, post-run sha256 identical). Production default path unchanged.

6-CHECK re-verified by Mos @c5a2bcc516ebabaf61b46bc19f09c51e4a1158bc: open/not-merged/mergeable/base main; CI success wp1973 @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869' (ruled); queue-guard clear.

## Gate-16 APPROVE -- stamped by Mos (id-11) Registered under the distinct Mos bot (id-11) for cryptographic author!=reviewer separation on PR #870 (issue #869 Point-1 Card C1). PR author jason.woltje(id2) != approver Mos(id11). Registers the independent reviewer's finalized R3 verdict; MS-LEAD prepared/relayed, Mos stamps+merges. VERDICT: APPROVE @ c5a2bcc516ebabaf61b46bc19f09c51e4a1158bc (Part of #869 -- umbrella card, intentionally no Closes; #869 closes at Point-2 per Mos disposition). Finding-0 (commit-author metadata): PR commits carry local git author ms-lead-reviewer <reviewer@mosaic.local> (shared-checkout config) which resolves to author:null/unsigned via the Gitea commit API -- inert metadata, NOT a credential, never the load-bearing signal. Load-bearing separations HOLD: PR-author jason.woltje(id2) != approver Mos(id11); process independence is real (distinct adversarial subagent, 3 rounds, which found the ERR_PACKAGE_PATH_NOT_EXPORTED probe-always-null bug AND a destructive test that overwrote real dist/cli.js). Durable fix (c) tracked. Independent review, 3 rounds dual-gate converged: R1 REQUEST-CHANGES (probe resolved a non-exported subpath -> always-null, whole card defeated; tautological test masked it); R2 REQUEST-CHANGES (destructive test overwrote real dist/cli.js); R3 APPROVE (DI refactor, mkdtemp+injected resolver, real dist verified untouched -- rebuilt dist/cli.js, 77/77 files 1451/1451 passed CI-way, post-run sha256 identical). Production default path unchanged. 6-CHECK re-verified by Mos @c5a2bcc516ebabaf61b46bc19f09c51e4a1158bc: open/not-merged/mergeable/base main; CI success wp1973 @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869' (ruled); queue-guard clear.
Mos merged commit db90da347e into main 2026-07-23 17:14:58 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#870