@teamscala/service-boot 0.23.522 → 0.23.524

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 (119) hide show
  1. package/package.json +1 -1
  2. package/src/agreement-gate/agreement-lookup.ts +0 -7
  3. package/src/agreement-gate/authorized-emails.ts +0 -7
  4. package/src/agreement-gate/placeholder-strip.ts +0 -8
  5. package/src/agreement-gate/resolution-caches.ts +0 -5
  6. package/src/agreement-gate/settings.ts +0 -7
  7. package/src/agreement-gate/signing-session.ts +0 -8
  8. package/src/agreement-gate/signing-shell.ts +0 -9
  9. package/src/agreement-gate.ts +0 -18
  10. package/src/apply-logger-config.ts +1 -9
  11. package/src/asset-cache-key.ts +1 -5
  12. package/src/asset-optimize.ts +1 -7
  13. package/src/asset-proxy.ts +15 -9
  14. package/src/asset-social-card.ts +1 -9
  15. package/src/attach-agreement-magic.ts +0 -14
  16. package/src/attach-portal-members.ts +0 -17
  17. package/src/attach-proposal-accept.ts +0 -20
  18. package/src/attach-proposal-magic.ts +0 -12
  19. package/src/attach-transfers-upload.ts +0 -21
  20. package/src/auth/sign-out.ts +1 -42
  21. package/src/auth-gate/public-paths.ts +1 -10
  22. package/src/auth-gate.ts +1 -22
  23. package/src/bind-page-handler.ts +1 -7
  24. package/src/build-client-assets/build-lock.ts +1 -11
  25. package/src/build-client-assets/hashed-output.ts +1 -11
  26. package/src/build-client-assets/hydrate-bundle.ts +1 -11
  27. package/src/build-client-assets/manifest.ts +1 -12
  28. package/src/build-client-assets/source-candidates.ts +0 -17
  29. package/src/build-client-assets/sourcemaps-upload.ts +1 -10
  30. package/src/build-client-assets/styles-build.ts +1 -21
  31. package/src/build-client-assets.ts +1 -30
  32. package/src/capability-slug.ts +1 -35
  33. package/src/compose-page-title.ts +1 -25
  34. package/src/content-gate/access.ts +0 -8
  35. package/src/content-gate/catalog.ts +0 -23
  36. package/src/content-gate/config-types.ts +0 -6
  37. package/src/content-gate/gate-entry-clears.ts +0 -18
  38. package/src/content-gate/gate.ts +0 -7
  39. package/src/content-gate/parse-cookie.ts +0 -4
  40. package/src/content-gate/token.ts +0 -22
  41. package/src/content-page-floor.ts +0 -17
  42. package/src/csrf-protection.ts +1 -28
  43. package/src/error-page-logo.ts +0 -23
  44. package/src/expand-roster.ts +1 -16
  45. package/src/gate-ttls.ts +0 -12
  46. package/src/gpt-gate.ts +0 -18
  47. package/src/hono-error-handler.ts +0 -15
  48. package/src/install-harness.ts +1 -12
  49. package/src/install-logging.ts +1 -15
  50. package/src/is-react-element-type.ts +1 -11
  51. package/src/load-page-manifest.ts +1 -23
  52. package/src/load-service-install-row.ts +1 -6
  53. package/src/module-not-found.ts +0 -6
  54. package/src/mount-orpc-router.ts +0 -6
  55. package/src/mount-server-packages.ts +1 -8
  56. package/src/mount.ts +1 -4
  57. package/src/mutable-app-ref.ts +0 -10
  58. package/src/orpc-openapi-document.ts +1 -13
  59. package/src/orpc-openapi-gate.ts +1 -13
  60. package/src/page-route-shell-types.ts +1 -8
  61. package/src/page-server-loader-graph.ts +0 -10
  62. package/src/page-server-orpc/hub-mcp-client.ts +1 -7
  63. package/src/page-server-orpc/server-client.ts +1 -23
  64. package/src/pg-boss-handler-registry.ts +1 -12
  65. package/src/pg-boss-mount.ts +1 -8
  66. package/src/proposal-authorized-emails.ts +0 -32
  67. package/src/proposal-gate.ts +0 -11
  68. package/src/quiz-gate.ts +0 -22
  69. package/src/read-html-template.ts +1 -49
  70. package/src/readiness-gate.ts +0 -8
  71. package/src/register-api-routes.ts +1 -11
  72. package/src/register-crash-handlers.ts +1 -6
  73. package/src/register-page-routes.ts +1 -12
  74. package/src/register-page-server-client.ts +1 -13
  75. package/src/register-registry-page-route/ai-chat-prefetch.ts +1 -6
  76. package/src/register-registry-page-route/apply-page-branding.ts +1 -10
  77. package/src/register-registry-page-route/captured-loader-config.ts +1 -11
  78. package/src/register-registry-page-route/graph-cache.ts +1 -21
  79. package/src/register-registry-page-route/live-loader-config.ts +1 -22
  80. package/src/register-registry-page-route/meta-title.ts +1 -12
  81. package/src/register-registry-page-route/page-data-shared.ts +1 -12
  82. package/src/register-registry-page-route/prefetch-org-id.ts +1 -7
  83. package/src/register-registry-page-route/proposal-approval-email-token.ts +1 -11
  84. package/src/register-registry-page-route/proposal-brand-stamping.ts +1 -11
  85. package/src/register-registry-page-route/public-shell-page-data.ts +1 -8
  86. package/src/register-registry-page-route/region-slots-page-data.ts +1 -8
  87. package/src/register-registry-page-route/resolve-page-data.ts +1 -10
  88. package/src/register-registry-page-route/resolve-panel-page.ts +1 -10
  89. package/src/register-registry-page-route/resolve-request-graph.ts +1 -11
  90. package/src/register-registry-page-route/run-content-gates.ts +1 -10
  91. package/src/register-registry-page-route/single-page-data.ts +1 -9
  92. package/src/register-registry-page-route/stamp-session-context.ts +1 -10
  93. package/src/register-registry-page-route/warn-uncovered-blocks.ts +1 -8
  94. package/src/register-registry-page-route.ts +1 -18
  95. package/src/register-static-assets.ts +1 -8
  96. package/src/register-ws-routes.ts +1 -21
  97. package/src/resolve-brand-favicon.ts +0 -13
  98. package/src/resolve-service-port.ts +1 -50
  99. package/src/resolve-theme-mode.ts +1 -22
  100. package/src/resolvers/app-header.ts +1 -19
  101. package/src/resolvers/fetch-org-brand.ts +1 -10
  102. package/src/resolvers/portal-route-paths.ts +1 -12
  103. package/src/resolvers/website-by-host-map.ts +1 -7
  104. package/src/resource-chat-inject.ts +0 -17
  105. package/src/resource-not-found.ts +0 -24
  106. package/src/run-boot-hook-list.ts +1 -13
  107. package/src/run-boot-hooks.ts +1 -19
  108. package/src/scala-tools-currency-watchdog.ts +1 -7
  109. package/src/security-headers.ts +1 -7
  110. package/src/select-service-routes.ts +1 -17
  111. package/src/server-error-page.ts +0 -18
  112. package/src/theme-fonts-link.ts +1 -16
  113. package/src/transfers/presigned-upload.ts +1 -27
  114. package/src/two-phase-full-app.ts +0 -6
  115. package/src/two-phase-minimal-app.ts +0 -6
  116. package/src/two-phase-readiness.ts +0 -6
  117. package/src/types.ts +0 -3
  118. package/src/ui-service.ts +1 -6
  119. package/src/ws-boot-self-test.ts +1 -14
@@ -1,35 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Builds the per-service client bundle and stylesheet into `dist/client/`
7
- * at UI-service boot time. Outputs are content-hashed so deployments
8
- * naturally cache-bust:
9
- *
10
- * src/entry-client.ts ──bun build──> dist/client/hydrate-<hash>.js
11
- * src/app.css ──tailwindcss──> dist/client/styles-<hash>.css
12
- * (and) dist/client/asset-manifest.json
13
- *
14
- * The HTML template references `{{stylesPath}}` / `{{hydratePath}}` —
15
- * read-html-template.ts substitutes from the manifest at template-read
16
- * time. Bytes-level changes → new hash → new URL → browser refetch.
17
- * Without this, `cache-control: immutable` on a fixed filename means
18
- * returning visitors keep their stale copy forever.
19
- *
20
- * This module is the ORCHESTRATION unit: host-wide lock → parallel
21
- * hydrate/CSS builds → manifest write (or failure tombstone). The
22
- * build runs on EVERY boot: the content-fingerprint skip cache and its
23
- * closure-walking hashers (input-fingerprint.ts, input-hashing.ts) are
24
- * deleted. A skip is the only path that can serve a stale bundle, and
25
- * it has done so twice (the mtime skip that baked stale bundles until
26
- * the fingerprint replaced it, 2026-06-14) — no skip is the one shape
27
- * that cannot skip wrongly, at the cost of a bounded per-boot build
28
- * serialised by the host-wide lock below. The individual concerns live
29
- * as siblings under ./build-client-assets/
30
- * (source-candidates, manifest, hashed-output, hydrate-bundle,
31
- * sourcemaps-upload, styles-build, build-lock)
32
- * — split when the parent held them all at 962 lines.
33
4
  */
34
5
 
35
6
  // Each build is wrapped in its own catch so a JS bundle failure (e.g. transient cross-package export drift) doesn't take down the service
@@ -1,40 +1,6 @@
1
1
  /**
2
- * @system ui-service-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Deployable slug -> capability slug.
7
- *
8
- * A DEPLOYABLE is named `<capability>-<language>-<tier>`
9
- * (`deployables-carry-language-and-tier`, as amended by the operator ruling of
10
- * 2026-09-08: "so actually the apps are ts, not bun" — the language segment
11
- * spells the LANGUAGE, so Bun-launched services are `ts`): `scala-projects-ts-dev`
12
- * and `scala-projects-ts-prod` are two DEPLOYMENTS of one APPLICATION. The
13
- * ecosystem generator derives that name per lane, so one service arrives here
14
- * under a DIFFERENT identity in each lane.
15
- *
16
- * A `page_route` row describes the APPLICATION url space, which is the same
17
- * in both lanes, so it carries the CAPABILITY name and there is exactly one
18
- * row per route rather than one per lane. Joining the two by raw string
19
- * equality therefore matches at most one lane, and does so SILENTLY:
20
- * measured 2026-09-11, 8 of 10 dev UI services registered ZERO page routes
21
- * and served 404 on every path -- including `/sign-in` -- while booting
22
- * clean and reporting healthy, because the dev lane ran as
23
- * `scala-projects-bun-dev` while every page_route row said `scala-projects`.
24
- *
25
- * Both sides normalize through this one function, so the join is
26
- * tier-agnostic by construction and a row may name either form.
27
- *
28
- * The suffix grammar is a CLOSED SET defined by doctrine and emitted by the
29
- * generator, so this is a parse of a documented contract rather than a
30
- * pattern heuristic. A slug that does not end in one is already a capability
31
- * name and is returned unchanged.
32
- *
33
- * `bun` REMAINS IN THE SET AS RETIRED SPELLING TOLERANCE, mirroring the
34
- * generator side (`ecosystem/deployable_identity.rs capability_of` strips
35
- * `-ts`, `-bun` and `-rust`): the fleet was renamed under `-bun` and rows or
36
- * supervised names still carrying it must converge to the same capability
37
- * rather than fail the join during the migration window.
38
4
  */
39
5
 
40
6
  /** Language segment of a deployable name. `bun` is the retired spelling. */
@@ -1,30 +1,6 @@
1
1
  /**
2
- * @system ui-service-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The single canonical page-title composer. Every SSR page handler routes
7
- * through this (portal/region-shell, public shell, single-page resource). A
8
- * page title is DERIVED from data that already exists — the website name +
9
- * the page/entity name — with `builda_pages.meta_title` as an explicit
10
- * per-page OVERRIDE only, never the primary source.
11
- *
12
- * Mirrors the brand-is-org-owned-full-scheme precedent: a value that should
13
- * be DERIVED was instead hand-set per row, and when unset it silently fell
14
- * back to something wrong. For themes the wrong fallback was the generic
15
- * light palette; for titles it was the bare org name — the `default`
16
- * page_meta_template `titleTemplate: "{org.name}"` produced "GBG Indo" with
17
- * no site or page context, and per-row `meta_title` carried stale product
18
- * literals like "Dashboard | scala Hub". The fix is identical in shape:
19
- * derive by default, override explicitly.
20
- *
21
- * Canonical format: `<website.name>: <subject>` (subject = entity title or
22
- * page name), site-first so a browser tab reads the surface then the page.
23
- * A resource surface (forms/quiz/gpt) may supply a curated `templateTitle`
24
- * (e.g. "{agent.name} — {org.name}"); it wins over the default derivation
25
- * but NOT over an explicit per-page override. The composer NEVER returns a
26
- * bare org name — that is the regression this closes, and the
27
- * `page-title-not-bare-org` guard backstops it at the template layer.
28
4
  */
29
5
 
30
6
  export interface TitleContext {
@@ -1,14 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The content-gate ACCESS endpoint mounter. One generic POST handler for every
7
- * gate: a plain <form> posts {<recordIdField>, email}; on a match against the
8
- * resource's authorized set it sets the signed access cookie and redirects back,
9
- * so the page loader's gate then releases the content. No bespoke endpoint per
10
- * type — each gate is one AccessConfig. Mounted BEFORE the auth gate so it is
11
- * publicly reachable.
12
4
  */
13
5
 
14
6
  import type { Hono } from "hono";
@@ -1,29 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * THE CATALOG OF GATED CONTENT TYPES — the one place a gate is registered.
7
- *
8
- * The gate ENGINE (gate.ts) and the access MOUNTER (access.ts) were already
9
- * generic and config-driven, but the SEQUENCE was not: `register-registry-page-
10
- * route.ts` named proposalGateConfig + agreementGateConfig + gptGateConfig in a
11
- * hand-written array, and `two-phase-boot.ts` named the same three AccessConfigs
12
- * in a second hand-written array. Two lists, one fact, and adding a fourth gated
13
- * resource meant editing both — the same drift as the assistant, one layer down
14
- * (`information-exists-in-one-location`).
15
- *
16
- * Now both call sites iterate THIS. A gate is registered once, as a pair, and
17
- * the SSR gate and its public access endpoint cannot disagree about which gates
18
- * exist — the shape doctrine asks for when behaviour genuinely is code:
19
- * `business-logic-is-rows-plus-executors`, "extend the executor catalog".
20
- *
21
- * WHY A CATALOG AND NOT A REGISTRY ROW. A gate is not data — `verifyToken`,
22
- * `resolveAuthorizedEmails` and the withholding rules are executable behaviour,
23
- * which the rule places in a typed executor, never in a row. What belongs in a
24
- * row is the SELECTION: which gate governs a given page. That is a `page_route`
25
- * field, and a row can only select from a catalog, so this file is the thing it
26
- * will select from — not a step on the way to it.
27
4
  */
28
5
 
29
6
  /**
@@ -1,12 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The config contracts that PROGRAM a content gate. Each gated content type
7
- * (agreement, proposal, …) is one ContentGateConfig + one AccessConfig — no
8
- * bespoke gate functions per type; the engine (gate.ts) and the access mounter
9
- * (access.ts) are the single shared primitive driven by these.
10
4
  */
11
5
 
12
6
  type Resolved = Record<string, unknown>;
@@ -1,24 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * REQUEST-SCOPED channel for gate-entry marker clears. A gate that declares an
7
- * entry marker (config.entryCookieName) consumes it on the render that the
8
- * access endpoint's own redirect produces; that render's response must clear
9
- * the marker, or the next load (a refresh) would still carry it and skip the
10
- * gate. The gate writes the name here; bindPageHandler — which owns the page
11
- * Response — drains it into Set-Cookie, so the marker lives exactly one view.
12
- *
13
- * THE CLEARS COME BACK AS THE CALL'S RETURN VALUE, never from a post-hoc
14
- * read: the store only exists inside the run() scope, so a reader called after
15
- * the awaited call has already left it and would see an empty list — which is
16
- * exactly how the first version of this file shipped a marker that was never
17
- * cleared (measured live 2026-09-14: the cookie survived the render).
18
- *
19
- * AsyncLocalStorage rather than a module-level map: request-scoped by
20
- * construction, no accumulated process state that a restart would silently
21
- * reset (in-memory-state-is-not-durable).
22
4
  */
23
5
  import { AsyncLocalStorage } from "node:async_hooks";
24
6
 
@@ -1,13 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The content-gate ENGINE. Runs in the page loader after the resource graph
7
- * resolves, BEFORE SSR. Orchestration only — the per-type behaviour (which
8
- * resource, who's authorized, what to seed, how to withhold) is supplied by a
9
- * ContentGateConfig, so agreements/proposals/future gates are configs, not new
10
- * functions. The single place that decides whether gated bytes reach the HTML.
11
4
  */
12
5
 
13
6
  /**
@@ -1,10 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Parse a single cookie value out of a raw Cookie header — shared by the
7
- * content-gate engine and any gate config.
8
4
  */
9
5
 
10
6
  export function parseCookie(
@@ -1,28 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The single signed, short-lived access token for EVERY content gate (agreement,
7
- * proposal, and any future gated content). A gate withholds page content
8
- * server-side until the request carries a valid token for that resource; this is
9
- * the only thing that releases the content into the SSR, so without it a gated
10
- * page is never readable/scrapable by a link holder.
11
- *
12
- * HMAC-SHA256 (Bun-native) over {c:cookieName, r:resourceId, e:email, x:exp},
13
- * plus the optional captured identity a lead-capture gate carries (n:name,
14
- * p:phone) so a returning visitor is fully known at SSR with NO client storage.
15
- * `cookieName` is bound INTO the payload so a token minted for one gate
16
- * (e.g. ag_token) can never satisfy another gate's check (e.g. prop_token).
17
- *
18
- * Secret resolver, in preference order: AUTH_SECRET → ENCRYPTION_KEY →
19
- * the tenant DB address via @teamscala/db's named-need resolver (first one
20
- * ≥16 chars). A gate signs AND verifies inside one
21
- * service, so it only needs a stable per-deployment secret; the fallback keeps a
22
- * gate working on a service that was never given AUTH_SECRET (e.g. the former
23
- * public-only proposals viewer) instead of failing closed and locking out every
24
- * authorized viewer. Any service that has AUTH_SECRET transparently prefers it.
25
- * Fail-closed: no usable secret yields null (no token, no access).
26
4
  */
27
5
 
28
6
  import type { GateSegmentation } from "./config-types.ts";
@@ -1,23 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Pure decision: does a CONTENT route (no resource_type) whose loader resolved
7
- * a `page` body source warrant the branded not-found floor? Extracted from
8
- * register-registry-page-route so the rule is unit-testable independent of the
9
- * Hono/DB/graph machinery that wraps it (the fix's correctness does not depend
10
- * on the publish — verify-before-done).
11
- *
12
- * Three outcomes:
13
- * - no page row (deleted/unmatched path, e.g. /roadmap) → dead-link 404 floor
14
- * - page row exists but zero content blocks → empty-page 200 floor
15
- * - otherwise → null (render normally)
16
- *
17
- * The "Vision Toolbar" leak this closes: a content catchall synthesized an empty
18
- * page for an unmatched path and served a cacheable 200; resource-not-found-is-
19
- * the-root-floor only fired for resource_type routes, so dead content links fell
20
- * through. See resource-not-found.ts for the floor + operator ruling 2026-07-21.
21
4
  */
22
5
  import { PrimaryResourceMissingError } from "./resource-not-found.ts";
23
6
 
@@ -1,33 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * csrf-protection.ts — platform CSRF middleware (`security-csrf-protection`).
7
- *
8
- * Hono's csrf() with a DERIVED trusted-origin set, never a hand-authored
9
- * allowlist: every trusted origin comes from ONE source per
10
- * `offered-is-derived` —
11
- * - the managed base domain (registry portal-domains) covers the platform's
12
- * own `*.scala.business` origins, INCLUDING the cross-service form POST
13
- * redirect chains CSP form-action allowlists (proposals → sign etc.), so
14
- * the CSRF origin set and the CSP form-action set cannot disagree about
15
- * what "one of ours" means;
16
- * - customer custom domains resolve through `isTrustedAuthOrigin`
17
- * (builda_websites lookup) — the SAME lookup better-auth's trustedOrigins
18
- * uses in @teamscala/auth, so a new custom domain is trusted everywhere at
19
- * once or nowhere.
20
- *
21
- * hono csrf() arms only on form-element content types (urlencoded, multipart,
22
- * text/plain) — browsers cannot produce a cross-site application/json request
23
- * from a form, so the JSON API surface (ORPC) is protected by cookie
24
- * SameSite=Lax + this middleware's text/plain coverage.
25
- *
26
- * Custom-domain lookups are cached per-hostname with a TTL: a POSITIVE result
27
- * is cached for the full TTL (a domain that stopped being ours keeps working
28
- * for at most the TTL), a NEGATIVE result for a short window (a brand-new
29
- * custom domain becomes trusted within seconds). The cache is a request
30
- * accelerator over a DB fact, never the source of truth.
31
4
  */
32
5
 
33
6
  import { getManagedBaseDomain } from "@teamscala/auth/auth-env-resolution";
@@ -1,29 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The error/holding page's brand logo: resolve the host org's dark-theme
7
- * wordmark from its install row and inline it as a data URI. Split from
8
- * resource-not-found.ts under single-purpose-file (the builder consumes a
9
- * logoUrl argument; only these resolvers touch storage and the registry).
10
- *
11
- * The host org's LOGO (wordmark) for the dark error page — the favicon is a
12
- * tab glyph, the wrong asset at page scale (operator-observed: a lone "h"
13
- * where the launcha wordmark belonged). Resolution: the service's own
14
- * install row names its owner (brand_org); the org's dark-theme logo (a
15
- * light-coloured mark, correct on the dark page) is preferred, light-theme
16
- * second. Every step skew-tolerant: any miss → null → the builder renders a
17
- * logo-less dark page.
18
- *
19
- * Returns the logo as an INLINE data URI (not a `/api/assets/` proxy URL) so
20
- * the branded holding/error page ships the logo IN the initial HTML bytes —
21
- * no second fetch, no post-paint pop-in, no dependency on the asset proxy the
22
- * degraded service is failing to serve (incident 2026-07-27: the logo on a
23
- * DEGRADED render-layer holding page appeared after the page painted). The
24
- * logo sibling of icons-render-ssr-inline. Size-capped: a pathological
25
- * (>MAX_INLINE_LOGO_BYTES) logo falls back to null rather than bloating the
26
- * HTML; the page renders logo-less, never broken.
27
4
  */
28
5
 
29
6
  import { getClient } from "@teamscala/object-storage/create/client/get-client";
@@ -1,21 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Server-side roster expansion — the executor that handles "render N of block X
7
- * per data row" so the page never needs a domain-specific panel/list block. A
8
- * block authored with `props.dataRepeat: "parties"` is a TEMPLATE; before SSR
9
- * this clones it once per roster item into its parent slot (top-level content or
10
- * any Container's `content` slot), then drops the marker. Each clone:
11
- * - sets `role` + `dataKey` to the item's key, so a prop-driven block (the
12
- * SignatureField cell) finds its own party with no per-clone wiring;
13
- * - substitutes `{{name}}` / `{{role}}` / `{{company}}` / `{{date}}` /
14
- * `{{status}}` placeholders in any string prop (so a recipient row is plain
15
- * `Container` + `Text` primitives — the copy lives on the page, not here).
16
- * `{{status}}` resolves from the template's own `signedLabel` /
17
- * `awaitingLabel` props (DB-driven copy), never hardcoded.
18
- * Generic over any roster; the agreement page passes its parties.
19
4
  */
20
5
 
21
6
  export interface RosterItem {
package/src/gate-ttls.ts CHANGED
@@ -1,18 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Cached readers for the content-gate access-token TTL config rows
7
- * (agreement-gate / proposal-gate / gpt-gate / quiz-gate). initGateTtls() runs once at boot
8
- * (two-phase-boot, before the gates mount) to populate the cache from the
9
- * registry rows; the sync getters (agTtlMs/propTtlMs/gptTtlMs) return the
10
- * cached value — or the former hardcoded default before init / on row-miss — so
11
- * the sync AccessConfig.ttlMs fields + sync hono magic-link handlers read them
12
- * without async. Replaces the per-file AG_TTL_MS / PROP_TTL_MS / GPT_TTL_MS
13
- * constants (config-constants: the registry row is the source, dedup'd across
14
- * each gate + its attach-* magic counterpart so token TTL and cookie maxAge
15
- * never drift).
16
4
  */
17
5
  import { loadRegistryConfig } from "@teamscala/db/registry/load-config";
18
6
  import * as v from "valibot";
package/src/gpt-gate.ts CHANGED
@@ -1,24 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The PUBLIC GPT identity gate, expressed as config for the content-gate
7
- * primitive (content-gate/). Unlike proposals/agreements this is NOT access
8
- * control — every email is a valid visitor (lead capture). Its whole job is to
9
- * make a returning visitor's identity known AT SSR so the GPT body renders the
10
- * real conversation on first paint instead of server-rendering the capture gate
11
- * and then swapping to the chat after hydration (the ssr-then-live-handoff +
12
- * "know the visitor at SSR first paint" violation: building-applications).
13
- *
14
- * Flow: the capture form POSTs {gptId, email} to /api/gpt-access (openCapture →
15
- * any email matches); the access endpoint sets a signed `gpt_token` cookie. On
16
- * the next request runContentGate verifies that cookie for this agent and seeds
17
- * resolved.initialVisitor — which the GPT body serverLoader (ssrGptSession) then
18
- * uses to SSR-restore the conversation, and GptBodyBlock uses to skip the gate.
19
- * onUnauthorized is a no-op: an un-gated (first-time) visitor sees the capture
20
- * form the block renders itself when no visitor is known — that is correct, not
21
- * a flash (they ARE anonymous), so there is nothing to withhold.
22
4
  */
23
5
 
24
6
  import type { AccessConfig, ContentGateConfig } from "./content-gate/config-types.ts";
@@ -1,21 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The single app.onError factory for every Hono app (UI + backend). Two error
7
- * classes, deliberately separate:
8
- * - HTTPException: a DELIBERATE status-bearing throw (e.g. hono bodyLimit's
9
- * 413). Preserve its status + message verbatim — buildErrorReport only knows
10
- * 500/503 and would erase a deliberate 413/401/etc.
11
- * - anything else: an UNEXPECTED error → buildErrorReport (errorId correlation,
12
- * transient-vs-bug classification, 503 + Retry-After when retryable, else 500).
13
- *
14
- * Resolves the apparent tension between `hono-global-error-handler` (HTTPException
15
- * → status + JSON) and `error-responses-via-error-report` (every error response
16
- * via buildErrorReport): the latter governs UNEXPECTED errors; HTTPException is
17
- * the documented deliberate-status carve-out. The handler is a consumer of
18
- * buildErrorReport, never a second classifier for the unexpected branch.
19
4
  */
20
5
 
21
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,17 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Install harness — sandbox-mounts each declared package and captures
7
- * registry.require() calls via AsyncLocalStorage. Validates all required
8
- * rows exist before returning. Throws HarnessInstallError with full
9
- * context on failure. Per Constitution Rule 2 step 3.
10
- *
11
- * Marked: HarnessInstallError is the error class
12
- * validatePackageDependencies (might) throw — they form one canonical
13
- * "install harness validation" unit (the validator + the failure-mode
14
- * class it produces).
15
4
  */
16
5
 
17
6
  import {
@@ -1,20 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * One-call install of the entire logging pipeline. After installLogging()
7
- * runs, every error in the service — process-level, HTTP middleware, Hono
8
- * handlers, Puck blocks, even third-party packages calling console.* — is
9
- * captured by @teamscala/logger and flows to Sentry/GlitchTip (prod) or
10
- * loga (dev) with full context. Application code never imports the logger
11
- * or wraps anything in try/catch for telemetry purposes.
12
- *
13
- * Two phases, two files (single-purpose-file: the guard counts them as two
14
- * export groups in one file): installLogging (early bootstrap, hardcoded
15
- * defaults) lives here; applyLoggerConfig (late-stage, DB-driven) lives in
16
- * apply-logger-config.ts. The service boot pipeline calls them in sequence
17
- * with the same serviceSlug.
18
4
  */
19
5
 
20
6
  import { ManagedCache } from "@teamscala/cache/cache";
@@ -1,16 +1,6 @@
1
1
  /**
2
- * @system ui-service-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Is a resolved module export something React can render?
7
- *
8
- * `typeof x === "function"` is NOT that question. memo(), forwardRef() and
9
- * lazy() all return OBJECTS carrying a `$$typeof` symbol, so a shape check on
10
- * `function` rejects three of React's own idioms as "not a component".
11
- * Measured 2026-09-01: scala-projects skipped PresenceAvatars for exactly this
12
- * reason — the block is `memo(function PresenceAvatarsBlock(...))` — and every
13
- * page rendering it came out blank with no error beyond one boot WARN.
14
4
  */
15
5
 
16
6
  export function isReactElementType(value: unknown): boolean {
@@ -1,28 +1,6 @@
1
1
  /**
2
- * @system ui-service-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Loads the service's GENERATED `src/page-manifest.ts` — the codegen output of
7
- * the `page-manifest` transform, emitted from `block_schema` rows. That file is
8
- * the single artefact describing which Puck components this service renders.
9
- *
10
- * This REPLACES `discover-blocks.ts`, a hand-written runtime scan that queried
11
- * block_schema at boot and dynamic-imported each row. Deleting it is the point.
12
- * The scan was introduced to survive one dead row, and it bought that by moving
13
- * a build-time, deterministic, generated fact into a runtime path with four
14
- * independent ways to silently resolve nothing: a registry read that answers
15
- * with an empty array instead of failing, an install probe that reports "not
16
- * installed" for installed packages, a `node_modules` lookup relative to
17
- * `process.cwd()`, and a per-block try/catch that swallows each failure into a
18
- * string nobody reads when the boot dies first. Measured 2026-09-03: 91 active
19
- * block_schema rows, a generated manifest carrying 63 blocks sitting on disk,
20
- * and the scan loading ZERO — for hours, across every UI service.
21
- *
22
- * Generation is the platform's answer to exactly this (`one-generator-pattern`,
23
- * `business-logic-is-rows-plus-executors`): a bad row fails codegen loudly, at
24
- * build time, with the file in front of you. It cannot present as a service
25
- * that boots and renders nothing.
26
4
  */
27
5
 
28
6
  import { join } from "node:path";
@@ -1,11 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Loads the service_install registry row for a given service slug.
7
- * Uses registry.require() per Constitution Rule 11.
8
- * Schema is owned by @teamscala/db-validation (Rule 7).
9
4
  */
10
5
 
11
6
  import { getCache } from "@teamscala/db/registry/loader/cache/get-cache";
@@ -1,12 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Classifies a failed dynamic import as "this candidate is absent" vs a real
7
- * error, for the server_package mount candidate chain. Extracted from
8
- * mount-server-packages so the classification is testable: as a closure it
9
- * could not be exercised, and it shipped wrong.
10
4
  */
11
5
 
12
6
  /** `@scope/name/sub/path.ts` -> `@scope/name`; `name/sub` -> `name`. */
@@ -1,12 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Mounts the composed ORPC app router (generated Prisma CRUD + custom fn routers)
7
- * at /api/rpc/* over HTTP via @orpc/server/fetch's RPCHandler.
8
- * Uses getAppRouter() from @teamscala/orpc/app-router which merges generated
9
- * routers plus the row-composed fn namespace (orpc-bundle from api_route rows).
10
4
  */
11
5
 
12
6
  import { join } from "node:path";
@@ -1,13 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Walks `service_install.server_packages[]`, dynamic-imports each, and calls
7
- * its `mount(ctx)` function. Server packages register handlers / boot hooks /
8
- * scheduled jobs / WS routes via the MountContext exposed in ctx; the
9
- * resulting MountRegistry is consumed by register-api-routes /
10
- * register-ws-routes / run-boot-hook-list / start-scheduled-jobs.
11
4
  */
12
5
 
13
6
  /**
package/src/mount.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Server-package mount for opt-in service-boot watchdogs.
7
4
  */
8
5
 
9
6
  import type { MountContext } from "@teamscala/os/runtime-contracts/mount-context";
@@ -1,16 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Mutable app reference for two-phase boot. Allows swapping the Hono app
7
- * atomically after building the full app in the background. The initial app
8
- * is a minimal tool-only surface (ORPC, data routes) that starts serving
9
- * immediately; the full app (pages, hydration) is built off-to-the-side and
10
- * swapped in once complete.
11
- *
12
- * Thread-safety: Bun is single-threaded, so a simple module-level variable
13
- * is sufficient. Atomics are not needed.
14
4
  */
15
5
 
16
6
  import type { Hono } from "hono";
@@ -1,18 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
- * @status handwritten — none derivable: the title and the server URL are the MOUNT's facts (the slug it was handed, the host the request arrived on) and no row carries either — searched the portal's package-manifest rows (scala-portal, scala-portal-ts-dev, portal-ui, portal-shell) for a public base URL and the service_env rows for a configured origin; none carries one, so the reachable address can only come from the request
4
- * @edit edit directly
5
- *
6
- * THE DOCUMENT'S OWN FACTS, DERIVED WHERE THEY ARE KNOWN: the service
7
- * names itself, and the request knows where it arrived. Deriving rather
8
- * than configuring is what makes ONE document correct for the internal
9
- * address the MCP gateway uses and for a public host at the same time
10
- * (`information-exists-in-one-location` — the request is the only place
11
- * the reachable address exists).
12
- *
13
- * The `/api/rpc` prefix is part of the answer, not an accident of the
14
- * mount: it is the prefix the POST handler serves under, so a client that
15
- * reads the document can call what the document describes.
3
+ * @status handwritten
16
4
  */
17
5
 
18
6
  /**