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.phpandconfig/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_IDand request-timegetClientId()in the current runtime; long-term tenant identity is the Control Tower-managed UUID described inCONTROL_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.phpowns 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.phpowns Codex Portal file-backed session discovery, archive restore/archive actions, and background runner state understorage/codex-portal/*; it must not introduce Codex session database tables.app/shared/files/api_routes.phpowns sharedT_FILEmetadata lookup, registration, type/sequence update, bulk update/delete, and transaction-document metadata routes.app/addons/reports/api_routes.phpowns 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.phpowns yard-map section/state read routes.app/systems/YMS/yard-move/api_routes.phpowns yard-move queue/location read routes and move/reserve/confirm/cancel write command routes.app/systems/YMS/container-tracking/api_routes.phpowns container, equipment-load, repair-log, validation, yard-container, and seal photo AI OCR API v2 routes.app/systems/YMS/dock-scheduling/api_routes.phpowns appointment, dock option, and yard-location option API v2 routes.app/systems/YMS/scale/api_routes.phpowns scale schedule/list/detail reads and scale check-in/check-out/finalize writes.app/systems/TMS/transload/api_routes.phpowns 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.phpowns delivery driver identity/log/list reads, delivery status/field/driver-assignment writes, and dispatch creation API v2 routes.app/systems/TMS/booking/api_routes.phpowns 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.phpowns carrier master phone lookup and carrier export API v2 routes.app/systems/OMS/commercial-order/api_routes.phpowns commercial order list/detail/options/create/update/delete API v2 routes.app/systems/OMS/inbound-order/api_routes.phpowns 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.phpowns 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.phpowns 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.phpowns allocation workbench read/autocomplete routes and allocation apply writes to outbound demand.app/systems/OMS/trade-summary/api_routes.phpowns trade summary and deal ledger read-only API v2 routes.app/systems/BMS/rate/api_routes.phpowns 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.phpowns BMS scale-ticket create/read/update/delete, closing scale-ticket, and outbound-weight-apply API v2 routes.app/systems/BMS/invoice/api_routes.phpowns local purchase invoice and transaction document read-only API v2 routes.app/systems/BMS/transaction/api_routes.phpowns BMS transaction detail, create/delete by source, close, chassis bulk input, final transload apply, and transaction billing-address lookup API v2 routes.api/index.phploads 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 theCapabilityResolutioncontract inTENANT_CONFIG.mdand 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 throughmoduleGuardEndpointModuleKey()- API v2 route -> explicit
moduleKeyon 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.phpfiles. - 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_IDvalues are globally stable across deployments; they are compatibility data until tenant UUID migration is complete. - Do not add hard tenant blocks such as
clientId !== Nunless 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_LOCATIONare updated in the same transaction as authoritative yard state. KJDatabaserecords 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
TenantExecutionContextalways represents exactly one effective tenant.localTenantIdmust be a positive deployment-localCLIENT_ID. ALL_TENANTSis not a tenant context.JobExecutionContextmay hold an explicitly authorizedALL_TENANTSselection, but it must create one validatedSINGLE_TENANTchild 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.
authorizationRefis 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 anENTITY_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:
- regular session and user impersonation parity with
getClientId(),getSessionUserId(), andgetSessionUsername(); - client-only impersonation keeps original admin actor and a null subject;
- super-admin global, allowlisted target override, and forbidden override remain distinct;
- missing/malformed tenant, mapping mismatch, credential mismatch, and compatibility-only write fail before I/O;
- safe serialization contains no session, token, credential value, request body, or raw impersonation structure;
- strict API propagation is reconstructed and checked against the legacy scalar tenant before a context-aware consumer runs;
- 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_TENANTorALL_TENANTS; absence never means the deployment default. SINGLE_TENANTconstruction requires a positive local tenant input and creates a strictTenantExecutionContextwithEXPLICIT_JOB,CLI,SYSTEM, the same job name, actor, request ID, and authorization reference.ALL_TENANTSconstruction requires exact job-name membership in an injected allowlist plus a safe non-secretauthorizationRef. 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>/orfrontend/pages/platform/<area>/. index.php?page=<slug>resolves page files throughconfig/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.jsmodules/shared/kjtable.core.jsmodules/shared/popup.core.js- Prefer these shared JS patterns before adding page-specific runtime helpers.
- Newer MK pages prefer:
KJModalkjTable- 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/ACTIONorUSERNAMErequest fields. - Shared
query.php/post.phpcontroller 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.