@volter/world-runtime 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/LICENSE +202 -0
  2. package/dist/known-external-services.json +1108 -0
  3. package/dist/src/ancestry.d.ts +2 -0
  4. package/dist/src/ancestry.js +42 -0
  5. package/dist/src/app-url.d.ts +47 -0
  6. package/dist/src/app-url.js +239 -0
  7. package/dist/src/attach.d.ts +48 -0
  8. package/dist/src/attach.js +87 -0
  9. package/dist/src/branch.d.ts +20 -0
  10. package/dist/src/branch.js +65 -0
  11. package/dist/src/browser-proxy-cli.d.ts +2 -0
  12. package/dist/src/browser-proxy-cli.js +41 -0
  13. package/dist/src/ca-trust.d.ts +5 -0
  14. package/dist/src/ca-trust.js +64 -0
  15. package/dist/src/catalog.d.ts +31 -0
  16. package/dist/src/catalog.js +148 -0
  17. package/dist/src/changeset.d.ts +142 -0
  18. package/dist/src/changeset.js +570 -0
  19. package/dist/src/cli.d.ts +2 -0
  20. package/dist/src/cli.js +1262 -0
  21. package/dist/src/command-lifetime.d.ts +15 -0
  22. package/dist/src/command-lifetime.js +98 -0
  23. package/dist/src/configs.d.ts +18 -0
  24. package/dist/src/configs.js +119 -0
  25. package/dist/src/console-apart.d.ts +38 -0
  26. package/dist/src/console-apart.js +107 -0
  27. package/dist/src/consumers.d.ts +46 -0
  28. package/dist/src/consumers.js +200 -0
  29. package/dist/src/covers.d.ts +183 -0
  30. package/dist/src/covers.js +800 -0
  31. package/dist/src/fixture-env.d.ts +42 -0
  32. package/dist/src/fixture-env.js +221 -0
  33. package/dist/src/host-cli.d.ts +2 -0
  34. package/dist/src/host-cli.js +92 -0
  35. package/dist/src/host-fault-fixture.d.ts +32 -0
  36. package/dist/src/host-fault-fixture.js +100 -0
  37. package/dist/src/host-worker.d.ts +1 -0
  38. package/dist/src/host-worker.js +23 -0
  39. package/dist/src/host.d.ts +38 -0
  40. package/dist/src/host.js +135 -0
  41. package/dist/src/index.d.ts +48 -0
  42. package/dist/src/index.js +35 -0
  43. package/dist/src/infra-cli.d.ts +2 -0
  44. package/dist/src/infra-cli.js +136 -0
  45. package/dist/src/init.d.ts +227 -0
  46. package/dist/src/init.js +1117 -0
  47. package/dist/src/inject-map.d.ts +34 -0
  48. package/dist/src/inject-map.js +56 -0
  49. package/dist/src/lifecycle-record.d.ts +47 -0
  50. package/dist/src/lifecycle-record.js +196 -0
  51. package/dist/src/origin.d.ts +31 -0
  52. package/dist/src/origin.js +139 -0
  53. package/dist/src/pack-facts.d.ts +75 -0
  54. package/dist/src/pack-facts.js +98 -0
  55. package/dist/src/pglite-backing.d.ts +21 -0
  56. package/dist/src/pglite-backing.js +158 -0
  57. package/dist/src/pglite-host.mjs +147 -0
  58. package/dist/src/placeholder.d.ts +20 -0
  59. package/dist/src/placeholder.js +100 -0
  60. package/dist/src/prerequisites.d.ts +21 -0
  61. package/dist/src/prerequisites.js +49 -0
  62. package/dist/src/process-groups.d.ts +4 -0
  63. package/dist/src/process-groups.js +49 -0
  64. package/dist/src/project-inspect.d.ts +109 -0
  65. package/dist/src/project-inspect.js +827 -0
  66. package/dist/src/proxy-daemon.d.ts +2 -0
  67. package/dist/src/proxy-daemon.js +18 -0
  68. package/dist/src/redirect-proxy.d.ts +105 -0
  69. package/dist/src/redirect-proxy.js +665 -0
  70. package/dist/src/reflect.d.ts +74 -0
  71. package/dist/src/reflect.js +392 -0
  72. package/dist/src/resources.d.ts +26 -0
  73. package/dist/src/resources.js +22 -0
  74. package/dist/src/root.d.ts +114 -0
  75. package/dist/src/root.js +312 -0
  76. package/dist/src/run-task-worker.d.ts +1 -0
  77. package/dist/src/run-task-worker.js +38 -0
  78. package/dist/src/run-task.d.ts +18 -0
  79. package/dist/src/run-task.js +48 -0
  80. package/dist/src/runtime-test-support.d.ts +59 -0
  81. package/dist/src/runtime-test-support.js +205 -0
  82. package/dist/src/runtime.d.ts +256 -0
  83. package/dist/src/runtime.js +3502 -0
  84. package/dist/src/schema.d.ts +449 -0
  85. package/dist/src/schema.js +605 -0
  86. package/dist/src/serve.d.ts +30 -0
  87. package/dist/src/serve.js +82 -0
  88. package/dist/src/served-world.d.ts +194 -0
  89. package/dist/src/served-world.js +986 -0
  90. package/dist/src/service-exit.d.ts +46 -0
  91. package/dist/src/service-exit.js +195 -0
  92. package/dist/src/service-recorder.d.ts +1 -0
  93. package/dist/src/service-recorder.js +121 -0
  94. package/dist/src/sibling.d.ts +1 -0
  95. package/dist/src/sibling.js +9 -0
  96. package/dist/src/signals.d.ts +1 -0
  97. package/dist/src/signals.js +11 -0
  98. package/dist/src/storage-capacity.d.ts +8 -0
  99. package/dist/src/storage-capacity.js +61 -0
  100. package/dist/src/tail.d.ts +30 -0
  101. package/dist/src/tail.js +160 -0
  102. package/dist/src/tcp-port.d.ts +2 -0
  103. package/dist/src/tcp-port.js +36 -0
  104. package/dist/src/up-task-worker.d.ts +1 -0
  105. package/dist/src/up-task-worker.js +61 -0
  106. package/dist/src/up-task.d.ts +17 -0
  107. package/dist/src/up-task.js +49 -0
  108. package/dist/src/websocket-relay.d.ts +3 -0
  109. package/dist/src/websocket-relay.js +40 -0
  110. package/known-external-services.json +1108 -0
  111. package/package.json +83 -0
  112. package/src/ancestry.ts +36 -0
  113. package/src/app-url.ts +253 -0
  114. package/src/attach.ts +117 -0
  115. package/src/branch.ts +63 -0
  116. package/src/browser-proxy-cli.ts +44 -0
  117. package/src/ca-trust.ts +57 -0
  118. package/src/catalog.ts +156 -0
  119. package/src/changeset.ts +627 -0
  120. package/src/cli.ts +1111 -0
  121. package/src/command-lifetime.ts +79 -0
  122. package/src/configs.ts +110 -0
  123. package/src/console-apart.ts +90 -0
  124. package/src/consumers.ts +185 -0
  125. package/src/covers.ts +934 -0
  126. package/src/fixture-env.ts +230 -0
  127. package/src/host-cli.ts +90 -0
  128. package/src/host-worker.ts +23 -0
  129. package/src/host.ts +169 -0
  130. package/src/index.ts +171 -0
  131. package/src/infra-cli.ts +133 -0
  132. package/src/init.ts +1316 -0
  133. package/src/inject-map.ts +72 -0
  134. package/src/lifecycle-record.ts +168 -0
  135. package/src/origin.ts +134 -0
  136. package/src/pack-facts.ts +128 -0
  137. package/src/pglite-backing.ts +141 -0
  138. package/src/pglite-host.mjs +147 -0
  139. package/src/placeholder.ts +89 -0
  140. package/src/prerequisites.ts +66 -0
  141. package/src/process-groups.ts +33 -0
  142. package/src/project-inspect.ts +770 -0
  143. package/src/proxy-daemon.ts +21 -0
  144. package/src/redirect-proxy.ts +684 -0
  145. package/src/reflect.ts +440 -0
  146. package/src/resources.ts +22 -0
  147. package/src/root.ts +290 -0
  148. package/src/run-task-worker.ts +27 -0
  149. package/src/run-task.ts +44 -0
  150. package/src/runtime-test-support.ts +208 -0
  151. package/src/runtime.ts +3357 -0
  152. package/src/schema.ts +922 -0
  153. package/src/serve.ts +102 -0
  154. package/src/served-world.ts +812 -0
  155. package/src/service-exit.ts +175 -0
  156. package/src/service-recorder.ts +89 -0
  157. package/src/sibling.ts +10 -0
  158. package/src/signals.ts +10 -0
  159. package/src/storage-capacity.ts +60 -0
  160. package/src/tail.ts +205 -0
  161. package/src/tcp-port.ts +35 -0
  162. package/src/up-task-worker.ts +40 -0
  163. package/src/up-task.ts +45 -0
  164. package/src/websocket-relay.ts +32 -0
@@ -0,0 +1,800 @@
1
+ // `volter-world covers <world> --repo <path>` — the room-setup COVERAGE PROOF (the twin
2
+ // half of the "room ensure params" doctrine): check detected vendor and connection
3
+ // requirements against the named World. Static discovery is conservative and incomplete;
4
+ // an app scenario must verify behavior and consistency across interfaces.
5
+ //
6
+ // Detection (repo side) reuses inspect-project's signals: every package.json across the
7
+ // root + all workspace members (pnpm/yarn/npm/bun), mapped through SDK_TWINS, plus
8
+ // vendor-shaped env names in committed .env* files and direct literal fetch destinations
9
+ // in production source (a repo may use a vendor without declaring its SDK).
10
+ //
11
+ // Matching (world side) reads the world's INSTANCE when one exists (.volter/worlds/<name>/
12
+ // instance.json — the truth for a booted world) and falls back to the stable CONFIG
13
+ // (worlds/configs/<name>.json) so the proof also works pre-boot.
14
+ //
15
+ // LOUD FAILURE SEMANTICS — this is a proof, not a report:
16
+ // exit 0 ⇔ every detected vendor is covered AND nothing external-service-shaped is
17
+ // unaccounted for. A vendor with no twin in the world → MISSING → exit 1.
18
+ // A twin that IS in the world but that no traffic can reach (its service
19
+ // resolves to no injector vendor and exposes no app-read endpoint env — the
20
+ // LibreChat AWS_TWIN_URL trap) → UNINTERCEPTABLE → exit 1: presence is not
21
+ // coverage. An external-service-shaped dep/env var with no vendor mapping →
22
+ // unknown-sdk → exit 1 by default (`--allow-unknown` downgrades to a warning:
23
+ // unknowns still print, but stop failing the proof).
24
+ import { readFileSync } from 'node:fs';
25
+ import { dirname, join, resolve } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { loadWorldConfig } from "./configs.js";
28
+ import { injectableVendorKeys, loadInject, twinUrlVendorFor } from "./inject-map.js";
29
+ import { connectionProtocol, projectConnections, projectDependencies, projectEnvNames, projectFetchUrls, projectNpmRegistries, PYPI_TWINS, SDK_TWINS } from "./project-inspect.js";
30
+ import { overlayCoversMaps, packFacts } from "./pack-facts.js";
31
+ import { statusWorld } from "./runtime.js";
32
+ import { worldSelectionIncludes } from "./schema.js";
33
+ /** identifiers are compared case/punctuation-insensitively: 's3', 'S3_TWIN_URL' stem,
34
+ * a 'stripe' service id and the 'ai-gateway' pack name all normalize cleanly. */
35
+ function normalizeId(text) {
36
+ return text.toLowerCase().replace(/[^a-z0-9]/g, '');
37
+ }
38
+ /** Which world-side identifiers (normalized service ids / *_TWIN_URL stems / pack names)
39
+ * count as covering a vendor. Default: the vendor key itself. Extras cover the packs whose
40
+ * world identity differs from the SDK vendor key (the consolidated aws twin answers as
41
+ * s3/dynamodb/timestream; cookbook supabase worlds inject SUPABASE_MGMT_TWIN_URL). */
42
+ const VENDOR_WORLD_IDS = {
43
+ // (empty — every entry has moved onto its pack descriptor. See the note below.)
44
+ };
45
+ function worldIdsFor(vendor) {
46
+ return VENDOR_WORLD_IDS[vendor] ?? [normalizeId(vendor)];
47
+ }
48
+ /**
49
+ * The INJECTOR vendor keys that can carry this vendor's traffic, in a stable order — the single
50
+ * source of truth for "can zero-edit interception work here, and under which `*_TWIN_URL` name?".
51
+ *
52
+ * Read from `@volter/world-core/inject`'s own VENDOR_HOSTS table (never a re-encoding of it), and
53
+ * filtered through the same `VENDOR_WORLD_IDS` aliasing the proof matches with: `aws` yields
54
+ * `['s3','dynamodb','timestream']` (the consolidated twin's real keys — `AWS_TWIN_URL` is the
55
+ * inert trap), `stripe` yields `['stripe']`, and a pack in vendor-hosts.test.ts's
56
+ * descriptor hostsNone ruling yields `[]`.
57
+ *
58
+ * Two callers, one answer: `covers` uses it to decide whether an UNINTERCEPTABLE finding is a
59
+ * fixable wiring bug (keys exist) or an acknowledgeable allowlist gap (none do), and `init` uses
60
+ * it to DERIVE the canonical `injectEnv` it emits — so an emitted world can never claim a
61
+ * `*_TWIN_URL` var the injector does not read.
62
+ */
63
+ export function injectorVendorKeysFor(vendor) {
64
+ const worldIds = new Set(worldIdsFor(vendor));
65
+ return [...injectableVendorKeys()].filter((key) => worldIds.has(normalizeId(key)));
66
+ }
67
+ /** Shell-safe env name read by the injector for a vendor key, including hyphenated keys. */
68
+ export function injectorEnvNameForKey(key) {
69
+ return `${key.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_TWIN_URL`;
70
+ }
71
+ /** Vendor-shaped env var detection (the secondary signal). The var must end in a
72
+ * credential/endpoint suffix; the remaining stem (framework prefixes stripped,
73
+ * normalized) is looked up here. */
74
+ // Exported for scripts/packless-claims.test.ts, which checks the packless registry against
75
+ // the OVERLAID map (legacy entries + descriptor adoption.envStems in one view) — importing
76
+ // the real map is unbreakable by formatting, unlike the source-parsing it replaced (§9
77
+ // skeptic M3, 2026-08-31: a reflow or quote-style change silently emptied the parsed set).
78
+ export const ENV_STEM_VENDORS = {
79
+ // (empty — every entry has moved onto its pack descriptor. See the note below.)
80
+ };
81
+ const ENV_FRAMEWORK_PREFIX = /^(NEXT_PUBLIC_|VITE_|REACT_APP_|EXPO_PUBLIC_|NUXT_PUBLIC_|PUBLIC_)/;
82
+ /** Broad credential/endpoint suffixes — used to derive a stem for KNOWN-vendor detection
83
+ * (a false stem is harmless: it only counts when the stem hits ENV_STEM_VENDORS). */
84
+ const ENV_SUFFIX_BROAD = /_(ACCESS_KEY_ID|SECRET_ACCESS_KEY|SERVICE_ROLE_KEY|PUBLISHABLE_KEY|WEBHOOK_SECRET|SIGNING_SECRET|CLIENT_SECRET|CLIENT_ID|CONSUMER_KEY|ACCOUNT_SID|OAUTH_TOKEN|AUTH_TOKEN|ACCESS_TOKEN|BEARER_TOKEN|BOT_TOKEN|API_TOKEN|API_SECRET|API_KEY|APIKEY|SECRET_KEY|API_URL|BASE_URL|REST_URL|SECRET|TOKEN|KEY|DSN)$/;
85
+ /** Narrow, unmistakably vendor-shaped suffixes — a var with one of these whose stem maps
86
+ * to NO known vendor is an unknown external service and fails the proof. (Deliberately
87
+ * excludes bare _SECRET/_TOKEN/_KEY: JWT_SECRET / SESSION_SECRET are app-local. _REST_URL
88
+ * is here on purpose: it is a hosted-service endpoint shape — UPSTASH_REDIS_REST_URL
89
+ * produced NO row at all in the Dub blind-adoption run. _CLIENT_ID/_CLIENT_SECRET/
90
+ * _CONSUMER_KEY are here on purpose too: they are the OAuth-app credential shape, and
91
+ * before they were strict the Cal.com blind run silently dropped NINE OAuth vendors —
92
+ * ZOOM_CLIENT_ID, MS_GRAPH_CLIENT_SECRET, SALESFORCE_CONSUMER_KEY, GOOGLE_CLIENT_ID, … —
93
+ * from a proof that exited 0. The env-detection heuristic was structurally biased against
94
+ * exactly the vendor class integration-heavy apps are made of.) */
95
+ const ENV_SUFFIX_STRICT = /_(API_KEY|APIKEY|API_TOKEN|API_SECRET|OAUTH_TOKEN|CLIENT_ID|CLIENT_SECRET|CONSUMER_KEY|ACCOUNT_SID|DSN|REST_URL)$/;
96
+ /** App-local stems that must never be flagged as an unknown vendor. `redis` is deliberately
97
+ * NOT here: a redis-stemmed var only reaches this lookup through a vendor-shaped suffix
98
+ * (e.g. REDIS_REST_URL — hosted Redis, an external service), while plain local-Redis vars
99
+ * (REDIS_URL, REDIS_HOST, REDIS_PASSWORD) never match ENV_SUFFIX_STRICT in the first place. */
100
+ const ENV_STEM_IGNORE = new Set([
101
+ 'jwt', 'session', 'auth', 'nextauth', 'app', 'api', 'admin', 'internal', 'server', 'client',
102
+ 'cookie', 'csrf', 'encryption', 'webhook', 'test', 'dev', 'local', 'demo', 'example', 'my',
103
+ 'database', 'db', 'postgres', 'mysql', 'mongo', 'smtp', 'volter', 'twin', 'world',
104
+ // generic IdP-config stems: OAUTH_CLIENT_ID / OIDC_CLIENT_SECRET name a PROTOCOL role, not a
105
+ // vendor — now that _CLIENT_ID/_CLIENT_SECRET are strict suffixes these would otherwise raise
106
+ // a false unknown-vendor alarm on every app with a generic OAuth config block. Vendor-named
107
+ // stems (ZOOM_, SALESFORCE_, GOOGLE_, …) still surface.
108
+ 'oauth', 'oidc',
109
+ ]);
110
+ function envVendorStem(name, suffix) {
111
+ const stripped = name.replace(ENV_FRAMEWORK_PREFIX, '');
112
+ const match = stripped.match(suffix);
113
+ if (!match || match.index === undefined || match.index === 0)
114
+ return null;
115
+ return normalizeId(stripped.slice(0, match.index));
116
+ }
117
+ /** Is this env NAME shaped like a CREDENTIAL/endpoint at all (any broad vendor suffix)? The
118
+ * question `init` asks before deciding whether a value in a repo's `.env.example` may be copied
119
+ * verbatim: `APP_NAME` may, `ANYTHING_SECRET` may not — a committed example file is exactly where
120
+ * live keys hide (the ponder blind-adoption run found real ones), so a credential-shaped name is
121
+ * ALWAYS replaced by a fake, whether or not its stem maps to a vendor this repo twins. Exported
122
+ * from here so the emission path and the proof read ONE definition of "credential-shaped". */
123
+ export function isCredentialShapedEnvName(name) {
124
+ return ENV_SUFFIX_BROAD.test(name.replace(ENV_FRAMEWORK_PREFIX, ''));
125
+ }
126
+ /** The vendor a credential-shaped env NAME betrays, or null. Same broad-suffix route the
127
+ * coverage proof's secondary signal uses (`STRIPE_SECRET_KEY` → 'stripe'). */
128
+ export function envNameVendor(name) {
129
+ // ⚠️ This deliberately does NOT claim the SMTP host/URL vars, even though `detectRepoVendors`
130
+ // below detects them as vendor `smtp`. The two functions answer DIFFERENT questions, and §9
131
+ // found both halves of that the hard way:
132
+ // • round 1 called the divergence an inconsistency ("one answer per name");
133
+ // • round 2 showed what happens when you remove it. This function's only caller is `init`'s
134
+ // env emission (`init.ts`), which REPLACES a credential-shaped value with `twin-fake-<name>`.
135
+ // Claiming `SMTP_URL`/`EMAIL_SERVER` here turned NextAuth's `smtp://user:pass@host:port` into
136
+ // `twin-fake-email-server`, which `new URL(...)` cannot parse — breaking the app to protect a
137
+ // value that was never a credential in the first place.
138
+ // So: this asks "is this name a VENDOR CREDENTIAL that must be faked?" (a hostname is not), while
139
+ // `detectRepoVendors` asks "which vendors does this repo talk to?" (the mail path certainly is
140
+ // one). Same string, two honest answers.
141
+ const stem = envVendorStem(name, ENV_SUFFIX_BROAD);
142
+ return stem === null ? null : ENV_STEM_VENDORS[stem] ?? null;
143
+ }
144
+ /** SMTP-shaped env names — the raw-protocol mail path (nodemailer et al.) the HTTP-vendor
145
+ * heuristics above are structurally blind to. The Cal.com blind run's booking-confirmation
146
+ * mail (EMAIL_SERVER_HOST/EMAIL_SERVER_PORT) was invisible to the proof while being the
147
+ * single most important vendor touchpoint of the app. Conservative stem+suffix approach:
148
+ * only the HOST/URL forms count (`SMTP_HOST`, `MAILGUN_SMTP_HOST`, `SMTP_URL`, and
149
+ * NextAuth/nodemailer's `EMAIL_SERVER`/`EMAIL_SERVER_HOST` connection forms) — a bare
150
+ * SMTP_USER/_PASSWORD without a host proves nothing and would duplicate rows.
151
+ *
152
+ * This signal used to surface as unknown-sdk ('smtp') because no SMTP twin existed — the honest
153
+ * outcome at the time. The `smtp` pack now twins the protocol, so it is a DETECTED vendor and a
154
+ * world that runs an smtp service covers it. What has NOT changed is why the signal has to be a
155
+ * dedicated route at all: SMTP is intercepted through the app-read env name itself, not through a
156
+ * host the injector rewrites, so the env var IS the touchpoint rather than a hint about one.
157
+ * (The 'smtp' entry in ENV_STEM_IGNORE guards the generic-suffix stem route; this signal is
158
+ * deliberate and separate.) */
159
+ function isSmtpEnvSignal(name) {
160
+ const stripped = name.replace(ENV_FRAMEWORK_PREFIX, '');
161
+ return /(^|_)SMTP_(HOST|URL)$/.test(stripped) || /^EMAIL_SERVER(_HOST)?$/.test(stripped);
162
+ }
163
+ /** npm scopes whose EVERY package is one vendor's client surface — an unmapped
164
+ * satellite package (`@sentry/cli`, `@datadog/browser-logs`, `@octokit/graphql`)
165
+ * still detects (and is covered by) that vendor's twin. Deliberately NOT `@aws-sdk`
166
+ * or `@azure`/`@google-cloud`: those scopes span many services and the consolidated
167
+ * twins only model some of them — unmapped clients there must stay unknown-sdk. */
168
+ const SDK_SCOPE_VENDORS = {
169
+ // (empty — every entry has moved onto its pack descriptor. See the note below.)
170
+ };
171
+ // descriptor-first migration (adding-a-twin.md §3), COMPLETE for these three tables: every pack declares its own
172
+ // scopes / env stems / world ids (`adoption` on its descriptor, compiled into the committed
173
+ // pack-facts artifact), and all three literals above are now empty — they are populated entirely
174
+ // by the overlay below. They are kept as the overlay's targets, not as homes: the object identity
175
+ // is what every use site already reads, so nothing downstream changed. Do NOT re-add an entry
176
+ // here; a fact declared in both homes throws at module init.
177
+ overlayCoversMaps({ scopeVendors: SDK_SCOPE_VENDORS, envStemVendors: ENV_STEM_VENDORS, vendorWorldIds: VENDOR_WORLD_IDS });
178
+ export function scopeVendorFor(name) {
179
+ const entry = Object.entries(SDK_SCOPE_VENDORS).find(([prefix]) => name === prefix || name.startsWith(prefix));
180
+ return entry?.[1];
181
+ }
182
+ /** npm scopes holding at least one twin-mapped package (from SDK_TWINS' scoped keys and
183
+ * SDK_SCOPE_VENDORS). An UNMAPPED sibling from such a scope is almost always another external
184
+ * surface of the same platform — in the Dub blind-adoption run, `@upstash/redis` and
185
+ * `@upstash/ratelimit` produced NO row at all while `@upstash/qstash` was mapped, a silent
186
+ * blind spot. Such siblings surface as unknown-sdk instead of disappearing. */
187
+ const MAPPED_SCOPES = new Set([...Object.keys(SDK_TWINS), ...Object.keys(SDK_SCOPE_VENDORS)]
188
+ .filter((name) => name.startsWith('@'))
189
+ .map((name) => `${name.split('/')[0]}/`));
190
+ export function isMappedScopeSibling(name) {
191
+ if (knownExternalServices().notExternal[name] !== undefined)
192
+ return false;
193
+ return [...MAPPED_SCOPES].some((scope) => name.startsWith(scope));
194
+ }
195
+ let registryCache;
196
+ export function knownExternalServices() {
197
+ if (registryCache === undefined) {
198
+ const path = join(dirname(fileURLToPath(import.meta.url)), '..', 'known-external-services.json');
199
+ registryCache = JSON.parse(readFileSync(path, 'utf8'));
200
+ }
201
+ return registryCache;
202
+ }
203
+ let patternCache;
204
+ function unknownDepPatterns() {
205
+ patternCache ??= knownExternalServices().patterns.map((p) => new RegExp(p.pattern));
206
+ return patternCache;
207
+ }
208
+ function isExternalServiceShapedDep(name) {
209
+ const registry = knownExternalServices();
210
+ if (registry.notExternal[name] !== undefined)
211
+ return false;
212
+ if (registry.deps[name] !== undefined)
213
+ return true;
214
+ return unknownDepPatterns().some((pattern) => pattern.test(name));
215
+ }
216
+ /** The registry's STANDING acknowledgment for an unknown-signal name (npm dep or env stem),
217
+ * or null when the signal is unlisted or listed as a demanded (loud) gap. Exported for init,
218
+ * whose plan partitions the same signals the proof does.
219
+ *
220
+ * DEMANDED ANYWHERE WINS: the unknown map merges npm deps, env stems and registry-host ids
221
+ * under one key space, so a name listed in BOTH registry sections must never let one
222
+ * section's acknowledgment quiet the other's demanded gap (§9 round two H5, 2026-08-31: an
223
+ * acknowledged dep entry silently green-lit a repo whose only signal was the same-named
224
+ * DEMANDED env stem). Loud beats quiet; the gate also refuses conflicting dispositions. */
225
+ export function registryAcknowledgedReason(name) {
226
+ const registry = knownExternalServices();
227
+ const entries = [registry.deps[name], registry.envStems[name]].filter((entry) => entry !== undefined);
228
+ if (entries.length === 0 || entries.some((entry) => entry.disposition !== 'acknowledged'))
229
+ return null;
230
+ return entries[0].reason;
231
+ }
232
+ function declaredTwin(services) {
233
+ const declared = new Map();
234
+ for (const service of services) {
235
+ if (service.type !== undefined && service.type !== 'twin')
236
+ continue;
237
+ const config = service;
238
+ const identity = config.package?.match(/^@volter\/twin-(.+)$/)?.[1] ?? servicePackNames(config)[0] ?? service.id;
239
+ const vendor = Object.keys(packFacts()).find((candidate) => worldIdsFor(candidate).includes(normalizeId(identity))) ?? identity;
240
+ const ids = new Map();
241
+ addServiceIds(ids, service);
242
+ const match = [...worldIdsFor(vendor), vendor].map(normalizeId).map((id) => ids.get(id)).find(Boolean);
243
+ declared.set(vendor, match ?? { label: `${service.id} (declared twin)`, interceptable: false });
244
+ }
245
+ return declared;
246
+ }
247
+ function addId(ids, key, label, interceptable) {
248
+ const existing = ids.get(key);
249
+ if (!existing)
250
+ ids.set(key, { label, interceptable });
251
+ // first label wins (stable output), but interceptability is an OR across contributors: a
252
+ // second service that CAN be reached upgrades the id.
253
+ else if (interceptable && !existing.interceptable)
254
+ existing.interceptable = true;
255
+ }
256
+ function servicePackNames(service) {
257
+ const commandParts = [
258
+ ...(service.package ? [service.package] : []),
259
+ ...(Array.isArray(service.command) ? service.command : service.command ? [service.command] : []),
260
+ ...(service.args ?? []),
261
+ ...(service.colocate?.module ? [service.colocate.module] : []),
262
+ ];
263
+ return commandParts.flatMap((part) => {
264
+ const fromPath = part.match(/packages\/twin\/([a-z0-9-]+)\//);
265
+ const fromBin = part.match(/^world-([a-z0-9-]+)$/);
266
+ const fromPkg = part.match(/(?:^|[/\\])@volter\/twin-([a-z0-9-]+)(?:[/\\]|$)/);
267
+ return [fromPath?.[1], fromBin?.[1], fromPkg?.[1]].filter((name) => name !== undefined);
268
+ });
269
+ }
270
+ /** Endpoint wiring owned by a service, never a random URL from global config/env. Config
271
+ * templates must use the service's allocated host/port. Instance values must point at that
272
+ * listener, or have been discovered by the external lifecycle. HTTP pack facts veto native
273
+ * coverage even when someone has put a mysql:// template on an HTTP twin. */
274
+ function serviceConnections(services, effectiveEnv) {
275
+ const bindings = new Map();
276
+ for (const service of services) {
277
+ const instance = 'pid' in service;
278
+ const config = instance ? undefined : service;
279
+ const facts = servicePackNames(service).map((name) => packFacts()[name]).filter((fact) => fact !== undefined);
280
+ if (!instance && config?.injectEnv)
281
+ bindings.set(config.injectEnv, { protocol: 'http', label: `${service.id} (${config.injectEnv})` });
282
+ const values = instance ? service.env : config?.injectEnvTemplates ?? {};
283
+ for (const [name, value] of Object.entries(values)) {
284
+ const protocol = connectionProtocol(value);
285
+ const command = [...(Array.isArray(service.command) ? service.command : []), ...('args' in service ? service.args ?? [] : [])];
286
+ // The pack-transport veto is about NATIVE schemes (a mysql:// template on an HTTP twin); an
287
+ // http(s) endpoint template on any twin is that twin's own HTTP door.
288
+ const incompatiblePack = protocol !== null && facts.some((fact) => {
289
+ if (fact.nativeTransport?.protocol === protocol && command.includes(fact.nativeTransport.flag))
290
+ return false;
291
+ return fact.transport !== 'raw-tcp' || !Object.values(fact.endpointEnv?.templates ?? {})
292
+ .some((template) => connectionProtocol(template) === protocol && protocol !== null);
293
+ });
294
+ let routed = false;
295
+ if (protocol !== null && !incompatiblePack) {
296
+ if (instance) {
297
+ if (effectiveEnv?.[name] !== value)
298
+ continue; // another service/env replaced this binding
299
+ if (service.type === 'external')
300
+ routed = service.external?.discoveredEnv?.[name] === value;
301
+ else {
302
+ try {
303
+ const url = new URL(value);
304
+ routed = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname) && Number(url.port) === service.port;
305
+ }
306
+ catch { /* unresolved connection stays unknown */ }
307
+ }
308
+ }
309
+ else if (config?.port !== undefined) {
310
+ try {
311
+ const url = new URL(value.replaceAll('${host}', '127.0.0.1').replaceAll('${port}', '54321'));
312
+ routed = url.hostname === '127.0.0.1' && url.port === '54321' &&
313
+ /^\w[\w+.-]*:\/\/(?:[^/?#]*@)?\$\{host\}:\$\{port\}(?:[/?#]|$)/.test(value);
314
+ }
315
+ catch { /* unresolved connection stays unknown */ }
316
+ }
317
+ }
318
+ bindings.set(name, { protocol: routed ? protocol : incompatiblePack ? 'incompatible' : /^https?:/.test(value) ? 'http' : null, label: `${service.id} (${name})` });
319
+ }
320
+ if (!instance) {
321
+ // Runtime applies CLI redirects last; their ${url} is always the HTTP listener.
322
+ for (const [name, template] of Object.entries(config?.cliRedirect ?? {})) {
323
+ bindings.set(name, { protocol: template.includes('${url}') || /^https?:/.test(template) ? 'http' : null, label: `${service.id} (${name}; cliRedirect)` });
324
+ }
325
+ for (const mapping of config?.external?.discover ?? []) {
326
+ bindings.set(mapping.as, { protocol: null, label: `${service.id} (${mapping.as}; discover after up)` });
327
+ }
328
+ }
329
+ }
330
+ return bindings;
331
+ }
332
+ function addServiceIds(ids, service) {
333
+ const type = service.type ?? 'twin';
334
+ const packNames = servicePackNames(service);
335
+ // How would traffic actually reach this twin?
336
+ // • the injector/ambient proxy intercepts its vendor hosts — true when its injectEnv is a
337
+ // `*_TWIN_URL` var the injector reads, or its service id / pack name IS an injector
338
+ // vendor key (the consolidated aws twin's `s3` service id, for example);
339
+ // • OR the app/CLI reads its endpoint env directly — external services (discovered env),
340
+ // injectEnvTemplates (LIVEKIT_URL=…), cliRedirect, or a non-`*_TWIN_URL` injectEnv.
341
+ // A twin service with NEITHER is present but unreachable: interceptable=false.
342
+ const vendorKeys = new Set([...injectableVendorKeys()].map(normalizeId));
343
+ const envKeys = Object.keys(service.env ?? {}); // instance-path evidence of the same wiring
344
+ const injectorReads = (service.injectEnv !== undefined && twinUrlVendorFor(service.injectEnv) !== null) ||
345
+ envKeys.some((key) => twinUrlVendorFor(key) !== null);
346
+ const appConfigured = type === 'external' ||
347
+ service.cliRedirect !== undefined ||
348
+ Object.keys(service.injectEnvTemplates ?? {}).length > 0 ||
349
+ (service.injectEnv !== undefined && !/_TWIN_URL$/.test(service.injectEnv)) ||
350
+ envKeys.some((key) => !/_TWIN_URL$/.test(key));
351
+ const hostMatched = vendorKeys.has(normalizeId(service.id)) || packNames.some((pack) => vendorKeys.has(normalizeId(pack)));
352
+ const endpointValues = [...Object.values(service.env ?? {}), ...Object.values(service.injectEnvTemplates ?? {})];
353
+ // Native infrastructure can share a vendor name with an HTTP pack. Identity alone must not
354
+ // turn its database listener into that vendor's HTTP API (e.g. PlanetScale's two clients).
355
+ const vendor = Object.keys(packFacts()).find((name) => worldIdsFor(name).includes(normalizeId(service.id)));
356
+ const facts = vendor === undefined ? undefined : packFacts()[vendor];
357
+ const httpNames = new Set([
358
+ ...(vendor === undefined ? [] : injectorVendorKeysFor(vendor).map(injectorEnvNameForKey)),
359
+ ...(facts?.endpointEnv ? [facts.endpointEnv.name, ...Object.keys(facts.endpointEnv.templates ?? {})] : []),
360
+ ]);
361
+ const vendorHttpEndpoint = Object.entries({ ...(service.env ?? {}), ...(service.injectEnvTemplates ?? {}) })
362
+ .some(([name, value]) => httpNames.has(name) && (/^(?:https?|wss?):/.test(value) || value === '${url}'));
363
+ const nativeOnly = packNames.length === 0 && endpointValues.some((value) => connectionProtocol(value) !== null) &&
364
+ !vendorHttpEndpoint;
365
+ const nativeMode = packNames.some((name) => {
366
+ const mode = packFacts()[name]?.nativeTransport;
367
+ return mode && [...(Array.isArray(service.command) ? service.command : []), ...(service.args ?? [])].includes(mode.flag);
368
+ });
369
+ const interceptable = !nativeOnly && !nativeMode && (injectorReads || appConfigured || hostMatched);
370
+ // twin + external services cover their vendor by identity; a plain 'process'
371
+ // service only counts via an explicit twin marker (injectEnv/*_TWIN_URL, pack path).
372
+ if (type === 'twin' || type === 'external') {
373
+ const label = type === 'twin' ? `${service.id} (twin service)` : `${service.id} (external service)`;
374
+ addId(ids, normalizeId(service.id), label, interceptable);
375
+ }
376
+ if (service.injectEnv) {
377
+ const stem = service.injectEnv.match(/^(.+)_TWIN_URL$/);
378
+ if (stem)
379
+ addId(ids, normalizeId(stem[1]), `${service.id} (${service.injectEnv})`, interceptable);
380
+ }
381
+ for (const pack of packNames) {
382
+ addId(ids, normalizeId(pack), `${service.id} (pack ${pack})`, interceptable);
383
+ }
384
+ }
385
+ /** The twins actually IN the named world: instance first (the booted truth), stable
386
+ * config as the pre-boot fallback. Throws loudly when the world resolves to neither. */
387
+ export function worldTwinInventory(world, root) {
388
+ let instanceError;
389
+ try {
390
+ const status = statusWorld(world, root, { readOnly: true });
391
+ const ids = new Map();
392
+ for (const service of Object.values(status.services))
393
+ addServiceIds(ids, service);
394
+ for (const key of Object.keys(status.env)) {
395
+ const stem = key.match(/^(.+)_TWIN_URL$/);
396
+ // a bare env `*_TWIN_URL` var is only a REACHABLE twin when the injector reads it
397
+ if (stem)
398
+ addId(ids, normalizeId(stem[1]), `env ${key}`, twinUrlVendorFor(key) !== null);
399
+ }
400
+ return { source: 'instance', path: `${status.dirs.instance}/instance.json`, ids, connections: serviceConnections(Object.values(status.services), status.env), selection: status.selection, declared: declaredTwin(Object.values(status.services)) };
401
+ }
402
+ catch (error) {
403
+ instanceError = error instanceof Error ? error.message : String(error);
404
+ }
405
+ try {
406
+ const { path, config } = loadWorldConfig(world, root);
407
+ const ids = new Map();
408
+ for (const service of config.services)
409
+ addServiceIds(ids, service);
410
+ return { source: 'config', path, ids, connections: serviceConnections(config.services), selection: config.selection, declared: declaredTwin(config.services) };
411
+ }
412
+ catch (error) {
413
+ const configError = error instanceof Error ? error.message : String(error);
414
+ throw new Error(`volter-world covers: world "${world}" not found as an instance or a config.\n` +
415
+ ` instance: ${instanceError}\n config: ${configError}`);
416
+ }
417
+ }
418
+ function evidenceUsage(vendor, evidence) {
419
+ if (evidence.startsWith('dependency-tool:') || evidence.startsWith('registry:') || (vendor === 'npm-registry' && evidence === 'env: NPM_TOKEN'))
420
+ return 'dependencies';
421
+ if (evidence.startsWith('build-tool:'))
422
+ return 'build';
423
+ if (evidence.startsWith('deployment-tool:'))
424
+ return 'deployment';
425
+ return 'application';
426
+ }
427
+ function usesOf(signals) {
428
+ return new Map([...signals].map(([vendor, evidence]) => {
429
+ const toolUsages = [...new Set(evidence
430
+ .filter((item) => item.startsWith('build-tool:') || item.startsWith('deployment-tool:'))
431
+ .map((item) => evidenceUsage(vendor, item)))];
432
+ const hasApplicationEvidence = evidence.some((item) => !item.startsWith('env:') && evidenceUsage(vendor, item) === 'application');
433
+ return [vendor, evidence.flatMap((item) => {
434
+ // A credential beside a declared vendor tool supports that tool unless the repo also has
435
+ // independent application evidence. The credential name alone cannot turn Wrangler into
436
+ // an application dependency; an SDK/fetch signal still records the application use too.
437
+ if (item.startsWith('env:') && !hasApplicationEvidence && toolUsages.length) {
438
+ return toolUsages.map((usage) => ({ usage, evidence: item }));
439
+ }
440
+ return [{ usage: evidenceUsage(vendor, item), evidence: item }];
441
+ })];
442
+ }));
443
+ }
444
+ /** Detect every vendor (and every unmapped external-service-shaped signal) an application repo
445
+ * betrays: mapped npm dependencies across the root + all workspace members, vendor-shaped env
446
+ * names in committed `.env*` files, the SMTP/raw-protocol signal, and literal vendor URLs passed
447
+ * directly to fetch in production source. URL attribution reads the injector's VENDOR_HOSTS table
448
+ * rather than re-encoding host rules here. Insertion order follows the sorted dependency, env-name,
449
+ * and source lists, so callers that preserve it are deterministic. */
450
+ export function detectRepoVendors(repoPath) {
451
+ const repo = resolve(repoPath);
452
+ // the platform's own packages — the twins an app installed, the command, the runtime — are
453
+ // never a vendor signal: an app that installed @volter/twin-slack did not gain a Slack dependency
454
+ const dependencies = projectDependencies(repo).filter((dep) => !dep.startsWith('@volter/') && !dep.startsWith('pypi:volter'));
455
+ const envNames = projectEnvNames(repo);
456
+ const npmRegistries = projectNpmRegistries(repo);
457
+ // vendor → detection sources
458
+ const detected = new Map();
459
+ const mappedDeps = new Set();
460
+ const tools = new Map();
461
+ for (const [vendor, facts] of Object.entries(packFacts())) {
462
+ for (const tool of facts.adoption?.tools ?? [])
463
+ tools.set(tool.package, { vendor, usage: tool.usage });
464
+ }
465
+ for (const dependency of dependencies) {
466
+ const tool = tools.get(dependency);
467
+ if (tool !== undefined) {
468
+ mappedDeps.add(dependency);
469
+ detected.set(tool.vendor, [...(detected.get(tool.vendor) ?? []), `${tool.usage}-tool: npm: ${dependency}`]);
470
+ continue;
471
+ }
472
+ // `pypi:<name>` is the Python half (project-inspect's PYPI_TWINS, descriptors' adoption.pypi).
473
+ const py = dependency.startsWith('pypi:') ? dependency.slice(5) : undefined;
474
+ const vendor = py !== undefined ? PYPI_TWINS[py]?.vendor : (SDK_TWINS[dependency]?.vendor ?? scopeVendorFor(dependency));
475
+ if (vendor === undefined)
476
+ continue;
477
+ mappedDeps.add(dependency);
478
+ detected.set(vendor, [...(detected.get(vendor) ?? []), py !== undefined ? `pypi: ${py}` : `npm: ${dependency}`]);
479
+ }
480
+ for (const name of envNames) {
481
+ if (/_TWIN_URL$/.test(name) || /^VOLTER_/.test(name))
482
+ continue;
483
+ // The raw-protocol mail path. This used to fall through to `unknown` because no SMTP twin
484
+ // existed, which was the honest answer THEN; the `smtp` pack now twins it, so the same signal
485
+ // is a DETECTED vendor and a world carrying an smtp service covers it. (The env NAME is the
486
+ // whole interception story here — there is no host for the injector to rewrite, so
487
+ // SMTP_HOST/EMAIL_SERVER_HOST pointed at the twin's listener is what redirects the app.)
488
+ if (isSmtpEnvSignal(name)) {
489
+ // NB this sits BEFORE the stem lookup, so a vendor-NAMED mail host (`POSTMARK_SMTP_HOST`,
490
+ // `MAILGUN_SMTP_HOST`) attributes to the `smtp` twin rather than to that vendor's HTTP pack.
491
+ // Deliberate: the var names an SMTP endpoint, and pointing it at the twin's listener is what
492
+ // actually redirects the mail — postmark's REST twin would not receive it.
493
+ detected.set('smtp', [...(detected.get('smtp') ?? []), `env: ${name}`]);
494
+ continue;
495
+ }
496
+ const stem = envVendorStem(name, ENV_SUFFIX_BROAD);
497
+ const vendor = stem === null ? undefined : ENV_STEM_VENDORS[stem];
498
+ if (vendor)
499
+ detected.set(vendor, [...(detected.get(vendor) ?? []), `env: ${name}`]);
500
+ }
501
+ // The npm registry injector deliberately owns only npmjs.org and GitHub Packages. A custom
502
+ // configured registry is a distinct uncovered destination even when NPM_TOKEN also detects the
503
+ // official npm twin; treating the credential as coverage would silently send private-registry
504
+ // traffic to the real network.
505
+ const officialNpmHosts = new Set(['registry.npmjs.org', 'npm.pkg.github.com']);
506
+ for (const registry of npmRegistries) {
507
+ const hostname = new URL(registry.url).hostname.toLowerCase();
508
+ if (officialNpmHosts.has(hostname)) {
509
+ detected.set('npm-registry', [...(detected.get('npm-registry') ?? []), `registry: ${registry.source} -> ${hostname}`]);
510
+ }
511
+ }
512
+ // Raw-fetch vendor discovery. A hand-written client has no SDK dependency, and its credential
513
+ // may use a project-specific name; the literal destination is the one unambiguous signal left.
514
+ // Match it through the injector's OWN predicates, including pathname-aware shared hosts.
515
+ const knownVendorById = new Map();
516
+ for (const vendor of [
517
+ ...Object.values(SDK_TWINS).map((entry) => entry.vendor),
518
+ ...Object.values(ENV_STEM_VENDORS),
519
+ ...Object.keys(VENDOR_WORLD_IDS),
520
+ ])
521
+ knownVendorById.set(normalizeId(vendor), vendor);
522
+ const canonicalVendor = (injectorKey) => {
523
+ const key = normalizeId(injectorKey);
524
+ for (const [vendor, ids] of Object.entries(VENDOR_WORLD_IDS)) {
525
+ if (ids.includes(key))
526
+ return vendor;
527
+ }
528
+ return knownVendorById.get(key) ?? null;
529
+ };
530
+ const vendorHosts = loadInject().VENDOR_HOSTS;
531
+ for (const signal of projectFetchUrls(repo)) {
532
+ const url = new URL(signal.url);
533
+ const vendors = new Set();
534
+ for (const [injectorKey, matches] of Object.entries(vendorHosts)) {
535
+ if (!matches(url.hostname, url.pathname))
536
+ continue;
537
+ const vendor = canonicalVendor(injectorKey);
538
+ if (vendor !== null)
539
+ vendors.add(vendor);
540
+ }
541
+ for (const vendor of [...vendors].sort()) {
542
+ const source = `fetch: ${signal.source} -> ${url.hostname}`;
543
+ detected.set(vendor, [...(detected.get(vendor) ?? []), source]);
544
+ }
545
+ }
546
+ // unknown external-service-shaped signals (no vendor mapping). A dep from a scope with a
547
+ // twin-mapped sibling counts even when its own name is not service-shaped — the Dub run's
548
+ // @upstash/redis + @upstash/ratelimit disappeared exactly here while @upstash/qstash mapped.
549
+ const unknown = new Map();
550
+ for (const registry of npmRegistries) {
551
+ const hostname = new URL(registry.url).hostname.toLowerCase();
552
+ if (officialNpmHosts.has(hostname))
553
+ continue;
554
+ const id = `npm-registry@${hostname}`;
555
+ unknown.set(id, [...(unknown.get(id) ?? []), `registry: ${registry.source} -> ${hostname}`]);
556
+ }
557
+ for (const dependency of dependencies) {
558
+ if (mappedDeps.has(dependency))
559
+ continue;
560
+ if (isExternalServiceShapedDep(dependency) || isMappedScopeSibling(dependency)) {
561
+ unknown.set(dependency, [...(unknown.get(dependency) ?? []), `npm: ${dependency}`]);
562
+ }
563
+ }
564
+ for (const name of envNames) {
565
+ if (/_TWIN_URL$/.test(name) || /^VOLTER_/.test(name))
566
+ continue;
567
+ if (isSmtpEnvSignal(name))
568
+ continue; // now a DETECTED vendor (above), no longer an unknown
569
+ const stem = envVendorStem(name, ENV_SUFFIX_STRICT);
570
+ if (stem === null || ENV_STEM_VENDORS[stem] || ENV_STEM_IGNORE.has(stem))
571
+ continue;
572
+ // The registry's notExternal suppressions apply to ENV STEMS too — the census adjudicated
573
+ // 40 app-internal stems ("names no vendor") that this loop kept flagging because only the
574
+ // dep-side checks consulted the registry (§9 round two H4, 2026-08-31: 17 of 18 formally
575
+ // dead stems still failed the proof).
576
+ if (knownExternalServices().notExternal[stem] !== undefined)
577
+ continue;
578
+ unknown.set(stem, [...(unknown.get(stem) ?? []), `env: ${name}`]);
579
+ }
580
+ return { detected, unknown, uses: usesOf(detected), unknownUses: usesOf(unknown) };
581
+ }
582
+ export function coverWorld(world, repoPath, options = {}) {
583
+ const root = resolve(options.root ?? process.cwd());
584
+ const allowUnknown = options.allowUnknown ?? false;
585
+ const acknowledge = options.acknowledge ?? {};
586
+ for (const [vendor, reason] of Object.entries(acknowledge)) {
587
+ if (!reason.trim())
588
+ throw new Error(`covers: --acknowledge ${vendor} requires a non-empty reason (vendor=reason)`);
589
+ }
590
+ const repo = resolve(repoPath);
591
+ const inventory = worldTwinInventory(world, root);
592
+ const { detected, unknown: unknownSources, uses, unknownUses } = detectRepoVendors(repo);
593
+ // Does ANY injector vendor key exist for this vendor? Decides whether an UNINTERCEPTABLE
594
+ // finding is acknowledgeable: a key that exists means the twin COULD be intercepted and the
595
+ // wiring is merely wrong (fix it, don't acknowledge it); no key at all means the pack is a
596
+ // stated vendor-hosts allowlist gap (explicit-endpoint wiring only) and a world that still
597
+ // declares the twin may honestly acknowledge the finding rather than delete the twin.
598
+ const injectorKeysFor = injectorVendorKeysFor;
599
+ const rows = [];
600
+ const covered = [];
601
+ const acknowledged = [];
602
+ const missing = [];
603
+ const uninterceptable = [];
604
+ const excluded = [];
605
+ for (const [vendor, sources] of [...detected.entries()].sort(([a], [b]) => a.localeCompare(b))) {
606
+ const selected = inventory.selection === undefined || (uses.get(vendor) ?? []).some((use) => worldSelectionIncludes(inventory.selection, vendor, use.usage));
607
+ if (!selected) {
608
+ excluded.push(vendor);
609
+ rows.push({ vendor, detectedVia: sources, twinInWorld: null, status: 'excluded' });
610
+ continue;
611
+ }
612
+ // a vendor is only COVERED by a twin traffic can actually reach; a present-but-unreachable
613
+ // twin (no injector vendor, no app-read endpoint env) is a distinct, failing status.
614
+ const candidates = worldIdsFor(vendor).filter((id) => inventory.ids.has(id));
615
+ const matchId = candidates.find((id) => inventory.ids.get(id).interceptable) ?? candidates[0];
616
+ const match = matchId === undefined ? null : inventory.ids.get(matchId);
617
+ let status = match === null ? 'missing' : match.interceptable ? 'covered' : 'uninterceptable';
618
+ let ackRefused;
619
+ if (acknowledge[vendor] !== undefined) {
620
+ if (status === 'missing')
621
+ status = 'acknowledged';
622
+ else if (status === 'uninterceptable') {
623
+ const keys = injectorKeysFor(vendor);
624
+ if (keys.length === 0)
625
+ status = 'acknowledged';
626
+ else {
627
+ ackRefused =
628
+ `acknowledge REFUSED: injector key(s) ${keys.join('/')} exist for this vendor — the twin CAN be intercepted, ` +
629
+ `the wiring is merely wrong. Fix it (${keys.map(injectorEnvNameForKey).join(' / ')}) instead of acknowledging`;
630
+ }
631
+ }
632
+ }
633
+ if (status === 'covered')
634
+ covered.push(vendor);
635
+ else if (status === 'missing')
636
+ missing.push(vendor);
637
+ else if (status === 'acknowledged')
638
+ acknowledged.push({ vendor, reason: acknowledge[vendor] });
639
+ else
640
+ uninterceptable.push(vendor);
641
+ rows.push({
642
+ vendor,
643
+ ...(packFacts()[vendor]?.transport ? { connection: packFacts()[vendor].transport } : {}),
644
+ detectedVia: status === 'acknowledged'
645
+ ? [...sources, `acknowledged: ${acknowledge[vendor]}`]
646
+ : ackRefused !== undefined
647
+ ? [...sources, ackRefused]
648
+ : sources,
649
+ twinInWorld: match === null ? null : match.label,
650
+ status,
651
+ });
652
+ }
653
+ const unknown = [];
654
+ for (const [name, sources] of [...unknownSources.entries()].sort(([a], [b]) => a.localeCompare(b))) {
655
+ const selected = inventory.selection === undefined || (unknownUses.get(name) ?? []).some((use) => worldSelectionIncludes(inventory.selection, name, use.usage));
656
+ if (!selected) {
657
+ excluded.push(name);
658
+ rows.push({ vendor: name, detectedVia: sources, twinInWorld: null, status: 'excluded' });
659
+ continue;
660
+ }
661
+ if (acknowledge[name] !== undefined) {
662
+ acknowledged.push({ vendor: name, reason: acknowledge[name] });
663
+ rows.push({ vendor: name, detectedVia: [...sources, `acknowledged: ${acknowledge[name]}`], twinInWorld: null, status: 'acknowledged' });
664
+ continue;
665
+ }
666
+ // STANDING acknowledgments from the packless-vendor registry: a signal the census rulings
667
+ // adjudicated acceptable (config-time adapters, per-repo-judged surfaces) acknowledges
668
+ // itself with the RECORDED reason — the same status a per-run `--acknowledge` grants, but
669
+ // sourced from committed adjudication instead of a flag. Demanded registry entries fall
670
+ // through to unknown-sdk on purpose: known-but-packless is still an uncovered vendor.
671
+ const standing = registryAcknowledgedReason(name);
672
+ if (standing !== null) {
673
+ acknowledged.push({ vendor: name, reason: standing });
674
+ rows.push({ vendor: name, detectedVia: [...sources, `acknowledged (registry): ${standing}`], twinInWorld: null, status: 'acknowledged' });
675
+ continue;
676
+ }
677
+ unknown.push(name);
678
+ rows.push({ vendor: name, detectedVia: sources, twinInWorld: null, status: 'unknown-sdk' });
679
+ }
680
+ for (const [vendor, declared] of [...inventory.declared].sort(([a], [b]) => a.localeCompare(b))) {
681
+ // Selection can exclude a detected use from automatic coverage obligations, but it cannot
682
+ // hide broken routing for a service the operator explicitly declared. Keep the excluded row
683
+ // and add the declaration's independent routing verdict.
684
+ if (rows.some((row) => row.vendor === vendor && row.status !== 'excluded'))
685
+ continue;
686
+ const status = declared.interceptable ? 'covered' : 'uninterceptable';
687
+ if (status === 'covered')
688
+ covered.push(vendor);
689
+ else
690
+ uninterceptable.push(vendor);
691
+ rows.push({ vendor, detectedVia: ['declared in world'], twinInWorld: declared.label, status });
692
+ }
693
+ for (const requirement of projectConnections(repo, new Set([...detected.values(), ...unknownSources.values()].flat().filter((source) => source.startsWith('env: ')).map((source) => source.slice(5))))) {
694
+ const vendor = `${requirement.protocol ?? 'unknown-connection'}:${requirement.env ?? requirement.sources[0]}`;
695
+ if (inventory.selection !== undefined && !worldSelectionIncludes(inventory.selection, vendor, 'application')) {
696
+ excluded.push(vendor);
697
+ rows.push({
698
+ vendor,
699
+ connection: requirement.protocol ?? 'unknown',
700
+ detectedVia: requirement.sources,
701
+ twinInWorld: null,
702
+ status: 'excluded',
703
+ });
704
+ continue;
705
+ }
706
+ const binding = requirement.env === undefined ? undefined : inventory.connections.get(requirement.env);
707
+ // An HTTP-valued endpoint variable the world injects from a twin (its endpoint env or a
708
+ // template) is routed to that twin; only an HTTP URL nobody in the world supplies stays unknown.
709
+ const injectedHttp = requirement.protocol === null && binding?.protocol === 'http';
710
+ const unresolved = !injectedHttp && (requirement.protocol === null || requirement.env === undefined || (binding !== undefined && binding.protocol === null));
711
+ let status = unresolved ? 'unknown-sdk'
712
+ : injectedHttp || binding?.protocol === requirement.protocol ? 'covered' : 'missing';
713
+ if (status !== 'covered' && acknowledge[vendor] !== undefined) {
714
+ status = 'acknowledged';
715
+ acknowledged.push({ vendor, reason: acknowledge[vendor] });
716
+ }
717
+ else if (status === 'covered')
718
+ covered.push(vendor);
719
+ else if (status === 'missing')
720
+ missing.push(vendor);
721
+ else
722
+ unknown.push(vendor);
723
+ rows.push({
724
+ vendor,
725
+ connection: requirement.protocol ?? 'unknown',
726
+ detectedVia: [...requirement.sources, ...(status === 'acknowledged' ? [`acknowledged: ${acknowledge[vendor]}`] : [])],
727
+ twinInWorld: binding?.label ?? null,
728
+ status,
729
+ });
730
+ }
731
+ return {
732
+ world,
733
+ worldSource: inventory.source,
734
+ worldPath: inventory.path,
735
+ repo,
736
+ twinsInWorld: [...inventory.ids.values()].map((id) => id.label).sort(),
737
+ rows,
738
+ covered,
739
+ missing,
740
+ uninterceptable,
741
+ unknown,
742
+ acknowledged,
743
+ excluded,
744
+ allowUnknown,
745
+ ok: missing.length === 0 && uninterceptable.length === 0 && (allowUnknown || unknown.length === 0),
746
+ };
747
+ }
748
+ export function formatCoverageReport(report) {
749
+ const lines = [];
750
+ lines.push(`World ${report.world} (${report.worldSource}: ${report.worldPath})`);
751
+ lines.push(`Repo ${report.repo}`);
752
+ lines.push('');
753
+ const header = ['vendor / connection', 'transport', 'detected-via', 'twin-in-world', 'status'];
754
+ const cells = report.rows.map((row) => [
755
+ row.vendor,
756
+ row.connection ?? '-',
757
+ row.detectedVia.join('; '),
758
+ row.twinInWorld ?? '-',
759
+ row.status === 'missing' ? 'MISSING' : row.status === 'uninterceptable' ? 'UNINTERCEPTABLE' : row.status,
760
+ ]);
761
+ const widths = header.map((title, column) => Math.max(title.length, ...cells.map((row) => row[column].length)));
762
+ const renderRow = (row) => row.map((cell, column) => cell.padEnd(widths[column])).join(' | ').trimEnd();
763
+ lines.push(renderRow(header));
764
+ lines.push(widths.map((width) => '-'.repeat(width)).join('-|-'));
765
+ for (const row of cells)
766
+ lines.push(renderRow(row));
767
+ if (report.rows.length === 0)
768
+ lines.push('(no external vendor dependencies detected in the repo)');
769
+ lines.push('');
770
+ if (report.missing.length > 0) {
771
+ lines.push(`NOT COVERED: ${report.missing.length} vendor(s) with no twin in world ${report.world}: ${report.missing.join(', ')}`);
772
+ lines.push(` add the missing twin service(s) to the world config, then re-run the proof.`);
773
+ }
774
+ if (report.uninterceptable.length > 0) {
775
+ lines.push(`UNINTERCEPTABLE: ${report.uninterceptable.length} vendor(s) have a twin in world ${report.world} that no traffic can reach: ${report.uninterceptable.join(', ')}`);
776
+ lines.push(` the twin is present, but its service resolves to no injector vendor (VENDOR_HOSTS in @volter/world-core/inject) and exposes no app-read endpoint env — SDK calls would go to the REAL vendor. Use the injector's vendor keys for injectEnv (e.g. S3_TWIN_URL, not AWS_TWIN_URL) or wire explicit endpoint env, then re-run the proof. If NO injector key exists for the vendor at all (a stated vendor-hosts allowlist gap), --acknowledge vendor=reason is accepted; when a key exists, acknowledgment is refused — fix the wiring.`);
777
+ }
778
+ if (report.unknown.length > 0) {
779
+ const verdict = report.allowUnknown ? 'WARNING (allowed by --allow-unknown)' : 'UNKNOWN-SDK: fails the proof';
780
+ lines.push(`${verdict}: ${report.unknown.length} external-service-shaped signal(s) with unresolved SDK/connection mapping: ${report.unknown.join(', ')}`);
781
+ if (!report.allowUnknown)
782
+ lines.push(' resolve the reported SDK/connection and its endpoint wiring; boot the World to resolve external.discover bindings. --allow-unknown explicitly accepts remaining unknowns.');
783
+ }
784
+ // Acknowledgments are part of the verdict, not fine print: a proof that passed WITH
785
+ // acknowledged rows must say so (and how many were standing/registry vs per-run flags) —
786
+ // "COVERED" alone would overstate a world whose acknowledged vendors have no twin at all
787
+ // (§9 round two M1, 2026-08-31: CoverageReport.acknowledged had no production consumer).
788
+ if (report.acknowledged.length > 0) {
789
+ lines.push(`ACKNOWLEDGED: ${report.acknowledged.length} signal(s) accepted with recorded reasons (no twin): ${report.acknowledged.map((a) => a.vendor).join(', ')}`);
790
+ }
791
+ if (report.excluded.length > 0)
792
+ lines.push(`EXCLUDED BY SELECTION: ${report.excluded.join(', ')} (not covered; no access granted).`);
793
+ lines.push(report.ok
794
+ ? report.acknowledged.length > 0
795
+ ? `COVERED (with ${report.acknowledged.length} acknowledgment(s)): every other detected vendor has a twin in world ${report.world}.`
796
+ : `COVERED: every detected vendor has a twin in world ${report.world}.`
797
+ : `PROOF FAILED for world ${report.world}.`);
798
+ lines.push('Static connection check only; run an app scenario to verify behavior and shared state.');
799
+ return `${lines.join('\n')}\n`;
800
+ }