{
  "manifestVersion": 1,
  "generatedUtc": "2026-08-04T07:45:00Z",
  "purpose": "CLI implementation contract for converting BuildWithHQ admin HTML previews into a working application.",
  "scaffoldRules": {
    "previewRowsAreFixtures": true,
    "removeFixturesAfterApiIntegration": true,
    "identityValuesMustBeServerInjected": true,
    "authorizationMustBeRecheckedOnMutation": true,
    "operationalHistoryPagination": "keyset",
    "safeErrorsOnly": true,
    "requiredStates": [
      "loading",
      "empty",
      "success",
      "forbidden",
      "not-found",
      "validation-error",
      "retryable-failure"
    ]
  },
  "databaseBoundaries": {
    "controlPlane": "buildwithHQ_Customers",
    "goldenTenantClone": "buildwithHQ_CustomerCopy",
    "sharedTemplateCatalog": "BuildWithHQ_TemplateLibrary",
    "perApp": [
      "core",
      "logging",
      "AI/vector"
    ],
    "runtimeBridges": [
      "CustomerLogDatabaseBridge",
      "CustomerAiDatabaseBridge"
    ]
  },
  "identityBoundaries": {
    "builderPlane": [
      "CustomerAccountId",
      "BuilderUserId"
    ],
    "app": [
      "SaaSAppId",
      "SaasAppIdInt"
    ],
    "tenantPlane": [
      "AppAccountId",
      "Tenant UserId"
    ],
    "neverReuse": [
      "BuilderUserId as tenant UserId",
      "browser-supplied ownership identifiers"
    ]
  },
  "verifiedJourney": {
    "status": "steps-1-through-7-verified-in-SSMS",
    "step8Status": "reference-package-generated-not-integrated",
    "ids": {
      "journeyRunId": "7C581885-5280-494F-93E5-9233FAE060A1",
      "customerAccountId": "322C4E65-7053-41B8-A90E-113A1B172531",
      "ownerBuilderUserId": "5E350AEB-9B54-47B0-9432-33D0297DD819",
      "platformSubscriptionId": "CC82EA67-BF8F-F111-8DD0-F4642CD2565B",
      "saasAppId": "8CB7CEA8-CB8F-F111-8DD0-F4642CD2565B",
      "saasAppIdInt": 4,
      "appAccountId": "A04115F6-63CF-4EAC-9296-8A034BCC221C",
      "tenantOwnerUserId": "8612FF02-CF8F-F111-8DD0-F4642CD2565B",
      "homeSaasAppPageId": "6289861B-67FC-4C0D-8B34-F1CB4D14ACAE",
      "componentId": "545AFC5A-2C6D-4061-A8CE-A17D27A41213",
      "firstPageBlockId": "F954AC54-214E-46D1-9543-7866C5716797",
      "draftPageHistoryId": "2096CC49-D98F-44A6-8057-44C6AE5D8EEF",
      "publishPageHistoryId": "51B1ACCC-D692-45AB-8DF8-D42990A103D1",
      "runtimeRequestId": "E552BD3C-3FAA-408D-B4A4-5ADAB51A9323"
    },
    "databases": {
      "core": "bwq_core_17D9EBBE6FCF",
      "logging": "bwq_logs_17D9EBBE6FCF",
      "ai": "bwq_ai_17D9EBBE6FCF"
    },
    "validFinalState": {
      "appStatus": "Active",
      "deploymentType": "BuilderOnly",
      "containerStatus": "NotStarted",
      "primaryDomainStatus": "Pending",
      "publishedHomePageVersion": 2
    }
  },
  "runtimeContract": {
    "procedure": "dbo.sp_Runtime_GetPageBootstrap",
    "resultSets": [
      "Runtime request metadata",
      "Shell JSON",
      "Effective component contracts",
      "Authorized page JSON"
    ],
    "rendererKey": "core.text",
    "frontendReference": "reference/runtime-react-contract/"
  },
  "templateReadiness": {
    "currentState": "catalog concepts exist; no published immutable versions or members are ready",
    "installableRequirements": [
      "TemplateType=Pack",
      "Status=Published",
      "ArtifactSchemaVersion=2",
      "valid manifest payload fingerprint",
      "valid member-set fingerprint",
      "all member payload fingerprints valid",
      "manifest identity/version/member count match",
      "at least one Page member"
    ],
    "selectionRule": "pin exact TemplatePackVersionId; never latest"
  },
  "knownSqlCorrections": [
    {
      "area": "sp_UpsertComponent",
      "rule": "A supplied new ID inserts; existing ID updates; natural key reuses canonical ID."
    },
    {
      "area": "sp_SaveBasePageRaw",
      "rule": "Nested PageHistoryId and outer JSON can both appear; parse only valid JSON result rows."
    },
    {
      "area": "sp_PublishBasePage",
      "rule": "Execute directly; do not wrap in INSERT...EXEC because it emits FOR JSON."
    },
    {
      "area": "vectors",
      "rule": "Treat vectors as numeric arrays; never truncate with string functions."
    },
    {
      "area": "usage meters",
      "rule": "Use canonical meter keys and precise units; legacy quota columns remain zero."
    }
  ],
  "pages": [
    {
      "file": "accept-invitation.html",
      "title": "Accept invitation \u2014 BuildWithHQ (preview)",
      "heading": "Join Scott Holdings LLC",
      "subtitle": "You've been invited by Maya Ruiz to join as a Developer .",
      "route": "/accept-invitation",
      "access": "invitation token",
      "retrieve": "Invitation summary resolved from the hashed token (org, assigned role, invited email, expiry). Expired/used tokens render a dedicated state.",
      "save": "sp_Builder_AcceptInvitation creates the builder user, binds the role, marks the invitation accepted, and starts a session \u2014 all in one transaction.",
      "success": "Valid token shows org + role before commit. Email is fixed by the invite, not editable. Expired/revoked tokens cannot be redeemed.",
      "pageId": "accept-invitation",
      "category": "Authentication & Onboarding",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#accept-invitation"
    },
    {
      "file": "account-preferences.html",
      "title": "Preferences \u2014 BuildWithHQ (preview)",
      "heading": "Preferences",
      "subtitle": "Defaults for how dates, numbers, and currency display across the console.",
      "route": "/account/preferences",
      "access": "CanManageAccount",
      "retrieve": "Preference values come from the account block of sp_Builder_GetAccountAdminDashboard .",
      "save": "Persisted through sp_Builder_UpdateAccountProfile (the preference fields).",
      "success": "Timezone/locale/format/currency persist per account. Currency here is display-only; invoices remain in USD. Changes apply on next render.",
      "pageId": "account-preferences",
      "category": "Dashboard & Account",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#account-preferences"
    },
    {
      "file": "account-profile.html",
      "title": "Account profile \u2014 BuildWithHQ (preview)",
      "heading": "Account profile",
      "subtitle": "Your organization's identity and primary contact on BuildWithHQ.",
      "route": "/account",
      "access": "CanManageAccount",
      "retrieve": "No dedicated getter exists \u2014 the account block comes from sp_Builder_GetAccountAdminDashboard .",
      "save": "sp_Builder_UpdateAccountProfile writes identity + contact + address; carries a RowVersion for optimistic concurrency.",
      "success": "Fields load from the current account only. Stale save returns HTTP 409, not last-write-wins. Contact email change triggers re-verification.",
      "pageId": "account-profile",
      "category": "Dashboard & Account",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#account-profile"
    },
    {
      "file": "account-recovery.html",
      "title": "Account access \u2014 BuildWithHQ (preview)",
      "heading": "Account access",
      "subtitle": "Why access may be limited, and how to restore full access.",
      "route": "/account/recovery",
      "access": "always available (read-only safe)",
      "retrieve": "sp_Builder_GetAccountAccessState returns lock reason, trial expiry, dunning state, and balance.",
      "save": "Recovery actions route to billing: add a card, or sp_Builder_RequestSubscriptionCancellation to close the account.",
      "success": "This page is reachable even when the account is locked. State explains the exact reason and remedy. Read-only/billing/recovery routes never lock.",
      "pageId": "account-recovery",
      "category": "Dashboard & Account",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#account-recovery"
    },
    {
      "file": "account-selector.html",
      "title": "Select account \u2014 BuildWithHQ (preview)",
      "heading": "Choose an account",
      "subtitle": "You have access to several BuildWithHQ organizations. Pick one to continue.",
      "route": "/select-account",
      "access": "authenticated",
      "retrieve": "sp_Builder_GetUserForLogin returns every account the signed-in user belongs to, with role, status, and member counts.",
      "save": "No write \u2014 selecting an account sets the active account context for the session server-side.",
      "success": "Only accounts the user actually belongs to appear. Role and ownership badges are shown per account. Last-used account is highlighted.",
      "pageId": "account-selector",
      "category": "Authentication & Onboarding",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#account-selector"
    },
    {
      "file": "active-sessions.html",
      "title": "Active sessions \u2014 BuildWithHQ (preview)",
      "heading": "Active sessions",
      "subtitle": "Devices signed in to your account. Revoke anything you don't recognize.",
      "route": "/security/sessions",
      "access": "authenticated",
      "retrieve": "sp_Builder_ListMySessions returns device, IP, location, and last-seen for the signed-in user.",
      "save": "Sessions are issued/rotated by sp_Auth_RegisterSession / sp_Auth_UseRefreshSession ; reuse is caught by sp_Auth_HandleRefreshReuse .",
      "success": "Reuse revokes the whole token family. Current session is flagged and non-revocable here. Revoke-all keeps the current session.",
      "pageId": "active-sessions",
      "category": "Security & Audit",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#active-sessions"
    },
    {
      "file": "app-analytics.html",
      "title": "Analytics \u2014 GoClaw CRM \u2014 BuildWithHQ (preview)",
      "heading": "Analytics",
      "subtitle": "How GoClaw CRM is actually being used.",
      "route": "/apps/:saasAppId/analytics",
      "access": "ownership-validated",
      "retrieve": "SaaSAppUsageTracking holds monthly period rows per metric key. The builder-plane reader is sp_Builder_GetAppUsage (period + keys, ownership-validated, up to 24 months of history). Writes go only through UpsertUsageMetering / SetUsageGauge (bwhq_metering) \u2014 never from the portal.",
      "save": "Read-only. Export produces a CSV of the visible period.",
      "success": "Counters accumulate, gauges overwrite \u2014 rendering respects AggregationMethod. Values are BIGINT in bytes/MB; conversion happens at display. users.active.monthly / sessions.duration.avg / bandwidth.egress.mb are new keys published by the tenant runtime; api.requests and db gauges are live today.",
      "pageId": "app-analytics",
      "category": "SaaS Products",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-analytics"
    },
    {
      "file": "app-api-clients.html",
      "title": "API clients \u2014 BuildWithHQ (preview)",
      "heading": "API clients \u00b7 Ledgerly",
      "subtitle": "External systems calling into this app. Keys are shown once; for outbound calls see API connections.",
      "route": "/apps/:saasAppId/api-clients",
      "access": "CanManageApiKeys",
      "retrieve": "sp_Builder_ListApiClients returns clients, scopes, and status \u2014 never the secret or its hash.",
      "save": "Create \u2192 sp_Builder_CreateApiClient (app generates secret, stores hash, shows raw once); RotateApiSecret ; RevokeApiClient .",
      "success": "Raw secret is displayed once and never returned again. Keys are scoped, rate-limited, auditable, and revocable. The gateway applies the same server-side field permissions and data masking used by the visual runtime. External apps never receive unrestricted database access.",
      "pageId": "app-api-clients",
      "category": "SaaS Products",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-api-clients"
    },
    {
      "file": "app-api-connections.html",
      "title": "API connections \u2014 BuildWithHQ (preview)",
      "heading": "API connections \u00b7 Ledgerly",
      "subtitle": "Outbound connections this app makes \u2014 third-party APIs and in-container services. Secrets live in the encrypted vault and never reach the browser.",
      "route": "/apps/:saasAppId/api-connections",
      "access": "CanManageIntegrations",
      "retrieve": "sp_Builder_ListIntegrations over ExternalIntegrations (name, base endpoint, AuthType , status, secret-configured flag \u2014 EncryptedSecret omitted). Container services resolve from ApplianceVersionEndpoints + egress policy.",
      "save": "Add/edit \u2192 sp_Builder_UpsertIntegration (secret encrypted at rest). Request tester, retry/timeout policy, circuit-breaker state, and full OAuth flows are roadmap \u2014 no schema yet ; do not fake them.",
      "success": "Directionality is explicit: this page is outbound only. Vault-backed secrets, redacted test results. Container calls obey the appliance egress allowlist.",
      "pageId": "app-api-connections",
      "category": "SaaS Products",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-api-connections"
    },
    {
      "file": "app-backups.html",
      "title": "Backups \u2014 BuildWithHQ (preview)",
      "heading": "Backups \u00b7 Ledgerly",
      "subtitle": "Backup history and on-demand snapshots for this app's databases.",
      "route": "/apps/:saasAppId/backups",
      "access": "CanManageBackups",
      "retrieve": "sp_Builder_ListBackups returns history with type, destination, size, status, and timestamps.",
      "save": "Create \u2192 sp_Builder_RequestBackup (queues an async worker). sp_Builder_RequestRestore queues a Pending row in SaaSAppRestores (one in-flight restore per app; only Completed backups qualify); sp_Builder_CancelBackup cancels Pending jobs only \u2014 a leased job is past the point of cancellation.",
      "success": "Requests create a Pending job for workers. Status is polled to completion. Restores are their own job records \u2014 backup history stays immutable.",
      "pageId": "app-backups",
      "category": "SaaS Products",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-backups"
    },
    {
      "file": "app-domains.html",
      "title": "Domains \u2014 BuildWithHQ (preview)",
      "heading": "Domains \u00b7 Ledgerly",
      "subtitle": "Connect custom domains for this app. Verification and TLS run through Cloudflare.",
      "route": "/apps/:saasAppId/domains",
      "access": "CanManageDomains",
      "retrieve": "sp_Builder_ListAppDomains returns domains with primary flag and SaaSAppDomains.Status (Pending/Active/Inactive).",
      "save": "Add \u2192 sp_Builder_AddDomain ; primary \u2192 sp_Builder_SetPrimaryDomain ; remove \u2192 sp_Builder_RemoveDomain . Verify runs the Cloudflare Custom Hostname check in the app tier and records the outcome via sp_Builder_RecordDomainVerification \u2014 hostname + certificate statuses combine into Pending \u2192 Active / Failed, stamping VerifiedUtc and LastCheckedUtc .",
      "success": "Exactly one primary domain per app. Cloudflare/provider status is normalized into Pending, Active, Failed, or Inactive. An app can be Active while its domain remains Pending; the generated BuildWithHQ hostname remains usable. Template workspaces and other prohibited app purposes fail server-side.",
      "pageId": "app-domains",
      "category": "SaaS Products",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-domains"
    },
    {
      "file": "app-integrations.html",
      "title": "Integrations \u2014 BuildWithHQ (preview)",
      "heading": "Integrations \u00b7 Ledgerly",
      "subtitle": "Connect external services for this app. Secrets are stored encrypted and never shown.",
      "route": "/apps/:saasAppId/integrations",
      "access": "CanManageIntegrations",
      "retrieve": "sp_Builder_ListIntegrations returns type, name, status, and a \"secret configured\" flag \u2014 the EncryptedSecret is omitted.",
      "save": "Add/edit \u2192 sp_Builder_UpsertIntegration ; disable is the same proc with @IsActive=0 . Test is an app-tier call ( proc pending ).",
      "success": "Secrets never leave the server. Test returns a redacted result with timeout/retry. List shows only whether a secret is set.",
      "pageId": "app-integrations",
      "category": "SaaS Products",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-integrations"
    },
    {
      "file": "app-logs.html",
      "title": "Logs \u2014 GoClaw CRM \u2014 BuildWithHQ (preview)",
      "heading": "Logs & errors",
      "subtitle": "What the software inside GoClaw CRM has been doing \u2014 and where it struggled.",
      "route": "/apps/:saasAppId/logs",
      "access": "CanDeploy",
      "retrieve": "Control-plane provisioning and operation failures use stable keyset pagination. Runtime events are read through the app route and CustomerLogDatabaseBridge ; the server validates builder ownership before crossing into the dedicated logging database. Do not issue cross-database names from browser input.",
      "save": "Read-only stream; \u201cRetry now\u201d queues the delivery worker for retry-eligible failures only.",
      "success": "Append-only runtime log writes. Secrets and provider payloads redacted before persistence. Correlation and journey/request IDs link provisioning, runtime, audit, and support. Pagination is stable under concurrent inserts.",
      "pageId": "app-logs",
      "category": "SaaS Products",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-logs"
    },
    {
      "file": "app-resources.html",
      "title": "App resources \u2014 BuildWithHQ (preview)",
      "heading": "Resources & storage \u00b7 Ledgerly",
      "subtitle": "Allocate this app's slice of your pooled database and container storage.",
      "route": "/apps/:saasAppId/storage",
      "access": "CanManageStorage",
      "retrieve": "Effective allocation comes from vw_SaaSAppEffectiveQuota ; pool headroom from vw_CustomerStoragePool . Current gauges use canonical meters: db.primary.gb , db.log.gb , db.ai.gb with unit GiB and decimal precision 9. api.requests is a request counter with precision 0.",
      "save": "UpsertStorageAllocation writes per-app allocations only after ownership and pool validation. Shrinks below the latest canonical gauge are rejected per dimension. Legacy quota columns remain zero and must not be revived in the UI.",
      "success": "Allocations stay within pool capacity. Decreases below used size are rejected with a distinct error. Freed headroom returns to the pool on deprovision.",
      "pageId": "app-resources",
      "category": "SaaS Products",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-resources"
    },
    {
      "file": "appliance-egress.html",
      "title": "Egress policy \u2014 BuildWithHQ (preview)",
      "heading": "Egress policy \u00b7 OCR Extract v2.5.0",
      "subtitle": "Explicit allowlist of outbound destinations. An empty list means a sealed appliance.",
      "route": "/appliances/versions/:packageVersionId/egress",
      "access": "CanManageAppliances",
      "retrieve": "Allowlist rows from ApplianceVersionEgressRequirements ; policy enum guarded by CK_APV_Egress (EgressPolicy IN Sealed/Allowlist).",
      "save": "Rows are materialized by sp_AppliancePkg_CreateVersion from the manifest networkAccess .",
      "success": "Empty allowlist = sealed appliance. Policy is constrained to Sealed or Allowlist. Egress is derived from the manifest, not free-typed.",
      "pageId": "appliance-egress",
      "category": "Appliance Packages",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-egress"
    },
    {
      "file": "appliance-endpoints.html",
      "title": "Endpoints \u2014 BuildWithHQ (preview)",
      "heading": "Endpoints \u00b7 OCR Extract v2.5.0",
      "subtitle": "The endpoints this appliance exposes, with schemas and permission keys.",
      "route": "/appliances/versions/:packageVersionId/endpoints",
      "access": "CanManageAppliances",
      "retrieve": "Endpoint definitions read from ApplianceVersionEndpoints (key, method, route, I/O schemas, permission, timeout, mode).",
      "save": "Managed as part of the draft version through sp_AppliancePkg_CreateVersion .",
      "success": "Each endpoint carries a required permission key. Schemas are validated at publish. Runtime enforces the contract independently.",
      "pageId": "appliance-endpoints",
      "category": "Appliance Packages",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-endpoints"
    },
    {
      "file": "appliance-install-preview.html",
      "title": "Install preview \u2014 BuildWithHQ (preview)",
      "heading": "Install preview \u00b7 OCR Extract v2.4.0",
      "subtitle": "Exactly what a tenant install will provision, resolved from the signed payload.",
      "route": "/appliances/versions/:packageVersionId/install-preview",
      "access": "CanManageAppliances",
      "retrieve": "sp_AppliancePkg_GetVersionForInstall + sp_Marketplace_GetInstallJobPayload (job context) + syn_TLBridge_GetApplianceInstallPayload resolve the exact, signed install payload.",
      "save": "Read-only preview. The actual install is entitlement-driven from the marketplace bridge.",
      "success": "Preview matches what tenants receive byte-for-byte. Only published, non-revoked versions resolve. Resource + egress requirements are shown up front.",
      "pageId": "appliance-install-preview",
      "category": "Appliance Packages",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-install-preview"
    },
    {
      "file": "appliance-package-detail.html",
      "title": "Appliance package \u2014 BuildWithHQ (preview)",
      "heading": "OCR Extract",
      "subtitle": "Publisher Northwind Labs \u00b7 Document \u00b7 pkg.ocr.extract",
      "route": "/appliances/:packageId",
      "access": "CanManageAppliances",
      "retrieve": "sp_AppliancePkg_GetCatalog (single package) returns publisher, metadata, and version timeline.",
      "save": "Deprecate \u2192 sp_AppliancePkg_DeprecateVersion . \"Set recommended\" \u2192 sp_Marketplace_SetRecommendedProductVersion \u2014 verified present in the schema; Published versions only, single-flag flip.",
      "success": "Recommended selection \u2192 sp_AppliancePkg_SetRecommendedVersion (Published package versions only; single flag per package). Deprecated versions block new installs; existing installs keep working until SupportEndsUtc. Timeline is newest-first.",
      "pageId": "appliance-package-detail",
      "category": "Appliance Packages",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-package-detail"
    },
    {
      "file": "appliance-packages.html",
      "title": "Appliance packages \u2014 BuildWithHQ (preview)",
      "heading": "Appliance packages",
      "subtitle": "Self-hosted VM appliances that expose endpoints to your tenant apps.",
      "route": "/appliances",
      "access": "CanManageAppliances",
      "retrieve": "sp_AppliancePkg_GetCatalog returns packages joined to publishers with category and active state.",
      "save": "Read-only list. \"New package\" opens the version editor which calls sp_AppliancePkg_CreateVersion .",
      "success": "Catalog spans AppliancePackages + AppliancePackageVersions . Publisher identity is shown per row. Deprecated packages cannot be newly installed.",
      "pageId": "appliance-packages",
      "category": "Appliance Packages",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-packages"
    },
    {
      "file": "appliance-release-management.html",
      "title": "Release management \u2014 BuildWithHQ (preview)",
      "heading": "Release \u00b7 OCR Extract v2.5.0",
      "subtitle": "Publish gate, fingerprint verification, and lifecycle controls.",
      "route": "/appliances/versions/:packageVersionId/release",
      "access": "CanManageAppliances",
      "retrieve": "Gate status derived from the version fingerprint + latest validation run + security status.",
      "save": "Publish \u2192 sp_AppliancePkg_PublishVersion \u2014 recomputes the release fingerprint live and fails closed on a Passed validation run for that exact fingerprint; suspend/restore/revoke \u2192 sp_AppliancePkg_SetSecurityStatus ; deprecate \u2192 sp_AppliancePkg_DeprecateVersion ; set recommended \u2192 sp_AppliancePkg_SetRecommendedVersion . All live in the Template Library \u2014 the package layer, distinct from the marketplace product lifecycle ( sp_Marketplace_SetVersionLifecycle / sp_Marketplace_AdminSetVersionSecurityStatus ).",
      "success": "Publish is fail-closed on fingerprint + validation. Suspended/revoked versions block new installs; Revoked is terminal. Deprecated blocks new installs but keeps existing installs supported until SupportEndsUtc. No UI bypass for the gate.",
      "pageId": "appliance-release-management",
      "category": "Appliance Packages",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-release-management"
    },
    {
      "file": "appliance-validation.html",
      "title": "Validation \u2014 BuildWithHQ (preview)",
      "heading": "Validation \u00b7 OCR Extract v2.5.0",
      "subtitle": "A Passed run against the current fingerprint is required to publish.",
      "route": "/appliances/versions/:packageVersionId/validation",
      "access": "CanManageAppliances",
      "retrieve": "Runs read from the validation-run records keyed by fingerprint; status polled to completion.",
      "save": "Queue \u2192 sp_ApplianceValidation_Queue ; the pipeline lives in the Template Library, keyed by PackageVersionId + the exact ReleaseFingerprint; workers use sp_ApplianceValidation_Lease / _Complete / _Abort .",
      "success": "Publishing needs a Passed run for the exact fingerprint. Fingerprint change invalidates prior runs. Lease/complete belong to the worker, not the browser.",
      "pageId": "appliance-validation",
      "category": "Appliance Packages",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-validation"
    },
    {
      "file": "appliance-version-editor.html",
      "title": "Version editor \u2014 BuildWithHQ (preview)",
      "heading": "Version editor \u00b7 OCR Extract",
      "subtitle": "Draft v2.5.0 \u00b7 immutable image digest & manifest define the release fingerprint.",
      "route": "/appliances/versions/:packageVersionId/edit",
      "access": "CanManageAppliances",
      "retrieve": "Draft loaded from the package version record; fingerprint via fn_Appliance_ReleaseFingerprint .",
      "save": "sp_AppliancePkg_CreateVersion writes the draft (semver, digest, manifest, egress from networkAccess , SBOM).",
      "success": "Image digest is immutable once set. Fingerprint changes invalidate prior validation. Egress rows derive from the manifest.",
      "pageId": "appliance-version-editor",
      "category": "Appliance Packages",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#appliance-version-editor"
    },
    {
      "file": "audit-log.html",
      "title": "Audit log \u2014 BuildWithHQ (preview)",
      "heading": "Audit log",
      "subtitle": "Every administrative and marketplace mutation, with correlation IDs.",
      "route": "/audit",
      "access": "CanViewAuditLog",
      "retrieve": "sp_Builder_ListAuditLog reads BuilderAccountAuditLog with actor/action/target/correlation, filtered and paginated.",
      "save": "Read-only. Entries are written before commit by services via sp_Internal_WriteBuilderAudit .",
      "success": "Every mutation produces an entry before commit. Correlation IDs are searchable. Metadata preview is safe/redacted.",
      "pageId": "audit-log",
      "category": "Security & Audit",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#audit-log"
    },
    {
      "file": "backup-destinations.html",
      "title": "Backup destinations \u2014 BuildWithHQ (preview)",
      "heading": "Backup destinations \u00b7 Ledgerly",
      "subtitle": "S3-compatible targets where this app's backups are written.",
      "route": "/apps/:saasAppId/backup-destinations",
      "access": "CanManageBackups",
      "retrieve": "sp_Builder_ListBackupDestinations returns destination metadata: bucket/region, role ARN, external ID, object-lock, and enabled state.",
      "save": "Add/edit \u2192 sp_Builder_UpsertBackupDestination . Disable is the same proc with @IsEnabled=0 (no separate proc).",
      "success": "Credentials are never returned to the browser. Disable is blocked while a job requires the destination. Object-lock state is surfaced for compliance.",
      "pageId": "backup-destinations",
      "category": "SaaS Products",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#backup-destinations"
    },
    {
      "file": "billing-invoices.html",
      "title": "Invoices \u2014 BuildWithHQ (preview)",
      "heading": "Invoices & payments",
      "subtitle": "Your billing history on BuildWithHQ.",
      "route": "/billing/invoices",
      "access": "CanManageBilling",
      "retrieve": "sp_Builder_GetBillingHistory returns both the invoice and payment blocks (from PlatformInvoices / PlatformPayments ); there is no separate List proc.",
      "save": "Read-only. Invoice PDFs are generated server-side from the stored invoice.",
      "success": "History is account-scoped and paginated. Amounts shown in integer cents + currency. Refunded/void states are distinguished.",
      "pageId": "billing-invoices",
      "category": "Billing & Resources",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#billing-invoices"
    },
    {
      "file": "billing-overview.html",
      "title": "Billing \u2014 BuildWithHQ (preview)",
      "heading": "Billing & plans",
      "subtitle": "Your BuildWithHQ subscription, trial status, and payment state.",
      "route": "/billing",
      "access": "CanManageBilling",
      "retrieve": "sp_Builder_GetBillingHistory returns the billing profile, current subscription, and recent invoices/payments in one call.",
      "save": "Read-only page. Plan changes route to Manage plan; cancel \u2192 sp_Builder_RequestSubscriptionCancellation .",
      "success": "Trial/dunning state matches access state. Payment-method presence drives the CTA. Amounts are integer cents + currency. Partner-Comp: ExternalProcessor='Manual' , TrialEndsUtc NULL (never expires), IncludedSaaSCount 0 \u2014 Standard products blocked ( 51220 / 51221 ), workspaces unaffected.",
      "pageId": "billing-overview",
      "category": "Billing & Resources",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#billing-overview"
    },
    {
      "file": "billing-plan-management.html",
      "title": "Plan management \u2014 BuildWithHQ (preview)",
      "heading": "Plan & add-ons",
      "subtitle": "Change your subscription. Checkout runs securely through Stripe.",
      "route": "/billing/plans",
      "access": "CanManageBilling",
      "retrieve": "sp_Builder_GetBillingActionContext returns available plans, the current subscription, and change eligibility.",
      "save": "Create the Stripe Checkout session app-side, then persist it with sp_Billing_RecordCheckoutSession ; the webhook reconciles the result.",
      "success": "Plan change is confirmed only on webhook, not redirect. Downgrades respect seat/product limits. No card data is stored by the portal.",
      "pageId": "billing-plan-management",
      "category": "Billing & Resources",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#billing-plan-management"
    },
    {
      "file": "blueprint-schemas.html",
      "title": "Blueprint schemas \u2014 BuildWithHQ (preview)",
      "heading": "Blueprint schemas",
      "subtitle": "Grammar schemas that constrain the AI builder's structured output.",
      "route": "/templates/blueprints",
      "access": "CanManageTemplates",
      "retrieve": "Active schema read via sp_GetActiveBlueprintSchema ; header rows in BlueprintSchemas , versions in TemplateVersions .",
      "save": "Draft \u2192 sp_SaveBlueprintSchemaDraft (requires a Slots array); publish \u2192 sp_PublishBlueprintSchema .",
      "success": "Every schema must define a Slots array. Only one Active version per key. Constrains the AI planner output.",
      "pageId": "blueprint-schemas",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#blueprint-schemas"
    },
    {
      "file": "branding.html",
      "title": "Branding \u2014 BuildWithHQ (preview)",
      "heading": "Branding & white-label",
      "subtitle": "Your brand once \u2014 applied to every product you build and every email your customers receive.",
      "route": "/branding",
      "access": "CanManageAccount",
      "retrieve": "sp_Builder_GetBrandKit \u2014 one call: kit fields ( kitExists , logo URLs, designTokens, basedOnThemeKey via syn_TemplateThemes, brandVersion, base64 rowVersion) plus the nested senderDomains[] array with computed dkimRecordName . Migration 2026-08-01_02_customer_branding.sql .",
      "save": "sp_Builder_UpsertBrandKit (insert-or-update; @ExpectedRowVersion BINARY(8) base64 round-trip; conflict 55080 \u2192 409; bumps BrandVersion; audited) \u00b7 sp_Builder_UpsertSenderDomain (DKIM keypair generated app-tier, public key only to SQL; dup 55084 \u2192 409) \u00b7 sp_Builder_SetDefaultSenderDomain (verified-only, 55086 \u2192 422) \u00b7 sp_Builder_RemoveSenderDomain (soft). Verification state is written ONLY by the worker via sp_Communication_RecordSenderDomainVerification . Full endpoint + worker contract: docs/HANDOFF_branding.md .",
      "success": "Permission enforced in-proc (CanManageAccount, 55081 \u2192 403); identity params server-injected. Layering: kit = account base; per-app SaasAppTheme forks COALESCE down; seed-time propagation is a separate tenant-plane change (noted seam). Unverified domains can never be the default sender \u2014 mailer falls back to the platform sender. Remaining build items: sender_domain_verifier.py worker; theme-picker list proc over syn_TemplateThemes.",
      "pageId": "branding",
      "category": "Dashboard & Account",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#branding"
    },
    {
      "file": "cli-handoff.html",
      "title": "CLI implementation handoff \u2014 BuildWithHQ (preview)",
      "heading": "CLI implementation handoff",
      "subtitle": "Machine-readable page contracts, verified database journey, known SQL corrections, and the order for turning every preview into a working admin.",
      "route": "/cli-handoff",
      "access": "authenticated developer",
      "retrieve": "Static implementation documentation generated from the preview contracts and verified journey outputs.",
      "save": "Read-only. The CLI changes application code, not this page at runtime.",
      "success": "Every route is wired to real data. Fixture rows are removed. Server-side authorization is enforced. Integration tests cover critical workflows.",
      "pageId": "cli-handoff",
      "category": "Dashboard & Account",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#cli-handoff"
    },
    {
      "file": "communications.html",
      "title": "Communications \u2014 BuildWithHQ (preview)",
      "heading": "Communications",
      "subtitle": "Choose which notifications your organization receives, and who receives them.",
      "route": "/communications",
      "access": "CanManageAccount",
      "retrieve": "Settings row read from CustomerCommunicationPreferences \u2014 no dedicated getter ; the upsert returns the current row. Delivery history lives in CustomerNotificationLog .",
      "save": "Preferences saved via sp_Builder_UpsertCommunicationPreferences . Delivery history reads via sp_Communication_GetDeliveryHistory \u2014 keyset-paged, filterable by category and channel, gated on CanManageAccount .",
      "success": "Category toggles + recipients persist per account. Security category cannot be fully disabled. Recipients validate against account members.",
      "pageId": "communications",
      "category": "Dashboard & Account",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#communications"
    },
    {
      "file": "create-saas-product.html",
      "title": "Create SaaS product \u2014 BuildWithHQ (preview)",
      "heading": "Create a SaaS product",
      "subtitle": "Name the app, choose Blank or an exact published template version, and provision its isolated database trio.",
      "route": "/apps/new",
      "access": "CanCreateSaaS",
      "retrieve": "Load pooled headroom plus template-pack versions that are actually installable: Status=Published , ArtifactSchemaVersion=2 , valid manifest and member-set fingerprints, verified member payloads, and at least one Page member. Catalog-only packs render disabled. Blank is always enabled.",
      "save": "Register the control-plane app first so SaaSAppId , SaasAppIdInt , domain, route, allocation and usage-meter IDs exist before workers start. Provision physical databases, stamp SaasAppIdentityMap , create a separate tenant AppAccountId and owner UserId , create log/AI bridges, then activate. For Blank, create the shell and empty Home page. For Template, install the exact TemplatePackVersionId \u2014never \u201clatest.\u201d",
      "success": "Control-plane and tenant IDs are never conflated. App stays Provisioning until database identity, tenant root, security role, and bridges verify. Custom domain can remain Pending after the app becomes Active . BuilderOnly apps do not require a container.",
      "pageId": "create-saas-product",
      "category": "SaaS Products",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#create-saas-product"
    },
    {
      "file": "dashboard.html",
      "title": "Dashboard \u2014 BuildWithHQ (preview)",
      "heading": "Dashboard",
      "subtitle": "An overview of your organization's apps, tenants, and platform resources.",
      "route": "/dashboard",
      "access": "authenticated",
      "retrieve": "sp_Builder_GetAccountAdminDashboard returns the account block, app portfolio summary, pooled resource usage, and recent provisioning/install jobs as one account-scoped JSON payload.",
      "save": "Read-only page. The \"New SaaS product\" action routes to the create flow; nothing is written here.",
      "success": "Trial/dunning banner reflects live access state. Counts and pools are scoped to the current account. Jobs show live status via polling.",
      "pageId": "dashboard",
      "category": "Dashboard & Account",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#dashboard"
    },
    {
      "file": "forgot-password.html",
      "title": "Reset password \u2014 BuildWithHQ (preview)",
      "heading": "Reset your password",
      "subtitle": "Enter your account email and we'll send a reset link if it matches an account.",
      "route": "/forgot-password",
      "access": "public",
      "retrieve": "No read. Email is looked up server-side only to decide whether to mint a token.",
      "save": "sp_Auth_CreatePasswordResetToken stores a hashed, expiring token; the raw token is emailed, never returned.",
      "success": "Always returns the same generic success. Rate-limited per email/IP. No account-existence disclosure.",
      "pageId": "forgot-password",
      "category": "Authentication & Onboarding",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#forgot-password"
    },
    {
      "file": "getting-started.html",
      "title": "Getting started \u2014 BuildWithHQ (preview)",
      "heading": "Welcome to BuildWithHQ",
      "subtitle": "From builder account to active app, blank shell, first publish, and runtime delivery.",
      "route": "/getting-started",
      "access": "authenticated",
      "retrieve": "sp_Builder_GetAccountAdminDashboard supplies the trial state, verification flag, and app/team/payment counts that drive each checklist step; the lock banner reads sp_Builder_GetAccountAccessState . sp_Builder_GetOnboardingState returns the computed checklist in one read \u2014 email verified, MFA, payment method, app/team/domain counts; dismissal persistence stays out until it is a product requirement.",
      "save": "Read-only page. Every CTA routes to its own page with its own contracts; nothing is written here. FAQ copy is static frontend content.",
      "success": "Checklist state comes from server truth, never localStorage. Blank-app creation remains available when no published template version exists. App activation does not wait for optional custom-domain verification. BuilderOnly apps may be Active while container status remains NotStarted .",
      "pageId": "getting-started",
      "category": "Dashboard & Account",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#getting-started"
    },
    {
      "file": "harness-components.html",
      "title": "Harness components \u2014 BuildWithHQ (preview)",
      "heading": "The pieces of this pack",
      "subtitle": "Each row is one piece of the training binder for legal-practice-v1 . Edit a piece, then release it through a new version.",
      "route": "/templates/harnesses/:harnessPackKey/components",
      "access": "catalog publisher",
      "retrieve": "sp_GetHarnessPack returns the component rows with type, target feature, and payload.",
      "save": "sp_UpsertHarnessComponent \u2014 rerun-safe upsert by (pack, key); deactivate is @IsActive = 0 , never a delete.",
      "success": "Nothing changes published behavior until sp_SnapshotHarnessPack + a gated publish. Deactivated components drop from the next snapshot; publish soft-deactivates absentees. Invalid JSON is rejected in the editor before any proc call.",
      "pageId": "harness-components",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-components"
    },
    {
      "file": "harness-eval-sets.html",
      "title": "Harness tests \u2014 BuildWithHQ (preview)",
      "heading": "The test suite",
      "subtitle": "The checks every new version of legal-practice-v1 must pass before it can go live.",
      "route": "/templates/harnesses/:harnessPackKey/eval-sets",
      "access": "catalog validator",
      "retrieve": "HarnessEvalSets + HarnessEvalCases (CaseKey, InputJson, ExpectedJson, ScoringMethod: ExactRoute | Contains | Judge).",
      "save": "Case authoring: sp_UpsertHarnessEvalSet / sp_UpsertHarnessEvalCase \u2014 key-addressed upserts mirroring component authoring, scoring method CHECK-constrained; the run side is complete: lease \u2192 work \u2192 complete / abort.",
      "success": "Runs execute on the Python eval worker with UPDLOCK/READPAST leasing. Industry packs overriding RouterConfig must copy the hard-rule ExactRoute cases. Judge cases score via the Answerer lane against a rubric threshold.",
      "pageId": "harness-eval-sets",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-eval-sets"
    },
    {
      "file": "harness-evaluation.html",
      "title": "Evaluation run \u2014 BuildWithHQ (preview)",
      "heading": "Evaluation \u00b7 Support triage v2",
      "subtitle": "Run the pack's evaluation set before publishing a new version.",
      "route": "/templates/harnesses/:harnessPackKey/evaluations",
      "access": "CanManageTemplates",
      "retrieve": "Sets/cases/runs read over HarnessEvalSets , HarnessEvalCases , HarnessEvalRuns ; results poll to completion.",
      "save": "Queue \u2192 sp_QueueHarnessEvalRun ; workers use sp_LeaseHarnessEvalRun / sp_CompleteHarnessEvalRun .",
      "success": "Publish is blocked below the pass gate. Runs execute async with polling. Per-case expected vs got is shown.",
      "pageId": "harness-evaluation",
      "category": "Template Library & AI",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-evaluation"
    },
    {
      "file": "harness-installations.html",
      "title": "Harness installations \u2014 BuildWithHQ (preview)",
      "heading": "Who\u2019s running what",
      "subtitle": "Every customer using each pack, which version they\u2019re on, and the automatic upgrade that keeps them current.",
      "route": "/templates/harnesses/installations",
      "access": "catalog publisher/validator",
      "retrieve": "sp_GetPublishedHarnessVersions gives the published census; installed state lives in ai_harness_installs per tenant AI DB. This UI needs an aggregated install-census reader (gap) rather than walking databases.",
      "save": "Queue re-install marks tenants for the sweep worker, which re-runs sp_ai_harness_install using sp_GetPublishedHarnessPack (BasedOn chain, base-first).",
      "success": "Overrides survive: tenant / builder layers are separate rows. The sweep is scheduled; this page shows its cadence and last result. Provenance columns keep the link back to the library version for drift detection.",
      "pageId": "harness-installations",
      "category": "Template Library & AI",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-installations"
    },
    {
      "file": "harness-pack-detail.html",
      "title": "legal-practice-v1 \u2014 BuildWithHQ (preview)",
      "heading": "legal-practice-v1",
      "subtitle": "The Legal Practice harness pack \u2014 everything that makes the AI sound and think like it works at a law firm.",
      "route": "/templates/harnesses/:harnessPackKey",
      "access": "catalog publisher/validator",
      "retrieve": "sp_GetHarnessPack returns the header, active components, and BasedOn chain; the version strip reads TemplateVersions filtered to TemplateType='HarnessPack' .",
      "save": "Read-only hub \u2014 authoring happens on Components, Versions, and Eval Sets.",
      "success": "Harnesses configure behavior, never access \u2014 nothing here can widen a permission envelope. Builder / Tenant layers are downstream tenant-AI-DB rows shown as context. Same TemplateVersions store as page templates: lifecycle tooling carries over.",
      "pageId": "harness-pack-detail",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-pack-detail"
    },
    {
      "file": "harness-packs.html",
      "title": "Harness packs \u2014 BuildWithHQ (preview)",
      "heading": "Harness packs",
      "subtitle": "Evaluation-gated bundles of AI components with forkable, versioned lineage. (Internally: harness packs.)",
      "route": "/templates/harnesses",
      "access": "CanManageTemplates",
      "retrieve": "Pack + lineage read via sp_GetHarnessPack / sp_GetPublishedHarnessVersions ; components in HarnessComponents .",
      "save": "Component edits \u2192 sp_UpsertHarnessComponent ; snapshot \u2192 sp_SnapshotHarnessPack ; publish \u2192 sp_PublishHarnessPackVersion .",
      "success": "Publish is gated on a passing evaluation. Packs fork with attributed lineage. Based-on shows the parent version.",
      "pageId": "harness-packs",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-packs"
    },
    {
      "file": "harness-versions.html",
      "title": "Harness versions \u2014 BuildWithHQ (preview)",
      "heading": "Versions & releasing",
      "subtitle": "Changing how the AI behaves works like releasing software: freeze a draft, test it, then go live.",
      "route": "/templates/harnesses/:harnessPackKey/versions",
      "access": "catalog publisher",
      "retrieve": "TemplateVersions filtered to TemplateType='HarnessPack' , joined to the latest HarnessEvalRuns per version.",
      "save": "sp_SnapshotHarnessPack \u2192 sp_QueueHarnessEvalRun \u2192 sp_PublishHarnessPackVersion ; the prior published version auto-archives.",
      "success": "The gate lives in the proc \u2014 the UI can gray the button, SQL enforces it. A Passed run binds to the exact TemplateVersionId; re-snapshot means re-eval. The seed / upgrade worker only ever reads Published (sp_GetPublishedHarnessPack, base-first).",
      "pageId": "harness-versions",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#harness-versions"
    },
    {
      "file": "login.html",
      "title": "Sign in \u2014 BuildWithHQ (preview)",
      "heading": "Sign in",
      "subtitle": "Welcome back. Sign in to your BuildWithHQ builder account.",
      "route": "/login",
      "access": "public",
      "retrieve": "No pre-auth read. On submit, sp_Auth_GetLoginCandidates returns candidate rows; the API verifies the password before disclosing anything.",
      "save": "sp_Auth_CheckAndRecordLoginAttempt (throttle) \u2192 verify hash app-side \u2192 sp_Builder_RecordLogin + sp_Auth_RegisterSession .",
      "success": "Valid credentials create a revocable builder session. MFA-enrolled users continue to the challenge. Multiple builder organizations continue to the account selector. Builder-console authentication remains separate from each SaaS tenant login.",
      "pageId": "login",
      "category": "Authentication & Onboarding",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#login"
    },
    {
      "file": "marketplace-browse.html",
      "title": "Marketplace \u2014 BuildWithHQ (preview)",
      "heading": "Marketplace",
      "subtitle": "Templates and appliances from BuildWithHQ and third-party sellers.",
      "route": "/marketplace",
      "access": "authenticated",
      "retrieve": "sp_Marketplace_GetCatalog returns active listings with seller, type, and pricing summary.",
      "save": "Read-only. \"View\" opens product detail; purchase happens downstream.",
      "success": "Only active, approved listings appear. Pricing shown from the active offer. Seller identity on every card.",
      "pageId": "marketplace-browse",
      "category": "Marketplace Buyer",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-browse"
    },
    {
      "file": "marketplace-checkout.html",
      "title": "Checkout \u2014 BuildWithHQ (preview)",
      "heading": "Checkout",
      "subtitle": "Confirm your purchase. Payment is processed securely by Stripe.",
      "route": "/marketplace/checkout/:marketplacePriceId",
      "access": "CanPurchaseMarketplace",
      "retrieve": "Price + product locked from the selected offer; buyer account is server-injected.",
      "save": "sp_Marketplace_CreateOrder then sp_Marketplace_RegisterCheckoutSession (server-side); sp_Marketplace_ProcessPaymentSucceeded reconciles via webhook.",
      "success": "Entitlement is granted on webhook, not redirect. Version is pinned at order time. No card data touches the portal.",
      "pageId": "marketplace-checkout",
      "category": "Marketplace Buyer",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-checkout"
    },
    {
      "file": "marketplace-entitlements.html",
      "title": "Entitlements \u2014 BuildWithHQ (preview)",
      "heading": "Entitlements",
      "subtitle": "Products you own, and which SaaS app each is assigned to.",
      "route": "/marketplace/entitlements",
      "access": "CanInstallMarketplaceProducts",
      "retrieve": "sp_Marketplace_GetBuyerEntitlements returns owned entitlements with version pin and assignment.",
      "save": "Assign \u2192 sp_Marketplace_AssignEntitlementToSaaSApp , which also queues the install internally.",
      "success": "Each entitlement pins an exact version. Assigning queues installation. An entitlement maps to one owned app.",
      "pageId": "marketplace-entitlements",
      "category": "Marketplace Buyer",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-entitlements"
    },
    {
      "file": "marketplace-install-detail.html",
      "title": "Install detail \u2014 BuildWithHQ (preview)",
      "heading": "Install job \u00b7 Invoice Suite v3",
      "subtitle": "Target Ledgerly \u00b7 requested by George \u00b7 job job_7f2a\u2026",
      "route": "/marketplace/installations/:jobId",
      "access": "CanInstallMarketplaceProducts",
      "retrieve": "sp_Marketplace_GetInstallWork returns the job header, ordered components, and required/optional flags.",
      "save": "Workers call sp_Marketplace_CompleteInstallComponent per component, then sp_Marketplace_CompleteInstallJob .",
      "success": "Components install in order. Required failures fail the job. Status polls to completion.",
      "pageId": "marketplace-install-detail",
      "category": "Marketplace Buyer",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-install-detail"
    },
    {
      "file": "marketplace-installations.html",
      "title": "Installations \u2014 BuildWithHQ (preview)",
      "heading": "Installations",
      "subtitle": "Install jobs for your purchased products, with live status.",
      "route": "/marketplace/installations",
      "access": "CanInstallMarketplaceProducts",
      "retrieve": "Install jobs read from the marketplace install-job records with status and target app; polled to completion.",
      "save": "Requested via sp_Marketplace_RequestInstallVersion ; workers claim work with sp_Marketplace_ClaimInstallJob .",
      "success": "Installs run async with status polling. Jobs pin the entitlement + version. Failed jobs surface a reason on detail.",
      "pageId": "marketplace-installations",
      "category": "Marketplace Buyer",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-installations"
    },
    {
      "file": "marketplace-product-detail.html",
      "title": "Product detail \u2014 BuildWithHQ (preview)",
      "heading": "Invoice Suite Template pack",
      "subtitle": "by BuildWithHQ \u00b7 v3 (approved) \u00b7 4.8 \u2605 (126)",
      "route": "/marketplace/products/:marketplaceProductId",
      "access": "authenticated",
      "retrieve": "sp_Marketplace_GetProductDetail returns the product, seller profile, current approved version, and included components.",
      "save": "Read-only. \"Buy\" starts checkout for the selected price.",
      "success": "Only the current approved version is offered. Included components are enumerated. Rating/seller shown for trust.",
      "pageId": "marketplace-product-detail",
      "category": "Marketplace Buyer",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-product-detail"
    },
    {
      "file": "marketplace-purchases.html",
      "title": "Purchases \u2014 BuildWithHQ (preview)",
      "heading": "Purchases",
      "subtitle": "Your marketplace orders and their payment status.",
      "route": "/marketplace/purchases",
      "access": "CanPurchaseMarketplace",
      "retrieve": "Orders read from MarketplaceOrders with product/version, amount, and payment status.",
      "save": "Read-only. Entitlements derive from paid orders.",
      "success": "Amounts in integer cents + currency. Refunded/void distinguished. Each order pins a product version.",
      "pageId": "marketplace-purchases",
      "category": "Marketplace Buyer",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#marketplace-purchases"
    },
    {
      "file": "mfa-challenge.html",
      "title": "MFA challenge \u2014 BuildWithHQ (preview)",
      "heading": "Two-factor authentication",
      "subtitle": "Enter the 6-digit code from your authenticator app for george@buildwithhq.com .",
      "route": "/mfa-challenge",
      "access": "pending-mfa session",
      "retrieve": "Reads the pending-MFA session context only; the TOTP secret stays server-side.",
      "save": "TOTP verified app-side against the enrollment; recovery path calls sp_Auth_ConsumeMfaRecoveryCode ; success issues the full session via sp_Auth_RegisterSession .",
      "success": "Correct code elevates the session to full access. A recovery code works once, then is burned. Attempts are throttled.",
      "pageId": "mfa-challenge",
      "category": "Authentication & Onboarding",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#mfa-challenge"
    },
    {
      "file": "mfa-settings.html",
      "title": "MFA settings \u2014 BuildWithHQ (preview)",
      "heading": "Multi-factor authentication",
      "subtitle": "Manage your authenticator enrollment and recovery codes.",
      "route": "/security/mfa",
      "access": "authenticated",
      "retrieve": "Enrollment state read from the current session; the TOTP secret never leaves the server.",
      "save": "Begin \u2192 sp_Auth_BeginMfaEnrollment ; activate \u2192 sp_Auth_ActivateMfaEnrollment ; disable \u2192 sp_Auth_DisableMfa ; recovery \u2192 sp_Auth_ConsumeMfaRecoveryCode ; regenerate \u2192 sp_Auth_RegenerateMfaRecoveryCodes (replaces unused codes only; consumed codes stay as history; plaintext shown once).",
      "success": "Codes shown once, stored as hashes. Activation requires a valid TOTP. Disable requires re-authentication.",
      "pageId": "mfa-settings",
      "category": "Security & Audit",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#mfa-settings"
    },
    {
      "file": "ownership-transfer.html",
      "title": "Transfer ownership \u2014 BuildWithHQ (preview)",
      "heading": "Transfer ownership",
      "subtitle": "Hand the owner role to another administrator. This cannot be undone by you afterward.",
      "route": "/team/ownership",
      "access": "CanManageTeam \u00b7 owner only",
      "retrieve": "Eligible admins listed via sp_Builder_ListUsers filtered to verified + MFA-enabled administrators.",
      "save": "sp_Builder_TransferOwnership promotes the new owner and demotes the current one atomically, with typed confirmation + audit.",
      "success": "Only the current owner can start this. Target must be an eligible admin. Exactly one owner exists at all times.",
      "pageId": "ownership-transfer",
      "category": "Team & Permissions",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#ownership-transfer"
    },
    {
      "file": "register.html",
      "title": "Create account \u2014 BuildWithHQ (preview)",
      "heading": "Create your account",
      "subtitle": "Set up your builder organization on BuildWithHQ.",
      "route": "/register",
      "access": "public",
      "retrieve": "No pre-auth read.",
      "save": "sp_Provisioning_CreateBuilderAccount creates the customer account and owner builder user atomically; sp_Auth_CreateEmailVerificationToken stores only the token hash. The deterministic Step 01 journey script is reference evidence, not a browser-callable signup endpoint. Generate IDs server-side and write a real supported password hash\u2014never the structural test placeholder.",
      "success": "Builder account, owner user, plan and subscription commit together. Duplicate email returns one safe response. Verification gates privileged builder actions. BuilderUserId is not reused as a tenant UserId .",
      "pageId": "register",
      "category": "Authentication & Onboarding",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#register"
    },
    {
      "file": "reset-password.html",
      "title": "Reset password \u2014 BuildWithHQ (preview)",
      "heading": "Set a new password",
      "subtitle": "Choose a strong password for your builder account.",
      "route": "/reset-password",
      "access": "public (token)",
      "retrieve": "No read \u2014 the emailed token is the credential.",
      "save": "sp_Auth_CompletePasswordReset verifies the SHA-256 token hash and stores the new app-tier PBKDF2 hash; dead links throw 54282/54283 \u2192 422.",
      "success": "The refresh-session family is revoked server-side. Client returns to sign-in with a success note. Raw passwords never reach SQL \u2014 hash only.",
      "pageId": "reset-password",
      "category": "Authentication & Onboarding",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#reset-password"
    },
    {
      "file": "roles-permissions.html",
      "title": "Roles & permissions \u2014 BuildWithHQ (preview)",
      "heading": "Roles & permissions",
      "subtitle": "What each role can do across the builder console. Truth lives in the effective-permissions view.",
      "route": "/team/roles",
      "access": "CanManageUsers",
      "retrieve": "sp_Builder_ListRoles supplies the role set; vw_BuilderUserPermissions is the authoritative source of effective flags. Marketplace access is granular \u2014 CanManageMarketplaceStore , CanPurchaseMarketplace , CanInstallMarketplaceProducts , CanViewMarketplaceRevenue , CanManageMarketplacePayouts \u2014 enforced by fn_Marketplace_UserCan .",
      "save": "Role changes are saved from the member detail page via sp_Builder_UpdateUserRole ; this matrix itself is read-only.",
      "success": "Builder permissions come from the effective builder-permissions view and are rechecked server-side. BuilderUserId and tenant UserId are different identities. Tenant DataRoles and DataRolesXref never grant builder-console access. Owner overrides are explicit on each plane, never inferred across planes.",
      "pageId": "roles-permissions",
      "category": "Team & Permissions",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#roles-permissions"
    },
    {
      "file": "saas-product-overview.html",
      "title": "GoClaw CRM \u2014 BuildWithHQ (preview)",
      "heading": "GoClaw CRM",
      "subtitle": "goclaw-crm \u00b7 Standard product \u00b7 Live since Mar 2026 \u00b7 42 tenants",
      "route": "/apps/:saasAppId",
      "access": "ownership-validated",
      "retrieve": "sp_Builder_GetAppAdminDetail returns control-plane identity, app purpose, deployment type, container state, domain state, routing and effective quota after ownership validation. A separate tenant database call may expose runtime health through sp_Runtime_GetPageBootstrap ; do not join tenant data directly in the browser.",
      "save": "Mostly a read-only hub \u2014 manage actions navigate to their own pages. The one direct action: \u201cSave as template pack\u201d \u2192 sp_SaveAppAsTemplatePack , which runs in this product\u2019s tenant core DB (resolved via SaaSAppDatabaseRouting ) and creates an owned, unpublished pack in \u201cMy templates\u201d.",
      "success": "Revalidate :saasAppId against the signed-in builder account on every request. Active + Pending domain + BuilderOnly + NotStarted container is a valid state. Show the exact core/log/AI database routes only to authorized operators. Saving as a template creates a private draft; it is not installable until an immutable published version is validated.",
      "pageId": "saas-product-overview",
      "category": "SaaS Products",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#saas-product-overview"
    },
    {
      "file": "saas-product-provisioning.html",
      "title": "Provisioning \u2014 GoClaw CRM \u2014 BuildWithHQ (preview)",
      "heading": "Provisioning & deployment",
      "subtitle": "Durable stage history from control-plane registration through runtime bootstrap.",
      "route": "/apps/:saasAppId/provisioning",
      "access": "CanDeploy",
      "retrieve": "sp_Builder_GetAppAdminDetail returns app/routing state; provisioning history must use stable keyset pagination over SaaSAppProvisioningLogId . Show the durable stage code, safe message, timestamp, and retry eligibility.",
      "save": "Retry queues only the failed retryable worker stage with an idempotency key. The browser never creates databases, restores backups, stamps identity, creates tenant roles, or reruns tripwire procedures directly.",
      "success": "Active is allowed with primary domain Pending . BuilderOnly is allowed with container NotStarted . Partial database creation blocks automation and requires operator-safe recovery. Worker logs expose safe messages and correlation IDs, not provider secrets.",
      "pageId": "saas-product-provisioning",
      "category": "SaaS Products",
      "implementationStatus": "worker-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#saas-product-provisioning"
    },
    {
      "file": "saas-products.html",
      "title": "SaaS products \u2014 BuildWithHQ (preview)",
      "heading": "SaaS products",
      "subtitle": "Every app your organization builds and resells, with live provisioning health.",
      "route": "/apps",
      "access": "authenticated",
      "retrieve": "sp_Builder_ListApps returns the account-scoped portfolio including provisioning state, deployment type, tenant counts, and appPurpose ( Standard | TemplateWorkspace ) \u2014 the Workspace chip above.",
      "save": "Read-only page. \"New SaaS product\" routes to the create flow (gated by CanCreateSaaS); opening a product revalidates ownership via sp_Builder_ResolveAppRouting .",
      "success": "Leads with visual health state \u2014 cards, not a table (design review #1). Transient states poll and settle without a reload. Create beyond the plan limit returns 422 (51216) with an upgrade path. Template workspaces are excluded from that limit \u2014 the count gate reads fn_CustomerBillableSaaSAppCount (Standard products only). Workspaces: fixed 5/2/2/0\u00a0GB write-once storage ( 50224 ), builder-plane only, no custom domains ( 54406 ).",
      "pageId": "saas-products",
      "category": "SaaS Products",
      "implementationStatus": "journey-aligned-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#saas-products"
    },
    {
      "file": "security-overview.html",
      "title": "Security overview \u2014 BuildWithHQ (preview)",
      "heading": "Security overview",
      "subtitle": "Your organization's security posture at a glance.",
      "route": "/security",
      "access": "CanManageSecurity",
      "retrieve": "One read from sp_Builder_GetSecurityOverview (gated on CanManageSecurity ): member MFA and email-verification posture plus every active session account-wide. Personal-plane session listing stays on sp_Builder_ListMySessions ; recent events from sp_Builder_ListAuditLog .",
      "save": "Read-only. Revoking any listed session routes to sp_Auth_RevokeSession , which already permits admins with CanManageSecurity to revoke another member\u2019s session.",
      "success": "Posture aggregates three real readers. Failed-login and session data are live. MFA gaps are surfaced per member.",
      "pageId": "security-overview",
      "category": "Security & Audit",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#security-overview"
    },
    {
      "file": "seller-artifacts.html",
      "title": "Seller artifacts \u2014 BuildWithHQ (preview)",
      "heading": "Seller artifacts",
      "subtitle": "Published template and appliance versions you've registered to sell.",
      "route": "/marketplace/seller/artifacts",
      "access": "CanManageMarketplaceStore",
      "retrieve": "Registered artifacts read from the seller-artifact records with source version and fingerprint.",
      "save": "Register \u2192 sp_Marketplace_RegisterTemplateVersion or sp_Marketplace_RegisterApplianceVersion (published + owned only).",
      "success": "Only published, owned versions are eligible. Fingerprint pins the exact artifact. Artifacts back product components.",
      "pageId": "seller-artifacts",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-artifacts"
    },
    {
      "file": "seller-dashboard.html",
      "title": "Seller dashboard \u2014 BuildWithHQ (preview)",
      "heading": "Seller dashboard",
      "subtitle": "Your storefront performance across products and releases.",
      "route": "/marketplace/seller",
      "access": "CanManageMarketplaceStore",
      "retrieve": "sp_Marketplace_GetSellerDashboard returns product/release counts, active offers, and gross sales.",
      "save": "Read-only. Actions route to products, offers, and payouts.",
      "success": "Metrics scoped to the seller profile. Onboarding state gates payouts. Sales feed is live.",
      "pageId": "seller-dashboard",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-dashboard"
    },
    {
      "file": "seller-offers-pricing.html",
      "title": "Offers & pricing \u2014 BuildWithHQ (preview)",
      "heading": "Offers & pricing \u00b7 Invoice Suite",
      "subtitle": "Prices, billing models, and usage meters for this product.",
      "route": "/marketplace/seller/products/:marketplaceProductId/offers",
      "access": "CanManageMarketplaceStore",
      "retrieve": "Offers/prices/meters read from MarketplaceOffers , MarketplacePrices , MarketplaceUsageMeters .",
      "save": "Create \u2192 sp_Marketplace_CreateOffer , CreatePrice , CreateUsageMeter ; go live \u2192 ActivateOffer .",
      "success": "Amounts in integer cents + currency. Usage offers bind a meter. Only active offers are purchasable.",
      "pageId": "seller-offers-pricing",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-offers-pricing"
    },
    {
      "file": "seller-payouts.html",
      "title": "Payouts \u2014 BuildWithHQ (preview)",
      "heading": "Payouts",
      "subtitle": "Transfers from your available balance to your connected account.",
      "route": "/marketplace/seller/payouts",
      "access": "CanManageMarketplacePayouts",
      "retrieve": "Balance + history read from MarketplacePayouts and the seller ledger.",
      "save": "Request \u2192 sp_Marketplace_RequestPayout , allowed only when profile + payout state permit.",
      "success": "Only available (cleared) balance is payable. Connected-account status gates requests. Payout completion is a service contract.",
      "pageId": "seller-payouts",
      "category": "Marketplace Seller",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-payouts"
    },
    {
      "file": "seller-product-editor.html",
      "title": "Product editor \u2014 BuildWithHQ (preview)",
      "heading": "Edit product \u00b7 Invoice Suite",
      "subtitle": "Storefront details buyers see.",
      "route": "/marketplace/seller/products/:marketplaceProductId/edit",
      "access": "CanManageMarketplaceStore",
      "retrieve": "Product loaded from MarketplaceProducts .",
      "save": "Saved via sp_Marketplace_CreateProduct (upsert semantics for the product record).",
      "success": "Slug uniqueness enforced. Media are references. Publishing happens at the version level.",
      "pageId": "seller-product-editor",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-product-editor"
    },
    {
      "file": "seller-product-version.html",
      "title": "Version composer \u2014 BuildWithHQ (preview)",
      "heading": "Version composer \u00b7 Invoice Suite v4",
      "subtitle": "Attach artifacts, wire actions, and submit for review.",
      "route": "/marketplace/seller/product-versions/:versionId",
      "access": "CanManageMarketplaceStore",
      "retrieve": "Version + components read from MarketplaceProductVersions and its component/binding tables.",
      "save": "Create \u2192 sp_Marketplace_CreateProductVersion ; components \u2192 AddProductComponent / RemoveProductComponent ; AddActionBinding ; submit \u2192 SubmitProductVersion ; SetRecommendedProductVersion .",
      "success": "Components must be active seller-owned artifacts. Submit locks the version for review. Recommended is the buyer default.",
      "pageId": "seller-product-version",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-product-version"
    },
    {
      "file": "seller-products.html",
      "title": "Seller products \u2014 BuildWithHQ (preview)",
      "heading": "Products",
      "subtitle": "Your marketplace listings and their current versions.",
      "route": "/marketplace/seller/products",
      "access": "CanManageMarketplaceStore",
      "retrieve": "Products read from MarketplaceProducts + MarketplaceProductVersions (current version + status).",
      "save": "Create \u2192 sp_Marketplace_CreateProduct ; versions composed on the version page.",
      "success": "A product wraps one or more versions. Only approved versions are buyable. Recommended version is buyer default.",
      "pageId": "seller-products",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-products"
    },
    {
      "file": "seller-profile.html",
      "title": "Store profile \u2014 BuildWithHQ (preview)",
      "heading": "Store profile",
      "subtitle": "How your storefront appears to buyers.",
      "route": "/marketplace/seller/profile",
      "access": "CanManageMarketplaceStore",
      "retrieve": "Profile read from the seller-profile record (slug, name, description, media).",
      "save": "Create \u2192 sp_Marketplace_CreateSellerProfile ; edit \u2192 sp_Marketplace_UpdateSellerProfile .",
      "success": "Slug is unique across sellers. Profile must exist before listing. Media are references, not blobs.",
      "pageId": "seller-profile",
      "category": "Marketplace Seller",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-profile"
    },
    {
      "file": "seller-revenue.html",
      "title": "Revenue \u2014 BuildWithHQ (preview)",
      "heading": "Revenue",
      "subtitle": "Gross sales, platform fees, and your net earnings.",
      "route": "/marketplace/seller/revenue",
      "access": "CanViewMarketplaceRevenue",
      "retrieve": "Earnings read from MarketplaceSellerLedger ; summary from sp_Marketplace_GetSellerDashboard .",
      "save": "Read-only. Payouts are requested on the payouts page.",
      "success": "Ledger separates gross, fee, and net. Pending vs available is tracked. All amounts in integer cents.",
      "pageId": "seller-revenue",
      "category": "Marketplace Seller",
      "implementationStatus": "external-integration-required",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#seller-revenue"
    },
    {
      "file": "shared-resources.html",
      "title": "Shared resources \u2014 BuildWithHQ (preview)",
      "heading": "Shared resources",
      "subtitle": "Your account's pooled storage across all SaaS products.",
      "route": "/resources",
      "access": "CanManageStorage",
      "retrieve": "Pool totals from vw_CustomerStoragePool ; per-app allocations roll up from sp_Builder_GetAccountAdminDashboard .",
      "save": "Read-only overview; per-app changes happen on the per-app Storage page via UpsertStorageAllocation .",
      "success": "Pools sum each per-app allocation. Over-allocation is prevented at write time. Container pool nearing cap is flagged.",
      "pageId": "shared-resources",
      "category": "Billing & Resources",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#shared-resources"
    },
    {
      "file": "sitemap.html",
      "title": "Sitemap \u2014 BuildWithHQ (preview)",
      "heading": "Sitemap",
      "subtitle": "The whole builder console at a glance \u2014 expand any branch and jump straight to a page, several levels down.",
      "route": "/sitemap",
      "access": "authenticated",
      "retrieve": "The tree renders from the frontend route registry \u2014 the same structure the router mounts, so it can never drift. vw_BuilderUserPermissions supplies the flags for permission chips; sp_Builder_ListApps expands :saasAppId branches into the account\u2019s real products.",
      "save": "Read-only. Every node navigates; expand / collapse state is ephemeral client state.",
      "success": "Restricted pages are shown with a chip, not hidden \u2014 scope stays visible while the server enforces on navigation. Deep links land exactly where the tree says (multi-level drill-down). With zero products, parameterized branches stay collapsed with a placeholder.",
      "pageId": "sitemap",
      "category": "Dashboard & Account",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#sitemap"
    },
    {
      "file": "team-invitations.html",
      "title": "Invitations \u2014 BuildWithHQ (preview)",
      "heading": "Invitations",
      "subtitle": "Pending and past invitations to your organization.",
      "route": "/team/invitations",
      "access": "CanManageUsers",
      "retrieve": "sp_Builder_ListInvitations returns invitations with status, role, and expiry, scoped to the account.",
      "save": "Create \u2192 sp_Builder_CreateInvitation ; resend \u2192 sp_Builder_ResendInvitation ; revoke \u2192 sp_Builder_RevokeInvitation .",
      "success": "Only unaccepted invites can be resent or revoked. Expiry countdown reflects the token TTL. New invite checks seat availability.",
      "pageId": "team-invitations",
      "category": "Team & Permissions",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#team-invitations"
    },
    {
      "file": "team-member-detail.html",
      "title": "Team member \u2014 BuildWithHQ (preview)",
      "heading": "Dan Kowalski",
      "subtitle": "dan@scottholdings.com \u00b7 Developer \u00b7 joined Mar 2026",
      "route": "/team/users/:builderUserId",
      "access": "CanManageUsers",
      "retrieve": "User loaded from sp_Builder_ListUsers (single) joined to vw_BuilderUserPermissions for the effective set.",
      "save": "Role change \u2192 sp_Builder_UpdateUserRole ; disable/enable \u2192 sp_Builder_SetUserStatus . Both carry RowVersion + audit.",
      "success": "Owner rows are protected \u2014 role/disable are blocked. Permission preview is derived, never edited directly. Stale writes return 409.",
      "pageId": "team-member-detail",
      "category": "Team & Permissions",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#team-member-detail"
    },
    {
      "file": "team-members.html",
      "title": "Team members \u2014 BuildWithHQ (preview)",
      "heading": "Team members",
      "subtitle": "Manage who can access your BuildWithHQ organization and what each person can do.",
      "route": "/team",
      "access": "CanManageUsers",
      "retrieve": "sp_Builder_ListUsers (server-injected identity) \u2192 FOR JSON PATH roster via vw_BuilderUserPermissions ; read with ExecProcJson , no secrets in payload.",
      "save": "None on this list. Role \u2192 sp_Builder_UpdateUserRole ; status \u2192 sp_Builder_SetUserStatus ; invite \u2192 sp_Builder_CreateInvitation ; transfer \u2192 sp_Builder_TransferOwnership .",
      "success": "Roster scoped to the account. Seat count reconciles to plan limit. 403 renders permission-denied, not an error.",
      "pageId": "team-members",
      "category": "Team & Permissions",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#team-members"
    },
    {
      "file": "template-components.html",
      "title": "Template components \u2014 BuildWithHQ (preview)",
      "heading": "Template components",
      "subtitle": "Catalog of reusable template components available to templates and packs.",
      "route": "/templates/components",
      "access": "CanManageTemplates",
      "retrieve": "Catalog read over TemplateComponents (key, type, scope, schema summary).",
      "save": "Authoring flows through template versions \u2014 sp_NewTemplateVersion / sp_SaveTemplateVersion \u2014 not raw DML.",
      "success": "Components are pack-scoped or global. Schema/props drive the editor. Referenced by bindings.",
      "pageId": "template-components",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-components"
    },
    {
      "file": "template-data-bindings.html",
      "title": "Data bindings \u2014 BuildWithHQ (preview)",
      "heading": "Data bindings",
      "subtitle": "Catalog of reusable data bindings available to templates and packs.",
      "route": "/templates/data-bindings",
      "access": "CanManageTemplates",
      "retrieve": "Catalog read over TemplateDataBindings (key, type, scope, schema summary).",
      "save": "Authoring flows through template versions \u2014 sp_NewTemplateVersion / sp_SaveTemplateVersion \u2014 not raw DML.",
      "success": "Bindings join a component to a connector. Validated against both references. Query/action config lives here.",
      "pageId": "template-data-bindings",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-data-bindings"
    },
    {
      "file": "template-data-connectors.html",
      "title": "Data connectors \u2014 BuildWithHQ (preview)",
      "heading": "Data connectors",
      "subtitle": "Catalog of reusable data connectors available to templates and packs.",
      "route": "/templates/data-connectors",
      "access": "CanManageTemplates",
      "retrieve": "Catalog read over TemplateDataConnectors (key, type, scope, schema summary).",
      "save": "Authoring flows through template versions \u2014 sp_NewTemplateVersion / sp_SaveTemplateVersion \u2014 not raw DML.",
      "success": "Connectors are query or action. Config schema is validated. Bindings reference connectors by key.",
      "pageId": "template-data-connectors",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-data-connectors"
    },
    {
      "file": "template-detail.html",
      "title": "Template detail \u2014 BuildWithHQ (preview)",
      "heading": "Invoice layout Page",
      "subtitle": "Key tpl.invoice.layout \u00b7 version v4 \u00b7 published Jul 20, 2026",
      "route": "/templates/:templateVersionId",
      "access": "CanManageTemplates",
      "retrieve": "sp_GetTemplateVersionPayload loads identity, status, payload, metadata, and preview for any status.",
      "save": "New version \u2192 sp_NewTemplateVersion ; publish \u2192 sp_PublishTemplateVersion . Register uses sp_MarketplaceBridge_GetTemplateVersion (published-only + fingerprint).",
      "success": "The bridge proc is only for marketplace registration. Published/archived versions are read-only. Register requires a Published version.",
      "pageId": "template-detail",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-detail"
    },
    {
      "file": "template-editor.html",
      "title": "Template editor \u2014 BuildWithHQ (preview)",
      "heading": "Edit \u00b7 Invoice layout Draft v5",
      "subtitle": "Structured editor and raw JSON stay in sync. Only drafts are editable.",
      "route": "/templates/:templateVersionId/edit",
      "access": "CanManageTemplates",
      "retrieve": "sp_GetTemplateVersionPayload loads the draft payload, CSS variables, and metadata into both panes.",
      "save": "Save \u2192 sp_SaveTemplateVersion (payload + css + metadata + notes); publish \u2192 sp_PublishTemplateVersion .",
      "success": "Only Draft versions are editable. Structured and JSON views stay in sync. Publish is blocked until validation passes.",
      "pageId": "template-editor",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-editor"
    },
    {
      "file": "template-library.html",
      "title": "Template library \u2014 BuildWithHQ (preview)",
      "heading": "Template library",
      "subtitle": "Reusable artifacts. The current development database has no published installable versions yet.",
      "route": "/templates",
      "access": "CanManageTemplates",
      "retrieve": "List actual version rows, not catalog concepts. Published artifacts are immutable. Surface draft, published and archived status, payload fingerprint state, validation state and ownership scope. The current development database should return an empty published list.",
      "save": "Read-only list. \"New template\" \u2192 sp_NewTemplateVersion creates a Draft to edit.",
      "success": "No mock templates appear after API integration. Drafts can be edited; published versions cannot. Install actions require an exact version ID and validated fingerprint. Empty state links to authoring, not to a fake install.",
      "pageId": "template-library",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-library"
    },
    {
      "file": "template-pack-detail.html",
      "title": "Pack detail \u2014 BuildWithHQ (preview)",
      "heading": "Roofing CRM Pro Yours",
      "subtitle": "Key roofing-crm-pro-9f2c1a \u00b7 Field Service \u00b7 Published v2 \u00b7 saved from Summit Roofing",
      "route": "/template-packs/:templatePackKey",
      "access": "CanManageTemplates",
      "retrieve": "Pack header + members from the Template Library, gated by fn_TemplatePack_CanUse with the server-injected account. A non-owner requesting this key gets not-found \u2014 never a permission hint.",
      "save": "Validate \u2192 sp_ValidateTemplatePack ; publish \u2192 sp_PublishTemplatePack with @RequestingAccountId (a published:false result carries the validation report). \u201cUse for new product\u201d opens the create wizard with this pack preselected; provisioning passes @RequestingAccountId into sp_SeedAppFromTemplatePack .",
      "success": "56110 publish by a non-owner refused. 50019 seed by a non-owner refused \u2014 fail-closed if the account id is omitted. 50009 a draft cannot seed; the UI disables the action but the server rule is the enforcement. Publish rolls all members to a pinned pack version.",
      "pageId": "template-pack-detail",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-pack-detail"
    },
    {
      "file": "template-packs.html",
      "title": "Template packs \u2014 BuildWithHQ (preview)",
      "heading": "Template packs",
      "subtitle": "Catalog concepts plus immutable versions. No pack is installable until it has a published schema-v2 version and verified members.",
      "route": "/template-packs",
      "access": "CanManageTemplates",
      "retrieve": "sp_GetTemplatePackCatalog may return catalog concepts, but the installable selector must join an exact TemplateVersionId where TemplateType=Pack , Status=Published , ArtifactSchemaVersion=2 , both fingerprints are present and valid, all member fingerprints verify, manifest counts match, and at least one Page member exists.",
      "save": "Save-as-pack creates a private unpublished draft. Validation prepares the immutable member set. Publication writes a new version; never mutate an existing published version. App installation pins TemplatePackVersionId , not a pack key and not \u201clatest.\u201d",
      "success": "Catalog-only rows show \u201cNot ready\u201d and cannot be selected. Owned packs stay isolated to the owning builder account. Snapshot installation creates tenant-private copies with no downstream sync. Fingerprint or manifest mismatches fail closed.",
      "pageId": "template-packs",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-packs"
    },
    {
      "file": "template-shell-design.html",
      "title": "Shell design \u2014 BuildWithHQ (preview)",
      "heading": "Shell design",
      "subtitle": "Menus, themes, top bars, sidebars, layouts, widgets, and actions for the app shell.",
      "route": "/templates/shell",
      "access": "CanManageTemplates",
      "retrieve": "Reads over TemplateMenus , TemplateThemes , TemplateTopBars , TemplateSidebars , TemplateLayouts , TemplateWidgets , TemplateActions .",
      "save": "Versioned assets publish via sp_PublishTemplateVersion (Page/Theme/TopBar/Sidebar/Module). Menu/Layout/Widget/Action have no publish-mirror branch yet.",
      "success": "Seven asset types share one editor. Preview renders the composed shell. Confirm live-row vs versioned per type.",
      "pageId": "template-shell-design",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-shell-design"
    },
    {
      "file": "template-user-types.html",
      "title": "User types \u2014 BuildWithHQ (preview)",
      "heading": "User types",
      "subtitle": "Catalog of reusable user types available to templates and packs.",
      "route": "/templates/user-types",
      "access": "CanManageTemplates",
      "retrieve": "Catalog read over TemplateUserTypes (key, type, scope, schema summary).",
      "save": "Authoring flows through template versions \u2014 sp_NewTemplateVersion / sp_SaveTemplateVersion \u2014 not raw DML.",
      "success": "Each type maps to a dashboard page. Carries default role/data-role. Seeds tenant personas.",
      "pageId": "template-user-types",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-user-types"
    },
    {
      "file": "template-validation.html",
      "title": "Validation \u2014 BuildWithHQ (preview)",
      "heading": "Validation \u00b7 Invoice layout",
      "subtitle": "Structural and reference checks run before a version can be published.",
      "route": "/templates/:templateVersionId/validation",
      "access": "CanManageTemplates",
      "retrieve": "Validation summary for the version; pack-level checks run via sp_ValidateTemplatePack .",
      "save": "Read-only view of results. Publish is gated on a clean run through sp_PublishTemplateVersion .",
      "success": "Errors block publication; warnings do not. Schema-v2 manifest identity, version, member count and fingerprints must match. Every member payload fingerprint verifies before publication or installation. Published versions are immutable and selected by exact ID.",
      "pageId": "template-validation",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-validation"
    },
    {
      "file": "template-version-history.html",
      "title": "Version history \u2014 BuildWithHQ (preview)",
      "heading": "Version history \u00b7 Invoice layout",
      "subtitle": "Every version is immutable once published. Changes create a new draft.",
      "route": "/templates/:templateKey/versions",
      "access": "CanManageTemplates",
      "retrieve": "sp_GetTemplateVersions filtered by template key returns the full version timeline with author, dates, and status.",
      "save": "Read-only. Each fingerprint is computed by fn_Marketplace_TemplateFingerprint at publish time.",
      "success": "Exactly one Published version at a time. Fingerprints pin marketplace installs. Older versions stay Archived and immutable.",
      "pageId": "template-version-history",
      "category": "Template Library & AI",
      "implementationStatus": "catalog-schema-ready-content-not-seeded",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#template-version-history"
    },
    {
      "file": "app-organizations.html",
      "title": "Organizations — BuildWithHQ",
      "heading": "Organizations",
      "subtitle": "AppAccounts hosted by the selected SaaS app.",
      "route": "/apps/:saasAppId/organizations",
      "access": "authenticated builder with access to the selected app",
      "retrieve": "The control plane resolves the selected app's registered Core route and calls sp_Builder_ListAppAccounts with SQL-side search and keyset pagination.",
      "save": "Read-only in task 29a. Account administration actions are separate reviewed tasks.",
      "success": "Only organizations from the selected builder-owned app are returned; no database name or remote fleet enumeration reaches the browser.",
      "pageId": "app-organizations",
      "category": "SaaS Products",
      "implementationStatus": "production-contract",
      "scaffoldData": "none",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#app-organizations"
    },
    {
      "file": "verify-email.html",
      "title": "Verify email \u2014 BuildWithHQ (preview)",
      "heading": "Email verified",
      "subtitle": "Your address is confirmed. Publishing and provisioning are unlocked for your account.",
      "route": "/verify-email",
      "access": "public (token)",
      "retrieve": "No read \u2014 the token in the link is the whole input.",
      "save": "sp_Auth_CompleteEmailVerification consumes the SHA-256 token hash; invalid, expired, or used links throw 55014 \u2192 422 with a resend action.",
      "success": "Idempotent-safe messaging on repeat visits. Raw tokens are never persisted or logged. Signed-in users can resend from account settings.",
      "pageId": "verify-email",
      "category": "Authentication & Onboarding",
      "implementationStatus": "schema-backed-scaffold",
      "scaffoldData": "preview-fixture",
      "requiredStates": [
        "loading",
        "empty",
        "success",
        "forbidden",
        "not-found",
        "validation-error",
        "retryable-failure"
      ],
      "identityPolicy": "server-injected",
      "manifestAnchor": "#verify-email"
    }
  ]
}
