Skip to content

Architecture

  • Status: CANONICAL CURRENT
  • Owns: confirmed current runtime architecture, paths, dependencies, request/data flow, and compatibility boundaries
  • Does not own: product direction, target system decomposition, process lifecycle, or modernization status
  • Update when: verified runtime architecture or an active compatibility boundary changes
  • Related sources: docs/DESIGN.md, docs/PRODUCT.md, API_CONTRACTS.md, DB_QUERY_RULES.md, AUDIT_AND_TRANSITION_EVIDENCE.md

Purpose

This file describes the current runtime architecture, routing, ownership boundaries, and architecture rules. Product/module strategy lives in docs/PRODUCT.md and target decomposition in docs/DESIGN.md.

Runtime Overview

  • Logistics operations system built as a PHP application without Laravel/Symfony.
  • Apache rewrite plus index.php?page=<slug> page routing.
  • MySQL access through app/utils/KJDatabase.php.
  • Session authentication through login.php and config/session.php.
  • Server-rendered PHP pages with shared JavaScript.
  • Frontend stack is Bootstrap legacy plus Tailwind on newer screens.
  • Specialized UI includes OpenLayers for yard map.
  • Integrations include Telegram, Gmail, Gemini-backed AI extraction, QuickBooks, and WordPress-linked driver identity.
  • Tenant isolation through CLIENT_ID and request-time getClientId() in the current runtime; long-term tenant identity is the Control Tower-managed UUID described in CONTROL_TOWER.md.

Directory Ownership

app/controllers/
  sync.php                 checksum polling compatibility
  excel.php                Excel/CSV export
  media.php                upload/download
  gmail.php                email/PDF dispatch
  chatgpt.php              AI extraction compatibility endpoint

api/
  index.php                API v2 HTTP entrypoint for React-facing routes

app/platform/
  auth/                    account/session handlers
  menu/                    menu tree and role page handlers
  tenant/                  module guard, tenant config/company helpers, effective tenant checks
  module/                  module page registry helpers
  audit/                   super-admin/admin-console handlers
  codex/                   Codex Portal file-backed project/session/archive/runner helpers

app/shared/
  api/                     API v2 request parsing, response envelope, auth, route dispatch helpers, and shared AI extraction helper
  files/api_routes.php     shared file metadata API v2 routes
  endpoint/                JSON response/auth/trace helpers and endpoint bootstrap
  sync/                    write sync task dispatch
  table/                   jTable protocol runtime
  ui/                      shared UI contract/runtime helpers
  controller/              reusable legacy helper functions only; no shared read/write entrypoints

app/addons/
  gmail/                   Gmail intake/add-on routes and helpers
  reports/api_routes.php   add-on report API v2 read routes and ingate-ticket AI extraction

app/systems/
  YMS/ TMS/ WMS/ OMS/ BMS  module-owned handlers and helpers
  YMS/container-tracking/api_routes.php  YMS container/equipment API v2 routes, including seal photo AI OCR
  TMS/transload/api_routes.php   TMS transload API v2 routes, including load schedule AI extraction/prompt ownership
  TMS/booking/api_routes.php     TMS booking API v2 routes, including booking instruction AI extraction/prompt ownership
  OMS/inbound-order/api_routes.php  OMS inbound order API v2 routes, including doortrade packing-list AI extraction
  OMS/export-brokerage/api_routes.php  OMS export brokerage API v2 routes, including export brokerage document AI extraction
  WMS/inbound/api_routes.php     WMS inbound API v2 check-in/completed/import routes, including import delivery order AI extraction/prompt ownership
  WMS/inventory/api_routes.php   WMS inventory lot/report API v2 routes

frontend/pages/
  system/
    YMS/ TMS/ WMS/ OMS/ BMS  system/module-owned server-rendered pages
  platform/                   admin/core/menu/master pages

resources/views/
  shared page/components where still used

frontend/react/src/api/
  planned API v2 React client modules once the React scaffold exists

public/assets/
  legacy static assets and shared JavaScript/CSS

config/
  runtime config, session/bootstrap, page route registry

config/page_routes.php
  page slug -> page file registry

docs/
  production/runtime documentation that is not agent instruction

.agents/rules/
  current source-of-truth system and agent rules

.agents/reference/
  historical system analysis only

Entry Points

Entry Purpose
index.php page route, role guard, module page guard
api/index.php API v2 route dispatcher for /api/... requests
app/systems/<SYSTEM>/<module>/query.php legacy module-owned read endpoint where still present
app/systems/<SYSTEM>/<module>/post.php legacy module-owned write endpoint where still present
app/controllers/sync.php checksum polling compatibility; active app callers use /api/platform/sync/checksums

Page Request Flow

HTTP request
  -> config/session.php
  -> login/session check
  -> config/page_routes.php slug lookup
  -> role/menu access check
  -> moduleGuardPageDecision(page slug)
  -> include page file or module-disabled page

config/modules.php maps a page slug to one module key by default. A plain array keeps legacy "any enabled module" behavior; use ['all' => [...]] only for pages whose workflow requires every listed module to be enabled.

AJAX Flow

Active app AJAX callers
  -> /api/... route
  -> API v2 auth and explicit route module key
  -> active handler
  -> API v2 JSON envelope

API v2 Flow

/api/... request
  -> Apache/router.php API rewrite
  -> api/index.php
  -> platform/shared/addon/system api_routes.php registry
  -> apiAssertAuthenticated()
  -> explicit route moduleKey guard
  -> JSON body parser when the method expects a body
  -> route handler
  -> { data, requestId } or { error, requestId }

Active YMS API v2 route owners:

  • app/platform/api_routes.php owns platform account password, sync checksum polling, menu management, module page registry, user role/username checks, and super-admin admin-console API v2 routes.
  • app/platform/codex/session_file_helper.php owns Codex Portal file-backed session discovery, archive restore/archive actions, and background runner state under storage/codex-portal/*; it must not introduce Codex session database tables.
  • app/shared/files/api_routes.php owns shared T_FILE metadata lookup, registration, type/sequence update, bulk update/delete, and transaction-document metadata routes.
  • app/addons/reports/api_routes.php owns add-on inventory, MK daily commodity, account trucking, FBOL, AI verification read-only, and ingate-ticket AI extraction API v2 routes.
  • app/systems/YMS/yard-map/api_routes.php owns yard-map section/state read routes.
  • app/systems/YMS/yard-move/api_routes.php owns yard-move queue/location read routes and move/reserve/confirm/cancel write command routes.
  • app/systems/YMS/container-tracking/api_routes.php owns container, equipment-load, repair-log, validation, yard-container, and seal photo AI OCR API v2 routes.
  • app/systems/YMS/dock-scheduling/api_routes.php owns appointment, dock option, and yard-location option API v2 routes.
  • app/systems/YMS/scale/api_routes.php owns scale schedule/list/detail reads and scale check-in/check-out/finalize writes.
  • app/systems/TMS/transload/api_routes.php owns transload planning board, planner event, appointment block, timeblock status, planning command, runtime command, email queue/action, BOL email, and load-label API v2 routes.
  • app/systems/TMS/delivery/api_routes.php owns delivery driver identity/log/list reads, delivery status/field/driver-assignment writes, and dispatch creation API v2 routes.
  • app/systems/TMS/booking/api_routes.php owns booking list/detail/editor/options, packing-list read/file/save/email, loading options, weight requirement, duplicate reference, clone booking, empty-plan, and export-container revert API v2 routes.
  • app/systems/TMS/carrier/api_routes.php owns carrier master phone lookup and carrier export API v2 routes.
  • app/systems/OMS/commercial-order/api_routes.php owns commercial order list/detail/options/create/update/delete API v2 routes.
  • app/systems/OMS/inbound-order/api_routes.php owns inbound purchase order list/detail/options/schedule/calendar/header/item API v2 routes plus doortrade packing-list AI extraction and the customer-PO by load-numbers bridge used by TMS popup flows.
  • app/systems/OMS/outbound-order/api_routes.php owns outbound sales order list/detail/options/schedule/header/item API v2 routes and outbound order cost read/upsert/delete routes.
  • app/systems/OMS/export-brokerage/api_routes.php owns export brokerage job create/cancel routes and export brokerage document AI extraction with outbound-order, inbound-order, and workflow-config gates.
  • app/systems/OMS/allocation/api_routes.php owns allocation workbench read/autocomplete routes and allocation apply writes to outbound demand.
  • app/systems/OMS/trade-summary/api_routes.php owns trade summary and deal ledger read-only API v2 routes.
  • app/systems/BMS/rate/api_routes.php owns BMS commodity/trucking/freight rate list/options/write routes, trucking/chassis lookup routes, and the trucking summary report route.
  • app/systems/BMS/scale-ticket/api_routes.php owns BMS scale-ticket create/read/update/delete, closing scale-ticket, and outbound-weight-apply API v2 routes.
  • app/systems/BMS/invoice/api_routes.php owns local purchase invoice and transaction document read-only API v2 routes.
  • app/systems/BMS/transaction/api_routes.php owns BMS transaction detail, create/delete by source, close, chassis bulk input, final transload apply, and transaction billing-address lookup API v2 routes.
  • api/index.php loads route files through an isolated helper so local variables inside route files cannot overwrite the accumulated route registry.

Module Guard Rules

  • Effective tenant is resolved before module decisions.
  • Super-admin bypass is allowed only for the effective super-admin context, not for impersonated target tenants.
  • Module enablement, menu/role visibility, page/API routing, named process capability, config presence, and credential ownership are separate decisions. A positive module decision grants none of the others.
  • Current isModuleEnabled() compatibility allows missing tenant-module rows and DB errors. Preserve that behavior until caller cutover, but new strict decisions use the CapabilityResolution contract in TENANT_CONFIG.md and report missing/error explicitly.
  • Platform-core routes use an explicit null/core classification; do not manufacture tenant module rows merely to represent authentication, users, files, or other baseline platform ownership.
  • Disabled module response:
  • page route: include module-disabled.php
  • endpoint: JSON error envelope with HTTP 403
  • Mapping source:
  • page slug -> module key through module page registry/config
  • TYPE/ACTION -> module key through moduleGuardEndpointModuleKey()
  • API v2 route -> explicit moduleKey on the route definition
  • Module guards do not replace authentication or T_MENU/effective-role checks. A named workflow/config gate remains independently fail-safe even when its owning module is enabled.

Controller Rules

  • Controllers are routing shells.
  • Business logic belongs in active platform/shared/system files.
  • Do not add legacy_*_dispatcher.php files.
  • Do not add thin compatibility wrappers that only call another function.
  • Module-owned legacy endpoint shells can include active module files until the shell itself has an API v2 replacement.
  • Module endpoint shells must use shared bootstrap/response helpers.
  • Extract PHP/JS helpers only when 3+ lines repeat at least 5 times.
  • Keep simple branches/messages inline unless an established local helper already exists.
  • Preserve visible error feedback. Do not create silent failures.

Data Consistency Rules

  • Every tenant-scoped query includes CLIENT_ID.
  • In page, routing, permission, and tenant logic, use getClientId() instead of raw $_SESSION['CLIENT_ID'].
  • During impersonation, the target tenant from getClientId() is the source of truth.
  • Do not assume integer CLIENT_ID values are globally stable across deployments; they are compatibility data until tenant UUID migration is complete.
  • Do not add hard tenant blocks such as clientId !== N unless explicitly requested. Branch behavior by effective client instead.
  • Write flows own their transaction boundary.
  • Yard occupancy source of truth is T_YARD_LOCATION.
  • Mirrors such as T_TRANSLOAD.WAREHOUSE_LOCATION are updated in the same transaction as authoritative yard state.
  • KJDatabase records request/mutation diagnostic metadata such as statement/table family, insert ID, affected rows, touched tables, transaction outcome, request context, timing, and error metadata. It is not the general immutable before/after business-row snapshot authority; determine business result through authoritative row reread and process/domain/transition/provider evidence.
  • Runtime SQL never uses FOR UPDATE; the clause is permanently forbidden.

Target Tenant Execution Context Contract

TenantExecutionContext is the additive contract for one tenant-scoped execution. The canonical implementation lives in app/platform/tenant/TenantExecutionContext.php and tenant_execution_context_adapter.php. Active adoption is bounded: selected strict API routes construct the context once before module/handler execution, retain the legacy scalar principal bridge, and pass a redacted tenantExecutionContext to context-aware consumers. Yard Map, Yard Move and Dock Scheduling reference callers consume that propagated context without re-reading session tenant state. Page bootstrap, non-selected API routes, imports, migrations, provider callbacks and active cron commands retain their existing compatibility paths until their separately verified P4-004 cutover.

The current Version 1 fields below describe the active coexistence contract, not the final identity shape. The final tenant identity is the one UUID value shared by Control Tower GLOBAL_TENANT_UUID, runtime T_CLIENT.UUID and tenant-owned TENANT_ID. localTenantId/numeric CLIENT_ID is removed only after P4 proves mapping, caller/data zero and rollback. P12 applies the same UUID-primary and UUID-reference direction to ordinary entities; no execution-context adapter pretends that schema or caller migration has already occurred.

Scope Boundary

  • A TenantExecutionContext always represents exactly one effective tenant. localTenantId must be a positive deployment-local CLIENT_ID.
  • ALL_TENANTS is not a tenant context. JobExecutionContext may hold an explicitly authorized ALL_TENANTS selection, but it must create one validated SINGLE_TENANT child context before any domain query, mutation, file access, credential resolution, or external call.
  • A super-admin platform-global action has no tenant context. A tenant-scoped super-admin action requires an explicit, allowlisted target and remains distinguishable from ordinary user execution.
  • Context construction normalizes identity only. It does not start a session, authorize a role/module, open a database connection, resolve config, or execute business behavior.

Version 1 Fields

Field Type / values Required contract
schemaVersion literal tenant-execution-context/v1 Required. Consumers reject unknown major versions.
scopeMode literal SINGLE_TENANT Required. ALL_TENANTS is invalid for this contract and belongs only to future job-level selection.
localTenantId positive integer Required for every context. It is the current runtime query key, not a portable tenant identity.
globalTenantId UUID string or null Populated only from a trusted published runtime mapping. null is allowed during migration when identityState=LEGACY_LOCAL_ONLY.
identityState MAPPED, LEGACY_LOCAL_ONLY MAPPED requires the UUID/local-id pair to match the published mapping.
tenantProvenance SESSION, IMPERSONATION_TARGET, ADMIN_ROUTE_OVERRIDE, EXPLICIT_JOB, INTEGRATION_BINDING, LEGACY_DEFAULT Records how the effective tenant was selected. It is not an authorization grant.
entrypoint WEB, API, CLI, INTEGRATION Required execution channel.
sourceName safe non-secret identifier Page slug, API route, job name, or integration key. Do not store query/body payloads here.
requestId 1-80 characters matching [A-Za-z0-9_.:-]+ Reuse a valid inbound ID or generate one at the entrypoint. It must remain stable through process/audit/outbox/evidence children.
accessMode DIRECT, IMPERSONATION, ADMIN_TENANT_OVERRIDE, SYSTEM Describes actor-to-subject behavior.
actor principal reference Required initiating principal: USER, SUPER_ADMIN, SYSTEM, or INTEGRATION, with a stable ID/name reference where available.
subject user reference or null Effective user for role/business-user semantics. It equals the actor for direct user access, is the target user for user impersonation, and is null for client-only impersonation/system work.
credentialOwnerRef credential ownership reference or null Required before an integration credential is used; omitted for executions that use no credential. Shape and safety rules are owned by TENANT_CONFIG.md.
authorizationRef safe allowlist/policy reference or null Required for admin tenant override. A child created by an authorized all-tenant job inherits the job's safe policy reference. It must not contain a token, session ID, or secret.
policyState STRICT, COMPATIBILITY_ONLY LEGACY_DEFAULT provenance requires COMPATIBILITY_ONLY; new tenant-scoped writes must not depend on it.

globalTenantId comes from the Control Tower-owned global registry/deployment mapping and, once published, must equal runtime T_CLIENT.UUID. CTC request handling consumes only a trusted published local snapshot or injected mapping; it must not query the Control Tower database or service on every request. Until such a runtime mapping is available, the context remains LEGACY_LOCAL_ONLY and must not claim that a durable reference is portable across deployments.

Actor, Subject, And Tenant Truth Table

Case Target result Required behavior
regular authenticated web/API session SINGLE_TENANT, DIRECT, actor=subject, provenance SESSION Use the same effective CLIENT_ID, user ID, username, role/module result, and request ID behavior as today.
super-admin impersonating a specific user SINGLE_TENANT, IMPERSONATION, actor=original admin, subject=target user, provenance IMPERSONATION_TARGET Original actor must match the current super-admin session. Target user and target tenant must exist together. Module/role checks use the target tenant/subject.
super-admin client-only impersonation (TARGET_USER_ID=0) SINGLE_TENANT, IMPERSONATION, actor=original admin, subject=null Do not combine the original admin ID with the target placeholder username as one identity. User-required workflows must reject the missing subject.
super-admin platform-global action without target no TenantExecutionContext Use the platform admin context. Tenant-scoped domain access requires a target first.
allowlisted super-admin API target override SINGLE_TENANT, ADMIN_TENANT_OVERRIDE, actor=super-admin, subject=null unless explicitly resolved Require positive target tenant and a non-secret authorizationRef; keep the allowlisted route boundary.
admin target override while impersonating invalid Reject the ambiguous tenant/subject combination before context construction.
target override on a non-allowlisted route invalid A future context adapter rejects it; existing API behavior remains unchanged until cutover.
new single-tenant CLI/integration invocation SINGLE_TENANT, SYSTEM, provenance EXPLICIT_JOB or INTEGRATION_BINDING Require an explicit positive tenant and source name before any write or external call.
existing CLI path falling back to Config::CLIENT_ID COMPATIBILITY_ONLY, provenance LEGACY_DEFAULT when observed by an adapter Preserve the active command until its migration packet, but do not use this provenance for a new caller or new write path.
authorized all-tenant job control no shared tenant context at fan-out level Future JobExecutionContext requires an allowlisted job and authorization reference, then creates one strict child context per tenant.
web/API request asking for all tenants invalid Reject; ordinary web/API domain execution cannot broaden tenant scope.
global UUID not yet published locally valid LEGACY_LOCAL_ONLY single-tenant context Permit current deployment-local behavior, but prohibit portable/global identity claims.
UUID/local-id mapping mismatch invalid Fail before domain read/write and record a redacted mismatch error.
tenant-owned credential owner does not match the target tenant invalid Fail before loading the credential or contacting the provider.

Failure Contract

New context-aware paths use stable internal error codes and visible failures. Existing callers keep their current behavior until cutover.

Error code Condition Default transport
TENANT_CONTEXT_REQUIRED new CLI/integration/domain path has no explicit tenant context HTTP 400 or CLI exit 2
TENANT_TARGET_REQUIRED tenant-scoped super-admin action has no explicit target HTTP 400 or CLI exit 2
TENANT_CONTEXT_FORBIDDEN web/API all-tenant request or non-allowlisted admin override HTTP 403 or CLI exit 3
IMPERSONATION_CONTEXT_INVALID original actor, target tenant, or requested target user relationship is incomplete HTTP 409 or CLI exit 4
TENANT_MAPPING_MISMATCH supplied global UUID does not map to the local tenant HTTP 409 or CLI exit 4
CREDENTIAL_OWNER_MISMATCH credential owner is another tenant or an undeclared deployment fallback HTTP 409 or CLI exit 4
TENANT_CONTEXT_COMPATIBILITY_ONLY a new write attempts to use LEGACY_DEFAULT provenance HTTP 409 or CLI exit 4
TENANT_RELATION_CONTEXT_MISMATCH supplied child observation is not scoped to the explicit tenant context HTTP 409 or CLI exit 4
TENANT_RELATION_TENANT_MISMATCH already-read parent and child have different CLIENT_ID values HTTP 409 or CLI exit 4
TENANT_RELATION_PARENT_MISSING required parent observation is absent HTTP 404 or CLI exit 2
TENANT_RELATION_PARENT_DELETED required parent observation is soft-deleted HTTP 409 or CLI exit 4
TENANT_CONTEXT_INVALID malformed IDs, enum, request ID, or required field HTTP 400 or CLI exit 2

Validation happens before tenant-scoped persistence or external I/O. Errors must not be converted into an empty result, default tenant, or silent skip. API exposure continues to use the standard JSON error envelope; internal context objects are not serialized directly to browser responses.

Redaction And Propagation

  • Safe logs may contain schema version, local/global tenant IDs, identity state, provenance, entrypoint, source name, request ID, access mode, actor type/ID, subject ID, and non-secret credential ownership metadata.
  • Default safe serialization omits usernames, cookies, session IDs, authorization headers, request bodies, config values, tokens, passwords, secret paths/content, and raw impersonation session data.
  • authorizationRef is an opaque policy/allowlist identifier. If its source value is sensitive, log only a stable hash or public policy key.
  • Child process, audit, outbox, evidence, and job contexts copy the tenant identity and request ID; they do not re-resolve getClientId() independently.
  • If a child receives a different tenant or credential owner, construction fails rather than overwriting the parent identity.

Target Tenant Relation Guard

TenantRelationGuard is an additive, inactive pure guard for callers that have already read a tenant-scoped child and its parent. It receives one strict TenantExecutionContext plus explicit child/parent observations; it does not call getClientId(), query a database, open a transaction, or load a provider.

  • Child and parent each require entity type, positive local ID, and CLIENT_ID. Both must equal the supplied context tenant before the relation is accepted.
  • A missing parent and a soft-deleted parent are distinct stable failures.
  • In this current compatibility guard, an ordinary entity UUID is optional reference metadata only. Null/non-unique legacy UUID remains LEGACY_LOCAL_ID; a present UUID is an ENTITY_UUID_REFERENCE. This is a current Runtime limitation, not the final P12 identity target.
  • The guard is not auto-wired into existing callers. Caller-specific read, transaction, and compatibility decisions remain separate P02/P03 work.

Compatibility And Verification Cases

P4-002 owns this additive adapter contract and its focused unit coverage proves:

  1. regular session and user impersonation parity with getClientId(), getSessionUserId(), and getSessionUsername();
  2. client-only impersonation keeps original admin actor and a null subject;
  3. super-admin global, allowlisted target override, and forbidden override remain distinct;
  4. missing/malformed tenant, mapping mismatch, credential mismatch, and compatibility-only write fail before I/O;
  5. safe serialization contains no session, token, credential value, request body, or raw impersonation structure;
  6. strict API propagation is reconstructed and checked against the legacy scalar tenant before a context-aware consumer runs;
  7. existing callers that do not opt into the adapter are unchanged.

The adapter accepts explicit request/session state or an explicit CLI/integration binding plus a trusted optional published mapping. It does not read globals, start a session, generate a request ID, query a database, load a credential, authorize a route, or perform external I/O. The current entrypoint remains responsible for producing the request ID and deciding whether an admin route is allowlisted before passing those facts to the adapter.

Target Job Execution Context Contract

JobExecutionContext and JobExecutionContextParser are additive contracts for future CLI/scheduler migration. The single-tenant parser reuses TenantExecutionContextAdapter::fromExplicitSystemBinding() instead of maintaining a second tenant-identity mapping implementation. They are not included by cron.php or any active runtime bootstrap. Existing command names, scheduler arguments, Config::CLIENT_ID fallback, database access, credential resolution, and provider calls remain unchanged until a caller-specific P4-004 migration.

Invocation And Fan-out Boundary

  • Every invocation explicitly selects SINGLE_TENANT or ALL_TENANTS; absence never means the deployment default.
  • SINGLE_TENANT construction requires a positive local tenant input and creates a strict TenantExecutionContext with EXPLICIT_JOB, CLI, SYSTEM, the same job name, actor, request ID, and authorization reference.
  • ALL_TENANTS construction requires exact job-name membership in an injected allowlist plus a safe non-secret authorizationRef. The parent carries no tenant context or credential reference.
  • An all-tenant runner must enumerate tenants outside this value object, construct one strict child tenant context per target, and call bindTenant() before any tenant query, mutation, file access, credential load, or external call.
  • Child binding rejects compatibility provenance and any request, actor, source/job, authorization, mapping, or credential-owner mismatch. It never overwrites mismatched child identity.
  • Parsing and binding are pure normalization. They do not inspect a scheduler, query a database, resolve config/secrets, authorize a module, invoke a provider, or execute the job.

Version 1 Fields

Field Type / values Required contract
schemaVersion literal job-execution-context/v1 Required; unknown versions fail.
selectionMode SINGLE_TENANT, ALL_TENANTS Required explicit selection.
jobName, sourceName safe non-secret references Identify the job contract and invocation source.
invocationId, requestId, correlationId 1-80 safe characters Supplied by the entrypoint; copied unchanged into tenant children.
attempt positive integer Explicit scheduler/runner attempt number.
actor SYSTEM principal reference Required initiating system identity; no user/session inference.
arguments flat scalar map Keys and values are bounded; secret-like keys and nested values are rejected.
authorizationRef safe reference or null Required for ALL_TENANTS; inherited by each child. Never a bearer token or secret.
tenantContext strict context or null Required for single tenant and forbidden on the all-tenant parent.

The single-tenant parser accepts an optional trusted published local/global mapping and a non-secret credentialOwnerRef. Mapping and tenant-owned credential mismatches reuse the tenant failure contract. No raw credential value is accepted by either job contract.

Job Failure Contract

Error code Condition Default transport
JOB_CONTEXT_REQUIRED selection, tenant, job, or invocation identity is missing HTTP 400 or CLI exit 2
JOB_CONTEXT_INVALID malformed mode/reference/attempt, secret-like argument, nested argument, or shared all-tenant credential input HTTP 400 or CLI exit 2
JOB_CONTEXT_FORBIDDEN job is not all-tenant allowlisted or has no authorization reference HTTP 403 or CLI exit 3
JOB_TENANT_BINDING_MISMATCH child source, request, actor, or authorization differs from its job parent HTTP 409 or CLI exit 4

TenantContextException remains authoritative for explicit tenant, mapping, credential-owner, and compatibility-only failures. Validation must finish before an executor callback can run. Errors are visible and must not become a default tenant, silent skip, or empty result.

Verification Cases

The no-I/O unit contract covers valid explicit single-tenant construction, authorized all-tenant parent and child binding, absent selection/tenant, unauthorized all-tenant selection, shared credential rejection, mapping and credential-owner mismatch, secret/nested argument rejection, compatibility-only child rejection, stable CLI/HTTP codes, and a no-op fixture proving rejected input never reaches execution.

Frontend Runtime

  • Page files live under frontend/pages/system/<SYSTEM>/<module>/ or frontend/pages/platform/<area>/.
  • index.php?page=<slug> resolves page files through config/page_routes.php.
  • Do not add compatibility page wrappers at frontend/pages/*.php; route aliases must point to the real module page file.
  • Existing shared JS remains valid:
  • kj.js
  • modules/shared/kjtable.core.js
  • modules/shared/popup.core.js
  • Prefer these shared JS patterns before adding page-specific runtime helpers.
  • Newer MK pages prefer:
  • KJModal
  • kjTable
  • module direct endpoints where available
  • Do not migrate UI to React unless a concrete task requires it.
  • When React screens are added, API calls must use the domain client layout in .agents/rules/REACT_API_CLIENT.md.

Utility Layer

Utility Role
KJDatabase PDO wrapper, request ID, mutation diagnostics
OrderSyncService cross-entity sync after writes
JsonlLogger structured runtime logs
Gmail* services Gmail OAuth, sending, intake
TelegramSender Telegram notification bridge
app/utils/ai/* AI prompt registry and extraction pipeline

Architecture Decisions

  • Preserve remaining module-owned legacy contracts only where a legacy module shell still exists.
  • API v2 routes are the successor contract and must not use TYPE/ACTION or USERNAME request fields.
  • Shared query.php / post.php controller cases were removed after runtime verification in #454.
  • Module gate must be enforced consistently for pages, shared endpoints, and direct endpoints.
  • Config-driven tenant behavior is preferred over client-id hardcoding.
  • Historical docs and dated runbooks are not source of truth.

Sources

  • API contracts: .agents/rules/API_CONTRACTS.md
  • DB rules/schema: .agents/rules/DB_QUERY_RULES.md, .agents/rules/DB_SCHEMA.md
  • Audit/transition evidence: .agents/rules/AUDIT_AND_TRANSITION_EVIDENCE.md
  • Target design: docs/DESIGN.md
  • Current migration inventory: .agents/rules/API_V2_CONTRACT_INVENTORY.md
  • Future migration order: docs/ROADMAP.md
  • React API client: .agents/rules/REACT_API_CLIENT.md
  • Control Tower: .agents/rules/CONTROL_TOWER.md

---

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