D057 — Unattended installer profile
The unattended installer is driven by a validated JSON install profile with explicit application, database, administrator, secrets, and selected-plugin sections. That keeps config-driven installs deterministic and lets the same shared installer readiness report back both text and JSON output without duplicating validation rules.
D056 — CLI installer entrypoint
The CLI installer lives at the repository root under `install/install.php` and delegates to the shared installer report. A sibling `install/checks.php` helper returns the same readiness data so terminal installation flows can stay in sync with the web installer without duplicating checks.
D055 — Web installer root detection
The web installer evaluates the project root derived from the framework bootstrap path, not the framework subdirectory itself. That keeps readiness checks aligned with the actual install surface, including the root-level `recovery/` folder and other project-level writable paths.
D054 — Feature flags
Feature flags are implemented as a framework-native in-memory service with installation, company, and user scopes. Resolution uses the most specific matching scope first: user, then company, then installation, so rollout controls remain predictable for gradual releases and targeted disablement.
D053 — Secrets service
The secrets service is implemented as a provider-backed framework abstraction rather than a config helper. Local storage uses AES-256-GCM with a required master key, logical-key lookup, and versioned immutable records so secret values stay encrypted at rest while future external secret providers can implement the same contract.
D051 — Audit service
The audit service is implemented as an append-only framework layer on top of the structured logger. It keeps business and security state changes in immutable audit records, and it applies the same sensitive-data redaction rules before persisting or logging audit metadata.
D052 — Deterministic retention checks
`FileService::upload` accepts an optional explicit timestamp so storage retention can be tested deterministically without depending on the host clock. The smoke test now passes the same baseline time through upload, temporary URL verification, and delete checks.
decisions.md
D092 - Explicit endpoint bootstrap
Generated deployment entrypoints are tiny managed `index.php` shims that set explicit endpoint identity in environment variables and then require a single shared bootstrap path from the configurable `GRAND_PROJECT_BOOTSTRAP` environment variable. That removes hostname discovery as a requirement for deployed Website Builder entrypoints and avoids baking secrets or hard-coded framework paths into every generated site file.
D093 - Endpoint deployment safety
Endpoint deployment is deny-by-default on filesystem scope: Local Web Roots must be in an allowlisted path, must be writable, and must not already belong to another registered endpoint. Unmanaged existing `index.php` files are never silently overwritten; replacement requires explicit approval and the prior file is backed up before the managed entrypoint is written.
decisions.md
D091 — Business Intelligence & Reporting module foundation
The BI & Reporting implementation is a real top-level plugin module with the `bir_` table prefix and no hard dependencies. The batch covers dashboards, KPIs, authorised analytical views, reporting, and exports while preserving the rule that derived analytical data is not authoritative over source plugins.
D090 — Supplier Portal module foundation
The Supplier Portal implementation is a real top-level plugin module with the `spl_` table prefix and a required dependency on Suppliers. The batch covers supplier records, catalogues, products, documents, purchasing interactions, and communications without inventing extra commerce responsibilities or loosening the supplier boundary.
D089 — Partner Portal module foundation
The Partner Portal implementation is a real top-level plugin module with the `par_` table prefix and no hard dependencies. The batch covers partner, distributor, and reseller-facing access based on role and relationship, plus document and communication surfaces, while keeping the portal boundary separate from Supplier Portal and the later commerce stack.
D088 — OpsRoom module foundation
The OpsRoom implementation is a real top-level plugin module with the `ops_` table prefix and no hard dependencies. The batch covers projects, procedures, documents, internal workflows, business knowledge, and management tools, while keeping Tendering outside the module boundary exactly as the specification requires.
D087 — Developer Platform module foundation
The Developer Platform implementation is a real top-level plugin module with the `dev_` table prefix and no hard dependencies, matching the spec's minimal-dependency stance for developer tooling. The batch covers scaffolding, diagnostics, migrations, test support, deployment utilities, documentation, plugin validation, and contract testing without turning the tooling surface into a general-purpose platform service.
D086 — Order Processing module foundation
The Order Processing implementation is a real top-level plugin module with the `ord_` table prefix and a required dependency on Inventory Tracking. The batch covers allocation, fulfilment, picking, packing, dispatch, completion, cancellation, and recovery states without collapsing into the upstream inventory ownership boundary or inventing extra commerce responsibilities.
D085 — Inventory Tracking module foundation
The Inventory Tracking implementation is a real top-level plugin module with the `inv_` table prefix and a required dependency on Products. The batch covers stock, warehouses, stores, bin locations, vehicles, transfers, receipts, allocations, returns, adjustments, serials, batches/lots, reorder levels, stocktakes, movement history, and valuation without collapsing into the later Order Processing boundary.
D084 — Suppliers module foundation
The Suppliers implementation is a real top-level plugin module with the `sup_` table prefix and an optional/recommended integration with Products. The batch covers supplier records, contacts, terms, lead times, catalogues, preferred-supplier status, purchasing information, and supplier/product relationships without inventing extra module boundaries or hard dependencies.
D083 — ServiceVault module foundation
The ServiceVault implementation is a real top-level plugin module with the `svc_` table prefix and a required dependency on Asset Registry. The internal customer and technician portals are represented as nested module folders under ServiceVault so the portal boundary exists without turning them into standalone top-level plugin modules. Optional integrations remain optional and do not become hard dependencies.
D082 — Asset Registry module foundation
The Asset Registry implementation is a real top-level plugin module with the `ar_` table prefix and a data model that covers assets, asset types, specifications, properties, locations, relationships, lifecycle events, maintenance, warranties, manuals, documents, QR identifiers, and equipment knowledge. The asset-class plugin architecture is represented as an internal catalog plus an `asset-classes/Pool` reference package so the first specialised class implementation exists without splitting the module boundary into a new top-level plugin.
D067 — Updates administration surface
The administration Updates page is a read-only operator surface built on the existing release manager, update client, and restore-point manager. It summarizes the staged/current/previous release slots, the current update plan from the same metadata format used by the CLI client, and the backup preflight state so operators can review update readiness without duplicating release logic in the admin layer.
D066 — Dependency visualiser graph source
The dependency visualiser is manifest-driven and reads relationships from `plugin.json` rather than from a separate admin-maintained graph store. Required, optional, recommended, and conflicting edges are all shown in the admin view, and missing targets are treated as visible warnings so operators can see the dependency gap without conflating it with a runtime failure.
D065 — Plugin manager manifests and dependency map
The administration plugin manager is manifest-driven: it inventories plugin module directories, validates `plugin.json`, and renders module details from the manifest rather than from separate admin-specific records. The dependency map shown in the admin UI is generated from the manifest `dependencies.required` list so operators see the same dependency truth that the plugin runtime consumes.
D064 — Administration health dashboard base paths
The administration health dashboard is rooted at the repository-level `administration/health.php` entrypoint, but it bootstraps the Common Application Framework from `Core Platform/Common Application Framework/`. The dashboard service treats the framework directory as the installer base path and derives the project root separately so framework readiness checks, restore-point discovery, plugin discovery, and storage checks all evaluate the correct filesystem surface.
D001 — Plugin categorisation
All installable applications are called **Plugin Modules** and live under `Plugin Modules/`. Do not use separate “foundational†and “business†plugin categories.
D002 — Core System Services
Infrastructure service plugins live under `Core System Services/`:
- Update Server
- Backup / Restore Server
- Storage Server
- Analytics Server
D003 — ServiceVault portals
Customer Portal and Technician Portal are internal modules of ServiceVault, not standalone Plugin Modules.
D004 — ServiceVault dependency
ServiceVault requires Asset Registry.
D005 — Tendering
Tendering is not part of OpsRoom. Project Tendering is its own Plugin Module.
D006 — Sales module naming
Sales Floor was renamed to **Lead Pipeline**.
D007 — Lead Pipeline supplier relationship
Lead Pipeline does not require Suppliers. Suppliers integration is optional.
D008 — Product/inventory/order chain
Products is authoritative for the product catalogue.
Inventory Tracking requires Products.
Order Processing requires Inventory Tracking.
D009 — E-commerce dependencies
E-commerce requires:
- Products
- Suppliers
- Payments & Billing
- Invoice Generation
Inventory Tracking, Order Processing and Website Builder are optional E-commerce integrations.
D010 — Payments
Payments & Billing requires Invoice Generation and supports Stripe, PayPal, future payment providers and Xero API accounting integration.
D011 — Account model
One user account can simultaneously hold customer, supplier, partner, technician, employee and other authorised relationships.
D012 — Data ownership
Each business record has one authoritative owning plugin. Cross-plugin direct database access is prohibited.
D013 — Plugin manifests
All hard dependencies, capabilities, version requirements, permissions, migrations and lifecycle metadata are declared in `plugin.json`.
D014 — Updates
No framework/plugin update proceeds without a valid pre-update restore point.
D015 — Historical records
Use reference + snapshot where historical accuracy matters.
D016 — AI access
AI agents use approved tools/scopes through the same authorisation architecture and do not receive unrestricted database access.
D017 — Canonical empty folders
Canonical empty project folders are kept versionable with `.gitkeep` placeholders until the corresponding framework or plugin files are added.
D018 — Local bootstrap autoloader
The framework bootstrap includes a local PSR-4 autoloader fallback so the project can bootstrap before Composer dependencies are installed. Composer remains the canonical package manager and the root `composer.json` is the authoritative package definition.
D019 — Baseline CI scope
The first CI pass validates the Composer manifest and PHP syntax before introducing package-installed analysis tools. This keeps the initial pipeline lightweight while the framework surface area is still small.
D020 — Repository root documentation
The authoritative master specification and supporting handover records are mirrored into the repository root so active implementation work can reference them without leaving the project tree.
D021 — Environment and config loading
The framework bootstrap loads project environment variables from a local `.env` file when present, but it does not overwrite already-defined environment values. Configuration is loaded through a dedicated loader and accessed through dot-notation keys on the `Application` object.
D022 — Request/response abstractions
The framework request object is immutable and represents method, URI, path, headers, query data, body content and attributes. The response object is immutable as well and supports plain text and JSON helpers for the later router and middleware phases.
D023 - Router and middleware pipeline
The framework router uses exact-match method/path registration with a 404 fallback for unmatched routes. Middleware is executed in registration order around route handlers, and middleware plus handlers must return framework Response objects so the dispatch contract stays consistent.
D024 — Database abstraction
The framework database layer exposes a connection interface with PDO and in-memory implementations. The in-memory adapter exists so the abstraction can be verified in environments without a local PDO driver, while the PDO adapter remains the production path.
D025 — Local PDO driver enablement
The local Windows PHP CLI was configured with `pdo_sqlite` and `sqlite3` enabled so the database abstraction could be verified against a real PDO driver in this environment.
D026 — Request and correlation IDs
The HTTP request object owns a request ID and a correlation ID. Incoming request headers are honored when present, and generated identifiers are used otherwise so logs and later middleware can share the same request context.
D027 - Central Authentication integration contract
The framework owns the central authentication integration contract rather than any plugin. The contract is represented as a gateway interface plus immutable value objects for identities, sessions, service credentials and service health.
D028 — User context
The framework owns a dedicated user context object for request-scoped identity data. It is built from the central authentication contract and carries authenticated identity details, session linkage and request identifiers, while keeping company/relationship resolution for a later phase.
D029 — Company and relationship context
The framework owns the company/relationship context as request-scoped identity state. It tracks the active company, related company records and relationship records, while leaving role and policy resolution for later phases.
D030 — Roles
The framework owns role context separately from permissions. Roles are represented as records and assignments that can be global or company-scoped, with request-scoped resolution based on the authenticated user and company context.
D031 — Permissions
The framework owns permission context separately from roles. Permissions are represented as records and grants that can be global or company-scoped, with request-scoped resolution layered on top of identity, role and company context.
D032 — Record policies
The framework owns record policy context separately from permissions. Record policies are represented as records and grants that can be global or company-scoped, with request-scoped resolution layered on top of identity, role, company and target-record context.
D033 — Service identities
The framework owns service identity context separately from human authentication. Service identities are represented as scoped identity records with explicit credential linkage, and service credentials track issuance plus rotation lineage so API requests can be authenticated and renewed without changing the security model.
D034 — Plugin manifest schema
The canonical `plugin.json` schema lives in the Common Application Framework as a reusable shared asset. It defines the required manifest structure from the master specification while allowing future manifest extensions without forcing a schema redesign.
D035 — Manifest validator
Plugin manifest validation is implemented in the Common Application Framework as a framework-native validator that checks the canonical required fields, nested lifecycle sections, and dependency shape before plugin runtime work consumes the manifest.
D036 — Plugin discovery
Plugin discovery scans immediate plugin module directories, validates each `plugin.json` through the manifest validator, and reports per-module errors without hiding successful discoveries from sibling folders.
D037 — Plugin registry
The plugin registry is an in-memory authoritative store for discovered modules and their lifecycle state. Registry entries are immutable snapshots that can be promoted through lifecycle statuses so later install and enable/disable work can build on a single source of truth.
D038 — Dependency resolver
Dependency resolution is handled in the Common Application Framework from registry entries and manifest data. The resolver validates framework compatibility, required dependency presence and status, conflicts, and circular graphs before later lifecycle code consumes an install order.
D039 — Capability registry
Capability registration is derived from plugin manifests and the registry state. The Common Application Framework keeps an in-memory capability index keyed by declared `provides` entries so later lifecycle and dependency checks can ask which enabled plugin supplies a given capability.
D040 — Install lifecycle
Install lifecycle is handled as a registry state transition from discovered to installed after dependency preflight succeeds. Installation does not implicitly enable a plugin; it records the installed state and leaves activation to the enable/disable lifecycle.
D041 — Enable/disable lifecycle and reverse-dependency guard
Plugin activation and deactivation are handled by the Common Application Framework lifecycle manager. Enable flows only promote installed plugins after dependency resolution succeeds, and disable flows refuse to deactivate any plugin that still has enabled reverse dependents. Smoke coverage captures the registry state immediately after the failed disable attempt so later cleanup steps do not invalidate the assertion.
D042 — Dependency lock file
The Common Application Framework maintains an in-memory dependency lock snapshot alongside the plugin registry. The lock stores exact installed plugin versions, package names, lifecycle status and timestamps, and the installer plus lifecycle manager refresh it after state transitions so the snapshot remains aligned with the authoritative registry.
D043 — Events/outbox
Important asynchronous events are handled through a durable transactional outbox in the Common Application Framework. Events are captured as envelopes with source, correlation and payload metadata, persisted through the database abstraction, and can be claimed, dispatched, released or replayed without turning events into imperative commands.
D044 — Queue/workers
Shared queue and worker behavior is implemented as an in-memory Common Application Framework service for now. Jobs carry priorities, scheduled availability, retry and dead-letter metadata, idempotency keys, and progress state, while workers consume registered handlers and move jobs through queued, processing, completed, canceled, retry and dead-letter states.
D045 — Scheduler/locks
The Common Application Framework scheduler owns task execution and uses explicit per-task locks to prevent duplicate scheduled runs. Scheduled task records track the latest run state, next run time, and execution metadata, while the lock registry can be snapshotted and reloaded alongside the scheduler state.
D046 — Workflow/saga engine
Cross-plugin business processes are modeled as saga-style workflows in the Common Application Framework. Workflow steps are executed in order, step history is captured for administration, and compensating actions run in reverse order when a later step fails so the workflow remains inspectable and recoverable without relying on giant SQL transactions.
D047 — Storage abstraction
Shared storage is implemented as a provider-backed abstraction in the Common Application Framework rather than a plugin-specific file helper. The batch establishes file identity, safe filename generation, versioning, retention, visibility, metadata, and signed temporary URLs in the framework layer so the later file service can build on one consistent contract for local and future remote backends.
D048 — File service
The file service sits on top of the storage abstraction and uses the existing authentication contracts to enforce company boundaries, owner metadata, permissions, and record policies. Public files remain readable without extra checks, while private file access and lifecycle operations flow through the same framework-level service so later plugin modules can consume one shared upload/download API.
D049 — PDF/document service
The document engine is implemented as a framework service layered over storage so rendered outputs become immutable historical snapshots with template version and checksum metadata. Instead of relying on an unavailable external PDF runtime, the service includes a deterministic internal PDF writer for batch-safe output while still supporting HTML renders, branding, headers, footers, attachments, numbering, and revision tracking through the shared document contract.
D050 — Notifications/email
Notifications are implemented as a shared framework service with channel preferences, in-app notices, and queued email delivery rather than as plugin-owned mail logic. The email layer keeps sender identities, templates, attachments, delivery records, and development capture in the Common Application Framework so mandatory security notifications can bypass suppression rules and retryable deliveries can be processed through a single reusable contract.
D058 — Update client planning
The update client is a framework-owned planning surface that reuses plugin dependency resolution to produce a dependency-ordered update plan before any package is applied. Update planning must not proceed when the release metadata is incomplete for the target registry, and the plan surface exposes both text and JSON output so the same contract can drive future operator and automation entrypoints.
D059 — Signed update packages
Signed update packages are verified against the downloaded package artifact with checksum validation and an RSA signature over the package bytes. The smoke test generates its own temporary OpenSSL config because the Windows PHP runtime in this environment lacks a usable default `openssl.cnf` path, but the production verifier still depends on real OpenSSL signature checks rather than a mocked checksum-only shortcut.
D060 — Controlled release slot switching
Update deployment uses isolated staging/current/previous directories so release promotion happens by whole-directory rotation instead of piecemeal live-file edits. The manager also keeps a rollback path by restoring the prior current release into the previous slot, which gives the later automatic-rollback work a concrete filesystem primitive to reuse.
D061 — Restore-point snapshots
Restore points are implemented as filesystem-backed manifests that capture the framework files, affected plugin files, database snapshot, plugin registry, configuration, migration state, dependency lock, version metadata, checksums and storage metadata reference that the specification requires. The manager exposes list and inspect operations plus status transitions so later rollback and emergency-recovery work can reuse the same snapshot format.
D062 — Automatic rollback coordination
Automatic rollback is implemented as a coordinator over the restore-point manager and release-slot manager. It marks a restore point as restoring, rolls the release directories back to the prior release, can invoke a database-restoration callback when the file rollback alone is not trustworthy, and then marks the restore point restored so critical-failure recovery remains auditable.
D063 — Independent recovery utility
The emergency recovery utility lives under `recovery/` and runs without depending on the normal framework bootstrap. It accepts a local recovery config file, falls back to `recovery/config.json` when present, and uses its own logging plus restore-point inspection so operators can restore files, database snapshots and configuration even when the main runtime is unhealthy.
D071 — Workflow viewer administration surface
The administration Workflow Viewer page is a read-only operator surface built on the workflow history snapshot format. It shows workflow instance status, recent runs, and step history so operators can review saga execution without duplicating workflow logic in the admin layer.
D070 — Scheduler administration surface
The administration Scheduler page is a read-only operator surface built on the in-memory scheduler snapshot format. It shows scheduled task inventory, due and upcoming work, lock registry state, and runtime readiness so operators can review scheduler health without duplicating scheduler logic in the admin layer.
D069 — Queues/jobs administration surface
The administration Queues / Jobs page is a read-only operator surface built on the in-memory queue snapshot format. It shows queue inventory, status counts, worker readiness, and recent job history so operators can review queue health without duplicating worker logic in the admin layer.
D068 — Backup/restore administration surface
The administration Backup / Restore page is a read-only operator surface built on the existing restore-point manager and recovery file layout. It shows restore-point inventory, latest snapshot integrity, recovery utility readiness, log history, and restore target checks so operators can review backup health without duplicating recovery logic in the admin layer.
D072 — Integration manager administration surface
The administration Integration Manager is a read-only operator surface built on the framework's existing authentication, service-credential, request-tracing, queue, webhook, logging, and audit primitives. It reports the current integration contract and delivery posture without introducing a second integration runtime, and it surfaces the absence of a dedicated rate-limiting service as an explicit gap instead of simulating one.
D073 — Credentials administration surface
The administration Credentials page is a read-only operator surface built on the framework's authentication contract, service credential value objects, and session boundary primitives. It reports credential lifecycle support, scoped issuance, rotation lineage, and credential-related redaction without inventing a persistent credential store or weakening the security model.
D074 — AI agents administration surface
The administration AI Agents page is a read-only operator surface built on the framework's AI gateway assumptions, authentication contract, queueing, audit, logging, and file-access primitives. It reports configured providers, agent scopes, approval gates, and safety controls without introducing an unrestricted AI runtime or direct database access path for agents.
D075 — Logs/audit administration surface
The administration Logs / Audit page is a read-only operator surface built on the framework's structured logging, buffered log handler, audit trail, and redaction primitives. It keeps the audit and logging views in the Common Application Framework while deliberately emitting a redacted summary event so the operator surface can prove sensitive fields are scrubbed in buffered output.
D076 — Diagnostics administration surface
The administration Diagnostics page is a read-only operator surface built on the installer readiness report, PHP runtime inspection, configuration snapshot data, and filesystem/tooling checks. It surfaces low-level platform facts without duplicating the installer flow or creating a second mutable diagnostics store.
D077 — Security centre administration surface
The administration Security Centre page is a read-only operator surface built on the framework's authentication, roles, permissions, record policies, encrypted secrets, logging redaction, notification channels, and update policy primitives. It reports the platform's security posture without inventing a separate mutable security store or bypassing the existing framework contracts.
D080 — Products module foundation
The first real business Plugin Module is implemented as `Plugin Modules/Products` with a standalone `prd_` prefix, optional/recommended Suppliers integration, and a minimal catalogue-facing service surface. The initial batch deliberately covers catalogue, SKU, brand, model, variant, specification, document, pricing, and supplier-link metadata plus lifecycle scaffolding, while leaving later Inventory Tracking, E-commerce, and ServiceVault integrations for their own batches.
D081 — Discovery order independence
Plugin discovery smoke tests now treat discovered modules as an unordered set keyed by plugin id rather than assuming the first entry is a specific module. That keeps the Test Plugin contract stable as additional real modules are introduced in `Plugin Modules/` and avoids regressions caused purely by filesystem ordering.
D078 — Test Plugin contract module
The Test Plugin is a deliberately small plugin module that exists to exercise the framework contract before broader plugin development begins. It includes manifest, config, routes, migration, seeder, service, event, job, install, update, rollback, health, and uninstall surfaces so the phase-7 smoke test can prove discovery, lifecycle, capability, lock-file, and release rollback behavior against a real module on disk.
D079 — Test Plugin lifecycle coverage
The phase-7 Test Plugin smoke is treated as the canonical contract test for the plugin runtime. It validates manifest discovery plus install, enable/disable, update promotion, and rollback restoration against the real plugin payload, so the remaining Phase 7 checklist items are satisfied by a single end-to-end lifecycle proof rather than by separate synthetic checks.
D087 — Project Tendering module foundation
The Project Tendering module is implemented as a first-party Plugin Module with its own manifest, navigation, routes, permissions, migration metadata, seed data, lifecycle scripts, and contract smoke test. It remains standalone with only optional integrations to Lead Pipeline, Products, Suppliers, and Invoice Generation so tendering can be deployed without forcing unrelated dependencies.
D086 — Lead Pipeline module foundation
The Lead Pipeline module is implemented as a first-party Plugin Module with its own manifest, navigation, routes, permissions, migration metadata, seed data, lifecycle scripts, and contract smoke test. It is intentionally dependency-light and treats Suppliers, Products, Project Tendering, Invoice Generation, and Order Processing as optional integrations rather than required prerequisites.
D085 — Invoice Generation module foundation
The Invoice Generation module is implemented as a first-party Plugin Module with its own manifest, navigation, routes, permissions, migration metadata, seed data, lifecycle scripts, and contract smoke test. Invoice output is treated as derived from authoritative order and payment records, and the module keeps Payment & Billing and ServiceVault as optional integrations rather than hard dependencies.
D084 — Payments & Billing module foundation
The Payments & Billing module is implemented as a first-party Plugin Module with its own manifest, navigation, routes, permissions, migration metadata, seed data, lifecycle scripts, and contract smoke test. Its manifest declares the spec-mandated required dependency on `Invoice Generation`, while install/lifecycle verification is deferred until that upstream module exists as a real plugin.
D083 — E-commerce module foundation
The E-commerce module is implemented as a first-party Plugin Module with its own manifest, navigation, routes, permissions, migration metadata, seed data, lifecycle scripts, and contract smoke test. Its manifest declares the spec-mandated dependencies on Products, Suppliers, Payments & Billing, and Invoice Generation, while install/lifecycle verification is deferred until those upstream modules are available locally.
D082 — Website Builder module foundation
The Website Builder module is implemented as a first-party Plugin Module with its own manifest, navigation, routes, permissions, migration metadata, seed data, lifecycle scripts, and contract smoke test. Published site content is treated as derived output and must not override source plugin records, which keeps the module inside the spec's website boundary and preserves source-of-truth separation.
D091 — Scaffolded business modules remain preserved but explicitly non-functional
Existing business Plugin Modules beyond Website Builder are preserved with their manifests, lifecycle scripts, migrations, and contract smoke tests intact. Repository status tracking and planning documents now distinguish contract/scaffold completion from genuine functional completion so deferred modules are not misrepresented as production-ready implementations.
D092 — Central Authentication is first-class shared runtime, not a plugin-local shortcut
Authentication for the operational milestone is implemented in the shared framework runtime and reused by Administration and Website Builder. Cross-application login uses exact redirect URI validation and an authorization-code exchange so multiple approved domains and subdomains can participate without weakening the security model through shared insecure cookies or module-specific login shortcuts.
D093 — First operational deployment path uses installer-backed SQLite
The first genuinely usable deployment path is implemented on top of a real SQLite database managed by the Common Application Framework installer. This keeps the milestone small enough to make Central Authentication, Administration, and Website Builder operational immediately, while still exercising real transactions, schema creation, migration tracking, and persistence instead of contract-only schema arrays.
D094 - Active company is the runtime security boundary for the first operational admin path
For the operational Administration plus Website Builder milestone, company-scoped roles and permissions are resolved from the active company membership only. The runtime must not merge privileges from unrelated memberships, and every company switch, site mutation, page mutation, domain assignment, publication event, and application SSO grant is validated against that active-company boundary.
D095 - Cross-domain Grand Project SSO uses local application sessions, not shared auth cookies
The central authentication cookie remains host-local to the auth application. Browser SSO between `auth.localhost` and Grand Project application hosts is implemented through redirect-based authorization code exchange with PKCE and a separate application-local session cookie on the callback host, which preserves browser realism without weakening isolation through a broad shared-domain cookie.
D096 - First production-facing security notifications reuse the shared notification service contract
Password reset and related security-delivery behavior stay inside the Common Application Framework notification and email contract instead of inventing auth-specific mail logic. Local development may still use development capture, but production-style responses must suppress reset-token disclosure and express delivery through the shared notifications path so provider-backed email can be wired in later without changing the auth surface.
D097 - First commerce implementation lives in a framework-owned repository layer
The first real commerce batch is implemented in a dedicated framework `CommerceRepository` instead of duplicating persistence logic across the Product, Supplier, E-commerce, Payments, and Invoice plugin stubs. That keeps the operational runtime aligned with the existing Website Builder and Authentication pattern, centralises schema bootstrap for the first usable deployment path, and gives plugin-facing admin/API routes one authoritative service boundary.
D098 - Storefront visibility is site-scoped and opt-in
E-commerce storefront visibility is controlled per Website Builder site through store configuration rather than by a single global catalogue switch. A site must have its own store record to expose `/shop` routes, and product/category visibility is resolved from that store so multiple sites can share the same framework runtime without leaking catalogue visibility across hosts.