Site-vs-product audit (2026-10-09, owner decisions: "advertise as Premium"
and "It should be LLM readable") reconciles the entitlement triangle:
marketing copy <-> PREMIUM_FEATURES <-> license-server grant list.
r1 judge blockers/polish addressed:
- B2 (synthetic-router tests): NEW e2e fleet-gate-composition.e2e.test.js
boots the REAL createApp() on an ephemeral port with a stdin control
channel to mutate the live LicenseManager instance returned by createApp,
proving: no activation -> 403 premiumRequired feature=fleet
currentTier=free; activation WITHOUT fleet -> still 403 (per-feature,
not tier-only); activation WITH fleet -> gate cleared; prefix inheritance
across /fleet/hosts, /fleet/status, /fleet/deploy (4 real-boot tests).
- B0 (hand-typed constants in the manifest): MCP transport identity moved
to MCP_RUNTIME in routes/mcp.js (the transport implementation's own
module), exported; /info responses use MCP_RUNTIME.transport, and
capabilities.js reads transport/protocolVersion/protocolVersions from
the SAME object — nothing re-typed. New assertion: manifest == MCP_RUNTIME.
- B1 (OpenAPI 401 on a public route): capabilities block documents only
200 with an explicit public-route note.
- P0 (migration replaced the whole array): now APPEND-ONLY union — current
PREMIUM_FEATURES first, unknown legacy entries preserved verbatim.
Dedicated test pins shape + persistence.
- P1 (eslint exit suppressed): verifier [5] hard-asserts rc==0.
- P2 (brief counts): brief quotes exact `git diff --stat` (below).
Product behavior:
- 'fleet' in PREMIUM_FEATURES; requirePremium('fleet') mounted at the
/fleet ROUTER prefix in app.js BEFORE fleetRoutes -> every sub-route,
present and future, inherits the gate.
- 'workflows' + 'auto-backup' (gated in routes forever, missing from the
constant — offline grants could never include them) listed with
descriptions quoted from src/recipes/bundled-workflows.js (5 ids) and
routes/backups.js schedule schema (interval/retention/maxStorageBytes/
destination/runImmediately). Manual one-click backup + Disaster Recovery
stay free; only the schedule trio is gated (as coded).
- Offline activation forward-migration on load(); online untouched (server
authoritative across the network boundary).
- GET /api/v1/capabilities: public ground truth derived from runtime
modules only — APP_TEMPLATES, mcp-core TOOLS, mcp.js MCP_RUNTIME,
PREMIUM_FEATURES, i18n, version.js. In PUBLIC_ROUTES (exact GET),
covered by public-routes-drift directMounts; no-secrets negative test.
- openapi.yaml documents the endpoint.
diff --stat 4143bff..HEAD: 9 files, +558/-6:
__tests__/dc165-uniformity.test.js +201 (9 tests)
__tests__/e2e-node/fleet-gate-composition +179 (4 real-boot tests)
__tests__/public-routes-drift.test.js +1
openapi.yaml +20
routes/capabilities.js +77 (NEW)
routes/mcp.js +24/-4 (MCP_RUNTIME single source)
src/app.js +11 (fleet gate + capabilities mount)
src/managers/license-manager.js +50 (3 feature entries + migration)
src/utilities/middleware.js +1 (PUBLIC_ROUTES)
Tests: 9 dc165 + 4 fleet-e2e + 76 license-manager + 21 tier-enforcement
+ 14 drift = 124 targeted; 73 MCP route/core tests unchanged green.
- __tests__/e2e-node/composition-root.e2e.test.js: `boot()` takes {timeoutMs, code}
and the timeout rejection carries the child it killed; a NEW test drives the real
timeout against a child that never reports PORT/READY and asserts the OS-observed
SIGKILL on that child (marker e2e-timeout-kill) — the r4 claim is now exercised.
- routes/deploys.js: bridge() coerces any non-object JSON body (null/[]/string) into
the same failure shape as a non-JSON body, so no `body.ok` deref can 500 or report a
false success; the subdomain label regex rejects leading AND trailing hyphens.
- __tests__/routes/deploys.catalog.r4.test.js: trailing-dash rejection case + three
malformed-successful-body cases (clean 502, never 500).
The e2e child now calls app.listen(0) and reports the OS-assigned port on
stdout (PORT:<n>); the parent reads it. The probe-then-release helper is
deleted, so no other process can take the port between probe and bind.
BLOCK0: catalog deploy audit now uses the REAL AuditLogger contract
(auditLogger.log({action,resource,details,outcome,ip})) fire-and-forget
via Promise.resolve().catch — async audit failures can never fail the
deploy response (tested with a throwing audit backend).
BLOCK1: verify.sh targeted-jest block captures jest exit code explicitly
(set +e / rc / loud FAIL) — no silent termination under set -e.
BLOCK2: subdomain advertised by the App Selector modal is now FORWARDED
end-to-end (selector -> /deploys/catalog/deploy validated charset ->
bridge --subdomain -> CLI CatalogInstallOptions, implemented in
dc-catalog-2 r3); invalid subdomains 400 before the bridge (4 cases).
POLISH: boot-timeout SIGKILLs the child (no leaked process).
New r4 suite: 7 tests (audit shape success/failure, subdomain forward +
4 rejections). Full suite 157 suites / 3519 tests green.
Judge r2 blockers addressed:
- MOUNTED vs UNKNOWN: probes now carry a REAL JWT minted by the same
authManager the app wires (sub claim; generated in the boot child,
emitted on READY). Every mounted-route assertion is compared against a
live notFoundHandler-404 baseline for a known-nonexistent route on the
same instance — a fall-through can no longer pass as mounted.
- Route-level behavior asserted with real payloads: /catalog/plex 200
must contain plexinc/pms-docker AND {{PORT}}:32400; /security/shield 200
must be success:true.
- OS-assigned ephemeral port (net server listen(0)) replaces the random
30000-49999 guess; boot-timeout cleared on readiness; SIGTERM+SIGKILL
shutdown with bounded wait.
- 7 tests (was 5). Suite: 156 suites / 3512 tests green.
- DC-160: /catalog/:appId read app.image/ports/env/volumes which live under
app.docker.* — every detail response was empty. Now reads docker.* and
surfaces defaultPort/subdomain/healthCheck/mediaMount/claimToken/difficulty/
setupInstructions for the deploy form. List/search use the templates own
category field (old hand-rolled CATEGORY_MAP mis-categorized apps).
- Shield (routes/shield.js + 5-line app.js mount) committed to source after
weeks as prod-only drift; regression test asserts the mount exists.
- New composition-root e2e suite: boots REAL createApp() on an ephemeral
port, probes the historically-broken routes through the REAL mount table
(wizard wiring, shield, catalog detail, deploys bridge) — the class of bug
all 3 P7 dogfood findings belonged to. 5 tests.
- Full suite 3499/3499 (155 suites); table-driven DC-160 test covers all 73
image templates.