Skip to content

Platform Tenant, Operating Profile, Capability And Configuration — Source Authority

  • Status: CURRENT SOURCE-AUTHORITY MAP / DOCUMENTATION AND OWNER RECONCILIATION ONLY
  • Current source baseline: qa@4f6165bc4c53a7d98b0c246392dd9ed5bd75c21c
  • Working documentation branch: agent/p6-process-detail-20260807
  • Related Phase owners: P4-001, P4-002, P4-003, P2-007, P5-001, P6-007, P8-001, P12
  • Target architecture: React + Next.js adapters over framework-independent TypeScript application/policy contracts, MySQL first, PostgreSQL later
  • Effect boundary: repository documentation only; no Runtime, DB, module, config, credential, Wiki, deployment or Production mutation
  • Important: this document does not activate P4-003, change a module default, introduce fail-closed rollout or authorize a new configuration table.

1. Authority Decision

This platform family is not one boolean, one table or one authorization decision.

Tenant identity
!= Operating Profile
!= Module enablement
!= Named Process Capability
!= Package / Entitlement
!= Config value
!= Credential availability
!= Menu visibility
!= Page/API availability
!= Action authorization
!= Record authorization

One positive result must never imply another.

Example:

module enabled
!= user may see the menu
!= route is role-authorized
!= named workflow is enabled
!= required config is valid
!= credential belongs to this tenant
!= referenced record belongs to this tenant

1.1 Owning authorities

Question Current / target owner
Which tenant is executing? TenantExecutionContext and entrypoint adapter; legacy scalar CLIENT_ID remains compatibility input
Which portable tenant identity applies? trusted published local↔global mapping; currently unavailable in many paths
Which operating model applies? Product/P6 operating-profile contract; current Runtime has no accepted first-class profile binding
Is a module available? T_MODULE + T_TENANT_MODULE through current module guard or target typed resolution
Is one named process behavior enabled? explicit process-capability definition plus prerequisites; not implied by module enablement
What config value applies? typed config definition plus tenant/published/env/default provenance
Is a secret available and correctly owned? credential owner reference plus allowlisted secret source
Can a user enter a page/API? authentication + menu/page/API/module owners
Can a user perform one action on one record? semantic capability/action authorization plus record predicate
Who owns drafts/releases/rollback? target Control Tower publish lifecycle; Runtime consumes applied local state

1.2 No new Process ID

This family is platform foundation, not one L3 business process. Do not add a generic PROC-PLATFORM-CONFIG merely to register these concerns.

Process packets consume the platform result. P4/P2/P5/P12 own platform implementation and rollout; P6 documents the operating and migration dependencies; P8 documents administrator/support procedures.


2. Current Source Families

Source Current responsibility Important limitation
config/config.php deployment constants, getClientId(), session/impersonation scalar identity, role/menu helpers, isModuleEnabled() deployment Config::CLIENT_ID fallback remains; multiple concerns coexist in a large compatibility owner
app/platform/tenant/TenantExecutionContext.php typed single-tenant actor/subject/provenance/request/credential-owner contract additive foundation; broad Runtime coverage is incomplete
app/platform/tenant/tenant_execution_context_adapter.php WEB/API, impersonation, admin override, explicit CLI/integration and legacy-default constructors only selected callers consume strict contexts; global mapping is injected rather than Runtime-owned
app/platform/tenant/api_strict_principal.php safe typed context bridge into API scalar context route opt-in only
app/shared/api/runtime.php / router.php authentication, optional strict principal, module, menu and numeric-role guards before handler many routes remain legacy scalar; record authorization stays handler-specific
app/platform/tenant/module_guard.php page/module/config decisions and legacy endpoint map missing module rows are compatibility-allowed; unmapped pages can remain allowed/unknown
config/modules.php page-slug to module/config prerequisite map page availability only; not process capability or action authorization
app/platform/tenant/config_helper.php tenant config read, fallback, type heuristic, encrypted secret envelope and provider definitions missing/empty/error often collapse to caller default; provenance and validity are lost to existing callers
app/platform/tenant/CapabilityResolution.php typed immutable-style resolution result foundation only
app/platform/tenant/capability_resolver.php typed module/capability/config/secret decision logic no accepted broad first-party caller activation is proven
frontend/pages/platform/admin-console/tenants.php current tenant/module/config/provider applied-state administration UI direct applied-state compatibility, not a versioned Control Tower release lifecycle
app/platform/api_routes.php current admin APIs for tenant, module, config and onboarding operations writes current Runtime state directly; publication/version/approval/rollback semantics are not the primary contract
.agents/rules/TENANT_CONFIG.md current/target concern model, compatibility baseline and resolver contract technical authority; does not prove caller rollout or current DB parity
docs/tasks/P4-001.md accepted prior source/Local read-only baseline dated evidence; not a fresh DB or Production snapshot for this wave
docs/tasks/P4-002.md typed context/adapters and selected strict caller handoff broad caller migration and owner acceptance incomplete
docs/tasks/P4-003.md planned resolver/configuration implementation Task not activated by this documentation wave
docs/tasks/P2-007.md live-tenant role/API/profile parameterization and Client 1 non-regression Gate broad default-deny or profile rollout remains prohibited without caller evidence

3. Current Tenant Execution Context

3.1 Legacy scalar resolution

Current common WEB/PHP compatibility:

IMPERSONATE.TARGET_CLIENT_ID when present
-> SESSION.CLIENT_ID when present
-> Config::CLIENT_ID deployment fallback

Actor/subject helpers similarly prefer impersonation target identity.

Implications:

  • a normal authenticated session has an effective local tenant;
  • impersonation changes effective tenant and subject;
  • CLI/direct legacy code can silently receive deployment tenant 1 unless it uses an explicit strict system binding;
  • local numeric CLIENT_ID is not a portable cross-deployment identity.

3.2 Typed context foundation

TenantExecutionContext already models:

localTenantId
optional globalTenantId
identityState: MAPPED | LEGACY_LOCAL_ONLY
tenantProvenance
entrypoint
sourceName
requestId
accessMode
actor
optional subject
optional credentialOwnerRef
optional authorizationRef
policyState: STRICT | COMPATIBILITY_ONLY

Supported provenance includes:

SESSION
IMPERSONATION_TARGET
ADMIN_ROUTE_OVERRIDE
EXPLICIT_JOB
INTEGRATION_BINDING
LEGACY_DEFAULT

assertStrictForWrite() rejects a compatibility-only context for a new write path.

3.3 Current request adapter behavior

normal WEB/API session
-> strict tenant + user actor/subject

super-admin tenant-scoped request
-> explicit tenant target required

impersonation
-> original super-admin actor
-> target tenant
-> optional target user subject

allowlisted admin API override
-> explicit tenant target + authorization reference

CLI/integration
-> explicit single-tenant binding

legacy CLI default
-> COMPATIBILITY_ONLY

WEB/API ALL_TENANTS is rejected. Cross-tenant fan-out belongs to an explicit job controller that creates one child context per tenant.

3.4 Current strict API rollout

A route opts in with:

'strictTenantPrincipal' => true

Then the router performs:

authentication
-> typed strict tenant principal
-> module decision using strict tenant
-> menu-route decision
-> numeric-role compatibility decision
-> handler
-> handler-specific record predicate

Non-opted routes continue to use legacy scalar context. Therefore:

typed foundation exists
!= repository-wide strict tenant execution

4. Operating Profile Authority Gap

The Product/process documents use stable operating-profile concepts:

ctc-execution
mk-order

Current Runtime does not expose an accepted first-class tenant→profile binding that every page/API/job consumes.

Current behavior is distributed across:

  • numeric Client checks;
  • page routes and menu availability;
  • module assignments;
  • named config toggles;
  • order class/direction and process-specific record state;
  • document/report compatibility branches;
  • current operator knowledge.

Therefore:

documented operating profile
!= first-class Runtime profile assignment

4.1 Target profile definition

A target profile definition must be a versioned semantic contract, not a customer ID alias.

Minimum logical fields:

profileKey
version
status
applicable product/value streams
allowed process families
required and optional modules
default named capabilities
terminology set
document-template set
provider/device adapter set
config-definition set
migration compatibility policy

Open ownership decision:

  • one primary profile per tenant;
  • primary profile plus optional capability composition;
  • multiple profile slices by business unit/location.

Do not choose a schema until Product and P4 accept the composition rule.

4.2 Profile non-implications

profile assigned
!= module enabled
!= user authorized
!= config valid
!= credential available
!= record eligible

Profile narrows applicable Product/process policy. It does not replace the other gates.


5. Current Module Enablement

5.1 Storage

T_MODULE
= global module catalog

T_TENANT_MODULE
= tenant-specific applied enablement/tier rows

5.2 Compatibility decision

Current isModuleEnabled() and module guard behavior is intentionally rollout-compatible:

explicit enabled row
-> allow

explicit disabled row
-> deny

missing tenant-module row
-> allow

module source/DB error
-> allow compatibility path

Callers commonly see:

reason = enabled_or_unconfigured

This means current resolution cannot distinguish a deliberate enablement from missing configuration at the ordinary boolean caller.

5.3 Page module decision

config/modules.php maps page slugs to:

  • one module;
  • any of several modules;
  • all required modules;
  • optional required config keys;
  • platform-core/no-module cases.

T_MODULE_PAGE can additionally mark a page inactive or supply a registry module. Current behavior includes:

registry lookup error -> deny
registry inactive -> deny
unmapped page -> allowed / unknown
missing module row -> compatibility allow
required config false/missing/error -> deny

5.4 Module does not own capability

Examples:

  • tms.booking does not imply every Booking flag, Packing output, email or rate correction is enabled;
  • oms.inbound-order does not imply Export Brokerage is enabled;
  • yms.scale does not imply every flow/stage or reversal action is authorized;
  • addon.quickbooks does not imply credentials are valid or the caller may send.

Target callers must consume a named process capability when behavior is narrower than a module.


6. Named Process Capability

Current code uses a mixture of:

  • module decisions;
  • page+config gates;
  • direct config booleans;
  • numeric Client defaults;
  • route-specific conditions;
  • record-state guards.

The typed CapabilityResolver already distinguishes PROCESS_CAPABILITY, but broad current caller adoption is not proven.

6.1 Target capability definition

Minimum logical fields:

schemaVersion
capabilityKey
ownerModuleKey
ownerProcessId when applicable
applicableProfiles[]
prerequisiteModules[]
prerequisiteCapabilities[]
requiredConfigDefinitions[]
credentialDefinition when applicable
policyState: STRICT | COMPATIBILITY
default policy
allowed resolution sources
safe diagnostic metadata
deprecation/replacement

6.2 Target capability result

ENABLED
DISABLED
UNAVAILABLE

with:

source
validity
policyState
compatibilityFallback
stable reason

A caller branches on the typed decision, not on a display message and not on tenant number.


7. Current Configuration Resolution

7.1 Storage and fallback

Current common resolution:

T_TENANT_CONFIG override
-> caller-specific env fallback when used
-> caller/legacy Config default

tenant_config_get() and typed helpers use request-scope static caches.

Current result often collapses:

missing row
stored empty
source error
invalid typed value

into the same caller default.

Therefore an ordinary current scalar result does not preserve:

  • provenance;
  • validity;
  • source error;
  • compatibility fallback use;
  • applied release version.

7.2 Current value typing

tenant_config_value_type() uses:

  • explicit provider definitions when available;
  • key-name heuristics;
  • allowlisted JSON/int/bool exceptions.

This is a compatibility classifier, not the target definition registry.

Known risk examples from prior accepted evidence:

  • a JSON policy can be stored with a string metadata type;
  • a URL containing TOKEN can be misclassified as secret by heuristic;
  • nonempty JSON text does not prove the expected object/list shape;
  • stored empty may mean disabled, missing or intentionally blank depending on caller.

7.3 Numeric tenant default example

Current MK document compatibility:

MK_DOCUMENT_WORKFLOW_ENABLED missing
+ effective CLIENT_ID = 2
-> default enabled

This is a current compatibility boundary, not an acceptable target profile definition.

Target:

Operating Profile / named capability definition
+ explicit typed config provenance

7.4 Config definition target

Minimum logical fields:

configKey
valueType
secret classification
ownerModuleKey
applicableProfiles[]
allowedSources[]
empty semantics
validation schema/range
JSON shape
default value/source policy
cache/reload behavior
safe redaction
publish metadata requirement
deprecation/replacement

Registered definitions outrank name heuristics.


8. Credential And Secret Ownership

Current config helper supports encrypted tenant secret envelopes:

enc:v1:<AES-256-GCM payload>

and provider definitions identify owning modules.

The typed context/resolver foundation models a non-secret credential owner reference:

scope: TENANT | DEPLOYMENT_FALLBACK | NONE
sourceType: TENANT_CONFIG | ENV | EXTERNAL_SECRET_REF
moduleKey
referenceKey
ownerLocalTenantId when tenant-owned
ownerGlobalTenantId when mapped

Rules:

credential exists
!= module enabled
!= capability enabled
!= user authorized
!= provider call permitted

Safe diagnostics expose availability/provenance/owner match, never the credential value.


9. Current Admin Console Boundary

The Admin Console currently provides direct applied-state operations for:

  • tenant identity fields;
  • tenant module enablement and tier;
  • tenant config values;
  • provider settings;
  • onboarding defaults.

Current APIs include:

GET /api/platform/admin/tenants
PATCH /api/platform/admin/tenants/{clientId}
GET /api/platform/admin/tenants/{clientId}/modules
PATCH /api/platform/admin/tenants/{clientId}/modules/{moduleKey}
GET|PATCH /api/platform/admin/tenants/{clientId}/configs
POST /api/platform/admin/tenants/{clientId}/onboarding

Super-admin can target tenants. A regular admin-console user is limited to the effective tenant for module/config access. Dedicated deployment renders the tenant-management surface read-only.

9.1 Current completion

API success
+ exact local row reread
= current applied-state update

It does not prove:

Control Tower draft approved
release version published
all deployment targets applied
Runtime caches refreshed
all pages/APIs observe the same state
rollback package exists
field role accepted the change

9.2 Target control-plane separation

Control Tower draft
-> review
-> approved release
-> target deployment apply
-> local applied-state reread
-> effective-state diagnostics
-> observation
-> rollback or superseding release

Runtime must not query Control Tower on every request.


10. Current Package / Entitlement Gap

Package/tier fields exist in catalog/admin surfaces, but current source evidence does not establish one accepted entitlement owner that determines every module/capability.

Therefore:

package selected
!= module enabled
!= capability enabled
!= billing entitlement accepted

Target P4-003 must decide whether package/tier is:

  • onboarding seed only;
  • a recurring entitlement source;
  • advisory metadata;
  • replaced by explicit module/capability releases.

Do not enforce package denial until existing tenants and callers have explicit parity seeds and acceptance.


11. Current Concern Resolution Sequence

request/session/integration input
-> effective local tenant scalar
-> optional strict typed context
-> page/API module gate
-> menu/role gate
-> process-specific config or numeric default
-> handler record predicate
-> provider/config helper fallback when applicable

The same concern may currently be resolved more than once by different layers. This can produce:

  • page/API parity mismatch;
  • visible control with denied API;
  • hidden page with callable API;
  • missing module row treated as enabled;
  • config gate and direct caller fallback disagreement;
  • one request using cached pre-write config after an admin update;
  • tenant-default fallback in CLI/direct page code;
  • credential source differing between callers.

12. Source-Confirmed Gaps

Gap ID Current gap Consequence
TPC-GAP-010 no accepted first-class Runtime tenant→operating-profile binding profile behavior remains inferred from tenant/module/config/page/record compatibility
TPC-GAP-020 CapabilityResolver foundation has no proven broad active caller set target contract exists without Runtime coverage
TPC-GAP-030 missing tenant-module row and module-source error can allow enablement provenance is ambiguous and global fail-closed would risk Client 1 outage
TPC-GAP-040 unmapped page can remain allowed/unknown page inventory is not a complete authorization catalogue
TPC-GAP-050 scalar config helpers collapse missing/empty/invalid/error to defaults diagnostics and safe fallback retirement are difficult
TPC-GAP-060 type heuristic can disagree with intended definition masking, validation and provider configuration can be wrong
TPC-GAP-070 numeric Client defaults/branches remain customer identity still selects Product behavior on some paths
TPC-GAP-080 strict typed context is route/caller opt-in pages/jobs/imports/provider callbacks can still use compatibility tenant defaults
TPC-GAP-090 current Admin Console edits applied state directly no single versioned draft/review/publish/apply/rollback history
TPC-GAP-100 config cache invalidation/read-after-write contract is not universal same-request or long-running caller can observe stale effective state
TPC-GAP-110 package/tier is not proven as entitlement authority package-driven denial would be speculative
TPC-GAP-120 global tenant mapping and Shared/Dedicated parity are not currently proven portable identity and cross-deployment replay remain incomplete
TPC-GAP-130 menu/page/API/action/record decisions are separately owned and incompletely inventoried module/config success can be mistaken for authorization
TPC-GAP-140 no universal effective-state diagnostic read model support must infer behavior from rows, fallbacks and code

13. Required Downstream Owners

Work Owner
tenant/global identity and typed context acceptance P4-002 / platform tenant owner
profile/module/capability/config resolver activation P4-003 after explicit activation
data/caller/strict write migration P4-004 / P5
secrets/provider binding P4-005 and provider owners
live Client 1/2 role/API parity P2-007
business process applicability P6 process owners
UI/module/access presentation P7 / current page owners
admin/support Wiki P8
React/Next.js target and one-writer cutover P12
Control Tower draft/release/apply lifecycle P3/Control Tower owner

14. Required Evidence Before Implementation Activation

  • exact current qa, deployed source and schema/config identity;
  • current tenant/profile/module/config applied-state snapshots without secret values;
  • current Client 1 and Client 2 module/menu/role/action caller matrix;
  • all numeric Client/profile compatibility callers on selected wave;
  • strict-context opt-in caller inventory;
  • missing/disabled/error/invalid config behavior by selected caller;
  • Admin Console before/after/readback and cache/freshness behavior;
  • Control Tower mapping and applied-release identity;
  • Shared and Dedicated deployment parity;
  • provider credential owner and negative cases;
  • rollback/observation plan that preserves Client 1.

This source-only wave performs none of those Runtime/DB/browser/provider checks.


15. Current Evidence Ceiling

Current source authority: RECONCILED
Prior P4 source/Local evidence: ACCEPTED HISTORICAL INPUT
Fresh DB/config snapshots: NOT RUN
Current role/browser/admin diagnostics: NOT RUN
Control Tower mapping/apply parity: UNKNOWN / NOT RUN
Runtime resolver caller activation: NOT PERFORMED
Wiki current content: NOT READ
P4-003 activation: NO
M4 migration readiness: NOT ESTABLISHED

---

Source: docs/reconstruction/PLATFORM_TENANT_PROFILE_CAPABILITY_CONFIGURATION_SOURCE_AUTHORITY.md — ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.