# NexusCRM Serviceification Refactor Learned Log

## Purpose and Authority

This file is the durable engineering memory for the NexusCRM serviceification
program. The project lead updates it after each meaningful closed loop so that
future work can recover the evidence, mistakes, decisions, and next hypothesis
without relying on chat history or memory.

Authority is intentionally split by fact:

- issue [#3555](https://github.com/pingcap-inc/NexusCRM/issues/3555) owns live
  phase, branch, PR, wait, and blocker status;
- the target architecture document owns normative service boundaries and
  invariants;
- this file owns technical reasoning, evidence learned, mistakes, decision
  rationale, and the technical next-step queue.

Do not copy transient PR/check status into this file as a second source of
truth. Link to the tracking issue instead.

## Update Protocol

Update this file when any of these occurs:

1. a design or implementation loop closes with new evidence;
2. an assumption is disproved;
3. an error, failed command, review blocker, or rollback teaches a reusable
   lesson;
4. a source of truth, owner, read path, or migration order is decided;
5. the next technical hypothesis changes.

Each loop entry records:

- **Plan** — the concrete question and success criteria;
- **Work completed** — actions and evidence, not activity narration;
- **Errors encountered** — including tooling and process errors;
- **Learned** — facts that update the system model;
- **Decisions** — choices now binding on subsequent work;
- **Next** — the smallest useful next loop.

## Normative Invariants

This learned log does not restate the normative contract. Service-boundary,
communication, consistency, and rollout invariants are owned by
[`nexuscrm-serviceification-target-architecture.md`](../../design/nexuscrm-serviceification-target-architecture.md).
L0/L1 ownership decisions are owned by
[`l0-l1-standard.md`](../../design/l0-l1-standard.md). Journal entries below
record how evidence changes the implementation plan; they cannot redefine those
invariants.

## Technical Plan

The live status for these phases remains in issue #3555.

1. Preserve selected-root as the first implemented and validated neutral
   Runtime record capability. Its semantic/wire contract, Runtime adapter,
   dormant sfcrm client, and CI-only issue-tracker fixture are complete, but it
   owns no production route and is not the first production cutover.
2. C0 selects UI API record-access as the first real CRM ownership slice. It is
   not selected-root: it needs one coarse neutral operation for optional type
   resolution plus bounded Read/Update/Delete/Locked decisions.
3. C0, A1a, and P7a supply the consumer, App-fact, and metadata-read-path facts.
   A1b then freezes only the App capability and HandlerBinding required by that
   consumer.
4. Before V1, record-access receives its own approved neutral semantic/wire
   contract, CRM legacy parity vectors, and capability-specific non-CRM CI
   conformance consumer. The selected-root fixture cannot be cited as proof of
   this different seam, and no in-process interface may be mirrored remotely.
5. P7b builds the complete immutable RuntimeMetadataView non-serving. P7c/S1
   atomically binds it after TrustedCall, switches all serving consumers, and
   deletes global/repository fallback before any route cutover.
6. V1 then moves exactly the record-access route with one-RPC enforcement,
   owner fencing, rollback, failure/compatibility proof, and legacy-path
   deletion. Mutation/worker and Integration follow as executable vertical
   slices rather than horizontal evidence phases.
7. Deployment/release mechanics remain owned by #3624 and L2 delivery. They do
   not create a second service-boundary or App-domain authority.

## Decision Ledger

| Decision | Rationale | Revisit trigger |
|---|---|---|
| Start contract-first, but write a use-case ADR and golden fixtures before proto | Generating IDL first would freeze the current 33-plane in-process dependency graph as a distributed monolith | The first bounded use case cannot be represented without a new neutral primitive |
| Require one real production CRM consumer plus a capability-specific non-CRM CI conformance consumer for every new L0 seam | It tests L0 neutrality, trusted context, bounded RPC count, and wire evolution without pretending the fixture is a product App; proof for selected-root does not transfer to record-access | A real non-CRM App exposes requirements the fixture does not cover |
| Distinguish the first implemented neutral capability from the first production ownership slice | Selected-root is implemented and validated but has no behaviorally equivalent production route; C0 selects record-access without mutating selected-root | A current production consumer becomes exactly selected-root-equivalent, or record-access parity disproves its boundary |
| Keep semantic IR and generated wire types in separate Go modules | `nexuscrm-contract` must remain a stdlib-only dependency root; generated gRPC code necessarily adds runtime dependencies | Go/tooling proves an equally strict dependency boundary with a simpler release model |
| Do not implement the full Runtime API in the first milestone | The exact wire semantics, identity, error model, limits, and compatibility contract are not yet executable | The first slice passes conformance, performance, and N/N-1 tests |
| Keep the architecture PR separate from implementation | Review must settle ownership and gates without hiding them in implementation churn | Architecture authority is merged to `main` |
| Classify issue-tracker as a test fixture, never a product App | The product owner states it exists only to test App-neutral L0 seams | The product owner explicitly promotes it into a separately owned real App with production requirements |
| Reclassify the old release-shaped issue-tracker pack, dialect, and frontend through an explicit compatibility sequence | Their current file/build shape predates the test-only product decision and can be mistaken for a production App | None; production-shaped residue must be removed before Runtime/L1 production cutover |
| Treat M2c envelope vectors as candidate/defense evidence, not final valid-request evidence | The 64 KiB exact vector used up-to-255-byte object/field names, while current direct TiDB physical-name ownership rejects the 65th byte | A metadata-owned logical-to-physical naming scheme removes the direct storage bound |
| Separate record-ID read-input admission from stored-ID shape | The core catalog owns 133 `VARCHAR(255)` and three `BIGINT` primary IDs, while generic records and metadata Flow generate 36/18-byte values; one global opaque bound can protect the read without promising every object stores that width | A current supported record authoring path emits more than the bound |
| Let the sfcrm AppService route owner own total route SLO and retry budget; let Runtime own per-attempt deadline and admission | Only the caller can budget the complete user route; Gateway propagates/enforces coarse deadlines but does not become a competing App SLO authority | Route ownership moves through the approved cutover protocol |
| Defer real codec N/N-1 and transport-interceptor proof until the corresponding wire and interceptor exist | P1c specifies compatibility semantics, but claiming generated-client/server or delegation-interceptor proof before P2/P3/P6 would be fictitious | None; the phase may move earlier only if the real production artifact moves with it |
| Treat P3's real-TiDB TCP test as service-boundary proof, not separate-OS-process proof | The generated client and server use a real listener and TiDB but are assembled in one Go test process | P4 launches the real client and Runtime as separate OS processes |
| Keep generic deployment artifacts in L0 and concrete org/environment overlays in L2 | L0 owns reusable mechanisms and release artifacts; L2 owns deployment-specific values, secrets, identity bindings, and rollout behavior | The repository/deployment ownership standard changes |
| Keep P3a workload identity non-serving | A projected token proves packaging/configuration wiring but cannot become a second authority before P6 delegation and isolation semantics exist | P6 supplies the authoritative verifier and session binding |

## Delegation Queue

The project lead may assign the following read-only evidence tasks in parallel.
Agents must not edit files, create branches, push, or invent a final protocol.
Their outputs are inputs to the first-slice ADR; the lead owns the integrated
decision.

### Task A — Derive the First Read-Only Use Case

```text
You are reviewing the current NexusCRM tree to derive the smallest real
read-only L0 Runtime use case that can be consumed by sfcrm and the
issue-tracker conformance fixture without embedding CRM semantics in L0.

Read current code, not old issue claims. Map the end-to-end handler -> dialect ->
contract.Deps -> engine calls for candidate record/query/read-graph paths.
Measure RPC amplification risk: records, metadata, policy, relationships, and
pagination calls per user operation. Identify process-local context or type
assertions that would become wire fields.

Return:
1. two or three candidate use cases;
2. exact current consumers and file/line evidence;
3. the recommended first use case and why it is neutral and bounded;
4. the minimum semantic request/response facts, without designing protobuf;
5. golden fixtures needed for both CRM and issue-tracker;
6. blockers or missing non-CRM evidence.

Do not propose remoting contract.Deps or its 33 interfaces. Do not edit files.
```

### Task B — Identity and Trusted Request Context

```text
Derive the current NexusCRM authentication/session/context chain that a future
L1 AppService -> L0 Runtime gRPC call must preserve.

Trace JWT/session validation, revocation, Connected App, scopes/audience,
OrgContext, principal, AppInstallation authorization, SecurityContext, request
ID, deadline, and formula user context from current entrypoint to engine use.
Separate authoritative facts from caller projections and caches. Identify all
context side channels that would be lost at a process boundary.

Return:
1. the current trust chain with file/line evidence;
2. the minimum signed/delegated execution facts;
3. which facts belong in authenticated transport metadata versus the semantic
request;
4. fail-closed rules, expiry/revocation behavior, and negative tests;
5. unresolved decisions that must be in the contract ADR.

Do not choose a new auth product or implement code. Do not edit files.
```

### Task C — Wire Module and Compatibility Gate

```text
Inspect nexuscrm-contract@main and the consuming NexusCRM modules. Design the
smallest repository/module/tooling shape that adds proto/common/v1 and
proto/runtime/v1 plus a separate generated wire-go Go module while preserving
the root contract module's stdlib-only rule.

Return:
1. current module and release facts with commit/file evidence;
2. proposed directory and Go module dependency graph;
3. reproducible generation, lint, and breaking-change commands;
4. package/version rules and N/N-1 compatibility test shape;
5. release ordering across contract, NexusCRM Runtime, and L1 clients;
6. risks, especially generated-code drift or accidental runtime dependencies in
the semantic root.

Do not add files, select broad RPC schemas, or modify repositories.
```

### Task D — Runtime Process-Split Inventory

```text
Inspect NexusCRM's current serverruntime.Run assembly and derive the minimum
safe runtime-api/runtime-worker process split skeleton.

Inventory HTTP serving, background goroutines, schedulers, outbox workers,
caches, registries, sync.Once globals, EventBus publishers/subscribers,
shutdown/drain behavior, database pools, and configuration. Classify each as
API-only, worker-only, deliberately shared implementation, externalized state,
or unresolved.

Return:
1. current startup/shutdown flow with file/line evidence;
2. the role ownership matrix;
3. state that would silently diverge after a process split;
4. minimum health/readiness/version/capability behavior;
5. skeleton tests that prove role=all means exactly API + worker and that no L1
code is embedded;
6. blockers before creating the two command directories.

Do not split code or edit files. Do not classify independent binaries as
independent logical services.
```

## Loop Journal

### 2026-07-11 — Architecture Acceptance and Implementation Entry

**Plan**

Determine whether the accepted serviceification architecture is sufficient to
begin implementation, and choose the first review-sized engineering step.

Success criteria:

- distinguish skeleton readiness from wire-contract readiness;
- inspect the live contract and runtime assembly rather than trusting the old
  API plan;
- create a durable implementation coordination surface;
- do not begin code on an unmerged architecture authority.

**Work completed**

- Reviewed the target architecture and its incremental Section 13 gates.
- Inspected `origin/main`, the current server/distribution commands, the live
  `shared/pkg/contract.Deps`, and `nexuscrm-contract@main`.
- Confirmed that the current contract repository has no proto packages or
  generated wire module.
- Confirmed that the current in-process L1 surface still contains 33 capability
  planes and wire-hostile transaction closures, callbacks, type assertions, and
  context side channels.
- Confirmed that `runtime-api` and `runtime-worker` command directories do not
  yet exist and the current `serverruntime.Run` assembly starts foreground and
  background behavior together.
- Created tracking issue #3555 and linked it as a sub-issue of task issue #3476.
- Marked architecture PR #3554 ready for formal review.

**Errors encountered**

1. The superseded external service API inventory contains a legacy JSON/HTTP
   and 33-plane API proposal. Treating its detailed endpoint list as current
   design would recreate the rejected distributed monolith.
2. `gh issue create --body-file -` was invoked without piping the prepared body
   to stdin. GitHub created issue #3555 with an empty body. The body was
   immediately replaced through the GitHub API and then read back in full.
3. The sub-issue POST response returned the parent issue object, which made its
   displayed number look like the wrong child. The relationship was verified
   independently through both the child parent endpoint and the parent's
   sub-issue list.
4. The current worktree's `backend/distributions/go.mod` replaces dialect
   modules into a local `nexuscrm-app-packs` checkout that is absent. A dedicated
   app-packs worktree or checkout is required before implementing the L1 slice.

**Learned**

- The high-level architecture is sufficient for an independently deployable
  skeleton and for writing the first contract ADR.
- It is not sufficient to freeze protobuf messages or implement a broad Runtime
  API. Exact trusted-context transport, typed values, error details, retry
  classification, deadlines, limits, pagination, capabilities, and first-use-
  case semantics remain undecided.
- Contract-first means first defining one bounded user operation and executable
  compatibility fixtures. It does not mean generating proto before the use case
  is proven.
- The first slice must exercise both CRM and a real non-CRM consumer. Merely
  compiling an issue-tracker partition is not proof of a neutral runtime API.
- Architecture authority should land before an implementation branch is based
  on it; otherwise the code and design can drift under review.

**Decisions**

- Do not implement all APIs first.
- P1 is a small first-slice contract ADR with evidence and golden fixtures.
- P2 adds proto and generated wire code only after P1 approval.
- The first use case is provisionally a bounded neutral record/read-graph
  operation. The ADR must choose its exact shape from live CRM and issue-tracker
  consumers.
- Keep current architecture PR #3554 unchanged while its formal review runs.

**Next**

1. Complete the current-head review and merge gate for PR #3554.
2. Collect Tasks A-D in parallel if additional agents are available.
3. Integrate their evidence into the first-slice contract ADR.
4. Resolve these contract decisions before proto generation:
   - exact first operation and RPC budget;
   - typed record/query value representation;
   - trusted execution context and workload authentication;
   - stable error/detail and retry model;
   - deadline, request/response size, pagination, and partial-result rules;
   - protocol/capability negotiation and N/N-1 tests;
   - CRM and issue-tracker golden fixtures.

### 2026-07-11 — Non-CRM Consumer Recheck and Phase Reshape

> **Superseded decision:** the product-App actions in this historical entry were
> withdrawn by “Issue-Tracker Product Classification Correction” below. The
> issue-tracker is a test-only conformance fixture. Retain this entry only as the
> record of how the mistaken product assumption was found.

**Plan**

Recheck whether a real issue-tracker record-read consumer already exists and
whether the proposed first Runtime contract has enough shared behavior to enter
an ADR.

Success criteria:

- inspect dialect routes, product hooks, generic host routes, pack metadata,
  frontend contributions, combined distributions, and CI;
- distinguish a Salesforce route reading an `Issue` record from an independent
  issue-tracker consumer;
- separate current behavior from proposed limits and projections;
- reshape the phase plan instead of manufacturing evidence.

**Work completed**

- Rechecked NexusCRM `21075d881` and nexuscrm-app-packs
  `f1f17a649f898e19dce319163fa658e39a9c35b5`.
- Confirmed that the issue-tracker dialect registers only `GET /info`, its
  frontend is a static queue stub, and its pack has object, list-field, and
  permission metadata but no record-detail product flow.
- Confirmed that a combined CRM + issue-tracker distribution can read `Issue`
  through the Salesforce SObject route. That remains a Salesforce-dialect
  consumer and does not satisfy the non-CRM proof.
- Derived the current CRM point-read capability amplification as
  `4 + 2T + R`, where `T` is the number of distinct lookup target types and `R`
  is the number of readable lookup references. Remoting those calls directly
  would create an unbounded-by-shape distributed call graph.
- Confirmed that the current route has no explicit maximum field, relationship,
  node, response-byte, or server deadline budget. Numeric limits are new ADR
  decisions, not baseline facts.

**Errors encountered**

1. The first Task A conclusion said issue-tracker could not read records. That
   was too broad: the combined distribution can read `Issue` through the
   Salesforce dialect. The corrected claim is that no independent
   issue-tracker-owned record consumer exists.
2. The initial phase plan treated the architecture's planned issue-tracker proof
   as if it were current consumer evidence. A normative future proof requirement
   does not prove current product behavior.

**Learned**

- No current issue-tracker UI, action, API, or CLI requires a record-detail
  projection.
- Relationship expansion is required by the current Salesforce projection but
  not by a second real consumer. It cannot enter the first neutral contract on
  CRM evidence alone.
- A minimal shared candidate is a bounded root-record read with caller-selected
  root fields, typed values after CRUD/RLS/FLS, and stable outcome metadata.
  This remains a candidate until the reference issue-tracker consumer is
  implemented and its baseline is executable.
- Existing disclosure semantics worth preserving are: missing and RLS denial
  are indistinguishable; object Read denial is a distinguishable forbidden
  result; FLS-hidden fields are omitted.
- The issue-tracker consumer can be built above existing neutral in-process
  seams without changing L0 or adding another `contract.Deps` plane.

**Decisions**

- Do not claim an existing non-CRM record consumer and do not generate proto.
- Approve an issue-tracker root-record read as a deliberate reference-App
  conformance product requirement mandated by the target architecture. Document
  it as new scope rather than invented historical demand.
- Split the prerequisite by release boundary:
  - P1a in nexuscrm-app-packs implements the production issue-tracker L1 read
    route and executable product-level behavior using existing in-process
    neutral seams;
  - P1b in NexusCRM pins that app-packs revision and proves the real
    issue-tracker-only distribution path and golden behavior;
  - P1c writes the first-slice contract ADR from the two executable baselines.
- The first candidate contract includes selected root fields only. Lookup or
  relationship expansion stays out until a real non-CRM use case requires it.
- P1a cannot modify L0, add proto, add a new contract plane, or present new
  numeric limits as current behavior.

**Next**

1. Assign P1a in nexuscrm-app-packs with the approved reference-App scope and
   guardrails.
2. After P1a merges, pin its source revision in NexusCRM and add the
   issue-tracker-only distribution/golden proof in P1b.
3. Integrate Tasks B-D plus the P1a/P1b baselines into P1c.
4. Freeze typed values, trusted context, errors, retry classification, limits,
   deadlines, version/capability negotiation, and N/N-1 behavior only in P1c.

### 2026-07-11 — Issue-Tracker Product Classification Correction

**Plan**

Record the product owner's authoritative clarification that issue-tracker
exists only as a test fixture, then remove every implementation-plan assumption
that treats it as a real App or production deployment.

Success criteria:

- preserve issue-tracker as a useful non-CRM contract test;
- stop requiring invented product demand, public routes, Gateway ownership, or
  an independently deployed issue-tracker service;
- distinguish contract neutrality proof from multi-App production proof;
- correct the normative architecture and live phase plan.

**Work completed**

- Reclassified issue-tracker as a CI-only non-CRM conformance fixture.
- Audited the target architecture, current technical plan, decision ledger, and
  tracking phases for places that called the fixture an AppService, product
  App, production route owner, or deployment unit.
- Preserved the useful Task A evidence: the Salesforce point-read path would
  amplify to `4 + 2T + R` RPCs if remoted naively, and relationship expansion
  currently has only CRM evidence.

**Errors encountered**

1. The architecture and initial implementation plan conflated two different
   proofs: "this contract contains no CRM semantics" and "the platform runs a
   second real product App." A test fixture can prove the first but never the
   second.
2. After Task A found no existing product consumer, the lead incorrectly
   promoted issue-tracker into a new reference-product requirement. That added
   unnecessary public-route, frontend, deployment, pin, and Gateway work.

**Learned**

- A conformance fixture is allowed to be designed specifically to exercise
  contract semantics. It does not need organic user demand because it makes no
  product claim.
- The fixture must remain outside production service composition and cannot be
  counted as an L1 AppService or production failure domain.
- The first contract ADR may use a production CRM consumer plus a non-CRM test
  fixture to prove neutrality. A later real non-CRM App is still required to
  prove independent product deployment and evolution.
- Fixture coverage should target semantic failure classes—typed values,
  CRUD/RLS/FLS, missing-versus-denied masking, deadlines, limits, and version
  compatibility—not reproduce Salesforce response projection.

**Decisions**

- Issue-tracker is test-only. It owns no production API, frontend obligation,
  Gateway route, AppInstallation, workload identity, service image, SLO, or
  release promise.
- P1a creates or reshapes a test-only issue-tracker point-read conformance
  fixture and golden cases. It does not add a production handler or service.
- P1b runs the fixture through the current NexusCRM in-process neutral seam to
  establish an executable non-CRM baseline. It does not pin a production
  issue-tracker App release.
- P1c compares the production CRM baseline with the fixture baseline and owns
  the first contract ADR.
- P4 proves the same gRPC family with the production sfcrm client and CI-only
  fixture client. Gateway migration applies only to the sfcrm production route.

**Next**

1. Update the target architecture and tracking issue with the fixture/product
   distinction.
2. Assign the test-only P1a conformance-fixture task.
3. Keep proto generation and Runtime command splitting blocked until P1c is
   approved.
4. Track support for a second real production App as a separate future product
   proof, not as an issue-tracker deliverable.

### 2026-07-11 — P1a Test Fixture Merged

**Plan**

Verify that nexuscrm-app-packs PR #362 actually creates a test-only non-CRM
point-read consumer without adding a production App, L0 dependency, wire
contract, or deployment artifact.

Success criteria:

- the fixture is physically test-owned and excluded from production dependency
  and artifact closures;
- it consumes only the current public neutral point-read seam;
- its golden matrix covers the approved semantic failure classes;
- review and repository checks pass on the merged head;
- remaining compatibility or CI work is made explicit rather than hidden.

**Work completed**

- Verified merged nexuscrm-app-packs PR #362 at merge commit
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.
- Confirmed that all six changed files live under
  `tests/conformance/issue-tracker/` and that the fixture contains no command,
  pack manifest, package manifest, Dockerfile, production route, or service
  entrypoint.
- Confirmed that the consumer uses the existing public
  `contract.Records.Retrieve` seam and adds no L0 helper, `contract.Deps` plane,
  protobuf, generated code, or relationship expansion.
- Confirmed golden outcomes for typed root values, explicit null, absent/FLS
  omission, missing/RLS masking, object Read denial, cancellation, and
  deterministic selected-field ordering.
- Confirmed successful Codex Review, dialect seals, and frontend source gates.

**Errors encountered**

1. The current repository still contains the old `issue-tracker-standard/`,
   `dialects/dialect-issue-tracker/`, and frontend stub in release-shaped
   locations. P1a correctly did not move them because NexusCRM pins and compiles
   those paths; treating P1a as physical reclassification would have broken the
   downstream repository.
2. The app-packs hosted workflow cannot run the new nested module because it
   lacks read access to the private NexusCRM and nexuscrm-contract sources.
   Copying or vendoring L0 into app-packs would violate ownership, so enforcement
   must run from NexusCRM CI in P1b.

**Learned**

- Test-only classification is now enforced for the new fixture path, but it is
  not yet fully enforced for the older release-shaped issue-tracker residue.
- The fixture provides legitimate non-CRM semantic demand without claiming a
  user-facing product, AppService, AppInstallation, deployment, or SLO.
- The fixture defines semantic typed values and outcomes, not a wire schema;
  P1c still owns the protobuf-neutral ADR decisions.
- Standard review identified two useful hardening opportunities: include test
  dependencies and future subpackages in the public-neutral closure guard, and
  add false/zero/empty-string scalar cases. They are not current correctness
  failures, but must be resolved before the contract baseline freezes.

**Decisions**

- Mark P1a complete with nexuscrm-app-packs PR #362.
- P1b runs the fixture module from NexusCRM CI and connects the same golden
  semantics to the real current in-process Runtime adapter. It must not vendor
  L0 into app-packs or turn the fixture into a production dependency.
- Track public-neutral test-dependency/subpackage coverage and falsey scalar
  cases as baseline-hardening acceptance before P1c approval.
- Perform physical cleanup of the old release-shaped issue-tracker artifacts
  through an app-packs packaging PR followed by a NexusCRM compatibility PR;
  do not mix that cleanup into the contract ADR.

**Next**

1. Merge the normative architecture clarification that issue-tracker is a
   fixture, not a second App.
2. Implement P1b in NexusCRM: CI checkout/wiring, fixture test execution, and a
   real in-process Runtime adapter/golden equivalence proof.
3. Plan the two-repository cleanup of the old release-shaped pack, dialect,
   frontend stub, source pins, distribution commands, and build tags.
4. Start P1c only after P1b makes the non-CRM baseline executable in NexusCRM
   CI and baseline hardening is closed.

### 2026-07-11 — P1b Real Runtime Adapter Conformance

**Plan**

Run the P1a issue-tracker fixture at the fixed app-packs merge revision against
NexusCRM's real current in-process Records adapter. Keep every composition edge
test-only, prove the normal production dependency closures remain clean, and
make semantic drift fail NexusCRM CI before beginning any wire design.

Success criteria:

- the fixture reaches the same `contract.Records.Retrieve` implementation used
  by Runtime assembly;
- the P1a golden matrix plus false, zero, and empty-string values passes;
- test dependencies and future fixture subpackages are included in the neutral
  boundary proof;
- no production module, command, distribution, image, route, or release pin
  acquires the fixture dependency;
- NexusCRM records an explicit app-packs provenance revision and reviews the
  exact executable fixture snapshot in-tree.

**Work completed**

- Mapped the production assembly from `serverruntime.Run`, through
  `services.NewServiceManager`, to `records.NewContractService(sm.Persistence)`.
  `ServiceManager.RecordsContract` exposes that same adapter, and
  `records.ContractService.Retrieve` delegates directly to
  `PersistenceService.Retrieve`.
- Added a build-tagged NexusCRM conformance test that constructs the production
  `records.ContractService` over the production `PersistenceService`. Only the
  storage and permission inputs are test doubles; the fixture itself receives
  only the public neutral `contract.Records` interface and never accesses the
  database.
- Ran app-packs fixture revision
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8` against that adapter and preserved
  typed values, explicit null versus absent, FLS omission, missing/RLS masking,
  distinguishable object-Read denial, cancellation, and deterministic selected
  field ordering.
- Added explicit false boolean, zero integer, zero decimal, and empty-string
  records to the real-adapter matrix.
- Added a test-only CI composition script. It creates temporary Go module files
  for the cross-repository edge, runs the tagged adapter test and the app-packs
  fixture tests, then removes those files. A separate trusted script checks the
  production closures. No checked-in production `go.mod` imports the fixture.
- Strengthened the fixture closure check to use `go list -deps -test` over
  `./...`, covering test dependencies and future fixture subpackages, and added
  a separate negative closure guard for the normal records module, app-blind
  backend command, and issue-tracker distribution.
- Added an inert reviewed snapshot of the P1a consumer, its upstream golden
  tests, and golden JSON. `app-packs-revision` records provenance at the P1a
  merge, while a SHA-256 manifest seals every copied file. CI executes only the
  reviewed in-tree snapshot; the provenance revision cannot trigger an external
  checkout or select executable code. The production Official App Packs source
  pin remains the single external executable app-packs read path.

**Errors encountered**

1. The P1a fixture's local `go.mod` replace assumes sibling source checkouts,
   while NexusCRM CI resolves the private contract module normally. The runner
   therefore uses a temporary fixture modfile that retargets shared to this
   checkout and removes the contract's development replace. The fixture source
   remains unchanged from the reviewed P1a snapshot.
2. A local dependency-closure check of the issue-tracker distribution initially
   followed its development-only relative dialect replace. The runner now uses
   a temporary distribution modfile pointing that replace at the existing
   Official App Packs checkout. This affects only the negative closure
   assertion.
3. App-packs cannot enforce this integration in its own hosted workflow because
   the real L0 implementation is private. NexusCRM consequently owns the
   executable adapter proof, while app-packs continues to own the neutral
   fixture and golden semantics.
4. The first CI draft placed the independent fixture checkout in the existing
   secret-bearing build job. Codex Review correctly blocked that design: a
   changed fixture SHA could execute external code with private deploy keys and
   made the production source job own two executable app-packs inputs.
5. Moving that checkout to a least-privilege job and erasing credentials was
   still insufficient: independently selected external code could execute on a
   runner holding private module contents. The final design has no independent
   external checkout. NexusCRM reviews an inert source snapshot and hash
   manifest; the SHA is provenance only, while `OfficialAppPacksSourceRevision`
   remains the sole owner of externally executed app-packs source.

**Learned**

- `records.ContractService` is already the neutral Runtime adapter. P1b needs no
  new helper, capability plane, Runtime server, or alternative read path.
- A fake repository underneath `PersistenceService` keeps the test deterministic
  while still exercising the production CRUD, RLS, and FLS decision path. The
  fixture remains unable to bypass that path or query storage directly.
- A production-only dependency scan is insufficient for a conformance fixture:
  its own test graph must be closed over the approved public neutral modules,
  and the scan root must include future subpackages.
- An independent test-fixture revision is not safe merely because it is pinned,
  and credential cleanup is not sufficient if private source remains on the
  runner. An in-tree reviewed snapshot makes the executable bytes part of the
  NexusCRM diff and leaves the production build's existing Official App Packs
  pin as the single external source owner.
- Falsey scalar values require presence-aware assertions. Boolean false, numeric
  zero, and empty string are returned values, not missing fields.

**Decisions**

- Keep P1b entirely in test-only composition. Do not add protobuf, gRPC,
  generated code, a Runtime command, a route, an AppService, or another
  `contract.Deps` capability.
- Record the P1a merge SHA as snapshot provenance, not as an executable checkout
  selector. Snapshot updates must be reviewed as NexusCRM source diffs and
  resealed; do not change the production app-packs revision merely to make the
  fixture available.
- Treat the production adapter plus neutral fixture as the executable semantic
  baseline for P1c. P1c may design a transport only after this matrix is green;
  it must not redefine the established outcomes.
- Keep physical cleanup of the older release-shaped issue-tracker artifacts in
  the previously identified two-repository packaging and compatibility sequence.

**Next**

1. Land P1b with NexusCRM CI and current-head Codex Review green.
2. Use the executable matrix as input to P1c's neutral wire ADR without adding
   relationship expansion or numeric-limit claims to the baseline.
3. Schedule the separate app-packs classification/packaging cleanup and its
   NexusCRM compatibility PR.

### 2026-07-11 — Serviceification Program Checkpoint after P1b

**Plan**

Build the correct serviceification shape incrementally: settle ownership and
deployment boundaries, prove one App-neutral use case against the current L0
engine, freeze its contract, and only then add transport and deployable service
roles. Preserve current compatibility while keeping every landed step usable by
the intended future architecture.

**Program status**

| Area | Status | Evidence |
|---|---|---|
| Architecture direction and incremental gates | Complete | PR #3554 |
| Durable plan, task packets, and learned log | Complete | PR #3556 |
| First consumer and seam recheck | Complete | PR #3557 |
| Issue-tracker fixture/product classification | Complete | PR #3559 |
| Non-CRM semantic fixture | Complete | app-packs PR #362 |
| Real current Runtime adapter conformance in NexusCRM CI | Complete | PR #3560 |
| First remote contract ADR | Not started | P1c is next |
| Protobuf and generated wire module | Blocked on P1c | P2 |
| Independent Runtime API/worker artifacts | Not started | P3 |
| L1 gRPC consumption and Gateway cutover | Not started | P4-P5 |
| Metadata snapshot/version cutover and mutation paths | Not started | P6-P9 |

**Work completed**

- Established the target logical boundaries: Catalog, Runtime, Integration,
  generic Gateway, and independently deployable executable L1 AppServices.
- Established the incremental rule: repository and deployment skeleton first,
  then one vertical slice at a time; no production owner moves without its own
  contract, fencing, rollback, performance, and failure-recovery gates.
- Corrected issue-tracker from a presumed second product App to a CI-only
  conformance fixture. It proves App-neutral contract behavior, not production
  multi-App operation.
- Selected the first neutral seam as a bounded selected-field record read and
  rejected remotely exposing the current 33 in-process Go interfaces.
- Added a non-CRM point-read fixture and golden semantics for typed values,
  null versus absent, CRUD/FLS/RLS behavior, missing/forbidden/canceled
  outcomes, deterministic ordering, and falsey scalar values.
- Connected that fixture to the production
  `ContractService -> PersistenceService.Retrieve` decision path and made the
  proof mandatory in NexusCRM CI.
- Kept the fixture out of production module, server, distribution, route,
  image, and deployment closures. No protobuf, gRPC, Runtime command,
  AppService, or new capability plane has been added yet.
- Closed the CI executable-source trust gap with a reviewed in-tree snapshot
  and SHA-256 manifest. The app-packs SHA records provenance only; the existing
  Official App Packs source pin remains the one external executable source.

**Errors encountered**

1. Early planning treated issue-tracker's release-shaped residue as proof of a
   real second App and proposed production route, frontend, service, identity,
   and deployment work for it. Product clarification showed that this was
   invented scope; the architecture and phase plan were corrected.
2. Early contract planning risked turning current fine-grained Go interfaces
   into RPC APIs. Consumer tracing showed that this would preserve coupling and
   create network fan-out, so the first seam was narrowed to one coarse neutral
   use case with a one-RPC budget.
3. The first P1b CI design let an independently pinned fixture revision select
   external Go code in a secret-bearing job. Moving credentials was still not
   sufficient because private module contents remained on the runner. The
   executable fixture was changed to reviewed, hash-sealed in-tree bytes.
4. Existing issue-tracker pack, dialect, distribution, build-tag, and frontend
   paths still look like production artifacts. They are compatibility residue,
   not current product authority, and require a separate two-repository cleanup
   before Runtime/L1 production cutover.

**Learned**

- Contract-first means executable semantic evidence before protobuf, not proto
  files before understanding consumers.
- The current `records.ContractService` is already the neutral in-process
  Runtime adapter; the first remote contract can be derived without inventing a
  second L0 read path.
- App-neutrality proof and production multi-App proof are different. The
  fixture proves the former; only an independently owned and operated real L1
  App can prove the latter.
- A deterministic repository below the real `PersistenceService` is an
  appropriate test boundary: storage is controlled while production CRUD, RLS,
  FLS, masking, and contract projection remain executable.
- CI source ownership is an architecture boundary. A pinned external SHA is
  still a second executable source of truth unless its bytes enter the normal
  review boundary or reuse the existing authoritative pin.
- The project has completed architecture and semantic-baseline work, not
  service extraction. No network boundary, deployable Runtime role, production
  L1 client, or route cutover exists yet.

**Decisions**

- P0 through P1b are complete. The architecture direction and first App-neutral
  semantic baseline are now evidence-backed.
- P1c is the next critical-path phase. It owns trusted context, typed values,
  stable errors and retry classes, request/response limits, deadlines,
  version/capability negotiation, revision/generation receipts, N/N-1 behavior,
  and the one-RPC budget.
- The first contract remains selected root fields only. Relationship expansion
  stays out until a real non-CRM product use case requires it.
- Protobuf generation remains blocked until P1c approval. Runtime command
  splitting remains blocked until the wire contract and the issue-tracker
  compatibility cleanup prerequisites are complete.
- P1x physical cleanup remains separate from contract design and can proceed as
  an independent compatibility sequence after P1b.

**Next**

1. Complete P1c: write and approve the first-slice contract ADR using the
   production CRM baseline and the CI-only non-CRM fixture baseline.
2. In parallel where ownership permits, execute P1x-a/P1x-b to remove the old
   release-shaped issue-tracker product residue without changing fixture
   semantics.
3. After P1c, implement P2: the minimal neutral protobuf family, separate
   generated `wire-go` module, and compatibility CI.
4. Then implement P3-P5: independent Runtime API/worker artifacts, production
   sfcrm client, fixture-only test client, and exactly one fenced Gateway route
   cutover.
5. Continue with P6-P9 only after the first slice proves performance, tenant
   isolation, upgrade compatibility, rollback, timeout, and failure recovery.

### 2026-07-11 — P1c Selected-Root Contract and Measurement Gate

**Plan**

Derive the first neutral Runtime read contract from the production CRM point
read, the merged issue-tracker conformance fixture, the real P1b adapter, and the
serviceification authority. Freeze every semantic decision supported by current
evidence, but do not approve invented numeric limits or begin transport work.

Success criteria:

- one selected-root record read and one Runtime data-RPC budget;
- trusted delegation cannot be replaced by caller-supplied identity or policy;
- values, disclosure, errors, retries, consistency, and compatibility are
  unambiguous;
- relationship expansion and the current CRM fan-out remain outside v1;
- missing limit/deadline evidence becomes an exact phase gate.

**Work completed**

- Reconstructed nexuscrm-app-packs production main
  `bb138effbe9c2857db432224892c232e14bddf80`: the CRM point-read route performs
  metadata describe, the neutral root retrieve, a policy read, lookup hydration,
  relationship nesting, CRM decoration, and activity tracking.
- Re-derived the naive network amplification as `4 + 2T + R`: four fixed
  metadata/record/policy/root-hydration calls, two calls per distinct lookup
  target type, and one target-schema read per readable reference.
- Reconstructed NexusCRM `d922eec39c7dd49520ece5f1fdbee0ec9a67b163`:
  `ContractService.Retrieve` delegates directly to
  `PersistenceService.Retrieve`, which enforces object Read, one root repository
  read, missing/RLS masking, and FLS omission.
- Rechecked the P1b snapshot from app-packs
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`; it supplies the common root-read
  projection and scalar/outcome goldens without a production dependency.
- Added `docs/design/runtime-selected-record-read-contract.md`. It freezes the
  capability, semantic request/result, trusted context, typed values,
  authorization/disclosure, stable errors/retries, consistency receipts,
  N/N-1 rules, and executable acceptance matrix without defining protobuf.
- Made the target architecture's first Runtime record capability and L1 slice
  explicitly selected-root and one-RPC, with no relationship fan-out.
- Classified the current limits instead of treating proposals as baselines: the
  current selected-field count, request/response sizes, identifier lengths, and
  record-read deadline have no single enforced numeric contract.

**Errors encountered**

1. The current CRM route cannot be named as a one-RPC target merely because its
   root record call is neutral. Its post-read lookup hydration is shape-dependent
   and would become an RPC graph if preserved call-for-call. The v1 cutover must
   use a root-only CRM request; lookup-expanded route behavior remains with its
   current owner.
2. The P1b fixture proves small deterministic semantics, not production
   capacity. Its 973-byte golden file and 481-byte largest compact result cannot
   justify maximum request/response sizes or field counts.
3. Current metadata validation enforces object/field identifier grammar but no
   length, current storage uses more than one record-ID width, and the current
   HTTP server has no read/write timeout. Selecting convenient numbers would
   violate the P1c task and the architecture's measured-performance gate.
4. The P1b fixture's minimal schema does not prove that requested fields exist
   in metadata. Future wire acceptance must use a declared-but-absent field for
   the absent-value case and reject unknown selected fields.

**Learned**

- The smallest common operation is not “return a map.” It is one authorized,
  typed, deterministic selected-root projection or one stable failure.
- One Runtime RPC is a network budget, not a database-query claim. Metadata,
  permissions, RLS, FLS, and storage remain internal Runtime work and must be
  measured separately.
- App metadata available to L1 may drive presentation/selection but cannot
  authorize a field or record. Runtime remains the one enforcement read path.
- Missing and RLS denial can remain indistinguishable while object Read denial
  remains forbidden; FLS omission must not reveal whether storage or policy
  caused absence.
- A required deadline can be frozen without inventing a duration: v1 has no
  implicit default and rejects a missing deadline, while the numeric maximum
  remains gated on measured route latency and retry budget.
- Compatibility is protocol/capability/engine based, never exact build-SHA
  equality.

**Decisions**

- Freeze capability `runtime.records.read-selected-root.v1` and semantic
  operation `ReadSelectedRecord`; the future transport spelling is not decided.
- The semantic request contains object, record ID, selected root fields, and an
  optional `MinCatalogRevision`. Identity and authority come only from an
  authenticated delegation and native RPC context.
- Runtime returns deterministic canonical field order, a closed neutral typed
  value union, explicit null, omission for absent/FLS-hidden fields, and protocol,
  catalog, schema, engine, security, and capability receipts.
- Reference IDs are allowed as root values. Lookup labels, parent/child records,
  relationships, graphs, pagination, streaming, and partial results are excluded.
- Mark overall P1c **BLOCKED**, while retaining the accepted non-numeric semantic
  decisions. P2 protobuf generation cannot begin until a reproducible production
  adapter benchmark/traffic replay supports every numeric field, byte,
  identifier, and maximum-deadline limit.

**Next**

1. Build the reproducible measurement harness described in ADR Section 9 against
   the current production adapter and representative CRM/L2 metadata.
2. Record field-count, identifier, encoded-size, latency, concurrency, and
   cancellation evidence with exact revisions and raw artifacts.
3. Amend only the blocked numeric cells and boundary tests after review chooses
   evidence-backed maxima.
4. Re-run current-head review. Begin P2 only after the ADR status changes from
   blocked to approved.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 1: Current-Path Inventory

**Run ID:** `p1c-m1-inventory-20260711`

**Revisions and environment**

- NexusCRM: `24d888c468234bf59d05aa1c5abe4b5539b59aeb` (main containing PR #3563).
- Production app-packs evidence revision:
  `bb138effbe9c2857db432224892c232e14bddf80`.
- Issue-tracker fixture revision:
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.
- Local inventory probe: Go 1.25.0, Darwin/arm64, Apple M4, 24 GiB RAM. No
  local TiDB server was available, so this loop contains no latency result.

**Workload and evidence inspected**

- Searched the repository for Go benchmarks, `testing.B`, `RunParallel`,
  `ReportAllocs`, TiDB integration fixtures, performance artifacts, and CI
  conventions. There is no existing records point-read benchmark or reusable
  representative TiDB-backed benchmark framework.
- Reused the established TiDB integration convention: a temporary synthetic
  database, environment-provided connection settings, explicit skip/fail
  behavior, and Actions artifacts. The existing `l0-neutrality-gate` pins TiDB
  `v8.5.0`; the separate downstream test2 saturation workflow is HTTP/tenant
  inspection traffic and cannot safely substitute for this adapter benchmark.
- Derived the 21-object production CRM pack field-count distribution from
  `standard_objects_spec.json`: p50 15, p95 36, p99 36, p999 36, observed
  maximum 39. These are pack-declared fields, not an approved Runtime limit and
  not a production-traffic selection distribution.

**Profile result and errors**

- `ContractService.Retrieve` delegates to `PersistenceService.Retrieve`, whose
  repository call is `RecordRepository.FindOne`. That repository currently
  selects `*`; the P1b selected-root consumer sorts and projects fields only
  after the full authorized record returns from the adapter.
- Therefore selected-field probes on the current path measure the real adapter
  plus post-adapter semantic projection, but they do **not** measure a future
  storage-level selected-column implementation. The harness and report must
  expose this rather than implying selection pushdown.
- The current test2 saturation workflow exercises public HTTP inspection cases
  with environment fixtures and tenant traffic. Reusing it would neither call
  the in-process adapter directly nor satisfy the no-private-payload rule.

**Learned / next hypothesis**

- Add one narrowly owned, build-tagged measurement harness beside
  `PersistenceService`, backed by a temporary synthetic TiDB database and the
  production `RecordRepository`. Reuse the reviewed P1b fixture snapshot for
  semantic projection; do not add a production package or capability.
- Count the repository query explicitly, record pool statistics and allocation
  deltas, and emit deterministic JSON plus a Markdown interpretation. Run it
  locally as a development probe and through a dedicated guarded test2 action;
  never read or persist tenant payloads.
- Measure transport-independent canonical semantic JSON only. Mark future wire
  size and transport headroom as unavailable until P2 supplies a generated
  codec; a later codec check must replay the same vectors before transport
  limits can be approved.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 2: Local TiDB Probe

**Run ID:** `local-dev-probe-20260711`

**Environment and command**

- Revisions: NexusCRM `24d888c468234bf59d05aa1c5abe4b5539b59aeb`,
  app-packs `bb138effbe9c2857db432224892c232e14bddf80`, fixture
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.
- Go 1.25.0; TiDB `8.0.11-TiDB-v8.5.0` in local unistore mode;
  Darwin/arm64, Apple M4, 24 GiB RAM. This was explicitly non-representative.
- Synthetic two-row, 257-declared-field dataset; 20 measured requests after two
  warmups at concurrency 1 and 4. Exact command and raw JSON are recorded by
  the harness; the development artifact was written under `/tmp` and is not a
  checked-in capacity result.

**Profile result**

- All candidate field counts `1,15,36,39,40,64,65,128,129,256,257` completed
  without error. Every success/missing/RLS request issued exactly one
  `RecordRepository.FindOne`; forbidden and pre-canceled requests issued zero.
- Cancellation and deadline expiry while TiDB was evaluating the synthetic slow
  view both returned in about 26 ms for a 25 ms trigger and each entered one
  repository query.
- At one field/concurrency one, the development probe measured adapter
  p50/p95/p99/p999/max of 1176/1461/2213/2213/2213 µs. At 257
  fields/concurrency four it measured 1892/2749/2857/2857/2857 µs. These small
  samples are harness validation only; p999 is not statistically useful here.
- Compact semantic JSON grew from 81-byte request / 103-byte response at one
  field to 9350 / 18856 bytes at 257 fields. These are not future wire sizes.
- The real repository/FLS path preserved true, false, signed integer, zero,
  date, datetime string, reference ID, explicit null, empty string, absence,
  and FLS omission. It converted the DECIMAL(38,9) large value to float64
  (`1.2345678901234568e+28`) and zero to `0`, losing source precision/scale.
  This is an observed current-path fact, not a semantic-contract amendment.
- TiDB accepted 64-character physical table/column identifiers and rejected 65;
  the synthetic VARCHAR(255) record ID accepted 255 and rejected 256. Metadata
  stores API names in VARCHAR(255), while validation remains grammar-only.

**Errors and learned**

1. The current debug logger emitted one line per Retrieve and would make a
   representative run artifact unnecessarily large. The tagged harness should
   raise the global log threshold for the measurement process only.
2. End-to-end timing stopped before compact semantic response encoding. Move
   the stop point after encoding so adapter and complete local consumer costs
   remain separately meaningful.
3. Repository-call counting equals database-query count only because the
   measured steady-state metadata/permission sources are deterministic test
   seams and `RecordRepository.FindOne` executes one query. The artifact must
   say that scope explicitly; it must not imply a cold production metadata
   query count.
4. No public/anonymized production selection histogram or route SLO/retry budget
   exists in the inspected sources. The report must leave those facts
   unavailable rather than manufacture a deadline or call test2 synthetic load
   “production traffic.”

**Next hypothesis**

- Tighten measurement accounting and artifact wording, add the guarded test2
  workflow with a pinned NexusCRM ref and production app-packs ref, then run at
  >=1200 samples across 1/4/16/64 concurrency so p999 has adequate sample count.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 3: Reproducibility and Data Boundary

**Run ID:** `local-dev-probe-2-20260711`

**Work completed**

- Re-ran the tagged harness against local TiDB v8.5.0 after moving end-to-end
  timing after semantic response encoding, adding database/pool configuration,
  suppressing per-request debug output, and declaring allocation/query scopes.
- Added a separate aggregate collector and verified it against a disposable
  synthetic source database. Its output contains only integer distributions,
  maxima, counts, timestamps, and exact revisions; it contains no object/field
  names, identifiers, records, or traffic payloads.
- Added a CI boundary gate that proves the harness file and app-packs fixture
  module are absent from the default production closure, then compiles the
  harness only with its explicit build tag and temporary modfile.
- Drafted a downstream test2 workflow on a clean nexuscrm-pingcap-pack worktree.
  It accepts only a full NexusCRM SHA, proves that SHA is already an ancestor of
  NexusCRM `origin/main` before executing it on the self-hosted runner, pins the
  production app-packs revision, uses a new isolated synthetic database, and
  uploads only the small JSON/Markdown artifacts.

**Validation result**

- The second development probe passed all semantic/outcome/identifier cases and
  emitted valid JSON plus Markdown.
- `scripts/ci/check-selected-root-capacity-harness.sh` passed. The default
  records closure excludes both the tagged test and
  `github.com/nexuscrm/app-packs-conformance/issue-tracker`; the tagged closure
  compiled successfully.
- The anonymized collector returned only the declared numeric schema and its
  revision/payload-policy guard was accepted by the harness.

**Learned / remaining gate**

- A safe test2 execution cannot run unmerged NexusCRM PR code on the
  secret-bearing self-hosted host. The reproducible sequence is: merge the
  reviewed NexusCRM harness; merge the guarded downstream workflow; dispatch it
  with that merged NexusCRM SHA; then attach the immutable Actions artifact to
  the decision follow-up.
- Test2 can be representative of the current test2 host, TiDB service, and pool
  settings while remaining explicitly non-representative of production traffic.
  The report records that narrower scope.
- Even a successful test2 run cannot resolve future codec headroom or invent a
  route deadline because neither the generated codec nor a route SLO/retry
  budget exists. M1 records this negative evidence; P1c remains blocked until
  the correct owners provide those inputs.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 4: Current-Head Safety Review

**Run ID:** `codex-review-29152179851`

**Review result**

The standard current-head Codex Review correctly blocked the first published
harness commit `3ea6da33f387d7f30d43a50c74dee29da169b146` on two HIGH findings:

1. `run-selected-root-capacity-evidence.sh` defaulted
   `TIDB_TLS_DISABLE=1`. That inverted `database.OpenDBFromEnv`'s existing
   remote-host TLS-by-default behavior and could expose credentials/queries.
2. The tagged Go test trusted caller-selected `TIDB_DATABASE` and immediately
   dropped/renamed fixed `Issue` objects. Direct or misconfigured execution
   outside the wrapper could mutate a live database.

**Root-cause decision / next change**

- Preserve the current connection owner: do not default
  `TIDB_TLS_DISABLE`; only an explicit local caller may disable TLS.
- Move scratch ownership enforcement into the Go harness, not just workflow
  convention. The wrapper creates a uniquely named `p1c_m1_*` database plus a
  random guard-token marker. Before any dataset DDL, the test must prove the
  selected database name, `SELECT DATABASE()`, marker row, and guard token all
  agree. A direct tagged test without this proof fails before mutation.
- Extend the CI boundary script with a negative run proving an unguarded tagged
  invocation cannot reach dataset setup.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 5: One Verified DB Read Path

**Run ID:** `codex-review-29152408708`

**Review result**

The current-head standard review accepted the scratch ownership fix, but the
adversarial review correctly found one remaining HIGH issue: the shell scripts'
`mysql --ssl-mode=REQUIRED` path encrypted remote traffic without verifying the
CA/hostname, while the current owner `database.OpenDBFromEnv` configures
ServerName-verified TLS.

**Root-cause decision / next change**

- Delete the shell/mysql database connection path instead of growing a second
  TLS configuration owner.
- Implement scratch create, marker verification, anonymous aggregate reads, and
  guarded scratch drop as additional build-tagged Go test entrypoints. Every
  database operation then uses `database.OpenDBFromEnv` and its existing TLS,
  ServerName, pool, and environment semantics.
- Keep shell as orchestration only: revision verification, random scratch name
  and token generation, temporary modfile construction, exact command capture,
  and test invocation.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 6: Provenance and TLS Usage SSoT

**Run ID:** `codex-review-29152933506-standard`

**Review result**

The current-head standard review found no blocker after all TiDB operations
moved to `database.OpenDBFromEnv`. It identified two remaining drift risks:

1. The required production app-packs and issue-tracker fixture revisions were
   repeated in the Go harness, runner, and CI negative invocation.
2. The single README example placed the local-only `TIDB_TLS_DISABLE=1` opt-out
   next to the normal invocation, making an otherwise secure-by-default harness
   easier to copy incorrectly for a remote environment.

**Root-cause decision / next change**

- Add one test-owned machine-readable revision file. The runner, tagged Go
  tests, and boundary check must all read the same file and fail if their
  environment differs, leaving exact revisions recorded in every artifact
  without multiple executable owners.
- Split local non-TLS and remote verified-TLS usage into separate README
  examples. The remote example must omit `TIDB_TLS_DISABLE`; the local opt-out
  remains explicitly scoped to loopback development TiDB.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 7: First test2 Dispatch

**Run ID:** `test2-actions-29153718367`

**Exact revisions / workload request**

- NexusCRM merge: `5f0909532d0b4aa330f46809792f6835ca17c1ea`.
- Downstream workflow merge:
  `08a8df31e9abb4ec89a2668279de01f8a1088b8c`.
- Requested matrix: 5,000 measured requests per cell, 200 warmups, concurrency
  `1,4,16,64`.

**Observed failure**

The run failed before reading the revision tuple, connecting to TiDB, creating
a scratch database, or executing any workload. `actions/checkout` correctly
used `persist-credentials: false`, but the following ancestry guard attempted
`git fetch origin main` without an authenticated transport and received
`fatal: could not read Username for 'https://github.com'`. No capacity sample
or artifact exists for this run.

**Root-cause decision / next change**

- Keep Git credentials non-persistent.
- Prove the selected NexusCRM SHA is merged by calling GitHub's compare API with
  the existing read-only cross-repository token and require status `ahead` or
  `identical` for `<selected SHA>...main`.
- Do not execute or increase load until that pre-database authority check
  passes.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 8: Runner Tool Inventory

**Run ID:** `test2-actions-29153793924`

**Observed failure**

The authenticated compare design reached the test2 runner but failed with
`gh: command not found`. As in Loop 7, this happened before revision-tuple read,
TiDB connection, scratch creation, or workload execution. The self-hosted runner
does not provide GitHub CLI, so no capacity artifact exists for this run.

**Root-cause decision / next change**

- Do not grow the runner image or install a second orchestration dependency for
  one API call.
- Call the same authenticated compare endpoint with `curl`, and parse the
  required status with `jq`; both are already part of the workflow's declared
  execution surface (`jq` verifies the final artifact).
- Require HTTP success and a non-null status, then retain the same fail-closed
  `ahead|identical` decision.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 9: Portable Snapshot Integrity

**Run ID:** `test2-actions-29153853385`

**Observed progress / failure**

The run passed the authenticated merged-SHA proof, read the revision SSoT,
checked out the pinned app-packs commit, installed Go, and configured temporary
private-module credentials. It then failed before TiDB connection or scratch
creation because the Linux test2 runner provides `sha256sum`, not the
macOS-style `shasum` command required by the harness wrapper. No workload or
capacity artifact exists for this run.

**Root-cause decision / next change**

- Keep fixture snapshot integrity mandatory.
- Select the platform-native SHA-256 verifier: prefer `sha256sum -c`, fall back
  to `shasum -a 256 -c`, and fail closed if neither exists.
- Land this portability correction in NexusCRM and use its new reviewed merge
  SHA for the next test2 run; do not hide it in runner image setup.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 10: Canceled Opaque Matrix

**Run ID:** `test2-actions-29153959366`

**Exact revisions / workload**

- NexusCRM merge: `b02b8d2765cf220982c466f27c9a0aab5f35d763`.
- Downstream workflow: `c0a1af0b68b43b12e8de0cf6956bb07c390b4eb4`.
- Requested matrix: 5,000 measured requests plus 200 warmups per cell,
  selected-field probes from 1 through 257, concurrency `1,4,16,64`.

**Observed progress / cancellation**

Fixture integrity, scratch creation (0.39s), and anonymized test2 aggregate
collection (3.66s) passed. The main matrix printed only
`=== RUN TestSelectedRootCapacityEvidence` and no cell progress from
13:16:21Z until the user-requested cancellation at 13:57:43Z. No complete or
partial artifact was emitted. GitHub then terminated the orphaned `bash`, `go`,
and `records.test` processes.

**Learned / root-cause decision**

- The loop is finite (44 cells, about 228,800 warmup plus measured reads), so
  the log alone does not prove a code deadlock. However, a 41-minute opaque
  interval cannot distinguish slow progress from one stuck database call and
  is not an operable capacity harness.
- Cancellation did not provide scratch-drop evidence. Because scratch identity
  was generated inside the killed shell, the workflow could not independently
  invoke the guarded cleanup. Treat the database as possibly orphaned until a
  marker-verified recovery probe proves absent or removes it.
- Add per-cell start/finish logs and partial checkpoints. Give each benchmark
  read a harness-safety timeout that is explicitly not a proposed Runtime
  deadline. Generate/mask scratch identity before the measurement step and run
  guarded idempotent cleanup in a separate `if: always()` step.
- Diagnose with the minimum accepted 1,000 samples before restoring 5,000; do
  not repeat the opaque full load.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 11: Error-Path Checkpoint Review

**Run ID:** `codex-review-29155552983`

Both current-head reviewers approved the cancellation-safety change, and the
gate auto-merged it as `5fae01954eefa3922b7f3036f928bd7ea6afa282`.
Adversarial review identified two non-blocking but material robustness gaps:

- after the first read error cancels the cell, queued consumer calls can stop
  before entering the timed adapter, so requiring exactly `samples` adapter
  durations would fatal before the intended error checkpoint;
- cleanup/recovery still performed unrelated app-packs checkout/provenance work,
  adding a failure dependency to the cancellation cleanup path.

Before the diagnostic test2 run, record the actual adapter sample count and
permit an error-bearing cell to checkpoint and stop escalation. Also branch
cleanup/recovery before app-packs-specific setup so their dependency closure is
only the Nexus test snapshot, public DB owner, and guarded scratch identity.

### 2026-07-11 — P1c-M1 Capacity Evidence Loop 12: Reproducible test2 Results

**Run IDs / immutable inputs**

- Diagnostic run:
  [`29156004886`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29156004886),
  artifact `8249854361`
  (`p1c-m1-selected-root-test2-test2-29156004886-1`, expires
  2026-08-10T15:10:56Z).
- Same-input replicate:
  [`29158105319`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29158105319),
  artifact `8250263037`
  (`p1c-m1-selected-root-test2-test2-29158105319-1`, expires
  2026-08-10T16:01:12Z).
- NexusCRM `1a00637fd0ba02b6f529f3cd49fa3306b6d48cb6`;
  downstream workflow `28b6ac961f00e95a7f4f8bd08d12f2b40f355609`;
  app-packs `bb138effbe9c2857db432224892c232e14bddf80`; fixture
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.
- Both runs used Go 1.25.12 on Linux/amd64, Intel Xeon Platinum 8259CL,
  16,558,141,440 bytes memory, TiDB 8.5.3 serverless, pool max-open/max-idle
  1,000, a two-row synthetic dataset with 257 declared fields, 1,000 measured
  requests plus 200 warmups per cell, concurrency `1,4,16,64`, and a 30-second
  harness-only per-read liveness timeout.

**Observed results**

- The diagnostic first recovered and deleted the uniquely matched,
  marker-owned scratch database from canceled run `test2-29153959366-1`.
  Both completed runs then removed their new scratch database; the independent
  cleanup step verified it was already absent after the runner EXIT cleanup.
- Each run completed all 44 cells: 44,000 measured reads, 44,000 timed adapter
  samples, zero errors, 44,000 repository/database reads, exactly one query per
  request, and zero database-pool wait-count/wait-time delta. Both reports have
  `complete:true`; their atomic checkpoints have `complete:false`.
- The current repository still executes `SELECT *`; selected-root projection is
  after the adapter. Increasing selected fields therefore did not reduce
  repository data read. Semantic compact-JSON request/complete-response sizes
  increased deterministically from 81/103 bytes at one field to 9,350/18,856
  bytes at 257 fields.
- Across both runs and excluding the first one-field group, single-request p50
  was 33.6-41.8ms and expected-normal concurrency-16 p50 was 40.8-54.3ms.
  The two-run overall tail envelope was p50 <=421.5ms, p95 <=614.8ms,
  p99 <=990.3ms, p999 <=1,045.8ms, maximum <=1,115.2ms.
- Saturation concurrency-64 was variable. At 257 fields, diagnostic
  p50/p95/p99/p999/max was 75.5/132.1/170.6/196.5/245.3ms; the replicate was
  180.9/525.5/748.6/1,045.8/1,068.3ms. Pool wait remained zero, so this observed
  tail growth was not caused by exhausting the configured Go SQL pool.
- The first one-field group repeated the same high-latency shape in both runs:
  concurrency-1 p50 was 400.6ms then 399.7ms; concurrency-4 was 390.2ms then
  397.6ms; concurrency-16 was 406.1ms then 421.4ms. Later selected-field groups
  returned to the ranges above. Because field-count order was fixed, this is a
  reproducible initial-run/environment ramp confounded with the one-field case,
  not evidence that selecting one field is intrinsically slower.
- The scalar/outcome matrix passed twice: false/true, signed and zero integer,
  decimal zero and large precision, date, datetime, reference ID, empty string,
  explicit null, absent/FLS omission, missing/RLS masking, distinguishable
  object-Read forbidden, cancellation before and during repository access, and
  deadline exceeded. Large DECIMAL remains observably lossy through float64.
- Public CRM pack field-count distribution was p50 15, p95/p99/p999 36, max 39
  across 21 objects. Anonymous test2 metadata distribution was p50 13, p95 59,
  p99 205, p999 287, max 352 across 239 objects. Observed test2 maxima were
  object API identifier 50, field API identifier 43, and CRM record identifier
  36 characters; these are observations, not API maxima.

**Derived facts / decision boundary**

- The two main tests took 1,644 and 1,680 seconds. Scaling only measured
  samples from 1,000 to 5,000 while retaining 200 warmups gives a derived
  4.33x workload and about 119-121 minutes, beyond the harness's 85-minute test
  timeout. Do not repeat the full 5,000-sample grid; use targeted follow-up
  sampling if a decision owner needs finer tails. With 1,000 samples, p999 is a
  coarse extreme observation and must not be overfit into an SLO.
- M1 now has reproducible semantic-size, identifier, query-count, allocation,
  concurrency, latency, cancellation, and authorization facts. It does **not**
  approve any numeric limit or P1c.
- P1c remains blocked on three decision inputs: no public/anonymized production
  selected-field traffic histogram, no reviewed generated codec/transport limit
  with which to measure wire headroom, and no route SLO plus total retry budget
  from which to select a maximum request deadline. A follow-up decision must
  also account for the repeatable initial-run ramp and concurrency-64 variance.

### 2026-07-11 — P1c-M2a DECIMAL Fidelity Root Fix

**Immutable input:** NexusCRM base
`da4b222a496e058d5434a269179dd766af7b64c4`; app-packs production revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture revision
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- Fix the existing storage-side exact-decimal carrier. Do not change the
  existing `contract.Records` field value from `float64`, add a capability
  plane, or pre-shape protobuf.
- Preserve raw driver text, derive coefficient and scale from that text, attach
  the carrier to every metadata field backed by DECIMAL, and retain only
  carriers whose fields survive FLS/projection.
- The future selected-root projection must read this carrier before the legacy
  adapter strips internal metadata. It may not reconstruct an exact value from
  the legacy float.

**证明方法**

- `nexuscrm-kernel/pkg/query/scanner_test.go` proves zero scale, a 38-digit
  coefficient, and numeric-looking VARCHAR text are no longer trimmed by the
  schema-blind SQL scanner.
- `nexuscrm-kernel/pkg/schemautil/schema_utils_test.go` proves Double, Currency,
  Percent, numeric Formula, and numeric RollupSummary fields retain exact text,
  canonical coefficient, and scale while their legacy public values remain
  `float64`.
- `nexuscrm-persistence/record_decimal_fidelity_test.go` executes the real
  `RecordRepository.FindOne -> query scanner -> FromStorageRecord` path with
  SQL-driver byte values and proves large precision plus decimal-zero scale.
- records tests prove authorization projection copies the carrier only for a
  surviving public field. `contract_adapter_test.go` and the CRM lifecycle
  bridge test prove every touched legacy conversion removes internal metadata,
  retains the neutral object-type receipt, and returns float.

**失败条件**

- Any DECIMAL driver value loses a coefficient digit or stored scale before
  selected-root projection.
- Formula or rollup numeric return types bypass the carrier.
- An absent, blocked, or FLS-hidden field leaves an exact-decimal side channel.
- Existing `contract.Records` consumers observe a new Go value type, or a
  selected-root implementation derives exact decimal text from `float64`.

**备选方案**

- If a database driver cannot provide decimal text, fail the future
  selected-root read as an internal storage/schema mismatch. Do not guess from
  float.
- A public closed decimal value and selected-root operation remain owned by the
  later semantic/projection slice. If that slice cannot consume this carrier
  without a second read path, P1c stays blocked rather than exporting internal
  record metadata or changing the legacy interface.

**最终 DoD**

- Raw DB decimal text reaches the authorized persistence record with canonical
  coefficient and stored scale for every DECIMAL-backed metadata type.
- FLS omission also removes its carrier, and legacy record consumers retain
  float behavior with no internal metadata exposure.
- Kernel, persistence, and records module tests plus `git diff --check` pass.
- No protobuf, generated code, gRPC, command, route, direct fixture database
  access, new capability plane, or production issue-tracker dependency is
  introduced. P1c and numeric limit approval remain blocked.

### 2026-07-11 — P1c-M2b Selected-Root Projection Pushdown

**Immutable input:** NexusCRM base
`816a5feac7cc070c97cb28848a731f5a07c9f716`; app-packs production revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture revision
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- Implement one concrete in-process selected-root execution path above the
  future transport. Do not change legacy `contract.Records.Retrieve`, add a
  `contract.Deps` field, or define protobuf.
- Keep object CRUD, field normalization, FLS, and typed projection in records.
  Reuse permissions' existing query-level `BuildRLSResult` authority and
  persistence's existing projected `QueryRepository.Find` lowering.
- Treat the database closure as selected result columns plus SQL evaluation
  dependencies. `Id` is the only always-selected internal column; Owner,
  criteria, parent, folder, target-record, deletion, manual-share, and team
  dependencies remain inside the RLS predicate/subquery and are never copied
  into the response merely to authorize it.

**证明方法**

- `nexuscrm-persistence/query_selected_root_projection_test.go` executes the
  real query builder and proves exact `Id + requested` SQL projection while
  `OwnerId` participates in the same query's RLS `WHERE` clause.
- `nexuscrm-analytics/selected_root_projection_test.go` proves the narrow path
  consumes the exact `BuildRLSResult` output, performs one bounded point query,
  retains the exact-decimal carrier, and uses an Id-only query rather than a
  wildcard when FLS hides every selected field.
- `nexuscrm-records/selected_root_read_test.go` proves schema-bound
  canonicalization and deduplication; deterministic UTF-8 byte order; typed
  string/boolean/integer/decimal/date/datetime/reference values; explicit null
  versus declared absence; FLS omission; missing/RLS masking; forbidden;
  cancellation; and exact-decimal fail-closed behavior.
- The production composition root compile-asserts that
  `analytics.QueryService` implements the narrow seam and returns a startup
  error if the configured query service does not implement it. There is no
  `SELECT *` fallback.

**失败条件**

- Any selected-root request reaches `RecordRepository.FindOne`, selects an
  unrelated root column, or selects `*`, including the all-FLS-hidden case.
- Records duplicates Owner/sharing/criteria/parent policy interpretation, or a
  partial row is authorized with `CheckRecordAccess` using an incomplete
  dependency set.
- A reference selection triggers lookup-label/relationship hydration, an FLS
  field or policy-only column reaches the result, or missing and RLS denial
  become distinguishable.
- A DECIMAL-backed present value is emitted from `float64` when its exact
  coefficient/scale carrier is absent or invalid.

**备选方案**

- If the query-level RLS twin cannot preserve a point-read authorization
  invariant for a policy shape, stop M2b and keep P1c blocked. The fallback is
  a permissions-owned prepared projection/dependency plan consumed by a
  projected point repository method; records must not independently enumerate
  policy fields and must not fall back to a full-row read.
- If a scalar metadata type cannot be represented by the frozen v1 union,
  reject that selected field until a separately approved semantic extension.
  Do not coerce it through JSON or `interface{}`.

**最终 DoD**

- `ContractService -> PersistenceService -> QueryService -> QueryRepository`
  executes one selected-root query through current production assembly with
  authoritative object CRUD, RLS, and FLS and no relationship work.
- SQL reads only the selected/result closure plus predicate-owned policy
  dependencies; the returned fields are deterministic and typed, and exact
  decimals consume M2a's carrier before the legacy adapter boundary.
- Analytics, persistence, records, kernel, backend, boundary/release checks,
  and `git diff --check` pass. No fixture production dependency, protobuf,
  generated code, gRPC, route, command, image, manifest, service extraction,
  new capability plane, or numeric-limit approval is introduced.
- P1c remains blocked on M2c/M2d and the final evidence-backed decision inputs.

### 2026-07-11 — P1c-M2b.1 Review Hardening

**Immutable input:** NexusCRM base
`f6161b846fbac8a6157da5728a5dfc735fc15dbb`; app-packs production revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture revision
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- Route selected-root through the existing analytics `readRLSResult` owner.
  Do not duplicate its SystemContext/scoped-read bypass condition.
- Normalize boolean storage values by metadata effective type, beside the
  existing date and decimal effective-type rules. Do not special-case formula
  or rollup booleans in the selected-root response builder.

**证明方法**

- Analytics tests prove an ordinary user read calls `BuildRLSResult` exactly
  once and passes that exact plan to the repository.
- The same test runs SystemContext and an FLS-enforcing scoped-internal read;
  both pass nil RLS to the repository and never call `BuildRLSResult`, matching
  `QueryWithSpec`.
- Schemautil tests hydrate physical, formula-return, and rollup-return boolean
  storage shapes into `bool`. The records semantic test proves formula `false`
  and rollup `true` remain present typed values.

**失败条件**

- Selected-root calls `BuildRLSResult` directly or invents another bypass
  predicate.
- A user-context read skips RLS, or a valid SystemContext/scoped read is masked
  as missing by user RLS.
- Formula/rollup boolean storage reaches strict projection as an integer or
  string, or invalid boolean input is silently coerced to false.

**备选方案**

- If formula/rollup boolean storage cannot satisfy the shared hydration rule,
  reject that metadata shape before selected-root execution. Do not add a
  response-layer coercion.
- If selected-root needs different RLS semantics later, define a separately
  reviewed policy capability. Do not fork the existing helper implicitly.

**最终 DoD**

- The two accepted-review risks have executable regressions at their owning
  layers, and ordinary plus elevated/scoped behavior agrees with QueryService.
- Kernel, analytics, records, backend, boundary and conformance checks pass.
- No protobuf, gRPC, generated code, numeric limit, route, command, fixture
  production dependency, or new permission path is introduced.

### 2026-07-11 — P1c-M2c Candidate Numeric/Transport Envelope

**Immutable input:** NexusCRM base
`a6c517f7e31d158f782f6f1a85066207fad12290`; production app-packs revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture revision
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- Validate the lead values as test-only candidate policy, not as current
  Runtime behavior: 256 fields, 64 KiB semantic request, 1 MiB complete
  response, 255-byte identifiers, five seconds, 128 KiB receive, 2 MiB send,
  and no compression.
- Use deterministic compact semantic JSON to construct boundary vectors. Do
  not invent protobuf or call the remaining transport budget measured
  headroom.
- Reject a candidate when current storage contradicts it. A metadata column
  width is not evidence that an API identifier can be installed and read.

**证明方法**

- `selected_root_envelope_candidate_test.go` runs in the normal records test
  closure and compares derived evidence with a reviewed JSON golden. The
  current selected-root path returns both 256 and 257 fields, proving 256 is a
  new candidate rather than hidden existing enforcement.
- Grammar-valid requests with 256 unique fields and every identifier at most
  255 bytes encode to exactly 65,536 and 65,537 bytes. Setting all independent
  identifier maxima at once yields 66,655 bytes, so the request-byte candidate
  is an active cross-limit rather than decorative headroom.
- Complete 256-field semantic responses including representative receipts
  encode to exactly 1,048,576 and 1,048,577 bytes. UTF-8 identifier vectors
  distinguish 255 bytes from 256 bytes even when rune count differs.
- The transport candidates reserve 65,536 request bytes and 1,048,576 response
  bytes above the semantic caps. These are algebraic codec/framing budgets;
  `wire_headroom_measured` remains false until P2 replays the vectors.
- M1's real TiDB probe accepted 64-byte physical table/column names and rejected
  65. Current schema assembly uses object API name as table name and initially
  uses field API name as physical column name. Therefore the 255-byte
  object/field candidate conflicts with current storage even though metadata
  rows use `VARCHAR(255)`.
- The 255-byte synthetic record probe is not installed-object evidence. Current
  generic object creation provisions `Id` as `VARCHAR(36)` and the current ID
  owner generates 18-byte values; other existing schemas use differing widths.
  Record-ID maximum therefore remains blocked rather than being inferred from
  the synthetic column.

**失败条件**

- Any report or ADR describes the candidates as enforced production limits,
  measured protobuf/gRPC sizes, or approved P1c values.
- An exact or one-over vector violates another independent candidate, uses an
  invalid field grammar, omits receipts from the complete-response model, or
  truncates/partially returns a response.
- P1c-D approves 255-byte object/field identifiers without either revising
  them to the real 64-byte physical bound or approving a separate logical-to-
  physical object/field mapping, or approves a 255-byte record ID from the
  synthetic probe without reconciling the generic `VARCHAR(36)` owner and
  differing installed schemas.
- Five seconds is approved without a route SLO and total retry budget, or the
  current acceptance of a missing/over-five-second deadline is mislabeled as
  compliance.

**备选方案**

- Preferred object/field direction: revise those maxima to 64 ASCII/UTF-8
  bytes, matching current TiDB physical identifiers without adding another
  naming authority. Keep record-ID maximum blocked until the installed schema
  owner and compatibility/migration evidence select one value; 36 bytes is the
  current generic-create bound, not yet an approved cross-schema contract.
- If the product requires 255-byte logical object/field names, keep P1c blocked
  and separately design one metadata-owned logical-to-physical mapping for
  objects and fields. Do not hide that migration inside transport work.
- P1c-D may approve semantic caps plus reserved transport budgets. P2 then
  generates the first codec and must replay these exact vectors before any
  deployment; failure to fit reopens the ADR instead of increasing transport
  limits silently.
- If the SLO owner cannot justify five seconds in M2d, choose a different
  evidence-backed deadline or keep the deadline decision blocked.

**最终 DoD**

- Normal CI deterministically checks exact and one-over field-count, request,
  response, identifier, deadline, and reserved-transport vectors against the
  machine-readable golden.
- ADR Section 9 separates observed current behavior, viable candidates,
  rejected object/field evidence, and still-blocked deadline/codec facts.
- Production code gains no limit, protobuf, generated code, gRPC dependency,
  compression setting, route, command, service, or fixture dependency.
- M2c is complete as candidate evaluation. P1c remains blocked on M2d and
  P1c-D; M2d must supply the route SLO/retry decision and the broader admission,
  fault, security, fuzz, leak, and N/N-1 evidence.

### 2026-07-11 — P1c-M2c.1 Identifier Owner Guard

**Immutable input:** NexusCRM base
`2554bb6594f78155855be5f99d1697d4ace5b05b`; production app-packs revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture revision
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- Address the accepted adversarial-review suggestion at the metadata owner.
  Do not leave M2c's `64/65`, `36`, and `18` evidence as an unrelated golden
  that can survive owner changes silently.

**证明方法**

- A normal metadata test calls `PrepareTableDefinition` and proves object API
  name remains the physical table name, field API name remains the default
  physical column name, and generic `Id` remains a `VARCHAR(36)` primary key.
- The same test calls the real `generateStandardSObjectID` owner through a
  metadata key-prefix snapshot and proves three prefix bytes plus fifteen
  digits for metadata Flow/FlowDefinition rows. It does not claim that this is
  the ordinary generic-record insert owner.
- The external 64/65 TiDB identifier boundary remains the reproducible M1
  database probe; the owner guard proves why that physical limit currently
  constrains logical object/field candidates.

**失败条件**

- Schema assembly or ID generation changes while ordinary CI continues to
  accept the old M2c storage conclusions.
- A second naming/ID constant is added only for the envelope test, or metadata
  tests import records/transport code.

**备选方案**

- If a future approved logical-to-physical mapping or record-ID migration
  intentionally changes these facts, update the owner test and M2c golden in
  the same reviewed change. Until then identifier maxima remain blocked.

**最终 DoD**

- Metadata-owner changes invalidate M2c evidence automatically in ordinary CI.
- Metadata full-module tests, records candidate tests, boundarygate, and
  `git diff --check` pass.
- No production behavior, protobuf, generated code, gRPC, route, command,
  capability plane, or numeric-limit approval is added.

### 2026-07-11 — P1c-M2c.2 Generic Record-ID Owner Guard

**Immutable input:** NexusCRM base
`12e852aaf275a18bf1df9f54c7849d0134c52c33`; production app-packs revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture revision
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- Narrow the metadata-flow 18-byte claim and guard the actual ordinary record
  insert owner in records. Do not treat `generateStandardSObjectID` as the
  generic record-ID owner.

**证明方法**

- A records-layer test calls `PersistenceService.generateSystemFields` with a
  real metadata snapshot and an insert missing `Id`. It proves the actual
  insert path returns a canonical 36-byte UUID from `utils.GenerateID`.
- The metadata test remains responsible only for `Id VARCHAR(36)` physical
  schema ownership, default physical names, and the separate 18-byte metadata-
  flow generator.

**失败条件**

- Documentation attributes generic record IDs to the metadata-flow generator,
  or generic insert changes ID shape without invalidating ordinary CI.
- A test calls `utils.GenerateID` directly and claims that proves the records
  insert path; the records owner itself must be exercised.

**备选方案**

- If a future contract unifies UUID and Salesforce-compatible IDs, approve the
  migration and installed-schema compatibility explicitly, then update both
  owner guards and M2c evidence together. Until then the public maximum stays
  blocked.

**最终 DoD**

- Metadata schema, metadata-flow IDs, and generic record-insert IDs each have a
  correctly scoped executable owner guard.
- Metadata and records full-module tests, boundarygate, and `git diff --check`
  pass; production behavior and P1c approval remain unchanged.

### 2026-07-11 — P1c-M2c Progress Audit and M2d Reshape

**Immutable input:** NexusCRM `origin/main`
`ad742ee0e502bf43e9084960e30c207b026a4bce`, including PRs
[#3577](https://github.com/pingcap-inc/NexusCRM/pull/3577),
[#3578](https://github.com/pingcap-inc/NexusCRM/pull/3578), and
[#3579](https://github.com/pingcap-inc/NexusCRM/pull/3579).

**Plan**

- Reopen the current evidence from first principles and decide whether M2c is
  genuinely complete, whether any merged work must change, and what the
  smallest correct pre-P2 sequence is.
- Success means the next phase tests only claims that have an owner and an
  executable artifact; it must not approve a semantically invalid request,
  invent a route SLO, or claim wire/interceptor compatibility before those
  artifacts exist.

**Work completed**

- Re-read the selected-root ADR, the candidate envelope golden and exact/
  one-over tests, the metadata physical-name/schema owner guard, the ordinary
  record-insert ID owner guard, and tracking issue #3555.
- Re-ran from the exact merged revision:
  - `(cd nexuscrm-records && GOWORK=off go test ./... && GOWORK=off go vet ./...)`;
  - `(cd nexuscrm-metadata && GOWORK=off go test ./... && GOWORK=off go vet ./...)`;
  - `GOWORK=off go run ./tools/boundarygate` with zero forbidden edges;
  - `git diff --check`.
- All checks passed. No production code, contract, limit, route, protobuf,
  generated code, service artifact, or tracked `nexuscrm-contract/` content was
  changed by the audit.

**Errors encountered**

- The first replan reused the labels `M2c.1` and `M2c.2`. Those labels already
  belong to the merged identifier-owner guard loops (#3578 and #3579). Reusing
  them would make the learned log and tracking issue ambiguous. The new work is
  therefore named under `M2d-A` onward; merged history is not renamed.
- The candidate 64 KiB semantic request vector is internally consistent as a
  size/defense vector but uses up-to-255-byte object and field identifiers.
  Current object/field logical names map directly to TiDB physical identifiers,
  and the merged evidence accepts 64 bytes and rejects 65. The old vector
  therefore cannot prove the largest *valid* v1 request.
- The proposed five-second maximum has exact/one-over fixtures but still lacks
  its owning end-to-end route SLO and total retry budget. Running load and fault
  tests against five seconds before that decision would measure an arbitrary
  number.

**Learned**

- M2c is correctly complete as candidate evaluation. It proved which values
  are viable, which are merely reserved transport budgets, and why one shared
  255-byte identifier rule is wrong. This does not make those values approved
  production limits, so no merged M2c work should be reverted.
- Identifier limits are not one fact. Object and field API names are currently
  constrained by their direct physical-name owner. Record ID needs one global
  opaque read-input bound distinct from stored value shape; it must not query
  physical column width or disclose that a narrower object cannot store the
  supplied value.
- The semantic request boundary must be recomputed after per-kind identifier
  policy is chosen. The existing 64 KiB vector remains useful adversarial input
  and transport-defense evidence, but it is not the final largest-valid-request
  fixture.
- The sfcrm AppService route owner owns the end-to-end user-route SLO and total
  retry budget. Gateway only propagates/enforces its generic route budget.
  Runtime owns its per-attempt cap, admission, cancellation, and
  capacity protection. A working candidate for review is a five-second total
  route budget, at most one retry, and no more than two seconds for each Runtime
  attempt; retries are limited to ADR-approved `UNAVAILABLE` and eligible
  `STALE_STATE` cases and must remain inside the original route deadline.
- P1c specifies semantic compatibility rules but does not create a second
  protocol-neutral client/server model. Generated-client/server N/N-1, actual
  codec sizes, compression behavior, oversized transport handling, and
  delegation-interceptor behavior require the real P2/P3/P6 artifacts.
  Pretending to test them in M2d would create false confidence.

**Decisions**

- Keep P1c blocked and P2 unopened. Green tests and dual Codex approval prove
  the merged evidence is reviewable; they do not approve the remaining limits.
- Reshape the remaining validation into review-sized, owner-aligned loops:
  1. `P1c-M2c-E` — decide per-kind identifier candidates and rebuild the
     structurally valid semantic request vector;
  2. `P1c-M2c-S` — assign route SLO/retry ownership and derive the Runtime
     per-attempt candidate;
  3. `P1c-M2d-A` — post-M2b warmup, concurrency, tail latency, overload, and
     admission evidence against those candidates;
  4. `P1c-M2d-B` — current in-process cancellation, faults, fuzzing, race, and
     resource/goroutine/connection leak evidence;
  5. `P1c-D` — final ADR approval or an explicit evidence-backed rejection.
- In P2, replay the P1c semantic vectors through the real generated codec and
  run actual client/server N/N-1 and transport-boundary tests. In P3, prove the
  authoritative delegation interceptor and workload identity path.
- The current default for direct-mapped object/field names is a 64-byte ASCII
  candidate, not yet an approved contract. Do not add a logical-to-physical
  mapping merely to preserve the rejected 255-byte proposal.

**Next**

- Tracking issue #3555 now records the M2c-E/S and M2d-A/B reshape; issue #3555,
  not this historical log, remains the live phase authority.
- Execute `P1c-M2c-E` first. Its PR must contain the owner inventory, explicit
  per-kind admission-versus-storage distinction, and recomputed exact/one-over
  structurally valid request fixture. Route SLO/retry remains M2c-S. Neither
  loop may add protobuf, gRPC, generated code, production route, service
  artifact, or limit enforcement before P1c-D approval.

### 2026-07-11 — P1c-M2c-E Valid Identifier and Request Envelope

**Immutable input:** NexusCRM
`ad742ee0e502bf43e9084960e30c207b026a4bce`; production app-packs
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`; test2 runs
`test2-29156004886-1` and `test2-29158105319-1`.

**默认答案**

- Use separate unapproved candidates: 64 ASCII bytes for object and field API
  identifiers, and 255 UTF-8 bytes for the opaque record read input.
- Raise the selected-field candidate from 256 to 512 because the anonymized
  test2 metadata maximum is 352 fields. Keep 64 KiB as a defensive request cap,
  not the largest structurally valid request.
- Do not inspect an object's physical Id column at read time. The global record
  bound owns resource admission; stored-ID generators and schemas keep their
  narrower facts.

**证明方法**

- The new ordinary-CI oracle derives the L0 managed-catalog ID owners through
  `schemacatalog.ManagedTableDefinitions`: 133 string primary IDs at
  `VARCHAR(255)` and three numeric `BIGINT` primary IDs, whose signed decimal
  representation needs at most 20 ASCII bytes.
- Current generic records use 36-byte UUIDs, metadata Flow records use 18-byte
  IDs, and the longest shared schema-package ID generator at 64-byte API names
  emits 171 ASCII bytes. All fit the 255-byte read-input candidate without
  claiming that narrower objects store 255 bytes.
- The production CRM pack has 21 objects with maximum 39 declared fields. The
  two test2 artifacts record 239 metadata objects, p999 287 fields, maximum 352,
  and observed object/field/CRM-record maxima of 50/43/36 bytes.
- A checked-in tenant-neutral numeric-only snapshot records both run IDs,
  artifact IDs, measured NexusCRM revision, and complete `result.json` SHA-256
  values. The oracle derives these observations from that snapshot and checks
  its app-packs/fixture tuple against `revisions.env`; provenance is not
  duplicated as unvalidated Go literals. L2 repository/workflow URLs remain
  only in this non-authoritative historical log and are not an L0 test input.
- With all revised maxima, compact semantic JSON is 34,720 bytes. A generic UUID
  request is 34,501 bytes and a metadata-Flow request is 34,483. One-over field
  count is 34,787 bytes; one over one identifier dimension is 34,721 bytes.
- The 512-field complete-response base is 54,301 bytes. Extending one authorized
  string produces exact 1,048,576/1,048,577-byte response vectors, preserving
  the 1 MiB candidate's valid boundary evidence.

**失败条件**

- The 255-byte record candidate is described as a stored-ID promise, a current
  production limit, or evidence that every object accepts 255-byte records.
- Runtime adds physical-column introspection or exposes a different public
  outcome for an in-bound impossible identifier on a narrower object.
- 256 fields is approved despite the observed 352-field metadata shape, or 512
  is approved without post-M2b M2d-A capacity evidence.
- The 64 KiB outer cap is called an active largest-valid-request boundary when
  the valid maxima reach only 34,720 bytes.

**备选方案**

- If M2d-A rejects 512 fields, keep P1c blocked and either select a measured
  higher/lower field limit with an explicit production-route compatibility
  rule or choose a narrower concrete route. Do not silently truncate fields.
- If a supported record authoring owner later emits more than 255 UTF-8 bytes,
  reopen the bound and transport budget together; do not infer a new limit from
  a wider database column alone.

**最终 DoD**

- Normal CI derives owner counts and exact valid/one-over vectors into a
  reviewed JSON golden, validates the anonymized evidence snapshot and revision
  tuple, and preserves the historical M2c oracle.
- ADR Section 9 distinguishes input resource admission, stored ID shape,
  largest-valid request size, and defensive outer cap.
- Production behavior remains unchanged. M2c-S and M2d-A/B remain required
  before P1c-D; P2 stays blocked.

### 2026-07-11 — P1c-M2c-S Route Budget and Retry Ownership

**Immutable input:** NexusCRM
`1968d182806e16633fd7d63041bc91a0ea508b3c`; production app-packs
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- The sfcrm AppService owner of
  `GET /services/data/:version/sobjects/:sobject/:id` owns the end-to-end SLO
  and the entire semantic retry budget. Gateway propagates the earlier trusted
  deadline and applies only a coarse routing ceiling. Runtime owns its
  per-attempt cap, cancellation, admission, and load shedding.
- Evaluate, but do not yet approve, a 5-second route budget: at most two
  2-second Runtime attempts, at most one retry, at most 250 ms full-jitter
  backoff, and a 750 ms remainder reserved for route request/response work.
- Retry only `UNAVAILABLE` and eligible `STALE_STATE`. Keep all attempts under
  the original parent deadline and preserve every revision/security
  requirement.

**证明方法**

- The current sfcrm handler passes its context directly to
  `contract.Records.Retrieve` at app-packs `bb138eff`,
  `dialects/dialect-sf-crm/records.go:46-61`; it creates no route deadline and
  contains no retry loop. NexusCRM `1968d182`,
  `backend/pkg/serverruntime/runtime.go:781-785`, sets only `Addr` and `Handler`
  on `http.Server`, with no `ReadTimeout` or `WriteTimeout`. M2c-S therefore
  records the proposed values as new ADR proposals, not current baselines.
- The ordinary-CI oracle and reviewed JSON golden close the worst-case budget
  exactly: `2 * 2s + 250ms + 750ms = 5s`. One nanosecond over each attempt
  produces 5 seconds plus 2 nanoseconds, while one extra retry produces 7.25
  seconds.
- The golden pins the complete retry allowlist/denylist, the neutral one-owner
  split, shorter-upstream-deadline rule, and minimum remaining budget needed to
  start a retry. The reviewed production diff separately proves that this slice
  changes no runtime behavior.
- M1 latency is only pre-M2b evidence and does not ratify these numbers. M2d-A
  must execute the post-M2b 512-field path with warmup, randomized order,
  representative concurrency, and retry-amplification observations.

**失败条件**

- Gateway, Runtime, and the AppService each add an independent retry or reset
  the deadline, allowing total work to exceed the original route budget.
- The retry loop includes deadline, internal, authorization, missing, invalid,
  incompatible, or resource-exhausted outcomes, or weakens revision/security
  requirements on a second attempt.
- A second attempt starts without enough time for a full attempt plus the
  response-work reserve, creating predictable timeout work with no response
  budget.
- The 5s/2s/250ms/750ms values are described as current behavior or approved
  limits before M2d-A and P1c-D.

**备选方案**

- If post-M2b test2 tails or retry amplification reject the candidate, keep
  P1c blocked and reduce attempts, reduce the attempt cap, increase the route
  budget with explicit product-owner approval, or disable automatic retry.
  Recompute the exact arithmetic and golden for the selected alternative.
- If the production CRM slice cannot reserve 750 ms for non-Runtime work,
  measure that route work and rebalance the attempt/backoff allocation; do not
  silently borrow beyond five seconds.

**最终 DoD**

- Normal CI pins owner assignment, retry categories, exact and one-over budget
  vectors, and candidate classification in a reviewed golden.
- ADR Section 9 explains how an external HTTP request becomes an explicit,
  bounded Runtime attempt deadline and why a shorter upstream deadline wins.
- No production timeout, retry, proto, gRPC, route, service, or admission code
  changes. M2d-A/B and P1c-D remain required.

### 2026-07-11 — P1c-M2c-S Acceptance Checkpoint

**Immutable input:** PR
[#3581](https://github.com/pingcap-inc/NexusCRM/pull/3581) merged as
`1968d182806e16633fd7d63041bc91a0ea508b3c`; PR
[#3582](https://github.com/pingcap-inc/NexusCRM/pull/3582) exact head
`33b471bea657c69de7d48d18c5976ce0dbbb92a9`, merged as
`146b2dea44aaf8e36f9f25afe7b703db6ab46fa8`.

**Plan**

- Verify the reported M2c-E/M2c-S state from the live PRs, exact-head review,
  merged trees, and tracking issue rather than copying the handoff summary.
- Record what the route-budget oracle proves, what remains only a policy
  candidate, and the exact boundary between current-artifact M2d-A evidence and
  future service/Gateway validation.

**Work completed**

- Confirmed #3581 is merged and ordinary public-core CI no longer consumes an
  L2 repository or workflow URL as executable evidence.
- Inspected all four files in #3582: ADR Section 9.8, this learned log, the
  ordinary-CI arithmetic/oracle test, and its reviewed JSON golden. The diff
  adds no production timeout, retry, route, admission, protobuf, gRPC, service,
  or generated-code behavior.
- Watched exact-head Codex Review run
  [29168504103](https://github.com/pingcap-inc/NexusCRM/actions/runs/29168504103)
  to completion. Standard and Adversarial both reported zero blocking issues;
  the aggregate result was `BOTH_APPROVED`. Auto-merge then produced
  `146b2dea44aaf8e36f9f25afe7b703db6ab46fa8`.
- Updated tracking issue
  [#3555](https://github.com/pingcap-inc/NexusCRM/issues/3555): M2c-S is Done,
  its DoD is checked, P1c remains blocked on M2d-A/M2d-B/P1c-D, and there is no
  external wait before M2d-A. The tracking issue remains a sub-issue of #3476.
- Independently re-ran `GOWORK=off go test ./...` and `GOWORK=off go vet ./...`
  in `nexuscrm-records`, the boundary gate from its own module with zero
  forbidden edges and zero contract runtime leaks, and `git diff --check`.

**Errors encountered**

- An initial `gh api` read omitted quoting around a query string containing
  `?`; zsh treated it as a glob. The retry quoted the endpoint and made no
  repository or GitHub mutation.
- The first issue-body transformation applied later string replacements to the
  JSON object instead of keeping them inside `.body`, so `jq` rejected the
  input. The empty PATCH did not alter the issue. The corrected transformation
  was re-read from GitHub and verified field by field.
- The first independent boundary-gate command was run from the repository root
  with `GOWORK=off`, where no root `go.mod` exists. It failed before executing.
  Re-running `go run . -root <workspace>` from `tools/boundarygate` passed; the
  failure was invocation scope, not an architecture result.

**Learned**

- M2c-S closes ownership and arithmetic, not latency feasibility. It proves
  that the candidate can fit on paper and prevents Gateway, Runtime, and L1
  from each inventing a retry budget; it does not prove that two attempts are
  useful under load or that production should accept the numbers.
- The exact equality is intentionally conservative. At the two-second first-
  attempt cap and 250 ms backoff maximum, any scheduling/serialization
  overhead suppresses the retry because the original parent deadline cannot be
  extended. Suppressing a retry is correct; starting work that cannot retain
  the response reserve is not.
- M2d-A can measure the current post-M2b Runtime-equivalent path, the two-second
  attempt candidate, concurrency/tail behavior, and retry amplification as a
  model. It cannot claim measured future Gateway or split-process overhead
  because those artifacts do not exist yet.
- The 750 ms route-work remainder is therefore a design allocation until one
  of two evidence paths closes it: measure the current full CRM route and
  clearly state its representativeness limits, or retain it as conservative
  reserved budget at P1c-D and make real AppService/Gateway route measurement a
  P4/P5 cutover gate. M2d-A must not relabel a Runtime-only measurement as an
  end-to-end route measurement.
- The successful review included two non-blocking but valid maintainability
  findings: ADR Section 12 still names M2c-S as next work, and the route-budget
  oracle duplicates revision strings instead of consuming the reviewed
  `tests/performance/selected-root-read/revisions.env` source. Neither changes
  the candidate decision, but both must be closed before P1c-D to preserve one
  status path and one provenance source.

**Decisions**

- Accept M2c-E and M2c-S as complete evidence phases. Keep every numeric value
  unapproved and unenforced until M2d-A/M2d-B evidence and P1c-D.
- Start M2d-A from merged NexusCRM
  `146b2dea44aaf8e36f9f25afe7b703db6ab46fa8`; the test2 merged-revision gate is
  now satisfied.
- Keep measurement claims artifact-scoped: current in-process Runtime evidence
  in M2d-A, current-seam fault/fuzz/leak evidence in M2d-B, generated codec in
  P2, real per-replica admission/unavailable behavior in P3, and actual
  AppService/Gateway end-to-end route proof in P4/P5.
- Carry the two accepted review suggestions as non-blocking follow-up work.
  They may land in the M2d-A preparation PR if that keeps the diff coherent;
  otherwise use a narrow follow-up. They may not survive through P1c-D.

**Next**

- Execute M2d-A with pre-readiness warmup, randomized cell order, the post-M2b
  selected-root path, 1/4/16/64 concurrency, 512-field coverage, p50/p95/p99/
  p999, errors, allocations, pool pressure, and overload/retry-amplification
  evidence. Do not add production admission or retry behavior.
- Report Runtime-equivalent latency separately from any full-route measurement.
  If the full current CRM route cannot be measured representatively, state that
  gap and attach the 750 ms reserve to the future P4/P5 executable cutover gate
  rather than inventing end-to-end evidence.

### 2026-07-11 — P1c-M2d-A Harness Preparation

**Immutable input:** NexusCRM `main`
`146b2dea44aaf8e36f9f25afe7b703db6ab46fa8`; production app-packs
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`; reviewed anonymous shape
snapshot under `tests/performance/selected-root-read/testdata`.

**默认答案**

- Extend the existing M1 framework rather than create a parallel benchmark
  system, but keep a separate M2d-A runner, schema version, test, artifact path,
  and interpretation so pre-M2b `SELECT *` history cannot be relabeled.
- Measure only the current in-process Runtime-equivalent post-M2b path. Treat
  future Gateway/AppService work, split-process transport, production admission,
  and retries as unmeasured or modeled, never observed.
- Warm 512 fields at concurrency 16 before measurement. Randomize field shapes
  within ascending concurrency tiers `1,4,16,64`; stop all later load tiers on
  the first error.

**Falsifiable performance hypotheses**

- Assume the current implementation is not fully optimized and that its
  absolute latency is a baseline, not the architectural ceiling. M2d-A is not
  a referendum on whether today's code is already production-optimal.
- The selected-root contract is viable only if one logical read remains one
  bounded Runtime-equivalent call and one root SQL query, with no metadata,
  policy, relationship, or repository fan-out whose call count grows with the
  selection.
- Resource cost should grow in a bounded, explainable way with selected fields.
  The expected optimization surface is inside Runtime -- SQL planning, policy
  preparation, hydration, typed projection, allocation, and serialization --
  rather than a requirement to change the neutral request/response semantics.
- Ordinary CRM shapes are expected to be closer to 15--39 selected fields; 512
  fields is a compatibility/capacity boundary, not the assumed median request.
  Concurrency 16 is the initial steady-capacity candidate. Concurrency 64 is a
  saturation probe, not a promise of unlimited throughput.
- Use the following diagnostic expectations, not approved SLOs: after warmup,
  ordinary 15--39-field reads at concurrency 16 should have p99 below 250 ms;
  the 512-field boundary at concurrency 16 should have p999 below 1 second;
  successful reads should remain comfortably below the 2-second attempt cap;
  concurrency 16 should retain one root query, zero read errors, and no
  material pool wait. Concurrency 64 may expose saturation, but must not cause
  an error avalanche, unbounded queue/memory growth, or resource leakage.
- The 2-second attempt value is a cancellation/admission ceiling, not an
  acceptable normal latency target. Retry is expected to be exceptional; if
  normal operation depends on retry, the candidate capacity model is wrong
  because one retry can nearly double Runtime work.

**Decision policy**

- First collect the unchanged randomized matrix. If an expectation fails,
  profile the smallest failing field-count/concurrency cell and account for
  policy/metadata preparation, SQL, hydration/projection, allocation/GC,
  serialization/harness overhead, and pool wait before changing code.
- Treat RPC/DB amplification, unbounded field-dependent work, resource leaks,
  unstable overload, retry-driven load amplification, or a required semantic
  contract change as structural failures. Keep P1c blocked and fix the owner or
  contract shape before P2.
- Treat an index/query-plan defect, avoidable hydration or allocation cost,
  repeated internal preparation, or serialization inefficiency as a local
  Runtime implementation problem when it can be fixed behind the unchanged
  contract. Keep the contract shape, fix the highest-value bottleneck, and
  rerun the identical workload. Do not require the old implementation to be
  perfect before creating a no-traffic skeleton, but do not cut over production
  traffic until the approved end-to-end SLO has measured headroom.
- Do not make a benchmark green by silently lowering the supported route,
  truncating fields, skipping authorization, weakening consistency, changing
  the dataset, or hiding a failing concurrency tier. If 512 itself is the
  structural problem, explicitly revise the field/route compatibility decision
  and repeat P1c-D evidence.
- M2d-A closes with one of three explicit outcomes: (1) boundary viable with
  measured headroom, continue to M2d-B; (2) boundary viable but implementation
  bottleneck identified, preserve the contract and continue a quantified root-
  fix loop before cutover; or (3) boundary invalid, stop P2 and redesign the
  contract. "Current performance is poor" without a bottleneck classification
  is not a valid outcome.

**证明方法**

- The tagged M2d-A test wires the same production composition seam:
  `ContractService.ReadSelectedRecord -> PersistenceService.ReadSelectedRecord
  -> analytics.QueryService.ReadSelectedRootProjection ->
  PermissionService.BuildRLSResult -> QueryRepository.Find` against an owned
  synthetic TiDB database. The query wrapper counts the real `Find` call; it
  does not access the database outside the adapter.
- The matrix derives CRM field-count percentiles from the pinned public pack and
  p999/maximum observations from the reviewed tenant-neutral snapshot. It adds
  512 and 513 explicitly and records its deterministic shuffle seed and exact
  cell order.
- Every cell records sample/warmup count, p50/p95/p99/p999/max adapter and
  harness latency, semantic request/complete-response bytes, allocations,
  mallocs, SQL query count, errors, and before/after pool pressure. The revised
  A1 model also records GC, heap, goroutine counts and opt-in process-local
  stage distributions for metadata/object security, selection normalization,
  FLS, projection planning, RLS, repository preparation, SQL execution, raw row
  scan, storage hydration, typed projection, and semantic serialization.
- The stage observer is a generic in-process diagnostic under the existing
  `perftiming` owner. It is not a contract capability, `contract.Deps` field,
  transport, or semantic input; with no observer installed it avoids the clock
  read and request behavior cannot depend on it.
- The existing scratch ownership marker, revision checks, TLS behavior,
  partial-result checkpoints, cleanup/recovery path, fixture-only temporary
  module dependency, and production-closure guard are reused. The boundary
  check now requires both historical M1 and new M2d-A tagged files to remain
  outside default builds.

**Errors encountered**

- The downstream test2 workflow still invokes the M1 runner and validates M1-
  specific `.wire` output. Dispatching it unchanged would produce a fresh old-
  path artifact, not M2d-A evidence. After this harness is merged, the L2
  workflow needs a narrow owner-local update before collection.
- M1's `DeclaredAbsent` field intentionally has metadata but no SQL column. It
  works as an omission probe after `SELECT *`, but projection pushdown would
  explicitly select the missing column and correctly fail as a schema/storage
  mismatch. Likewise, including `FLSHidden` in a 512-selection success load
  would measure only 511 authorized response fields. The M2d-A success-capacity
  dataset therefore uses physically present, readable fields; absent/FLS remain
  semantic golden and M2d-B cases.
- `shellcheck` is not installed in the local workspace. `bash -n` and the
  repository harness check passed; the missing optional local binary is recorded
  rather than represented as a successful shellcheck run.
- The first #3584 exact-head review completed while the A1 modeling revision was
  being prepared. Its bot auto-merge landed only head `d17143561` as merge
  `3b8fbf125`; the pushed `de3130eb5` modeling commit is therefore a separate
  follow-up input, not part of that merge. The follow-up also accepts both
  adversarial suggestions: M2d-A run/artifact IDs no longer inherit an M1
  namespace, and the M2c-S NexusCRM base revision now comes from the shared
  reviewed revision file.

**失败条件**

- Any artifact uses `Retrieve`/`FindOne`, `SELECT *`, or the fixture value
  normalizer while claiming post-M2b evidence.
- A local TiUP run, future-route estimate, or modeled retry is labeled as test2
  Runtime, Gateway, or production observation.
- Load proceeds to a higher concurrency tier after a smaller tier reports an
  error, or fixed cell order reintroduces the known initial-ramp bias.
- The harness or fixture enters a default/production dependency closure, or
  tenant metadata, identifiers, records, traffic payloads, repository names, or
  workflow URLs enter an L0 evidence artifact.

**备选方案**

- If test2 fails at readiness or a low tier, stop and diagnose that smallest
  failing shape; do not increase load. If 512 is the architectural failure,
  keep P1c blocked and choose a measured contract/route compatibility rule.
- If test2 cannot represent route work, retain 750 ms as a conservative design
  reserve and make the real P4/P5 cutover route measurement mandatory. Do not
  infer it from Runtime-only latency.

**最终 DoD**

- Merge the guarded post-M2b harness, update the L2 test2 workflow from its own
  repository, and collect a complete machine-readable artifact from a NexusCRM
  revision already merged to `main`.
- Review the artifact for all candidate field counts, concurrency tiers, one
  query per successful attempt, zero errors, tails, allocations, and pool wait.
  Record an explicit accept/reject result for 512 fields, concurrency 16, and
  the two-second attempt candidate without approving production behavior.
- Keep M2d-A In Progress until the test2 artifact and interpretation are
  reviewed; only then update #3555 to Done and move to M2d-B.

### 2026-07-12 — P1c-M2d-A A1 First Test2 Loop

**Immutable input:** NexusCRM
`132a3ce1faaac57040c0c983f94e21ebdf489d74`; downstream workflow merge
`5b52f3d3ae72fbfab653e49fdfb3f6140b9e28b7`; test2 run
`29169997201`; artifact `8253398175`; production app-packs
`bb138effbe9c2857db432224892c232e14bddf80`; fixture
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- The first run does not reject the selected-root contract. It isolates SQL
  execution/test2 database behavior as the dominant cost while preserving one
  Runtime-equivalent call, one database query, and microsecond-scale metadata,
  security, hydration, projection, and serialization stages.
- Stop after the smallest failed cell as designed. Correct the insufficient
  readiness model before rerunning; do not increase concurrency or weaken the
  two-second attempt candidate.

**证明方法**

- Readiness completed 200 512-field/c16 reads in 1.714 seconds with exactly 200
  queries. The first timed cell was 1 field/c1 with 5,000 reads.
- The cell recorded p50/p95/p99/p999/max of
  `56.650/508.882/826.647/1171.912/2000.388 ms`, one error, exactly one query per
  attempt, and zero pool wait. The driver logged one invalid connection; the
  pool moved from 16 idle connections to one during the 18-minute sequential
  cell.
- Stage p99 was: metadata/object security 34 µs; FLS 34 µs; RLS 28 µs;
  repository preparation 172 µs; raw row scan 118 µs; storage hydration 141 µs;
  typed projection 17 µs; semantic serialization 42 µs; SQL execution
  826.122 ms. SQL therefore accounts for essentially the entire tail.
- The run allocated about 335,505 bytes and 1,174 mallocs per read with 451 GC
  cycles. That is a real secondary Runtime optimization target, not the latency
  root cause for this cell.
- Both historical M1 test2 runs showed the same first-cell startup ramp:
  1-field/c1 p50 about 400 ms and p99 854/893 ms. After those runs exercised
  c4/c16/c64, later c1 field shapes stabilized to about 34-41 ms p50 and
  70-93 ms p99. The A1 result is therefore not evidence of a new M2b query-shape
  regression; 200 readiness reads were insufficient to cross a known
  environment ramp.

**失败条件**

- A second run with sustained readiness still reproduces the 2-second error or
  SQL-dominated tail at the smallest cell.
- Query count exceeds one, any non-SQL stage becomes field-count unbounded, pool
  wait appears at c16, or resource/goroutine state fails to recover.
- The rerun hides the failure by dropping 512/513, skipping permissions, raising
  the attempt deadline, or changing away from test2.

**备选方案**

- If sustained readiness does not remove the ramp, keep M2d-A open and profile
  the TiDB/serverless connection/query path with the smallest 1-field/c1 cell.
  Contract redesign remains unwarranted unless call/query amplification or a
  semantic constraint is implicated.
- If readiness stabilizes A1, complete the reduced declared matrix, then rerun
  only decision-critical cells at 5,000 samples if their tails need tighter
  confidence before P1c-D.

**最终 DoD**

- Perform 5,000 512-field/c16 readiness reads and record their distribution.
- Run exactly `1,15,39,256,512,513` fields across c1/c4/c16/c64 with 1,200
  samples per cell, stage distributions, sanitized error classes, allocations,
  GC/heap/goroutine/pool pressure, and the unchanged two-second deadline.
- Produce a complete artifact or a new smallest-cell diagnosis. Do not mark
  M2d-A Done from this partial run.

### 2026-07-12 — P1c-M2d-A A1 Complete Matrix and Readiness Correction

**Immutable input:** NexusCRM
`c37fe783ea40780013cbf764a884760926c58fd4`; downstream workflow
`3b02ce6974443a9b18b1fa88be6db03145946ae0`; test2 run
`29171276326`; artifact `8253736250` (SHA-256
`112b4731adf6f2d61feb9c9631a8ddc0027ea5fe340bd3280ec7b3177c11c90e`);
production app-packs `bb138effbe9c2857db432224892c232e14bddf80`;
fixture `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.

**默认答案**

- The selected-root boundary shape remains viable. All 24 cells completed
  28,800 measured reads with zero errors, exactly one query per read, and zero
  pool wait, including c64 saturation probes.
- Do not yet call A1's single-request baseline fully warmed. The 5,000-read
  512-field/c16 readiness phase completed in 121.6 seconds but did not consume
  the sequential serverless startup ramp. Add a separate c1 startup-readiness
  phase before the existing wide c16 phase, then rerun the same matrix.
- The workflow's red conclusion is not a measurement failure. Its post-run
  validator read `.revisions.fixture`; the report's stable JSON key is
  `.revisions.issue_tracker_fixture`. Fix the validator and rerun rather than
  discarding the complete artifact.

**证明方法**

- Ordinary c16's worst 15/39-field p99 was 119.776 ms. The 512-field c16 p999
  was 228.839 ms; the largest c16 maximum was 261.208 ms. All three diagnostic
  candidates were met without approving an SLO.
- At c64, 512/513-field p999 was 1.102/1.476 seconds and maximum was
  1.103/1.526 seconds, with zero errors and zero pool wait. No field-count or
  concurrency tier amplified repository queries.
- Allocated bytes/read increased from about 335 KiB at one field to 1.20 MiB at
  512/513 fields. This is a bounded, material internal optimization target; it
  is not evidence of call/query fanout or a required contract change.
- The first timed c1 cell (15 fields) remained cold at p50/p99
  383.743/884.255 ms. The next 39-field c1 cell moved to p50 27.392 ms but kept
  a 486.790 ms p99 tail; later c1 cells stabilized around 38-67 ms p50 and
  75-100 ms p99. Concurrent readiness therefore did not establish the declared
  single-request baseline.

**失败条件**

- After 5,000 39-field/c1 startup reads followed by 5,000 512-field/c16 wide
  reads, the first two c1 matrix cells still reproduce the startup transition.
- Any rerun produces more than one query/read, c16 pool wait, errors, unbounded
  field-count allocation growth, or non-recovering goroutine/connection state.
- The rerun is made green by increasing the two-second attempt deadline,
  removing 512/513, or skipping authorization/projection work.

**备选方案**

- If the explicit sequential phase still cannot stabilize c1, stop M2d-A and
  replace fixed-count readiness with a bounded, recorded convergence protocol;
  do not keep increasing opaque warmup counts.
- If the sequential phase stabilizes the baseline, accept this as an
  environment-readiness correction and keep allocation/hydration optimization
  as a Runtime-internal follow-up that does not block the service boundary.

**最终 DoD**

- A green main-only test2 run records both readiness phases and the unchanged
  24-cell matrix with complete provenance.
- The first c1 cells no longer contain the known startup transition, or the
  bounded convergence alternative is explicitly triggered.
- The evidence interpretation classifies boundary facts separately from
  Runtime-internal optimization work; M2d-A remains open until then.

### 2026-07-12 — P1c-M2d-A A1 Connection-Liveness Root Cause

**Immutable input:** NexusCRM
`011e313ddce441fb9c7c53b58a065a3cfbaa56ed`; downstream workflow
`a60bf9069bad68b50b231c78303b5345e9a48928`; test2 run
`29172353587`; artifact `8254007182` (SHA-256
`7d3666dfdf42ed9172e5b440a730011d5c576583ea1de7037e1f7dd276514ab5`).

**默认答案**

- Dual readiness fixed the cold c1 baseline. Do not add more warmup.
- The remaining repeatable failure is a production connection-wrapper defect,
  not a selected-root contract defect: `mysql-tracing` hides the MySQL driver's
  optional `driver.SessionResetter` and `driver.Validator` interfaces from
  `database/sql`, so closed connections are not rejected at pool reuse.
- Fix optional-interface forwarding in the telemetry wrapper, guard it with
  unit tests, then rerun the unchanged workload.

**证明方法**

- Startup readiness completed 5,000 39-field/c1 reads in 782.7 seconds with
  zero errors; wide readiness completed 5,000 512-field/c16 reads in 32.4
  seconds with zero errors.
- All six timed c1 cells then stabilized. The first 15/39-field cells recorded
  p99 56.995/37.221 ms instead of the preceding run's 884.255/486.790 ms cold
  tails.
- At about 19.5 minutes, the first c4 cell logged MySQL `invalid connection` and
  one request reached the unchanged two-second deadline. The first partial run
  had the same driver log and failure at about 18 minutes. Field count and
  concurrency differ; long-lived pool reuse is the common dimension.
- `mysql.Conn` in the pinned driver implements `ResetSession` and `IsValid`.
  `telemetry.TracingConn` embeds only `driver.Conn`; Go optional interfaces are
  not dynamically promoted through that interface field, and the wrapper does
  not forward either method. The pool therefore cannot execute the driver's
  liveness checks through `mysql-tracing`.

**失败条件**

- A focused wrapper test cannot prove `SessionResetter`, `Validator`, and
  `NamedValueChecker` forwarding and safe fallback behavior.
- The unchanged test2 workload still logs `invalid connection` or a deadline
  error after the wrapper fix.
- Fixing the failure requires changing selected-root request/response semantics,
  increasing the attempt deadline, or adding retries to normal reads.

**备选方案**

- If transparent optional-interface forwarding does not remove the failure,
  profile the TLS/serverless close path and connection-pool counters at the
  first long-duration c4 cell. Keep that work in the DB/telemetry owner; do not
  modify the Runtime contract.
- Do not hide the defect with a benchmark-only pool lifetime or keepalive
  override because production uses the same tracing wrapper.

**最终 DoD**

- The tracing connection transparently preserves MySQL liveness and named-value
  optional interfaces with deterministic unit tests.
- All affected kernel/database tests, race checks, records tests, boundary gate,
  and diff checks pass.
- The same dual-readiness test2 workload completes with zero invalid-connection
  events and zero errors; only then may M2d-A close.

### 2026-07-12 — P1c-M2d-A A1 Bounded Readiness Decision

**Immutable input:** NexusCRM
`079b976b0158623eca19cda02e6ebb47aa649af9`; downstream workflow
`a60bf9069bad68b50b231c78303b5345e9a48928`; test2 run
`29173225117`; artifact `8254240402` (SHA-256
`4e1032d48439a2b2f5dfd38622d738f1df5eec9ca8ffc6910429f65bfef9280a`).

**默认答案**

- The connection-liveness fix changed the observed failure: no MySQL `invalid
  connection` was logged, but 16 cold-start reads reached the two-second
  deadline inside the fixed 5,000-read startup phase.
- Do not increase the fixed count again. Require exactly 5,000 requests, record
  the full cold-start distribution, allow only early `deadline-exceeded` cold
  outcomes, and admit timed cells only when the final 1,200-request trailing
  window has zero errors and p99 below the existing 250 ms diagnostic target.

**证明方法**

- All 5,000 startup attempts reached the repository and produced 5,000 queries.
  The full window took 1,207.8 seconds at p50/p95/p99/p999
  45.534/910.293/1,253.242/2,001.186 ms with 16 deadline errors.
- Unlike both earlier failures, the log contained no `invalid connection` after
  optional-interface forwarding. The remaining signal is serverless cold
  latency, not stale pooled-connection reuse.
- The preceding dual-readiness run proved that after this ramp the same c1 cells
  stabilize to p99 37-83 ms. A trailing-window convergence decision can
  distinguish these phases without hiding the cold evidence.

**失败条件**

- Any early startup error is not `deadline-exceeded`, or the final 1,200 startup
  attempts contain any error or have p99 at or above 250 ms.
- The wide c16 readiness phase contains any error, or any timed matrix cell
  contains an error.
- The benchmark exceeds the existing 5,000 startup-attempt bound or changes the
  two-second attempt deadline.

**备选方案**

- If the bounded trailing window does not converge, keep M2d-A open and classify
  test2/serverless as unable to produce a stable baseline for this decision;
  do not add another warmup-count iteration.
- Preserve the connection-liveness fix independently because it restores
  standard `database/sql`/driver behavior even if test2 cold latency remains.

**最终 DoD**

- Artifact schema records full and trailing readiness distributions, errors,
  and an explicit convergence boolean.
- CI fails closed on a non-converged trailing window and accepts cold-start
  errors only when they are outside the final stable window.
- The unchanged 24-cell matrix completes after convergence, or M2d-A reports
  the bounded environment failure as its final result.

### 2026-07-12 — P1c-M2d-A Final Capacity Decision

**Immutable input:** NexusCRM
`849c98281391f046b77c82ad0df59292dee2f722`; app-packs
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`; downstream workflow
`538abaad006918ab0fb40e388b40b210a33d5fb1`; test2 run
[`29174439835`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29174439835);
artifact `8254639111` (SHA-256
`7029e82d2a86af6953b9cf4b5fc2916e740619257fbb12b239fa1f069a99b54d`).
The machine artifact is schema `p1c-m2d-a.v3`, run ID
`test2-29174439835-1`; it records Go `1.25.12`, TiDB
`8.0.11-TiDB-v8.5.3-serverless`, Linux/amd64, test2 hardware and database/pool
configuration. It contains synthetic aggregate measurements only.

**默认答案**

- Close M2d-A as decision category 2: the selected-root service boundary is
  viable, while current SQL tail and allocations remain Runtime-internal
  optimization work before production cutover.
- Continue to M2d-B and then P1c-D. Do not approve a production SLO, limit,
  admission policy, retry policy, transport envelope, or traffic cutover from
  this run.

**证明方法**

- Both bounded readiness phases converged. The 39-field/c1 phase completed
  5,000 reads with zero errors; its final 1,200 reads had p99 74.038 ms. The
  512-field/c16 phase completed 5,000 reads with zero errors and p99
  195.156 ms.
- All 24 shuffled workload cells completed 28,800 timed reads with zero errors,
  exactly one database query per read, and zero pool wait at concurrency 16.
  No run log contained a MySQL `invalid connection` event after the tracing
  wrapper root fix.
- The 512-field/c16 cell measured p999 263.284 ms and maximum 270.103 ms. The
  one-over 513-field/c16 maximum was 724.188 ms, below the unapproved two-second
  diagnostic attempt candidate. At the c64 saturation probe, 512 fields reached
  p99 1.168 s and maximum 1.894 s without errors or pool wait.
- The ordinary 39-field/c16 cell missed the unapproved p99-under-250-ms
  hypothesis: p99 was 535.439 ms. SQL p99 was 534.395 ms and typed-projection
  time was 0.414 ms, locating the tail inside the current database/runtime
  implementation rather than in extra Runtime calls, query fan-out, or graph
  expansion.
- Measured allocation increased from about 335 KiB/read at one field to about
  1.2 MiB/read at 512/513 fields. The increase is bounded across the declared
  matrix but is high enough to track as a Runtime optimization target.
- The current path executes no automatic retry. With one observed query per
  attempt, the M2c-S two-attempt candidate derives a worst case of two queries
  per route; this is a model, not measured retry behavior.

**失败条件**

- A repeat of the pinned workload produces more than one query/read, hidden
  metadata/policy N+1, c16 pool wait, errors after convergence, non-recovering
  resource growth, or requires selected-root semantic changes to optimize.
- M2d-B finds correctness, isolation, cancellation, stale-state, fuzz, or leak
  behavior that contradicts the same boundary.
- A later representative route/cutover test cannot fit its approved end-to-end
  budget without weakening authorization, projection, or response completeness.

**备选方案**

- If a structural failure appears, reopen P1c, stop P2, and change the contract
  before generating or deploying a wire surface.
- If later evidence continues to isolate SQL, hydration, projection, or
  allocation cost inside one attempt, retain the contract and optimize the
  existing Runtime/database owners; a no-traffic service skeleton may follow
  P1c-D, but production cutover remains blocked on its SLO gate.

**最终 DoD**

- The immutable artifact, revisions, run ID, digest, environment, workload,
  tails, errors, query count, pool pressure, and allocation findings are
  recorded in the ADR and this learned log.
- M2d-A is marked Done in tracking issue #3555 with category 2 stated
  explicitly and no proposed numeric value relabeled as an approved limit.
- SQL-tail and allocation optimization remain named production-cutover work;
  M2d-B and P1c-D remain the next contract gates.

### 2026-07-12 — P1c-M2d-B Current-Seam Correctness and Resource Gate

**Immutable input:** NexusCRM main
`5a70ec5041d76ac438dd6dd38ac038766ae53088`; app-packs production revision
`bb138effbe9c2857db432224892c232e14bddf80`; issue-tracker fixture
`ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`. This phase exercises only the
current in-process `ContractService -> PersistenceService -> QueryService ->
QueryRepository` selected-root path.

**默认答案**

- Close M2d-B when the current executable path passes cancellation/deadline,
  fault classification, terminal SQL error, fuzz, race, goroutine/connection,
  and live-heap retention checks in ordinary CI.
- Keep overall P1c blocked. The next gate is an explicit disposition for the
  39-field/concurrency-16 SQL p99 anomaly, followed by P1c-D. Do not interpret
  correctness completion as production performance approval.

**证明方法**

- Fault injection found that an empty repository result could win a concurrent
  cancellation race and become `MISSING`. Both analytics and records now check
  context after their asynchronous seam, after hydration/projection, and before
  returning a complete result. Already-expired deadlines reach neither RLS nor
  repository access, and cancellation never returns a partial record.
- A terminal `database/sql.Rows` error was reproducibly swallowed by the shared
  `query.ScanRowsToSObjects` loop because it did not inspect `rows.Err()`. The
  generic scanner now returns terminal driver errors, so every current caller
  consumes the same root fix.
- Raw repository/projection faults are normalized to the existing internal
  condition type. Causes remain unwrap-able for operators; this PR adds no wire
  serializer and therefore makes no transport-sanitization claim.
- SQL-mock lifecycle tests require rows to close on success and terminal scan
  failure and require `db.Stats().InUse == 0` after both failure and in-flight
  cancellation. A 64-caller cancellation test requires every blocked read to
  return and the projection in-flight counter to settle at zero.
- A retention test drives 64 one-MiB projection payloads, forces collection,
  and permits at most 8 MiB additional live heap. Three fuzz targets mutate
  object/record/field identifiers, duplicate/Unicode/root selections, and all
  supported scalar carriers. CI gives each target a five-second mutation
  budget and races the same records, analytics, and SQL repository seams.

**失败条件**

- Cancellation/deadline can still become missing, a partial result escapes, a
  raw fault loses internal classification, or terminal `rows.Err()` is ignored.
- Any targeted normal/race/fuzz command fails; an SQL row or connection remains
  in use; an in-flight read does not settle; or the 64 MiB retention probe leaves
  more than 8 MiB of additional live heap.
- Completing M2d-B requires inventing delegation, epochs, wire DTOs, replica
  behavior, admission, or an issue-tracker production dependency.

**备选方案**

- If a current-seam correctness or leak failure remains, keep P1c blocked and
  fix it at the records, analytics, kernel scanner, or repository owner before
  P1c-D.
- Leave generated-codec/N/N-1/oversize/compression tests to P2, real replica
  admission/process-failure tests to P3, and authoritative cross-org,
  AppInstallation, revocation, and stale-security tests to P6.
- For the SQL p99 anomaly, either profile/fix/re-run the identical 39-field c16
  workload before P1c-D, or explicitly retain it as a production-cutover gate;
  do not relabel `SQL-dominated` as a final root cause.

**最终 DoD**

- `scripts/ci/test-selected-root-m2d-b.sh` is an ordinary main CI step and runs
  targeted normal, race, and real fuzz mutation gates.
- A generic scanner regression test and selected-root stage/resource tests prove
  the two discovered failure classes and their broader ownership boundaries.
- ADR Section 9 records exactly what current behavior is proven and what remains
  unimplemented; #3555 marks M2d-B Done only after exact-head review/merge.
- The SQL p99 disposition remains visible between M2d-B and P1c-D, and no
  numeric limit, SLO, retry, admission, or transport value is approved here.

### 2026-07-12 — Post-M2d-B Program Checkpoint and M2d-C Decision

**Plan**

- Reconcile the merged M2d-B result with the whole serviceification plan and
  define the smallest evidence loop that can legitimately unblock P1c-D.

**Work completed**

- PR #3596 merged at `dc64f7ed35168ffe596f69d271839395bce3eebc`
  after exact-head standard and adversarial approval.
- M2d-B now guards cancellation ordering, terminal SQL row errors, fault
  classification, races, real fuzz mutation, goroutine/connection settling,
  and bounded live-heap retention on the current selected-root seam.
- Tracking issue #3555 was audited against the merged ADR and learned evidence.

**Errors encountered**

- The tracking issue simultaneously said #3593/#3594 had closed the accepted
  #3582 documentation follow-ups and later listed those same follow-ups as
  remaining. The phase table also lacked a distinct owner for the SQL-tail
  investigation even though it blocked P1c-D.

**Learned**

- The selected-root boundary has now survived both structural capacity and
  current-seam correctness/resource tests. This is enough to keep the contract
  shape; it is not evidence that production latency or limits are approved.
- `SQL p99 = 534.395 ms` localizes almost all of the anomalous 39-field/c16
  latency below the selected-root adapter. It does not explain why that cell is
  slower than the 512-field/c16 cell and therefore is not yet a root cause.
- P1c-D is an approval decision. Combining unresolved performance diagnosis
  with that approval would make its result subjective and difficult to audit.

**Decisions**

- Introduce `P1c-M2d-C` as the single owner of SQL-tail disposition.
- M2d-C must first reproduce and account for the anomaly using the identical
  workload, query-plan/statement evidence, execution order, cache/warmup,
  database scheduling, and Go allocation/GC evidence. It may optimize only a
  proven Runtime/database cause and must rerun the same cell after a change.
- M2d-C does not need to make the current implementation perfect. It is done
  when the anomaly is either removed by a verified root fix or classified with
  evidence as a local, contract-preserving issue with an explicit measurable
  production-cutover gate and sufficient attempt-budget headroom.
- P2 remains blocked until P1c-D. Production cutover remains independently
  blocked on real P4/P5 end-to-end SLO evidence even if a no-traffic skeleton
  later proceeds.

**Next**

- Execute M2d-C as one diagnostic/profile loop before P1c-D. Do not change the
  semantic contract, create protobuf/gRPC artifacts, approve candidate numeric
  values, or optimize from an unprofiled guess.

### 2026-07-12 — P1c-M2d-C Loop 1: Timed Connection Establishment

**Immutable input:** NexusCRM M2d-A revision
`849c98281391f046b77c82ad0df59292dee2f722`; test2 run
[`29174439835`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29174439835);
artifact `8254639111`, SHA-256
`7029e82d2a86af6953b9cf4b5fc2916e740619257fbb12b239fa1f069a99b54d`.

**默认答案**

- Treat the 535.439 ms 39-field/c16 p99 as benchmark contamination by timed
  database connection establishment unless an identical concurrency-aligned
  test2 rerun disproves that hypothesis.
- Fix the evidence harness, not Runtime or the selected-root contract.

**证明方法**

- The 39-field/c16 cell was first in the deterministic c16 order. Its pool
  changed from four open/idle connections before timing to 16 afterward; its
  SQL p99 was 534.395 ms and end-to-end p99 535.439 ms.
- All later c16 cells began with 16 connections. The final 15-field/c16 cell
  recorded SQL p99 67.578 ms, and the earlier 5,000-read 512-field/c16 readiness
  phase recorded p99 195.156 ms. The isolated result is not monotonic in field
  count or query shape.
- `p1cM2dAMeasure` labeled 50 sequential reads as per-cell warmup and never used
  its `concurrency` argument until the timed worker pool started. The c1/c4
  tiers can span the database/sql five-minute connection lifetime, so the pool
  can shrink before the first c16 timed cell.
- Artifact schema v4 replaces that loop with untimed reads at the declared
  concurrency, uses at least one request per worker, records connection closure
  counters, and refuses to time a cell with fewer open connections than its
  declared concurrency.

**失败条件**

- The identical rerun begins 39-field/c16 with fewer than 16 open connections,
  changes the timed sample count/order/deadline, or does not preserve one
  query/read and zero errors.
- With a ready 16-connection pool and no timed lifetime/idle closures,
  39-field/c16 remains anomalous. That rejects this root-cause hypothesis and
  requires a tighter statement-plan/database-scheduling probe; it does not
  permit P1c-D to proceed.

**备选方案**

- If the hypothesis is rejected, capture the stable synthetic statement plan,
  TiDB execution/scheduling facts, and per-sample connection lifecycle on the
  same test2 cell. Do not change SQL, indexes, field count, authorization, or
  deadline before that profile identifies an owner.
- If the rerun removes the anomaly, retain the selected-root implementation and
  make connection-ready warmup plus real P4/P5 route SLO evidence a measurable
  production-cutover gate.

**最终 DoD**

- A reviewed NexusCRM revision with schema-v4 concurrency-aligned warmup is
  merged and rerun through the existing L2 test2 workflow.
- The result records pool-open and connection-close counters around warmup and
  timing, the identical 39-field/c16 latency distribution, query count, errors,
  and pool wait.
- M2d-C closes only after the rerun either verifies the root cause and records
  the cutover gate or rejects it and completes the next evidence loop. P1c-D
  and P2 remain blocked meanwhile.

### 2026-07-12 — P1c-M2d-C Loop 2: Wide-Readiness Deadline Stop

**Immutable input:** NexusCRM revision
`2606040ad56a3c48e0e63dbbc61045c75b3357bd`; downstream revision
`cb89f5d86c44e47fa749a8853f4e9f54bb65e807`; test2 run
[`29178946215`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29178946215);
artifact `8255885804`, archive SHA-256
`7f653cf08c73581ef9c2231f9dbdc86c4d9d166bf6019bfe5998018d8a9d8bde`.

**默认答案**

- Do not retry the full matrix merely to seek a green run, increase the two-second
  diagnostic deadline, or weaken the zero-error readiness gate.
- Isolate the original 39-field/c16 question with a focused mode in the existing
  harness. Preserve the identical 1,200-request timed workload and require a
  concurrency-ready pool before its timing window.

**证明方法**

- Sequential 39-field/c1 startup readiness converged: its final 1,200 requests
  had zero errors and p99 130.533 ms.
- The following 5,000-request 512-field/c16 readiness phase issued exactly
  5,000 repository queries but returned two deadline-exceeded outcomes. Both
  occurred in its final 1,200 requests; that window recorded p99 222.557 ms,
  p999 2.001 s, and maximum 2.001 s.
- The failure therefore occurred before any randomized measured cell and cannot
  validate or reject the connection-establishment explanation for the old
  39-field/c16 result. Cleanup successfully removed the marker-owned scratch
  database, and the workflow retained a provenance-complete partial artifact.
- Because the two wide-readiness failures occurred in the trailing window, they
  cannot be classified as initial connection establishment. They are separate
  evidence that the candidate two-second attempt cap is not yet a production
  guarantee.
- Adversarial review rejected a first focused-artifact shape that reused broad
  15/39-field and all-c16 hypotheses. Schema v5 now emits only five
  39-field/c16 slice-specific hypotheses, records an explicit completion scope,
  and carries the immutable 512-field/c16 failure as an open machine-readable
  retained cutover gate.

**失败条件**

- The focused run changes the selected field count, concurrency, sample count,
  deadline, adapter path, authorization, or one-query-per-attempt invariant.
- Its timed window starts with fewer than 16 open connections, records a timed
  connection-lifetime/idle closure, pool wait, or any request error.
- A ready-pool 39-field/c16 result retains the old SQL-tail anomaly. That rejects
  Loop 1 and requires statement-plan/database-scheduling evidence before P1c-D.

**备选方案**

- If the focused run verifies Loop 1, close only the original anomaly. Retain
  the observed 512-field/c16 deadline tail as a measurable production-cutover
  gate for attempt-budget and real route-SLO work; do not present two seconds as
  approved behavior.
- If the focused run fails, keep M2d-C open and add the smallest per-sample SQL
  stage/plan diagnostic needed to identify the current Runtime/database owner.

**最终 DoD**

- The existing harness has a reviewed, provenance-recorded focused mode; it is
  not a second benchmark framework and cannot approve limits or SLOs.
- A focused artifact cannot look like full-matrix approval: its completion
  scope, hypothesis names/targets, and inherited open gate are validated by the
  downstream workflow.
- test2 executes exactly 1,200 timed 39-field/c16 reads after
  concurrency-aligned warmup, with pool lifecycle, SQL stage, query-count,
  error, allocation, and resource-pressure evidence.
- M2d-C closes only if that artifact verifies or rejects Loop 1 and records the
  independent 512-field/c16 two-second tail disposition. P1c-D and P2 remain
  blocked until then.

### 2026-07-12 — P1c-M2d-C Loop 3: Focused Root-Cause Verification

**Immutable input:** NexusCRM revision
`34e82a93feef1f248fb6da3b0077c7751f64dcc6`; downstream revision
`f268d7f6db48971d3ec84fc60f67906cd4d6f836`; test2 run
[`29180294181`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29180294181);
artifact `8256296025`, archive SHA-256
`e2e4d82ed1521b8d3d151831a67b3b380ab361fe0b1689f6e91530a7159a3804`.

**默认答案**

- Accept timed database connection establishment as the root cause of the old
  39-field/c16 anomaly because the identical ready-pool workload removed it
  without a Runtime, SQL, authorization, projection, or contract change.
- Close M2d-C, retain the independent 512-field/c16 deadline tail as a
  production-cutover gate, and move next to P1c-D rather than optimizing an
  anomaly that no longer reproduces under a valid timing window.

**证明方法**

- The focused artifact pins app-packs
  `bb138effbe9c2857db432224892c232e14bddf80` and fixture
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`. It ran 1,200 timed
  39-field/c16 reads with the unchanged two-second diagnostic deadline.
- After the long c1 startup, the pool had one open connection. Untimed c16
  warmup raised it to 16. The timed window began and ended with 16 and recorded
  zero lifetime closes, idle-time closes, pool waits, errors, or goroutine
  growth.
- All 1,200 attempts issued one SQL query. SQL p99 was 59.638 ms and
  end-to-end p99 was 61.179 ms; p999 was 73.224 ms and maximum 74.063 ms.
- Against the old SQL/end-to-end p99 values of 534.395/535.439 ms, the same
  workload improved 88.84%/88.57% (8.96x/8.75x) solely by moving connection
  establishment out of the timed window.
- The machine-readable artifact limits completion to the focused slice and
  retains failed run `29178946215`, artifact `8255885804`, and archive digest
  `7f653cf08c73581ef9c2231f9dbdc86c4d9d166bf6019bfe5998018d8a9d8bde`
  as an open 512-field/c16 production-cutover gate.

**失败条件**

- A later identical ready-pool 39-field/c16 run restores the old tail without
  connection lifecycle contamination, changes query count, or exposes pool
  wait/errors. Reopen the database-scheduling/statement-plan investigation.
- P1c-D presents the focused result as full-matrix evidence, approves the
  two-second value as a production SLO, or drops the inherited 512-field gate.
- P2 begins before P1c-D records the final contract decision.

**备选方案**

- If production-cutover route evidence cannot keep wide c16 attempts within
  the owned route/retry budget, optimize the current Runtime/database owner or
  revise admission/cutover policy. Do not infer a relationship expansion,
  second RPC, or semantic contract change from that implementation tail.
- If P1c-D cannot approve a candidate value from the collected facts, reject
  that value explicitly and name the finite evidence needed; do not reopen
  M2d-C's verified harness root cause.

**最终 DoD**

- The exact focused revision and L2 workflow are merged and the test2 artifact
  passes provenance, scope, retained-gate, connection lifecycle, query-count,
  stage-latency, error, and cleanup checks.
- ADR Section 9.11 and #3555 record M2d-C Done, the verified cause, the measured
  before/after values, and the still-open production-cutover gate.
- Overall P1c remains blocked only on P1c-D. Protobuf/gRPC and production
  cutover remain unapproved.

### 2026-07-12 — P1c-D Selected-Root Contract Approval

#### Goal and default answer

- Close P1c with one owner-aligned decision for every numeric or deployment
  candidate. The default answer is to approve facts that belong to the neutral
  capability and have exact/one-over plus real-path evidence, and to defer
  deployment or L1 route values to one named executable gate rather than turn
  them into L0 constants.
- P1c-D is a decision slice. It adds no protobuf, gRPC, generated code, Runtime
  command, production route, admission implementation, or issue-tracker
  production dependency.

#### Evidence reopened

- M2c-E proves the structurally valid envelope: 512 selected fields covers the
  anonymized metadata maximum of 352; 64-byte ASCII object/field identifiers
  match direct physical naming; 255 UTF-8 bytes is an opaque record-input bound
  rather than a stored-width promise; the largest valid request is 34,720 bytes;
  and the 1 MiB response has valid exact/one-over vectors.
- M2d-A proves the post-pushdown boundary remains one Runtime operation and one
  database query per read under the declared matrix. M2d-B proves current-seam
  cancellation, failure, race, and resource-release behavior. M2d-C proves the
  isolated 39-field/c16 p99 anomaly was timed connection establishment, not the
  query or contract shape.
- Failed run `29178946215` remains contradictory evidence that must not be
  averaged away: two of 5,000 512-field/c16 readiness reads exceeded the
  two-second diagnostic deadline, both in the final 1,200. Therefore two
  seconds can be a hard per-attempt execution bound, but it is not a measured
  production latency SLO or success guarantee.
- No private production traffic histogram is checked in. Current public and
  anonymized evidence covers the pinned CRM pack maximum of 39 fields and the
  test2 metadata maximum of 352. A later histogram is an L1 compatibility and
  capacity input, not an unbounded prerequisite for defining the initial
  neutral contract.

#### Decisions

- **APPROVED semantic contract:** 512 submitted and canonical unique selected
  fields; 64 KiB defensive semantic request cap; 1 MiB complete semantic
  response cap; 64 ASCII-byte object identifier; 64 ASCII-byte field
  identifier; 255 UTF-8-byte opaque record input; an explicit deadline; a
  two-second Runtime attempt cap; fail-fast bounded admission; and compression
  disabled for v1.
- The two-second cap is owned by Runtime and uses the earlier of the trusted
  caller deadline and the cap. It is a cancellation/resource ceiling, not an
  L0 SLO. Missing deadline is `INVALID_REQUEST`; semantic/identifier/field or
  response overflow is `RESOURCE_EXHAUSTED` with no partial result.
- **DEFERRED with finite owner/gate:** the sfcrm five-second route budget and
  one-retry candidates belong to the L1 route owner and must be decided on the
  real P4/P5 route; per-replica concurrency 16 belongs to P3 deployment
  admission; 128 KiB receive and 2 MiB send belong to P2 generated-codec
  replay. None is a protocol constant or current production baseline.
- Automatic retry semantics remain narrow even though the numeric retry policy
  is deferred: only `UNAVAILABLE` and an eligible `STALE_STATE` can be retried,
  inside the original parent deadline and without weakening revisions or
  security requirements.

#### Proof and regression guard

- `nexuscrm-records/selected_root_contract_decision_test.go` derives the
  approved envelope values from the M2c-E oracle and the Runtime cap from the
  M2c-S oracle. Its machine-readable golden makes every value `APPROVED` or
  `DEFERRED`, and requires every deferred value to retain one owner and one
  finite gate.
- Failure conditions are explicit: P2 stops if generated codec sizes do not fit
  the eventual transport caps or compression/oversized-message behavior is not
  enforced; P3 stops if real replica admission queues without bound or leaks
  DB/memory/goroutine resources; P4/P5 stop if the real route cannot disposition
  the retained 512-field deadline tail within its chosen SLO/retry budget.
- The fallback is owner-local. P2 may revise transport caps without changing
  semantics; P3 may revise replica concurrency without changing the protocol;
  P4/P5 may revise L1 route budget/capacity without turning it into an L0 SLO.
  If any gate proves that semantic maximums themselves require a different RPC
  shape, reopen P1c and stop traffic work rather than hide the failure.

#### Final DoD and next step

- The ADR status is `APPROVED`; Section 9.12 is the single decision table and
  Section 12 names P2 as the next phase. Candidate/history sections remain for
  provenance but no longer act as the current status.
- P1c is complete when the ordinary records tests, decision golden, boundary
  checks, and `git diff --check` pass and tracking issue #3555 reflects P1c-D
  Done. Production cutover remains independently blocked by its P2/P3/P4/P5/P6
  gates.
- Next: P2 creates only the minimal neutral selected-root protobuf/wire module
  and executes the approved semantic and codec compatibility vectors. Do not
  remote the 33 current interfaces or add a production route.

### 2026-07-12 — P3 Loop 1: Independent Runtime Process Shape

**Immutable inputs:** NexusCRM base `0e83de00edb5da84c42558434c0b198973c99d74`;
contract root `v0.1.3`; generated module `wire-go/v0.1.0`; api-host prerequisite
[pingcap-inc/nexuscrm-api-host#5](https://github.com/pingcap-inc/nexuscrm-api-host/pull/5),
merge `9b7906ef1d7726aa4c158d33a2b955008e5cb06a`, released as `v0.2.0`.

**默认答案**

- Build two real commands: an independently runnable `runtime-api` containing
  only the selected-root read graph, and a `runtime-worker` that deliberately
  contains no work engine, queue consumer, EventBus listener, database, gRPC,
  contract, or generated-wire dependency.
- Keep the server non-serving in production assembly until P6 supplies trusted
  delegation and P7 supplies authoritative receipts. A bound port or live probe
  is not permission to report ready or accept data-plane work.
- Reuse the approved `records.ContractService.ReadSelectedRecord ->
  PersistenceService.ReadSelectedRecord -> analytics.QueryService ->
  QueryRepository` path. Do not wrap the full `ServiceManager`, because that
  package compiles the monolith controlplane, L1 distribution, EventBus, and
  unrelated worker graph into the new process.

**证明方法**

- `backend/internal/runtimeassembly` constructs the minimum current L0 engine
  graph from the same metadata, permissions, query, persistence, and records
  implementations. It starts a no-work EventBus and does not import
  `backend/internal/application/services`, `serverruntime`, controlplane,
  connectors, a distribution, app-packs, or the issue-tracker fixture.
- `backend/internal/runtimeapi` maps the single generated
  `RecordsService.ReadSelectedRoot` RPC to the real adapter. Authority produces
  a concrete opaque `runtimegrpc.VerifiedCaller`; a separate resolver binds that
  handle to the current trusted in-process session without putting identity or
  permissions in the semantic request.
- The api-host policy owns one ordered, default-deny pre-handler path:
  cancellation, method allowlist, protocol/capability, compression, deadline,
  atomic drain/admission, two-second attempt cap, authority, a second
  cancellation check, then handler. Unknown methods and zero caller handles
  cannot execute a handler.
- `runtimeprocess` owns explicit assemble/start/ready/drain/stop/wait state and
  validates role/version/capabilities before listening. A gRPC role must supply
  a begin-drain admission hook. The process then calls the real
  `grpc.Server.GracefulStop`, which owns handler completion, response marshal,
  transport send, and listener shutdown; when the owner context expires it
  forces `Stop` and waits for graceful shutdown to unwind. Tests cover both a
  complete 900 KB in-flight response and the forced-stop deadline path.
- P3 makes the deferred replica value executable: Runtime defaults to and may
  not exceed 16 concurrent selected-root attempts. The exact 16 boundary is
  accepted, 17 is rejected at configuration, and saturation returns
  `RESOURCE_EXHAUSTED` without queueing or entering the adapter. This is a
  replica resource policy, not a protocol field or latency SLO.
- `scripts/ci/check-runtime-process-boundary.sh` scans production plus test
  command closures including future subpackages. It rejects L1/product owners,
  the full monolith assembly, auth/JWT initialization, compressor registration,
  and any work capability in the no-work worker.
- The real-TiDB build-tagged CI gate starts an actual TCP gRPC process and runs
  generated client -> api-host interceptor -> Runtime adapter -> metadata/CRUD
  -> RLS/FLS -> one real `QueryRepository.Find` -> typed wire response. It
  checks exact decimal, false, zero, string, date, datetime, reference, null,
  FLS omission, missing/RLS masking, forbidden-before-repository, deterministic
  projection, and one query per authorized/masked attempt.

**Root-cause correction discovered during assembly**

- The first command closure still initialized JWT state even though Runtime
  owns no bearer-token key. The edge was not an authority decision: records and
  analytics imported `kernel/pkg/auth` only for password hashing, display-name,
  lookup-label, and OAuth route-scope helpers; Go package initialization then
  pulled `jwt.go` into the process.
- These side-effect-free facts now have neutral owners:
  `kernel/pkg/passwordhash`, `useridentity`, `recorddisplay`, and
  `oauthroutes`. Existing auth functions delegate to those owners, preserving
  compatibility and one read path. The runtime-api production closure no
  longer contains `github.com/nexuscrm/auth` or
  `github.com/nexuscrm/kernel/pkg/auth`; the boundary gate makes that invariant
  executable.
- The first api-host draft also put a `WaitForDrain` promise on the interceptor.
  That layer can observe handler return but cannot observe protobuf marshal,
  network send, or client completion, so the promise was false even though its
  handler counter was accurate. The API was removed rather than renamed into a
  shutdown primitive. `Policy.BeginDrain` now owns only atomic rejection of new
  handlers; the real process owns complete RPC shutdown through
  `grpc.Server.GracefulStop`. A large-response cross-process regression starts
  shutdown while the adapter is active and proves the client receives the full
  response before graceful shutdown completes.
- Authority is now a directly nullable function value rather than an interface,
  eliminating typed-nil construction. Cancellation checks both the original
  parent context and the capped attempt context after authority and immediately
  before handler entry; the repeated race regression makes parent cancellation
  authoritative even before propagation reaches the derived context.

**失败条件**

- Stop P3 if the command must compile a distribution/L1 package, initialize a
  JWT secret, start background work, add a second selected-root implementation,
  access the database from the wire handler, or weaken the approved semantic
  contract to make transport convenient.
- Stop P3 if an unknown/compressed/expired/unauthenticated request can reach the
  handler, drain can race admission, response compression can be enabled, or a
  future compressor registration enters the production closure.
- Stop P3 if the TiDB gate uses a fake repository, performs more than one query
  for one selected-root attempt, distinguishes missing from RLS denial, leaks an
  FLS-hidden field, or requires an L1/app-packs production dependency.
- P3 cannot be released against an unpublished api-host branch or a pseudo
  version. NexusCRM must pin the reviewed semver release with no local replace.

**备选方案**

- If the minimum graph misses a read dependency, add the dependency at its
  current L0 owner and update both the dependency-closure guard and real-TiDB
  test. Do not fall back to the full `ServiceManager`.
- If transport policy needs another public/admin RPC, host it on the separate
  operational HTTP server or add an explicit reviewed allowlist policy; never
  restore default pass-through.
- If trusted authority or revision receipts are required to serve real traffic,
  keep production readiness false and complete P6/P7. Do not invent a test
  identity or zero receipt as production authority.

**最终 DoD**

- api-host #5 passes exact-head trusted build/vet/test CI, merges as
  `9b7906ef1d7726aa4c158d33a2b955008e5cb06a`, publishes `v0.2.0`, and
  NexusCRM pins it without a replace. CI ownership migration #6 is merged first
  at `2c3276edc075d370716d8f17a10645afa1abb239`.
- Every changed Go module passes `GOWORK=off go test ./...`, vet, targeted race,
  the Runtime boundary gate, the real-TiDB cross-process job, and
  `git diff --check`.
- Both commands build independently; runtime-worker remains no-work; production
  runtime-api reports live but not ready with rejecting authority; no existing
  CRM route or worker ownership changes.
- #3555 records P3 Done only after the exact NexusCRM head passes Codex Review
  and merges. P4 may then add disabled/non-authoritative real clients; P5/P6/P7
  remain the owners of traffic cutover, trusted delegation, and receipts.

### 2026-07-12 — P3a Loop 1: Deployable Runtime Packaging

**默认答案**

- Package `runtime-api` and `runtime-worker` as two independent, app-blind
  images. The generic L0 release owns binaries, image defaults, configuration
  schemas, operational metrics, and a reusable deployment base. L2 owns every
  concrete org/environment overlay, secret binding, identity binding, image
  digest, replica/resource value, and rollout workflow.
- Keep both P3a roles live but not ready. A projected workload-identity token
  proves only that packaging and rotation wiring exist; it grants no authority.
  P6 owns token verification/delegation, and P7 owns authoritative receipts and
  serving-state admission.
- A non-serving Runtime API must not require a database merely to expose its
  operational surface. The real `AssembleProduction` Runtime/DB graph remains
  the serving composition, while the P3a command installs rejecting authority,
  session resolution, receipts, and an unreachable unavailable reader.

**证明方法**

- `backend/internal/runtimeconfig` is the single deployment-environment owner.
  It validates canonical listen addresses, the 1..16 API admission value, and
  a non-empty absolute projected-token file. Embedded JSON schemas enumerate
  exactly the same keys and reject undeclared configuration properties.
- `runtimeprocess` exposes a private Prometheus registry at `/metrics`, with
  immutable role/version, lifecycle/live/ready, and API-only admission
  limit/in-flight plus accepted/capacity-rejected/drain-rejected counters. The
  counters come from the policy-owned snapshot rather than status inference;
  the no-work worker has no API admission metric.
- `infra/runtime.Dockerfile` has independent `runtime-api` and
  `runtime-worker` targets, fixed non-root UID/GID, role labels, role-specific
  schemas, health checks, and no distribution/app-pack source. Image smoke
  inspects the final binary module closure rather than trusting Dockerfile
  source scanning alone.
- The generic Kustomize base contains no Namespace or Secret and uses explicit
  projected service-account tokens. `infra/runtime/compose.yaml` is the local
  `role=all` form and renders exactly the two Runtime roles—no Catalog,
  Integration, L1 AppService, fixture, or database.
- Image smoke requires live=200, ready=503, exact role/version/capabilities,
  process/admission metrics, role/schema labels, fixed non-root execution, and
  clean SIGTERM exit for both images.

**错误与根因**

- The first OS-process probe exposed `capabilities: null` for the empty worker.
  The failure was in api-host `processhttp`: appending an empty slice to nil
  collapsed the stable list to nil. Accepting both JSON shapes in P3a would
  have made the first operational contract ambiguous. api-host #7 fixed the
  owner with a non-nil copy, merged at
  `92249c5d6ed6d0e323f0a30359058cd85a4b8e9a`, passed main CI run
  `29193063336`, and is released as `v0.2.1`.
- The first metrics draft exposed only admission limit/in-flight. That could
  not reveal load shedding or distinguish capacity from drain rejection.
  api-host #8 added the policy-owned cumulative snapshot without changing
  behavior, merged at `2123fd2cc8e491f95008ea2534c17d8192bd8052`,
  passed main CI run `29193650649`, and is released as `v0.2.2`.
- The issue initially assigned “per-org deployment manifests” to P3a. That
  contradicts the repository/deployment boundary: public-core L0 cannot own a
  concrete org deployment. P3a therefore provides only generic artifacts and
  records L2 as the concrete deployment owner.
- Local Docker image execution was unavailable because the desktop Docker
  daemon was stopped. Package, schema, Kustomize, Compose, boundary, binary OS
  process, full backend test/vet, targeted race, and distribution module gates
  were run locally; the trusted Docker CI job remains the exact image proof.
- The first exact image run `29193911229` reached the new Runtime build but
  failed while downloading unused `aws-sdk-go-v2` through the public module
  proxy. The Dockerfile had used unqualified `go mod download`, expanding the
  network and cache failure domain to the whole backend module rather than the
  two command closures. The correction builds both commands directly inside
  the credential/cache mount, so Go fetches only packages reachable from the
  Runtime roles; adding a retry to the oversized graph was rejected.

**失败条件**

- Stop if either final image contains a distribution, app-pack, sfcrm,
  issue-tracker fixture, test authority, database shortcut, compressor, or
  worker capability.
- Stop if a mounted token can make either role ready, authenticate a caller, or
  select a session before P6/P7; or if a no-work worker reports ready.
- Stop if the generic deployment base owns an org namespace, secret value,
  cloud identity binding, database endpoint, production replica/resource
  policy, or rollout workflow.
- P3a is not Done until trusted CI builds and runs both exact images. Command
  compilation or a same-process test cannot substitute for that artifact.

**备选方案**

- If final image construction shows the backend module graph itself pulls L1
  into the binary, split an app-blind build module or correct the offending
  dependency owner; do not hide the edge with a Docker copy exclusion.
- If P6 requires additional identity configuration, extend the same schema and
  generic projected-token seam. Do not add a second environment parser or a
  P3a authority implementation.
- If an L2 platform cannot consume the Kustomize base, it may render an
  equivalent environment-owned manifest from the two published images and
  schemas; L0 still must not absorb concrete deployment values.

**最终 DoD**

- Both images build from the reviewed NexusCRM head, contain the exact embedded
  schemas, pass final-binary dependency inspection and operational/shutdown
  smoke, and are enforced by ordinary main CI.
- Runtime package tests/vet/race, distribution tests/vet/build, boundarygate,
  runtime process boundary, Kustomize/Compose rendering, and `git diff --check`
  pass with no new public-core hit.
- #3555 marks P3a Done only after merge and exact-head main image CI. P4 remains
  the owner of real L1 clients and separate-client/server OS-process semantic
  equivalence; W1 requires both.

### 2026-07-12 — P3a Closure and P4 L1 Client Walking Slice

**默认答案**

- P3a is Done. NexusCRM #3607 merged the independent Runtime artifacts at
  `34b356f4f0a24624b688e47c837fcf3a6ba5c25c`; #3608 corrected the Docker
  dependency-fetch closure and merged at
  `eb58dfa0f795d4352a4ed42c055561b34a3c3af2`.
- Exact-main run `29194155995` passed the independent Runtime image build and
  operational smoke, the complete build/test seal, and the L0 neutrality gate.
  P3a therefore supplies deployable but deliberately non-serving Runtime
  skeletons; it does not supply P6 authority, P7 receipts/readiness, or traffic.
- P4 has one client-call owner per layer. `nexuscrm-api-host/runtimegrpc` owns
  app-blind protocol metadata, semantic validation/mapping, the approved client
  envelope, and fail-fast invocation. sfcrm owns only Salesforce root-value
  projection. NexusCRM owns only the test assembly that launches the real
  Runtime/Records/TiDB path as a separate OS process. The issue-tracker fixture
  remains an unchanged test-only consumer and never accesses the database.

**证明方法**

- api-host #9 merged at
  `2990914cd9396dfccd3b661c71512133ece7c843`, passed main CI run
  `29194938850`, and is released as `v0.3.0`. Its client wrapper removes any
  caller-supplied duplicates of the three protocol keys, installs exactly one
  current declaration, validates before RPC work, applies 128 KiB send / 2 MiB
  receive plus fail-fast per call, and decodes only validated semantic results.
- app-packs #365 merged its dormant sfcrm adapter at
  `2f4a2ff36d473bba46750a0a247a98cd83f698e9`, above the semantic function seam.
  Its production dependency closure rejects api-host, wire-go, and gRPC; its
  partition manifest and NexusCRM ProductHooks are unchanged. False, zero,
  empty string, null, omission, exact decimal, date/datetime, and reference ID
  projection are executable. Decimal rendering falls back to exact JSON
  exponent notation when plain zero expansion would itself exceed the approved
  complete-response budget, preventing scale-driven unbounded allocation.
- The NexusCRM P4 gate is compiled only with `crm && runtime_l1_integration`.
  Its parent test launches a second test-binary OS process. The child assembles
  `runtimeapi -> runtimeassembly -> records/persistence -> real TiDB`; the
  parent wires both sfcrm and the reviewed fixture snapshot through the same
  generated RPC and api-host client. Database query count is reported by the
  Runtime child after process-owned drain and is kept separate from RPC count.

**失败条件**

- Stop if sfcrm imports generated wire/gRPC policy, if NexusCRM L0 imports L1,
  if the fixture or test authority enters a production closure, or if P4
  registers/replaces the existing Salesforce route before P5 fencing.
- Stop if the two consumers do not use the same generic client or make more
  than one Runtime data RPC per read; or if object denial reaches the database,
  missing and RLS diverge, FLS-hidden data crosses the process, cancellation
  reaches repository work, or child drain loses the result.
- The P4 script must fail when its declared TiDB environment is absent. A
  skipped test or same-process listener cannot be reported as separate-process
  walking-slice evidence.

**备选方案**

- If the separate-process test exposes a real adapter bug, fix the current
  Runtime/Records owner and rerun the same gate; do not add a fixture database
  shortcut or a second semantic mapper.
- If sfcrm root projection cannot preserve Salesforce behavior for a concrete
  route, keep the adapter dormant and defer that route to P5. Do not broaden v1
  with lookup or relationship expansion.
- P6 may extend connection credentials/delegation around the same client call
  owner. It must not create a parallel client envelope or place identity facts
  in the semantic selected-root request.

**最终 DoD**

- api-host `v0.3.0`, the merged app-packs sfcrm adapter revision, and the exact
  reviewed fixture snapshot are explicit NexusCRM inputs.
- Trusted main CI launches the Runtime child as a separate OS process against
  TiDB and passes typed/null/omission/FLS/missing-RLS/forbidden/cancellation/
  deterministic projection with one RPC/read and one DB query for each
  repository-reaching read.
- Ordinary production dependency checks prove no fixture, test authority, L1
  package, route replacement, compressor, or database shortcut entered the
  Runtime images or app-blind server closure. Only then may #3555 mark P4 and
  W1 Done; P5/P6/P7 remain independent production-cutover prerequisites.

### 2026-07-12 — P6a Trusted-Delegation Owner Re-derivation

**默认答案**

- W1/P4 is Done, so the next security-critical path is P6. P6 cannot be
  implemented as extra caller metadata on the selected-root request or as a
  richer test `SessionResolver`.
- The current tree has no production Runtime authority, AppInstallation
  persistence, AuthEpoch, minimum SecurityEpoch, or SecurityLease owner. It
  also documents the current product as single-tenant through `DefaultOrgID`.
  Cross-org isolation therefore cannot be honestly claimed from the P4 fake
  authority.
- The correct shape is one Runtime Identity/Security issuer and one Runtime
  security-admission interceptor after api-host workload authentication. It
  installs one immutable TrustedCall before metadata/Records access. The
  selected-root semantic request remains unchanged.

**证明方法**

- At NexusCRM `fa1355aeb8292d8c5fc0404c4786e5676bb9cee2`, inspect
  `backend/internal/runtimeapi/process.go`, `records_server.go`, `bootstrap.go`,
  `backend/cmd/runtime-api/main.go`, `backend/internal/runtimeconfig/config.go`,
  `nexuscrm-auth/auth_service.go`, `_System_Session`, and
  `shared/pkg/constants/ids.go`.
- Compare those executable owners with api-host `v0.3.0` `runtimegrpc.Policy`
  and Sections 4/6/8 of the approved selected-root ADR. The current Policy can
  authenticate an opaque workload handle but cannot own NexusCRM session,
  installation, or epoch state.
- Keep the delegation field inventory in the P6 ADR only. The target
  architecture and selected-root ADR reference that inventory and add only
  their service-level invariants or required capability; neither duplicates
  token fields or preserves a competing scopes/capabilities set.
- Review `docs/design/runtime-trusted-delegation-contract.md` and require its
  real interceptor/TiDB matrix before any Runtime composition becomes ready.

**失败条件**

- Stop if Org, principal, installation, permissions, or security epoch enter
  the Records semantic request or unverified metadata; if a raw user bearer is
  forwarded to Records; or if configuration becomes an authorization SSoT.
- Stop if api-host transport policy is made responsible for NexusCRM session or
  AppInstallation persistence, or if the Records handler performs a second
  independently authoritative session lookup after admission.
- Stop if cross-org proof mutates only a fake `DefaultOrgID`, if a test/static
  authority enters a Runtime image, or if an expired security lease can remain
  ready.

**本轮错误与根因**

- The initial P6a draft added an exact delegation inventory while the target
  architecture and selected-root ADR still carried older inventories. The
  first review fix removed only the selected-root copy and missed the higher
  normative target-architecture copy.
- The failure class was treating a specialized ADR as additive documentation.
  Establishing a more-specific owner is instead a source-of-truth migration:
  every normative reader must switch and every superseded inline copy must be
  deleted atomically.
- The investigation originally searched the relevant documents but did not
  construct an authority graph before editing. Review pressure exposed that
  the search result was seen but not incorporated into the ownership model.
- During #3555 status editing, an unchecked transform failed before
  `gh issue edit --body-file -` and temporarily supplied an empty body. The
  body was recovered from GitHub edit history, then updated through an asserted
  GraphQL mutation. Future remote-body transforms must produce and validate a
  complete non-empty result before any write API is invoked.

**备选方案**

- If the current in-process L1 host cannot yet call a wire identity exchange,
  it may call the same L0 issuer in process. The token contract and authority
  stay identical; a second L1 issuer is forbidden.
- If P7 is not yet merged, P6 may prove the Runtime security state machine and
  remain non-serving. It must not create a temporary metadata or installation
  registry; P7 later binds the accepted epoch and metadata view atomically.
- If authoritative cross-org state cannot be represented in the current
  database topology, keep P6/P5 blocked and fix the partition authority. Do not
  treat the single-tenant constant or deployment namespace as proof.

**最终 DoD**

- P6a approves one owner and one admission path; P6b-P6d implement the neutral
  delegation model, workload/security interceptors, authoritative persistence,
  and real negative matrix in dependency order.
- P6-D explicitly approves or rejects delegation lifetime, key overlap, and
  `MaxSecurityStaleness`; no proposed value is presented as current behavior.
- P5 remains blocked until P1x-b, P6-D, and P7 are Done and the production
  composition contains no fake/static authority or alternate identity path.

### 2026-07-12 — P6b Neutral Delegation Crypto/Wire Contract

**默认答案**

- P6b is Done through
  [nexuscrm-contract#9](https://github.com/pingcap-inc/nexuscrm-contract/pull/9).
  Reviewed head `b02124aaac8d8bf22cd6e9cebce96be25617bf99`
  passed exact-head Contract CI and standard/adversarial Codex Review with
  `BOTH_APPROVED`; the squash merge is
  `e2fe0a97787042f060ab913aaa18b69ca8cc9214`.
- The stdlib-only semantic owner is
  `github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1`. It owns
  the exact P6a claim inventory, canonical v1 compact JWS, fixed Ed25519
  signing/verification, disclosure-safe failure categories, fixed four-axis
  capability intersection, protocol ceilings, and immutable verification-key
  snapshots. It owns no key provider, issuer service, admission interceptor,
  session/security store, revocation state, or transport host.
- `github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope` owns only the
  inert ASCII gRPC metadata key `x-nexuscrm-runtime-delegation`. No protobuf,
  generated file, `RecordsService.ReadSelectedRoot` request/response, or data
  RPC changed.
- Published annotated tags `v0.1.4` and `wire-go/v0.1.1` both resolve to the
  reviewed merge. A clean external Go module with no `replace` downloaded both
  tags and passed a public Sign/Verify plus metadata-carrier test.

**证明方法**

- The protected header authenticates fixed `alg=EdDSA`, `SigningKeyID`, the
  explicit type, envelope version 1, and
  `nexuscrm.runtime.delegation.v1` domain. The only public semantic read path is
  `Verifier.Verify`; raw header/payload parsers are package-private, so a
  consumer cannot obtain accepted Claims before signature verification.
- The immutable JSON vector freezes exact canonical header/payload bytes,
  RFC 7515 signing input, RFC 8037 Ed25519 public test key/signature, and
  compact serialization. P6b is the initial N baseline; there is no fictional
  N-1 delegation release.
- Exact/one-over tests cover 255-byte opaque identifiers, 1,024-byte workload
  identifiers, 128-byte signing key IDs and capabilities, 32 capabilities,
  16 KiB compact envelopes, ten-minute protocol lifetime, and 30-second
  verifier skew. The complete all-maxima semantic token, including worst-case
  JSON escaping and maximum integer widths, is 13,168 bytes.
- Adversarial tests cover every signed-claim and key-ID tamper, bad signature,
  malformed/truncated/duplicate/non-canonical input, unknown version/domain/
  capability/key, current/overlap/retired keys, expiry/not-yet-valid, workload/
  audience/org/AppInstallation substitution, empty authority axes, shared
  verifier races, and decode/verify fuzzing.
- Local validation passed `scripts/check.sh`, full root/wire test and vet,
  root/wire race suites, two ten-second fuzz runs, unchanged selected-root Buf
  baseline, dependency/product-neutrality guards, and `git diff --check`.
  Exact-head Contract CI run `29200251869` and Codex Review run `29200251035`
  passed.

**本轮错误与根因**

- The first draft produced canonical signing input but deliberately omitted a
  signer, verifier, hard lifetime/skew, and key rotation. That confused P6-D's
  ownership of shorter production policy with P6b's ownership of hard protocol
  safety. P6b must define the maximum safe grammar; P6-D may only tighten its
  production settings.
- The first draft exported raw canonical claims parsers. That would have
  allowed a second semantic read path bypassing verification. The final public
  API returns Claims only from `Verifier.Verify`.
- The first capability-intersection helper was variadic. Omitting an authority
  slice would silently widen the result. The final `CapabilityGrantSets` has
  fixed workload, AppInstallation, session/scope, and requested axes; an empty
  or unknown mapping produces no capability.
- First exact-head adversarial review blocked the immutable vector because its
  workload ID contained `sfcrm`, permanently freezing an L1 product name into
  L0. The vector now uses the reserved neutral `example.test` domain, all
  dependent signature bytes were regenerated, and the boundary guard scans
  delegation production code, tests, vectors, and its contract document for
  product vocabulary.
- The same review found that `time.Time.Add` could wrap near an advertised
  `int64` timestamp ceiling. Verification now uses overflow-safe integer-second
  comparisons and has a `math.MaxInt64` expiry regression.
- The first clean-consumer command used `go mod download` without materializing
  the complete test checksum set, so `go test` stopped on missing `go.sum`; its
  temporary module-cache cleanup also met Go's read-only extracted files. The
  corrected proof uses `go mod tidy`, then tests and explicitly verifies both
  versions and absence of `replace`, with writable cleanup.

**失败条件**

- Reopen P6b if a public consumer can parse accepted Claims without signature
  verification; select an algorithm; omit an issuance authority axis; accept
  unknown/non-canonical fields; mutate a verifier key snapshot; or exceed the
  hard size/time/set ceilings.
- Reopen P6b if an L1/product/tenant name, raw bearer, permission map, profile,
  record value, caller-selected security context, database/API-host dependency,
  or production key/state provider enters the semantic package or immutable
  vector.
- Stop P6c if it puts the delegation in the Records protobuf, adds another data
  RPC, trusts workload/Org/AppInstallation from ordinary metadata, exposes
  condition-specific existence details, or reconstructs a parallel Claims
  parser/verifier.

**备选方案**

- If another implementation language is introduced, reproduce the immutable
  byte vector and closed grammar before serving; do not relax v1 canonical
  acceptance. An incompatible representation requires a new envelope version
  and a real N/N-1 window.
- If provider rotation needs a different overlap duration or production token
  lifetime, P6-D may configure values below the hard ceiling. It must not
  rewrite the v1 vector or widen the ceiling implicitly.
- If P6c cannot yet expose an exchange RPC, the current host may call the same
  Runtime Identity issuer in process. The signed artifact and single issuer
  remain identical; no L1-owned issuer is allowed.

**最终 DoD**

- P6b is complete only with the exact-head dual approval, merged immutable
  vector, published `v0.1.4` / `wire-go/v0.1.1`, clean no-replace consumer, and
  unchanged Records wire contract recorded above.
- The next dependency-ordered task is P6c: Runtime-owned admission converts a
  verified delegation plus authoritative workload/session/installation facts
  into one immutable `TrustedCall`, removes the production
  `Authority + SessionResolver` split, and corrects durable scope/App fact
  reconstruction. P6c must reuse this verifier and cannot create another
  Claims read path.
- P6d still owns authoritative revocation/AppInstallation/AuthEpoch/
  SecurityEpoch/SecurityLease persistence and the real TiDB negative matrix.
  P6-D still owns the shorter production lifetime, configured skew, key-overlap
  interval, and maximum security staleness. P5 remains blocked until P6-D and
  P7 close.

### 2026-07-12 — P6c Runtime Security Admission and TrustedCall Cutover

**默认答案**

- P6c keeps api-host at transport altitude. Its existing `runtimegrpc.Policy`
  authenticates one opaque workload and installs it in private context; a
  second chained interceptor owned by NexusCRM Runtime reads only
  `x-nexuscrm-runtime-delegation`, calls the released P6b verifier, revalidates
  the current session through the AuthService owner, and installs one private
  immutable `TrustedCall`.
- Records no longer accepts or constructs a `SessionResolver`. It receives the
  selected-root semantic request unchanged and obtains the engine user only
  from `TrustedCall`. No raw bearer, Claims parser, identity field, second RPC,
  protobuf change, AppService, route, or production serving switch is added.
- P6c is a shape proof, not the P6d security-state proof. The non-serving
  command contains rejecting workload/security admission only. Test issuers,
  keys, bindings, and sessions remain in `_test.go`; P6d still owns durable
  AppInstallation, DelegationID revocation, AuthEpoch, SecurityEpoch, and
  SecurityLease state plus the real TiDB negative matrix.

**证明方法**

- gRPC supports repeated `ChainUnaryInterceptor` options in declared order, so
  no api-host prerequisite or semantic uplift is required. The api-host Policy
  remains outermost; Runtime security admission runs only after workload
  authentication and before `RecordsServer`.
- The admission constructor accepts one immutable `delegationv1.Verifier` and
  directly nullable binding/session function ports. It rejects absent,
  malformed, duplicate, tampered, expired, and wrong-audience delegations as
  unauthenticated; workload/Org/AppInstallation/capability mismatches as
  forbidden; and session invalidation as unauthenticated, all before the
  Records handler.
- api-host's `VerifiedCaller` is deliberately opaque. The Runtime binding port
  maps it to the canonical workload ID together with Org/AppInstallation/
  audience; verifier comparison never treats the opaque handle string as an
  identity fact.
- `TrustedCall` is stored by value. Signed capabilities and every pointer-backed
  `models.UserSession` field are cloned on construction and cloned again on
  access. Concurrent mutation probes run under the race detector and cannot
  modify another request or the AuthService-owned projection.
- The current P4 separate-process test now mints a signed test delegation and
  both sfcrm and the CI-only issue-tracker fixture use the same generated client
  with that metadata. Its existing CRUD/RLS/FLS/typed/null/absent/order and
  one-RPC/one-query assertions remain unchanged.
- The Runtime production-closure gate scans all current and future first-party
  command packages for delegation signing/private-key construction, excludes
  test issuers structurally, retains the raw-bearer auth initializer ban, and
  fails if a production `SessionResolver` symbol returns.

**本轮错误与根因**

- The first durable-session draft tried to persist `SessionClass` and then made
  SQL NULL clear the JWT field. Existing rows predate that column, while CRM
  OAuth profile selection consumes the JWT class. That was an unplanned source-
  of-truth migration without backfill or revocation and would have broken live
  `/userinfo` compatibility. P6c selected-root does not need SessionClass, so
  the migration was removed rather than hidden behind a legacy fallback.
- The next draft mapped durable NULL/empty Scope, Connected App, and GrantType
  to empty strings. Reopening all downstream consumers exposed the broader
  failure class: empty scope is the existing legacy/full-access sentinel. A
  storage-level “clear” could therefore become an authorization upgrade, with
  interactive OAuth authorize as only the first visible symptom.
- The root fix stays at AuthService: if a token was issued with a constrained
  fact and that durable authority disappears, the whole session becomes
  unauthenticated. Only sessions whose issued and durable facts were both empty
  retain legacy behavior. The interactive gate separately accepts only the
  explicit platform-browser class or the fully classless legacy shape.
- The first Runtime admission draft imported the whole `nexuscrm-auth` package
  from `runtimeapi`, which pulled the JWT/HMAC bearer initializer into the
  non-serving Runtime image. The AuthService adapter moved to a separate backend
  composition package; the command depends only on the neutral function port
  until P6d chooses the real serving composition.
- A first production-key guard scanned external standard-library and contract
  dependencies and falsely matched Ed25519 implementation/signing definitions.
  The corrected guard scans only first-party production Go files reachable from
  the command while the dependency/import bans cover external owners.

**失败条件**

- Reopen P6c if Records can be constructed with a separate identity resolver,
  if any handler/repository path accepts a caller `UserSession`, if a public
  Claims parser appears, or if missing `TrustedCall` reaches request projection
  or repository work.
- Reopen P6c if a production Runtime command/image contains a test issuer,
  private signing key, static Org/App/capability grant, raw user bearer path,
  L1 package, or gRPC compressor; or if selected-root protobuf/generated bytes
  change.
- Keep P5 blocked if P6d has not supplied current AppInstallation, revocation,
  epoch, lease, and cross-org authorities, or if P7 cannot atomically bind the
  security view to metadata receipts.

**备选方案**

- If P6d cannot reconstruct an additional capability-relevant session fact
  from durable state, reject that delegation and add the fact to the owning
  durable projection. Never fall back to the user JWT or add the fact to the
  Records request.
- If binding lookup becomes an authenticated-workload DoS bottleneck, P6d may
  publish an immutable epoch/lease-fenced in-memory projection. It must remain a
  cache of the one Runtime security SSoT, not a configuration or metadata
  registry competing with it.
- If a future Runtime command needs the AuthService adapter, import the narrow
  composition package only in the real P6d serving root. Do not weaken the
  non-serving image guard or move NexusCRM security semantics into api-host.

**最终 DoD**

- The durable-session PR and Runtime-admission PR must each pass current-head
  standard and adversarial review plus main CI; #3555 may mark P6c Done only
  after both merge.
- Full backend/auth tests and vet, affected race suites, Runtime lifecycle and
  real-repository gates, P4 separate-process walking slice, production-closure
  checks, public-core/boundary gates, unchanged wire baseline, and
  `git diff --check` must pass.
- The handoff is P6d: replace only the binding/session fakes with authoritative
  Runtime security persistence and close revocation/AppInstallation/epoch/
  lease/cross-org evidence. P5 remains blocked and no production route is
  enabled by P6c.

### 2026-07-12 — P6d-M1 Authoritative Runtime Security Persistence

**默认答案**

- P6d is split by ownership rather than by test symptom. M1 adds the durable
  Runtime security-state owner and the session AuthEpoch fact; M2 consumes the
  released caller-bound verifier, installs the durable owner in admission, and
  runs the real cross-process/TiDB negative matrix.
- The Runtime security projection consists of four protected L0 system tables:
  one per-Org Runtime audience/security epoch, one AppInstallation-to-canonical-
  workload capability projection, one replica/Org/epoch lease, and one
  delegation-revocation tombstone set. The repository owns atomic activation,
  lease issuance, revocation, replica-context read, and one-statement admission
  snapshot.
- `_System_Session.AuthEpoch` is the existing session owner's monotonic
  delegation fence. New sessions start at epoch 1; Runtime validation returns
  the epoch from the same durable row as revocation/scope/Connected App facts;
  compare-and-swap advancement cannot revive a revoked or stale session.
- M1 does not enable Runtime serving, create an issuer/private key, approve a
  lease duration, or connect a route. Until M2 and P6-D, the production command
  remains fail closed.

**证明方法**

- `ActivateProjection(expectedEpoch, projection)` locks the current Org epoch,
  compare-and-swaps it, replaces the complete AppInstallation binding set, and
  deletes every prior replica lease in one transaction. A reader therefore
  sees either the old complete projection/lease or the new projection with no
  lease; it cannot see a new grant under an old lease.
- `RuntimeAudience` is owned once by the per-Org state row. It is not copied to
  every installation grant and is not authored by Runtime process config.
  Infrastructure `RuntimeReplicaID` selects a persisted lease, which resolves
  the authoritative Org/audience/epoch used before delegation verification.
- AppInstallation rows contain canonical workload IDs only. The operator-owned
  opaque transport handle remains behind the existing P6c resolver port. This
  prevents Catalog activation from becoming a second workload-credential
  authority.
- AppInstallation, epoch, lease, and active revocation are returned by one SQL
  statement after cryptographic caller binding. Capability JSON is decoded only
  after the exact Org/App/workload row is selected; malformed state fails the
  owner read rather than silently becoming an empty grant.
- Epoch storage uses `DECIMAL(20,0)` and explicit base-10 parsing, preserving the
  contract's complete `uint64` range instead of silently narrowing it to signed
  SQL `BIGINT`.
- Binding and revocation records keep the approved managed-table
  `VARCHAR(255)` Id owner even though their concrete SHA-256 values are 64
  characters. The current catalog string-Id owner count therefore moves from
  133 to 135; identifier limits and request vectors do not change.
- Unit tests cover transaction ordering, stale-writer fencing, initial-create
  races, exact `uint64` epoch parsing, replica context, lease publication,
  revocation extension, and session AuthEpoch compare-and-swap. A tagged TiDB
  test uses two authoritative Org projections and proves cross-Org selection,
  revocation, lease invalidation, and stale activation on the real SQL dialect.

**本轮错误与根因**

- P6c's original `Bindings(opaque caller) -> one Org/AppInstallation` seam was
  sufficient for a shape test but cannot represent one L1 workload serving
  multiple installations. Deriving Org/App from an unverified JWS is unsafe;
  enumerating all bindings and repeatedly verifying is unbounded. The owner-
  level fix landed in nexuscrm-contract #10: caller-bound verification first
  authenticates signature/time/audience/workload/capability, then Runtime uses
  the signed selectors for its authoritative installation lookup. The old
  fully-bound verifier and wire bytes remain unchanged.
- The first M1 sketch copied `RuntimeAudience` into every installation row.
  That created competing facts and ambiguous conflict behavior. Audience moved
  to the per-Org state row, selected through the replica lease.
- The next sketch stored api-host's opaque caller handle in the same row as the
  Catalog-authored AppInstallation grant. That combined operator identity and
  Catalog authorization in one authoring surface. The handle was removed from
  persistence entirely; the existing operator resolver owns it, while Runtime
  persistence stores only canonical workload grants.
- A tempting single JSON snapshot was rejected. It would make revocation and
  installation count an unbounded per-request blob and turn one hot Org row
  into both an authorization registry and an ever-growing tombstone set.
  Normalized tables remain one logical repository/read path without creating
  one physical-row bottleneck.

**失败条件**

- Reopen M1 if Runtime audience appears in process configuration or binding
  rows, if an opaque transport handle enters Catalog-authored persistence, if
  activation can publish bindings without invalidating old leases atomically,
  or if an epoch is stored in a signed-width type.
- Keep P6d incomplete if admission can install `TrustedCall` after caller-bound
  verification without the authoritative state lookup, if AuthEpoch is not
  compared, or if missing/expired/mismatched lease state reaches Records.
- Keep P5 blocked until M2 proves two real authoritative Org bindings,
  AppInstallation isolation, session/delegation revocation, old AuthEpoch,
  old SecurityEpoch, expired lease, authority partition, cancellation, and
  zero repository reads for every denial through the real process path.

**备选方案**

- If the operator workload authority later needs durable handle resolution, it
  may add its own provider-owned projection keyed by the opaque handle. It must
  not be folded into Catalog AppInstallation rows or inferred from delegation
  claims.
- If per-call security SQL becomes a measured bottleneck, M2/P6-D may publish
  an immutable epoch-fenced snapshot protected by the same unexpired lease.
  The database projection remains the sole authority; the snapshot cannot
  become a configurable registry or outlive `MaxSecurityStaleness`.
- If a lease duration cannot be approved, retain authoritative per-call reads
  and fail readiness on authority loss. Availability may be lower, but no
  implicit staleness window is allowed.

**最终 DoD**

- M1: contract #10 is released as root `v0.1.5`; the four protected tables and
  session AuthEpoch are schema-owned; atomic repository/session tests, tagged
  TiDB proof, affected module tests/vet, boundary guards, and `git diff --check`
  pass; no production command depends on a key, issuer, static grant, or L1.
- M2: NexusCRM pins `v0.1.5`, replaces the single-binding verifier call with
  caller-bound verification followed immediately by authoritative state and
  AuthEpoch validation, binds lease expiry into readiness, and passes the full
  Section 9 matrix through a separate Runtime process and real TiDB.
- P6-D then approves or rejects production delegation lifetime, verifier skew,
  key overlap, lease duration, and maximum security staleness. P6 and P5 remain
  blocked until that explicit decision and P7's atomic metadata/security view.

### 2026-07-12 — P6d-M2 Durable Admission and Real Process Matrix

**默认答案**

- P6d-M2 replaces only P6c's test authority seams. The selected-root RPC,
  Records authorization/projection path, wire bytes, and non-serving production
  command remain unchanged.
- Admission order is fixed: authenticated transport workload; caller-bound
  delegation verification; authoritative Org/AppInstallation/capability/
  SecurityEpoch/lease/revocation snapshot; durable session/Connected App/
  AuthEpoch validation; one immutable private `TrustedCall`; Records. No signed
  selector or caller-authored user fact skips an owner.
- Replica readiness is lease-backed. Expiry or a detected epoch/lease mismatch
  makes the process not-ready and produces `STALE_STATE`. An authority read
  failure fails the call as unavailable and invalidates readiness; cancellation
  by the caller stops the next owner without poisoning process readiness.
- This finishes P6d implementation evidence, not P6. P6-D still owns production
  delegation lifetime, clock skew, key overlap, lease duration, and maximum
  staleness; P7 still owns the atomic metadata/security serving view.

**证明方法**

- Runtime pins contract root `v0.1.5` and uses `VerifyCallerBound`; a boundary
  guard rejects the old fully-bound verifier call and requires the immediate
  durable state and AuthEpoch comparisons.
- Unit and race tests prove fail-closed state transitions, exact lease expiry,
  explicit refresh after renewal, cancellation at the security owner, and no
  cross-request `TrustedCall` sharing.
- The tagged TiDB repository gate uses the real persistence and AuthService
  owners. The separate-process gate starts an independent Runtime OS process,
  crosses real TCP/gRPC, and proves valid CRM plus CI-only non-CRM reads as well
  as workload, audience, Org, AppInstallation, capability, delegation, key,
  session, Connected App, AuthEpoch, SecurityEpoch, authority-partition, and
  lease denials. Every security denial records zero Records database queries.
- The cross-Org case publishes two distinct authoritative Org projections and
  binds the serving replica to one of them; it does not compare a token with a
  process constant or fake binding.
- The same child-process environment helper removes every Runtime-owned key
  before installing scenario values, preventing a parent environment value
  from becoming a hidden second authority.

**本轮错误与根因**

- The first P6c seam mapped one opaque caller directly to one Org and
  AppInstallation. That was not merely a missing map entry: one real workload
  can serve many installations, while the required selector is inside the
  signed token and cannot be trusted before verification. Contract `v0.1.5`
  therefore authenticates caller/audience/capability first and authorizes the
  returned signed selectors immediately afterward through durable state.
- Treating cancellation as an owner outage would let one client cancel make a
  healthy replica not-ready. Readiness invalidation now distinguishes an
  authority failure from the request context's cancellation/deadline. The
  distinction comes only from `ctx.Err()`: an owner that independently returns
  a context-shaped error while the request is live is still unavailable and
  invalidates readiness; it cannot impersonate a client cancellation.
- Checking lease readiness before delegation authentication made an absent or
  malformed token observe `STALE_STATE` on an expired replica, contradicting
  the approved disclosure matrix. Runtime now retains only the last
  authoritative replica identity context for crypto binding; it authenticates
  the token first, while the same expired/invalid state still prevents
  authorization and serving. A valid token receives stale, but absent or
  malformed credentials remain unauthenticated and no later owner runs.
- A replica that has never loaded authoritative Org/audience state is different
  from one whose known lease expired. It has no safe audience against which to
  authenticate a present token, so Runtime returns generic `UNAVAILABLE` (an
  absent token is still immediately unauthenticated). It must not fabricate a
  sentinel audience or call this `STALE_STATE` with zero revisions.
- A stale semantic failure without `StaleAxisSecurity` was mapped as a generic
  internal error over the wire. Security stale failures now carry required and
  served revisions plus the security axis, preserving the stable contract.
- The first separate-process scenario wiring added an environment variable but
  did not add it to the child helper's owned-key set. A compile/test probe found
  that a stale parent value survived beside the intended child assignment. The
  helper now has one complete ownership list and the regression test covers it.
- NexusCRM's Codex Review enables auto-merge with `GITHUB_TOKEN`; GitHub
  intentionally suppresses workflows caused by that token, so neither #3615
  nor #3616 produced the expected push-only `go-ci`. The existing workflow now
  accepts only a named `repository_dispatch` recovery event, which GitHub loads
  from the default branch and cannot point at branch-local workflow code. All
  jobs also require `refs/heads/main`. This is not a parallel CI framework:
  after M2 merges, the same main-only matrix is explicitly dispatched and its
  exact main SHA is recorded before P6d closes.
- The boundary script remains mode `100755`, but the post-merge gate previously
  depended on that filesystem bit by executing the path directly. CI now invokes
  it explicitly with `bash`, so archive/copy/mode handling cannot turn the
  required merge-safety check into `Permission denied`.

**失败条件**

- Reopen P6d if any production Runtime path accepts signed Org/AppInstallation
  selectors without the durable state lookup, installs `TrustedCall` before
  AuthEpoch comparison, reaches Records after a security denial, treats caller
  cancellation as a replica outage, or serves after its lease is known stale.
- Keep P6 and P5 blocked if P6-D has not approved numeric staleness/key policy,
  or if P7 cannot bind one security epoch and metadata view atomically.
- Keep the production command non-serving if its dependency closure would gain
  a test key, issuer, static grant, default Org authority, L1 package, or fixture.

**备选方案**

- If authoritative per-call security SQL becomes a measured bottleneck, publish
  an immutable epoch-fenced snapshot behind the same repository and lease. It
  must preserve one authority/read path and cannot outlive the P6-D-approved
  maximum staleness.
- If no safe lease duration can be approved, retain authoritative reads and
  fail readiness on authority loss. Lower availability is preferable to an
  implicit revocation window.

**最终 DoD**

- P6d is complete when M1 and M2 are merged, inherited main CI runs the real
  TiDB gates, all affected module tests/vet/race and boundary checks pass, the
  separate-process matrix proves zero Records queries on every denial, and the
  production command remains non-serving with no test/L1 authority dependency.
- The next task is P6-D, followed by the P7 atomic serving-view gate. Neither
  this implementation nor its tests authorize production traffic cutover.

### 2026-07-13 — P6d Review Closure: Stable Readiness Causes

**默认答案**

- P6d cannot close by treating every not-ready replica as generic stale. The
  state owner must retain both the last authoritative Org/audience identity and
  the reason readiness was lost: stale epoch/lease versus unavailable owner.

**证明方法**

- Refresh-to-missing preserves the last identity, becomes unavailable, keeps
  malformed delegation unauthenticated, and returns unavailable for a valid
  delegation without calling admission/session/Records.
- Refresh-to-invalid or differently bound state cannot replace the last good
  identity; it becomes stale, authenticates against the preserved audience,
  and returns stale for a valid delegation.
- After an admission-owner outage, a second request remains unavailable and
  does not call the failed owner again. Explicit valid refresh is the only path
  back to ready.

**失败条件**

- Reopen P6d if refresh clears or replaces trusted Org/audience with missing or
  invalid state, if an owner outage later degrades to stale, or if any not-ready
  follow-up reaches session authority or Records.

**备选方案**

- A future immutable serving snapshot may carry the same readiness cause and
  identity context, but it must remain epoch-fenced and cannot create another
  authority or an implicit staleness window.

**最终 DoD**

- Merge the focused state-machine follow-up, pass unit/race/boundary checks,
  then rerun the exact-main `go-ci-main` repository dispatch. Only that run may
  close P6d and hand the numeric staleness decision to P6-D.
- Closure evidence: #3618 merged as
  `e06339fa10d88ef0f832aef594fc365912e596a5`; exact-main
  [go-ci run 29225504045](https://github.com/pingcap-inc/NexusCRM/actions/runs/29225504045)
  completed successfully at that SHA. `backend-docker-image-smoke`, the full
  `build-test-seal`, and the dependent TiDB `l0-neutrality-gate` all passed.
  P6d is Done; this run does not approve P6-D policy or production serving.

### 2026-07-13 — P6-D Security Timing Policy

**默认答案**

- P6-D approves a 60-second production delegation lifetime, 5-second verifier
  skew, 65-second planned old-key overlap, immediate retirement for a
  compromised key, 30-second SecurityLease with a 10-second renewal target,
  and zero intentional `MaxSecurityStaleness` for new admissions. A separate
  2-second value is the existing in-flight Runtime attempt-context cap, not a
  client response-delivery SLO.
- The 10-minute lifetime and 30-second skew in nexuscrm-contract remain hard
  protocol ceilings, not production defaults. The one-minute compatibility
  vector is evidence that the chosen lifetime is representable, not the owner
  of production policy.
- An attempt admitted after a security change commits still performs the
  current P6d authoritative security read, so its intentional stale-cache
  allowance is zero. A call admitted before commit keeps one immutable view and
  the existing 2-second Runtime attempt context. The first cutover explicitly
  rejects using an unexpired lease as permission to serve a cached stale
  authorization view.
- P6-D decides policy; it does not enable an issuer, key provider, lease
  controller, AppInstallation, serving Runtime command, or route. P7 owns
  authority-clock enforcement and atomic security/metadata publication; P5
  owns the production drills.

**证明方法**

- The machine-readable `p6-d.v1` golden pins NexusCRM P6d merge
  `e06339fa10d88ef0f832aef594fc365912e596a5`, nexuscrm-contract root `v0.1.7`
  at `917886fc6920e97f8ed93615629865a53cde8407`, api-host `v0.3.0` at
  `2990914cd9396dfccd3b661c71512133ece7c843`, and exact-main CI run
  `29225504045`.
- Contract `v0.1.5..v0.1.7` changes no file under
  `pkg/runtime/delegationv1`, `pkg/runtime/recordsv1`, or `wire-go`. The
  executable P6-D algebra and golden are re-ratified against `v0.1.7`, and a
  regression test now requires the approved contract tag to equal the direct
  backend SSoT pin.
- Executable algebra proves `60s <= 10m`, `5s <= 30s`, planned overlap equals
  `lifetime + skew`, the revocation tombstone tail equals configured skew, the
  lease contains three renewal intervals, `MaxSecurityStaleness` is zero, and
  the separately named in-flight window equals the already-approved 2-second
  selected-root attempt cap.
- The approved lease vector runs against the current fail-closed state machine:
  it is ready immediately before the exclusive 30-second expiry, not-ready at
  expiry, can be restored only by explicit authoritative renewal, and becomes
  unavailable immediately when that refresh reports an owner partition.
- P6d's current path performs one authoritative security-state snapshot per
  attempt before session and Records. Activation atomically invalidates old
  leases; owner failure becomes unavailable; a known lease/epoch mismatch is
  stale. No current code path serves from a cached permission view.
- Planned rotation is two-phase: prepublish and acknowledge the new public key,
  switch signing/current-key ownership, retain the old verification key for 65
  seconds, then retire it. Emergency compromise bypasses overlap. Delegation
  tombstones remain through authenticated `ExpiresAt + 5s`.

**本轮错误与根因**

- Treating SecurityLease duration as `MaxSecurityStaleness` would silently
  weaken the current path. The lease is a readiness/epoch fence; every request
  still reads the authority. An immutable in-flight request retains its attempt
  context, but that is not cached admission staleness.
- Treating the 2-second handler attempt cap as a client-visible revocation SLO
  would repeat the lifecycle-ownership error found during api-host #5: Runtime
  admission cannot observe protobuf marshal, network send, or client receipt.
  P6-D therefore names it only as the in-flight authorization window and leaves
  route/transport delivery guarantees with their actual owners.
- Writing only “30-second lease” would leave the clock owner undefined. The
  current persistence seam accepts an absolute `ExpiresAt`, while Runtime
  compares it with its local clock. P7 must derive expiry from authority/database
  time and publish a conservative remaining lifetime; replica time may only
  shorten it, and delegation verifier skew must never extend it.
- A single key-overlap number is unsafe without distinguishing planned rotation
  from compromise. Planned rotation preserves already-issued 60-second tokens
  for their final 5-second verifier allowance; a suspected compromised key must
  not remain accepted merely for availability.

**失败条件**

- Reopen P6-D if a production issuer emits a delegation over 60 seconds, a
  verifier accepts more than 5 seconds of skew, a planned old key retires before
  65 seconds or remains accepted beyond the configured transition, or a
  compromised key receives overlap.
- Block P5 if a lease can exceed 30 seconds, renew from replica-authored wall
  time, use verifier skew, admit a new request without the authoritative
  security read, or describe the 2-second attempt context as a response-delivery
  SLO.
- P6-D completion does not close P7. Production readiness remains blocked until
  one atomic serving view binds the accepted SecurityEpoch and metadata
  receipts with no second read path.

**备选方案**

- If the per-call authoritative security read becomes a measured bottleneck,
  retain it for the first cutover. A later immutable cached snapshot requires a
  separate measured ADR, the same single authority, explicit revocation
  semantics, and a staleness bound no weaker than the approved policy.
- If authority-clock lease issuance or two-phase key publication cannot be
  implemented cleanly in P7, keep `runtime-api` non-serving. Do not compensate
  with a longer lease, wider skew, static key, or deployment-authored grant.

**最终 DoD**

- P6-D is complete when the ADR and machine-readable golden approve or reject
  every timing value, executable algebra guards the protocol/attempt
  relationships, exact P6d merge-SHA CI is green, and #3555 records the finite
  P7/P5 enforcement gates. P7 may proceed in parallel, but P5 remains blocked
  until those gates execute against the production assembly.

### 2026-07-13 — A1a Current App Fact and Consumer Map

**默认答案**

- Do not freeze an App contract from names that already contain “app”. The
  current tree has several distinct authorities: pack source and install state,
  binary ProductHooks, Runtime authorization grants, frontend owner registries,
  and mutable `_System_App` navigation metadata. A1b must consume the real C0
  capability and map these facts without promoting any one by analogy.
- `_System_App` remains UI/navigation metadata. `PackInstallId` remains a pack
  incarnation. Neither is `AppID` or `AppInstallationID`.
- The existing Runtime security projection is structurally usable, but no
  production Catalog/AppInstallation publisher currently activates it. This is
  a missing upstream authority, not a reason to add another Runtime grant
  registry.

**证明方法**

- Audited exact revisions: NexusCRM
  `41061972fa7796e1d763f9e1737bb500fd39f3b8`, controlplane
  `dbd2fbbbc920a9863772865f7e3ef8b6c4e51788`, app-packs
  `a84ba803073c90e279af21f35eeeadd12962cac4`, and contract
  `3784e01cb99ae97cf24bbc192aaa11db15282419`.
- Distinguished repository main from the actual NexusCRM module pins:
  controlplane `v0.1.18` peels to
  `394bd0b79b3b55fe31196c78d87e9f45e31f2e21`, while contract `v0.1.5`
  peels to the audited contract main. All cited controlplane paths are
  byte-identical between the consumed release and audited current main.
- Derived the complete author/owner/persistence/writer/reader/lifecycle/
  classification/duplicate/migration map in
  [`app-fact-consumer-inventory.md`](app-fact-consumer-inventory.md) and its
  machine-readable JSON companion.
- Followed install/reapply, remove/failure, shallow/deep verify, persisted-only
  startup rebuild, three-state pack gates, distribution composition, Runtime
  security admission, Flow handler resolution, frontend source supply, and
  application navigation from their current writers to serving readers.
- Searched all non-test Go call sites of `ActivateProjection` and `IssueLease`.
  Only tests invoke them at this revision; the production tree supplies the
  Runtime persistence/admission substrate but not an upstream publication
  owner.
- Added a stdlib-only boundarygate test that strictly decodes the inventory,
  requires exact repository commits and all critical fact families, rejects
  unknown fields/duplicate IDs/unknown classifications, and validates every
  evidence coordinate.

**本轮错误与根因**

- “Capability” was previously discussed as if it named one fact. It currently
  covers at least pack-required host primitives, connector declarations,
  binary ProductHooks, and Runtime RPC grants. Their authors and authorization
  directions differ; merging them early would create a second source of truth.
- Target `AppPackage`, `AppManifest`, `AppRelease`, and authoritative
  `AppInstallation` are design concepts, not current implemented facts. The
  current release locks and installed-pack hashes are evidence inputs, not an
  already-approved AppRelease.
- Flow action handlers already show a useful descriptor/implementation split,
  but code labels the exact metadata-to-executor mirror transitional. Runtime
  route surfaces remain compiled callbacks. Neither justifies inventing a
  general HandlerBinding before C0.
- Frontend source revision, backend source revision, per-pack accepted revision,
  frontend owner ID, and `_System_App.Id` overlap in release/navigation flows
  without being the same identity. The correct next step is explicit mapping,
  not consolidation by name.

**失败条件**

- Reopen A1a if A1b must rediscover a current writer/reader, if a current
  production AppInstallation publisher was missed, or if any fact in the JSON
  lacks an exact audited-revision evidence coordinate.
- Block A1b if C0 is not complete, if PackManifest retirement is assumed rather
  than decided, or if `_System_App`/`PackInstallId` is reused as product App
  authority.
- Reject any follow-up that enables a second serving read path or creates a new
  App table/API/protobuf before the ownership flip and deletion prerequisite
  are explicit.

**备选方案**

- If C0 needs only an existing pack-owned Flow action capability, reuse the
  descriptor/implementation ownership split and map only the minimum App facts.
- If C0 selects a compiled route surface, first make the ownership binding
  explicit at the current composition seam; do not generalize all callbacks.
- If the target AppInstallation publisher cannot be completed in the first
  slice, keep Runtime non-serving. Static/test grants and deployment-authored
  defaults are not substitutes.

**最终 DoD**

- A1a is complete only after the inventory PR merges at an exact reviewed head,
  the boundarygate inventory test and repository checks pass, and #3555 is then
  updated from A1a In Review to Done. Before merge, the issue must remain
  unchanged.
- A1b receives one finite handoff: C0 capability, current fact map, missing
  AppInstallation authority, release/frontend mapping decisions, and explicit
  deletion prerequisites. It should not need another repository-wide consumer
  audit to make the first App-domain decision.
### 2026-07-13 — C0 First Real CRM Consumer Decision

**默认答案**

- No production CRM consumer is behaviorally equivalent to
  `ReadSelectedRoot`. The only sfcrm selected-root client remains explicitly
  dormant and owns no route.
- Select `POST /services/data/:version/ui-api/record-access` as the first real
  CRM ownership slice. It is a current production route with current frontend
  consumers, but it requires a different coarse neutral Runtime operation:
  bounded optional type resolution plus Read/Update/Delete/Locked evaluation
  for the submitted IDs.
- Do not modify selected-root and do not remote the legacy
  `RecordAccessReader` methods independently. One accepted non-empty HTTP
  request must become one L1-to-Runtime use-case RPC, including the
  object-unspecified multi-type branch; empty and rejected input performs zero.

**证明方法**

- Fixed the audit to NexusCRM
  `41061972fa7796e1d763f9e1737bb500fd39f3b8` and nexuscrm-app-packs
  `a84ba803073c90e279af21f35eeeadd12962cac4`.
- Re-derived consumers from the CRM route manifest, CRM ProductHooks/runtime
  route surfaces, generic-host Records calls mounted in the CRM process, all
  production sfcrm record-read seam calls, and app-frontend API calls. The
  serious candidates were SObject-by-ID, SObject-by-external-ID, record-ui,
  record-access, clone defaults, folder read, and dashboard definition read;
  query/list/composite and host-internal reads were classified explicitly
  rather than silently omitted.
- Verified that the record-access route/handler/frontend files are byte-equal
  between NexusCRM's production app-packs source pin `2f4a2ff36d...` and
  app-packs current main.
- Traced the selected path through object Read, schema resolution,
  `RetrieveManyRaw`, missing/RLS omission, permission-input projection,
  Read/Edit/Delete evaluation, and approval-lock computation. It returns no
  record value, metadata payload, lookup, relationship, file decoration, or
  activity side effect.
- Recorded the complete evidence, candidate classification, selected-root
  comparison, RPC requirement, and fifteen parity/conformance categories in
  `docs/refactor/serviceification/c0-first-real-crm-consumer.md`.
- The adversarial review exposed a source-of-truth ordering failure: C0 changed
  the selected production slice while the normative target still named
  selected-root. The same review unit now updates the target architecture,
  selected-root ADR status, Technical Plan, and Decision Ledger, and adds a
  fifteenth capability-specific non-CRM conformance gate.

**本轮错误与根因**

- The prior plan treated the selected-root portion of the generic SObject GET
  as if it were already a production consumer. The actual route contract also
  owns compound synthesis, lookup hydration, relationship nesting, CRM
  decoration, attributes, and activity. A reusable internal prefix is not an
  independently movable product capability.
- Choosing the primary `record-ui` route merely because it is the normal detail
  page would freeze too much at once: record values, access, object info,
  layouts, lookup display records, decoration, and activity. The smallest real
  ownership slice is the independently consumed access decision.
- Mirroring `ObjectTypesForIDs` followed by one `BatchRecordAccess` call per
  type would create `1 + T` network RPCs. The coarse operation must absorb that
  current in-process fan-out inside Runtime instead of preserving interface
  topology across the wire.
- Treating D0-N and C0 as independently mergeable was wrong even though their
  evidence work could run in parallel. Once C0 disproved the normative
  selected-root production-consumer assumption, its decision could not merge
  before or without the owning target-architecture update.
- CRM legacy/new-owner parity proves behavior preservation but not L0 App
  neutrality. The selected-root issue-tracker fixture proves only its own
  capability. The distinct record-access seam needs its own non-CRM CI consumer
  before V1, without promoting that fixture into a product App.

**失败条件**

- Reopen C0 if current-tree evidence shows the selected route has no production
  consumer, if it requires record values or Salesforce grammar inside Runtime,
  or if exact parity cannot preserve typed-object 403 versus untyped forbidden
  group omission.
- Block V1 if the implementation uses selected-root to infer edit/delete/lock,
  adds those facts to the frozen selected-root response, performs per-ID or
  per-type L1-to-Runtime RPCs, exposes raw policy input fields, or enables a
  second metadata/security read path.
- Keep route cutover blocked until P7c/S1 binds one TrustedCall and one immutable
  RuntimeMetadataView for the complete batch.

**备选方案**

- If the record-access parity or performance gate disproves the proposed
  boundary, keep that route on the legacy owner and choose the next real
  candidate from the evidence inventory. Do not fall back to the dormant
  selected-root adapter without a newly proven exact consumer.
- The SObject and record-ui routes remain later candidates for separately
  approved coarse record-view operations; dashboard and folder reads remain
  capability-specific operations. None should be generalized into graph or
  interface-mirroring contracts as a shortcut.

**最终 DoD**

- C0 is complete only after the English evidence PR merges and #3555 is updated
  at the merge SHA. The merged decision must name the exact route and frontend
  consumers, prove why selected-root is not equivalent, identify the one
  required coarse operation, retain executable golden/parity requirements, and
  leave the target architecture, selected-root ADR, Technical Plan, and
  Decision Ledger with one first-production-slice sequence.
- C0 itself adds no protobuf, generated code, production route, selected-root
  behavior, or issue-tracker product claim. A1b may then freeze only the App
  capability and HandlerBinding needed by this selected slice.
### 2026-07-13 — P7a Independent Runtime Metadata Closure

**Run ID:** `p7a-runtime-metadata-closure-20260713`

**默认答案**

- Inventory only the candidate independent Runtime composition rooted at
  `runtimeapi.AssembleProduction` and its selected-root operation. Do not turn
  P7 into a whole-monolith metadata migration.
- C0 has now selected UI API record-access as a different first production
  capability. It is not present in the current independent composition. Its
  object-unspecified branch resolves types through the Records repository, not
  through key-prefix metadata, so it does not create a new Catalog fact by
  implication.
- P7b must build a complete non-serving view of effective schemas,
  relationships, sharing rules, deterministic indexes, and receipts. P7c/S1
  must bind that view after `TrustedCall`, switch the five reachable consumer
  groups, and make the ten inventoried fallback/legacy edges unreachable in the
  same ownership flip.
- Per-user grants, sessions, roles, groups, shares, Runtime security bindings,
  revocations, and epochs remain security-authoritative. They do not enter
  ordinary CatalogSnapshot, but P7c/S1 must prove compatibility with the
  admitted SecurityEpoch.

**证明方法**

- Evidence was derived from NexusCRM main
  `41061972fa7796e1d763f9e1737bb500fd39f3b8`, starting at the executable command,
  `AssembleProduction`, `NewSelectedRootReadRuntime`, and
  `RecordsServer.ReadSelectedRoot`, then following CRUD/FLS, RLS, query routing,
  storage hydration, security admission, receipt, startup, refresh, and cache
  paths.
- The PR was rebased onto main
  `92702c5cec8fdb751994dfd1023403120806bdf6`. The inventoried production Go
  paths are byte-identical; the intervening C0/D0/A1a documentation changes are
  preserved and their new record-access decision is represented separately in
  the manifest scope.
- The machine-readable P7a manifest classifies 32 observed reads, 15
  process-global states/accessors, and 9 refresh/invalidation paths as required
  view data, security authority, view-derived, legacy fallback, or outside the
  independent closure. Every entry carries a current source anchor.
- The boundary guard parses the Go AST for the production assembler and
  selected-root composition root. Added or removed constructor calls require an
  explicit inventory update. It also proves the shipped command remains on the
  non-serving bootstrap.
- The trace found two Catalog-generation hazards: `MetadataService.GetSchema`
  can fall through to a raw repository schema after a cache miss, and sharing
  rules sit behind a separate five-minute process cache plus repository read.
  It also found one independent receipt read and a separate 60-second role
  hierarchy cache on the SecurityEpoch side.
- The current metadata graph owns no `CatalogRevision` or `SchemaGeneration`;
  `BootstrapReceipts` truthfully leaves both at zero. P7b must consume explicit
  revision/digest lineage from the Catalog publication owner and must not
  manufacture an authority from timestamps, refresh counts, or database maxima.
- `RefreshCache` overfetches flows, validation rules, autonumbers, and identifier
  rules even though selected-root does not consume them. Flow failure can block
  read-only startup; the other ancillary failures can log and continue. P7c
  removes this broad startup dependency from the independent read closure.

**本轮错误与根因**

- Treating all calls on `MetadataService` as one already-atomic snapshot would
  be false. The service has `cache` and `coreCache`, publishes core early, and
  each helper independently asks for current state. Permission helpers can
  therefore observe a different generation than the root schema carried by
  Records.
- Treating all permission inputs as Catalog metadata would merge owners and
  weaken revocation. Sharing-rule definitions are Catalog facts; effective
  grants, role/group/share membership, and durable identity are security facts.
  The serving barrier validates the two axes together without collapsing them.
- Treating package import reachability as behavior reachability would overstate
  the closure. List-view RLS, validation execution, most FormulaViewService
  methods, and ancillary MetadataService caches are constructed or available
  but not called by selected-root. The inventory records them separately instead
  of requiring a monolith-wide move.
- The actual `runtime-api` command still does not select
  `AssembleProduction`. P7a inventories the candidate graph being cut over; it
  does not claim that production traffic already reaches it.
- Folder-scoped RLS has no `FolderAccessService` in the candidate graph and
  therefore fails closed. P7a records this current fact rather than silently
  adding a dependency; A1b/V1 must prove record-access parity and keep that
  security fact outside ordinary Catalog metadata.
- During conflict resolution, the object-unspecified record-access branch was
  initially mistaken for key-prefix metadata demand. Current code disproved the
  inference: `ObjectTypesForIDs` reaches
  `RecordRepository.FindObjectTypesForIDs`. Keeping key-prefix indexes outside
  the current closure prevents an L3 Records lookup from being reassigned to
  Catalog by name or convenience.

**失败条件**

- Reopen P7a if a new independent Runtime constructor, metadata provider,
  decorator, cache, registry, receipt source, repository fallback, or current
  accessor becomes reachable without an inventory entry.
- Block P7b if it can publish a partial/lazy per-object view, omits all-schema
  target-record closure or parent/sharing-rule facts, includes secrets or
  per-user grants, or becomes selectable by a production consumer.
- Block P7c/S1 if a request can read an unbound global current pointer, fall
  through to metadata repository, use the five-minute sharing-rule cache, read
  receipts independently, mix CatalogRevision, or use security facts not proven
  compatible with the admitted SecurityEpoch.
- Block V1 if record-access is added without its object-type resolution,
  folder/security authority where applicable, approval-lock semantics, or any
  other capability-specific dependency proved by C0/A1b.

**备选方案**

- C0 selected a different first capability. Keep this inventory as the exact
  current selected-root closure and add only record-access metadata facts proved
  by the A1b-frozen contract before P7b parity. Keep its record-type resolution
  in Records and do not widen P7a retrospectively to every monolith consumer.
- If security-authoritative live reads cannot be made SecurityEpoch-consistent
  through the existing owner, use an immutable request-scoped security
  projection or owner-controlled database snapshot. Do not copy those facts into
  CatalogSnapshot or create a second authority.
- If a mechanically separate P7c series is required, every predecessor remains
  non-serving; only the final ownership-flip unit may bind consumers and delete
  fallbacks.

**最终 DoD**

- P7a is complete after its English inventory, machine-readable manifest,
  composition/source guard, learned record, and validations merge.
- Only after merge may #3555 mark P7a Done. P7b then has a complete build/parity
  baseline for the current closure, and P7c/S1 has a finite current-closure
  consumer-switch and fallback-deletion list. A1b may add only proven
  capability-specific metadata facts. No P7a artifact enables serving or
  changes public interfaces.

**验证结果**

- `(cd backend && go test ./...)`: PASS, including the existing CI-owned
  `backend/internal/boundary` package.
- `(cd backend && go vet ./...)`: PASS.
- `GOWORK=off go run . -root <repo>` in `tools/boundarygate`: PASS with zero
  forbidden edges, zero contract runtime leaks, and zero new dialect-token
  hits.
- `bash scripts/ci/check-runtime-process-boundary.sh`: PASS.
- Manifest JSON parsing, the focused closure guard, and `git diff --check`:
  PASS.

### 2026-07-13 — App Authority Scope Correction

**Run ID:** `app-authority-scope-correction-20260713`

**Default answer**

- The required refactor is the missing App-domain authority, not a repository-
  wide replacement of every `PackManifest`. Production serving needs one
  Catalog-owned `AppID`, immutable `AppRelease`, `AppInstallation`, capability,
  and `HandlerBinding` model before P7c/S1 or V1 can cut over.
- `PackManifest` remains a valid generic metadata-pack grammar for platform,
  tenant, connector, and other non-App pack categories. It must not become the
  product App identity or a second release/install authority.
- The current CRM `AppPack` path is migrated directly to one canonical
  `AppPackage` containing one App contract and one content-addressed metadata
  bundle. The target does not permanently nest the full legacy manifest or
  retain a serving-time compatibility adapter.
- Existing CRM installations require a one-time, owner-controlled migration
  into `AppRelease` and `AppInstallation` state. After equivalence and rollback
  proof, the legacy CRM AppPack identity, source-checkout install authority,
  startup projections, and fallback reads are deleted.
- Exact service image and frontend asset digests remain independently selected
  deployment/binding facts. `AppRelease` owns the immutable L1 metadata/App
  contract; it does not force a service-only or frontend-only change to create
  a new metadata release. L2 metadata remains Catalog-owned revisioned state.

**Evidence and reasoning**

- A1a proved that current `PackManifest` is an authoritative pack-source fact,
  `_System_InstalledPack.PackInstallId` is a pack incarnation, and neither is an
  approved `AppID`, `AppRelease`, or `AppInstallationID`.
- Runtime already persists and enforces an AppInstallation security projection,
  but A1a found no production Catalog authority/publisher for that projection.
  A contract-only A1b followed immediately by serving P7c would therefore skip
  the writer and publication owner required by the trusted-call model.
- C0 selected UI API record-access as the first real CRM capability. Its route
  cutover needs one App capability and handler owner but does not justify
  rewriting unrelated platform, tenant, or connector pack lifecycles.
- The release plan in #3624 requires L0 services, optional L1 services, L1
  metadata, frontend assets, and L2 metadata to upgrade independently. Putting
  exact service/frontend digests inside `AppRelease` would recreate the
  monolithic release boundary.

**Plan correction**

- A1b freezes the minimum App contract and the CRM AppPack transition. It also
  assigns every overlapping name/version/dependency/compatibility fact to one
  owner so `AppManifest` and metadata-bundle inputs cannot disagree.
- Add A1c as a non-serving implementation phase. It establishes the sole
  Catalog AppRelease/AppInstallation/HandlerBinding authority, its write path,
  the Runtime security projection publisher, and the one-time CRM migration.
- After A1b, A1c, P7b, and #3624 R1b can proceed in parallel from the same
  frozen contract. P7b remains non-serving; R1b owns artifact publication and
  BOM mapping, not App-domain reinterpretation or Catalog serving authority.
- P7c/S1 waits for A1c, P7b, and R1b. It then binds one TrustedCall and one
  immutable RuntimeMetadataView, switches the complete independent Runtime
  closure, and deletes all serving fallbacks atomically. V1 remains after that
  ownership flip.

**Failure conditions**

- Block A1b if it promotes `_System_App`, pack name, or `PackInstallId` to App
  identity; duplicates App version/dependencies in two authoritative manifests;
  or pins exact service/frontend deployment digests into the metadata release.
- Block A1c if it adds a second install registry, lets deployment or Runtime
  author AppInstallation state, keeps a permanent legacy adapter, or publishes
  projections before the authority transaction commits.
- Block P7c/S1 if it serves from migrated App state before immutable artifact
  provenance, Catalog authority, projection publication, and fallback deletion
  are proven together.
- Do not make P7b, A1c, or V1 wait for a global rewrite of non-App pack
  categories. Reopen their scope only when current-tree consumers prove a real
  shared authority conflict.

**Definition of done**

- #3555 records C0, A1a, and P7a as Done; A1b as the next App-contract decision;
  A1c as the missing non-serving Catalog authority implementation; and P7c/S1
  as dependent on A1c, P7b, and the #3624 R1b artifact proof.
- #3624 states that generic `PackManifest` is not globally replaced, the CRM
  AppPack path migrates directly with a one-time state conversion, and service,
  frontend, App metadata, and L2 metadata remain independent release planes.
- The eventual ownership flip leaves one App authority, one Catalog publication
  path, one Runtime projection/read path, and no permanent legacy CRM AppPack
  compatibility branch.

### 2026-07-13 — A1b Minimum App Authority Contract

**Run ID:** `a1b-app-authority-contract-20260713`

**Default answer**

- Freeze one publisher-authored canonical `AppPackage = AppManifest +
  MetadataBundle`, one content-addressed `AppRelease`, and one Catalog-owned
  AppInstallation/HandlerBinding write path. Do not implement or serve it in
  A1b.
- The first concrete identity is `io.nexuscrm.sfcrm`, published under
  `io.nexuscrm`. The C0 public capability is
  `io.nexuscrm.sfcrm.ui.record-access.v1`; it requires a distinct neutral
  `runtime.records.evaluate-access.v1` capability. Naming the neutral capability
  does not approve its semantic or wire contract.
- Keep AppRelease, ServiceRelease, FrontendRelease, and L2 CatalogRevision as
  independent release planes. AppManifest authors compatibility requirements;
  Catalog/deployment bindings select exact executable and frontend digests.
- Convert only the CRM AppPack path. Generic non-App PackManifest categories
  remain unchanged and issue-tracker remains a CI-only fixture.

**Evidence and root finding**

- C0 proved that record-access is a real bounded CRM route and is not
  selected-root-equivalent. A1a proved that no current target App authority
  exists and that pack name, PackInstallId, `_System_App`, frontend owner ID,
  Runtime security projection, and delegation claims are different facts.
  P7a bounded the new Runtime metadata closure but did not replace Catalog App
  authority.
- The existing target sketch put `AppRelease (digest + semantic version)`
  inside AppManifest, repeated `OwnerAppID` per component, and named a
  `FrontendManifestRef`. Those shapes allowed a circular self-digest and
  duplicate owner/release planes. The A1b ADR replaces them with a closed
  digest boundary: semver is covered data, the release digest is derived
  outside the manifest, the containing package supplies component ownership,
  and exact frontend/service digests live only in bindings.
- A first-slice-only deletion gate was also too strong. V1 moves record-access,
  but many CRM capabilities still consume the current pack/monolith metadata
  path. Deleting the complete legacy CRM source/install projection at V1 would
  break unmigrated routes; keeping it writable would create a second App
  authority. The finite transition therefore seals the converted legacy hash,
  forbids new writes/readers and App metadata upgrades during coexistence, and
  deletes the path only after every legacy reader has a disposition. Each
  capability still has exactly one selectable serving read path.

**Decisions**

- Canonical named IDs are lowercase dot-segment ASCII, at most 128 bytes, and
  reject rather than normalize noncanonical inputs. AppID is immutable;
  publisher transfer is an audited, epoch-fenced registry operation.
- AppManifest alone owns AppID, publisher, semver, App dependencies, namespaces,
  protocol/engine/frontend compatibility, and provided capabilities.
  MetadataBundle alone owns component identity, component graph, policies, and
  content hashes. It cannot repeat App-level facts or `OwnerAppID`.
- RFC 8785 canonical JSON, component-kind canonical bytes, domain-separated
  SHA-256 component/bundle/release digests, and a detached attestation make
  AppRelease cover every behavior-affecting App and L1 metadata byte without a
  self-digest. Informational metadata is detached and cannot affect behavior.
- The ADR and machine manifest freeze the closed snake_case JSON shapes,
  conditional capability/frontend variants, component identity derivation,
  policy enums, digest domains, and every set sort key. A1c, P7b, and R1b must
  consume these bytes directly rather than inventing parallel DTOs or field
  normalization.
- Catalog mints UUIDv7 AppInstallation/HandlerBinding IDs and owns lifecycle,
  dependency resolution, workload/capability grants, namespace projection,
  active releases, and bindings. Runtime remains the owner of its fenced
  serving projection and lease/readiness behavior, not the upstream App fact.
- HandlerBinding selects canonical workload identity and exact ServiceRelease
  digest, never an endpoint. Deployment owns endpoint resolution; Gateway owns
  RouteEpoch; neither becomes App authority.
- A1c, P7b, and R1b receive disjoint inputs/outputs from the same contract and
  can implement in parallel. P7c/S1 waits for all three.

**Rejected alternatives**

- Reuse `_System_App`, pack name, frontend owner ID, or PackInstallId as AppID.
- Make semver, Git SHA, source revision, or service/frontend digest the
  AppRelease identity.
- Let MetadataBundle/PackManifest repeat App dependencies, namespace,
  compatibility, or version facts.
- Globally replace PackManifest or permanently wrap the full legacy manifest.
- Put endpoint URLs in App metadata or treat Runtime/delegation/BOM as
  AppInstallation authority.
- Delete the entire legacy CRM projection after only the record-access slice,
  or keep it writable as an unbounded compatibility plane.

**Failure conditions**

- Block A1c if it creates another install registry, publishes Runtime security
  projection before the Catalog authority commit, loses the last active release
  on failure, or permits migration conflict overwrite.
- Block P7b if AppPackage and PackManifest become selectable metadata
  authorities, or if the new snapshot can serve before full parity.
- Block R1b if it reinterprets fields, introduces another package digest, or
  embeds exact service/frontend digests in AppRelease.
- Block P7c/S1 if A1c, P7b, or R1b evidence is missing, if one request can mix
  Catalog/security revisions, or if a legacy fallback remains selectable.
- Block CRM App metadata upgrades while a sealed legacy projection is still
  required. Reopen this decision only if current-tree evidence supplies a
  finite owner-correct projection strategy without a second authority.

**Next steps**

1. After merge, mark A1b Done in #3555.
2. Start A1c and P7b independently from the checked machine manifest.
3. Start #3624 R1b only when R1a is also Done.
4. Keep P7c/S1 and V1 blocked; do not start the record-access protobuf or route
   cutover in A1b.

## 2026-07-13 — #3624 R1a current release and delivery inventory

**Scope and evidence**

- Audited all seven repository main revisions recorded in
  `release-artifact-delivery-inventory-r1a.json`. The audit used current
  workflows, module manifests, Dockerfiles, build scripts, pack manifests,
  lineage, deployment configuration, release-tag payloads and production deploy
  code; issues and older design docs were not treated as release authority.
- This was a read-only release-model investigation. It did not publish an
  artifact, dispatch a tenant deployment, change App/Pack semantics, change
  metadata authority, or modify any runtime/build/deploy path.
- The public inventory records L2 input names and ownership, not tenant-specific
  configuration values or traffic/data payloads.

**Current release model facts**

- The seven repository boundaries are not seven independently deployed
  artifacts. Contract/root wire, controlplane, api-host and connectors publish
  tagged Go source modules. NexusCRM's selected source commit implicitly closes
  over those versions through its `go.mod` files.
- NexusCRM defines independent Runtime API/worker image targets, version labels,
  config schemas and generic manifests. Current NexusCRM CI builds and smokes
  runner-local images only; no audited workflow pushes them or records a
  registry digest. The current L2 production path does not consume them.
- There is no current L1 AppService command, image, deployment manifest or
  release identity. App-packs currently supplies L1 metadata, dialect Go source
  and frontend source manifests by Git commit/per-pack manifest.
- The current production release candidate is an L2 staging release tag. Its
  payload fixes downstream, NexusCRM and app-packs commits plus final-pack
  evidence. Production then rebuilds the final pack/frontend on the target and
  uses a generated `go run` backend runner rather than promoting a prebuilt
  backend/frontend artifact.
- `release/release-tuple.json` is reproducibility/gate evidence, not the sole
  deployment authority. Release-class deployments normally derive companion
  refs from committed final-pack lineage and do not verify the tuple hash.
- The effective database migration release is implicit: selected setup and
  controlplane code, metadata packs, optional patch input and existing database
  stamps. No single current artifact/digest names that complete closure.

**Root gaps**

- Source selection is mostly immutable, but output bytes are not: production
  rebuilds backend/frontend at the destination and records neither binary nor
  frontend content digests.
- L0 module pins, independent Runtime service versions, frontend identity,
  configuration/schema identity and resulting DB state do not coexist in one
  deployment BOM.
- The release-tuple/staging hash excludes `.build-lineage.json`, while PR-UAT
  hashes every final-pack file with a different path grammar. The same tree can
  therefore acquire different claimed hashes.
- PR-UAT pushes and deploys a mutable image tag even though its helper already
  has an `image_digest` field.
- Current production rollback can reselect retained code/frontend release
  directories, but it does not reverse shared database schema/data/metadata.
  It is not restoration of a complete prior tuple.
- The audited repos publish no release SBOM, SLSA/cosign attestation, or complete
  producer provenance. Consumer `go.sum` and Git/final-pack hashes are useful
  integrity evidence but do not fill that role.

**Decisions and handoff**

- R1a remains descriptive. Its JSON is an omission guard, not a build/deploy
  registry or new release authority.
- R1b owns one BOM identity, derivation rules, compatibility policy, one content
  hash grammar and complete prior-tuple semantics. It consumes the A1b App
  contract and must not reinterpret AppRelease/AppInstallation.
- R2 owns module/image/frontend/metadata publication, digests, SBOMs and
  attestations. R3 owns deployment consumption, final deployment records,
  artifact promotion and rollback/database gates.
- After the inventory PR merges, update #3624 to R1a Done and R1b Ready. Do not
  update the issue before merge.

**Validation**

- JSON parsing, closed inventory decoding, seven-repository/output coverage and
  complete artifact/deployment-input fields: PASS.
- All 65 machine evidence anchors pin the exact audited repository revision;
  all 35 cross-repository Markdown file/line links exist at that commit and are
  in range: PASS.
- Local Markdown links and code-fence balance: PASS.
- Backend boundary tests, targeted R1a race test and boundary-package vet: PASS.
- Boundarygate tests and executable scan: PASS, zero forbidden edges and zero
  new dialect-token hits.
- The audited L2 release tuple's remote refs and committed final-pack hash:
  PASS under its own validator.
- `git diff --cached --check`: PASS. Changed-path inspection contains only the
  inventory, learned/index docs and the inventory boundary test; no production
  runtime or release-pipeline file changes.
- Raw `scripts/scan-public-core-boundary.sh` remains non-zero on current-main
  baselined production/test-fixture hits. This diff adds no scanned production
  source or tenant payload; the baseline result is recorded rather than called
  a pass.

**Validation**

- `jq empty docs/design/app-authority-contract.json`: PASS.
- `(cd backend && GOWORK=off go test ./internal/boundary -count=1)`: PASS.
- `(cd backend && GOWORK=off go test -race ./internal/boundary -count=1)`:
  PASS.
- `(cd tools/boundarygate && GOWORK=off go test ./... -count=1)`: PASS.
- Exact evidence-revision source anchors, local Markdown links, balanced code
  fences, closed JSON decoding, identifier negative cases, owner uniqueness,
  release-plane independence, transition finiteness, and stale normative marker
  checks: PASS.
- `git diff origin/main...HEAD --check`: PASS. The diff changes only design/
  evidence docs and a backend boundary test; no production command, route,
  schema, migration, image, deployment manifest, protobuf/generated code, or
  serving implementation is changed.
- `go run ./tools/boundarygate`: **BASE BLOCKED**, not accepted in A1b. A clean
  detached `origin/main` at `483e285b70bd85270103b073cd875128e648fb45`
  fails identically on the unbaselined `SObject` token introduced by merged
  #3627 in `nexuscrm-automation/executor_notifications.go`. A1b changes neither
  that file nor the baseline; updating the baseline or modifying production
  automation here would violate this PR's no-production-change scope.
- `scripts/scan-public-core-boundary.sh` reports only existing current-tree
  hits in shared code, test fixtures, and OAuth. A1b adds no tenant payload or
  scanned production source.

## 2026-07-13 — A1c pre-serving neutrality correction

**Evidence and root cause**

- The first executable `nexuscrm-contract` implementation exposed that A1b's
  component fields `tenant_customizable` and `upgrade_policy` values containing
  `l1`/`l2` made the stdlib-only L0 dependency root name deployment layers.
- The underlying fact is neutral: a verified component declares whether a
  later replacement discards or preserves already-authorized downstream
  overrides. Repository layer names are neither needed to express that fact
  nor owned by the shared contract.
- No released package, Catalog row, Runtime projection, serving route, or
  production migration consumes the v1 bytes yet. Correcting the frozen shape
  before the first implementation release avoids carrying a compatibility
  alias or a permanent layer-specific field into L0.

**Decision**

- Replace the two fields with one closed `override_policy` whose values are
  `replace` and `preserve-authorized-overrides`.
- Rename the removal guard to `block-if-referenced-or-overridden` so the same
  neutral fact is used consistently.
- This is a normative pre-serving correction, not an alternate DTO. A1c's
  executable contract and immutable conformance vectors must implement only
  this corrected shape; there is no legacy adapter because no consumer exists.

**Failure condition and next step**

- Block A1c if any L0 implementation retains the old layer-specific names or
  accepts both shapes. Merge this normative correction before releasing the
  shared contract, then continue Catalog implementation from the corrected
  immutable vector.

## 2026-07-13 — P7b non-serving Catalog snapshot and Runtime view

**Run ID:** `p7b-catalog-snapshot-20260713`

**Scope and current-tree evidence**

- Re-derived the independently deployed Runtime closure at NexusCRM
  `ecb7a11b4d854ca32f284e561bec9866df317887`. The executable `runtime-api`
  still selects `AssembleNonServingBootstrap`; `runtime-worker` is still a
  no-work skeleton. The production-candidate selected-root graph reaches five
  metadata consumers that P7c/S1 must switch together: request receipts,
  Records root schema, Permissions CRUD/FLS/RLS metadata, Analytics RLS, and
  Persistence/formula read routing. This is intentionally not an inventory of
  every legacy-monolith metadata reader.
- P7b consumes the frozen A1b handoff only as opaque effective input:
  Catalog-authored AppInstallation scope, capability scope, Catalog revision,
  and compatibility lineage. It adds no AppRelease, AppInstallation, or
  HandlerBinding writer, lifecycle transition, migration, PackManifest reader,
  or active serving selector. A1c remains the only future authority owner.

**Root decisions**

- Added `github.com/nexuscrm/metadata/catalogsnapshot` as an app-blind compiler,
  exact artifact verifier, parity oracle, and immutable directory store. A
  snapshot carries distinct CatalogRevision, SchemaGeneration,
  EngineGeneration, and per-definition DefinitionVersion values. Every source
  item must name the same non-zero CatalogRevision; publication fails closed on
  a missing or mixed revision.
- Canonical artifact bytes are deterministic closed JSON and the content
  address is SHA-256 over those exact bytes. Reads reject digest, kind, schema,
  canonical-encoding, lineage, and source-snapshot mismatches. There is no
  compatible N-1 schema yet, so every unknown schema version is rejected
  explicitly rather than partially decoded.
- The complete Catalog artifact preserves effective source metadata and its
  physical-name sidecars. The consumer view is a separate least-privilege
  schema scoped by exact `(OrgID, AppInstallationID, capability)` and derived
  from that one snapshot. It contains only the read-shape, type/formula/read
  routing, CRUD/FLS/RLS and sharing dependencies of the current independent
  selected-root closure. Mutation hooks/policies/defaults, UI/navigation,
  Integration and secret payloads, records and identity/grant state are absent.
- The store exposes only `Put(exact artifact)` and
  `Get(exact kind, exact digest)`. Same-directory non-replacing hard-link
  publication makes concurrent identical writers converge. It deliberately
  has no current/latest/list/watch/TTL/activate operation, so P7b cannot become
  a freshness or serving authority.

**Errors found while implementing**

- The first Runtime projection copied almost all field presentation/write
  attributes and merely removed `RecordMutation`. That passed parity tests but
  was not least privilege. The artifact schema now structurally omits labels,
  metadata row IDs, create/update flags, create defaults, writable-reference
  targets, child-list names and all non-read policy arms unless an explicit
  read expression inherits one. A test scans canonical view bytes for these
  forbidden keys.
- A later scope audit found the same failure class in relationships and sharing
  rules: the draft reused full Catalog records and projected every ordinary
  lookup. That leaked row identities/presentation/mutation attributes and could
  force an unrelated lookup target into the AppInstallation scope. Runtime now
  receives only sharing-parent edges (including relationship type because it
  changes parent-controlled read semantics) and sharing-rule policy plus its
  required role/group references. Catalog-only identity/name changes remain in
  the source snapshot and source-digest binding but cannot enter view content.
- Exact-head adversarial review then found a different hidden expansion:
  `targetRecordRead` does not name its target in metadata, so PermissionService
  scans all schemas and filters them with request-time security state. Direct
  policy-reference validation could not prove that closure. The compiler now
  requires an explicit full same-revision object set whenever any projected
  object uses `targetRecordRead`; it reports all omissions and never silently
  widens scope. A current-shape regression proves incomplete publication fails
  and the complete view retains parity. This keeps the dynamic policy
  dependency in Catalog metadata without copying user grants into the view.
- Exact-head standard review found that closed-schema validation alone did not
  prove an opened Runtime view was the complete projection of its named source:
  a validly re-digested artifact could omit a field or a parent-policy edge.
  Runtime-view opening now revalidates sharing-parent and direct policy closure,
  while `VerifySource` regenerates the exact scoped projection and compares its
  digest. Hostile canonical artifacts prove both structural and semantic
  omissions fail closed.
- The first parity comparison depended on caller field order while the compiler
  correctly canonicalized fields. The oracle now builds the same canonical
  consumer record before comparison; reordering objects, fields,
  relationships, rules and projection sets produces byte-identical artifacts.
- The first store draft changed the staging file to read-only before writing.
  Although an already-open descriptor can still write on supported systems,
  that ordering made the invariant obscure. It now writes and syncs content
  first, then applies the published read-only mode before atomic publication.
- The first store durability proof synced only the artifact-kind directory
  after a new hard link. It omitted fsync of newly created root/kind directory
  entries, and an identical concurrent publisher could return before either
  caller had established its own directory barrier. The store now durably
  creates the full directory chain and every successful `Put`, including the
  `EEXIST` convergence path, syncs the publication directory before returning.
  Even an already-present directory is re-synced with its parent, so a caller
  cannot race ahead of another process that created the directory but had not
  yet persisted its parent entry.
- A deeper formula-path trace initially suggested storing one effective read
  table per object. That model was rejected: QueryRepository chooses base vs
  formula view from this request's selected fields and RLS, and
  `FormulaViewService.ResolveTableName` derives its answer from the already
  carried schema (its cache affects logging, not the target). The artifact
  therefore keeps the validated base read source and complete formula-routing
  facts; P7c must derive each request target from that one bound schema, not
  invent a second process-global metadata lookup or freeze a false per-object
  target.
- Descriptive parity alone did not prove which A1b and selected-root evidence
  remains live. The machine manifest and boundary test now anchor the A1b P7b
  handoff, the tagged issue-tracker CRUD/RLS/FLS golden, and the real-repository
  Runtime integration test.

**Evidence and failure conditions**

- Determinism tests cover input reordering, semantic digest changes and deep
  ownership of compiler input. Revision tests cover missing and mixed object,
  relationship, rule and projection revisions. Projection tests cover
  cross-scope exclusion, required parent closure, direct read-policy
  dependencies and `targetRecordRead`'s full-schema closure.
- Artifact/store tests cover corrupt bytes, non-canonical encoding, wrong kind,
  unknown schema, source mismatch, validly re-digested incomplete projections,
  missing sharing-parent/policy dependencies, nested durable store creation,
  exact not-found, canceled reads, idempotent writes, 16 concurrent writers and
  corrupt-at-rest reads. Current-source parity
  captures post-decorator MetadataService objects, effective relationships and
  sharing rules through a `_test.go` adapter and compares every projected
  behavior fact. Relationship identity/presentation/mutation attributes and
  sharing-rule identity/name are structurally excluded; required role/group
  references remain policy inputs while entities, memberships, effective
  grants and generated shares stay on the independent security authority.
- A backend boundary guard rechecks every inventoried source anchor, runs
  `go list -deps` on both Runtime commands, includes all future
  `catalogsnapshot/...` subpackages, forbids serving/repository dependencies,
  and scans commands, assembly, images and manifests for a snapshot selector.
  P7b fails if production composition imports or names the artifact, if the
  package adds an activation/freshness channel, or if the current behavior
  oracle disappears.

**Exact P7c/S1 follow-up**

P7c/S1, after A1c and release-publication prerequisites, must install
ServingAdmission after TrustedCall; validate one exact AppInstallation,
SecurityEpoch, CatalogRevision, SchemaGeneration, EngineGeneration and digest;
regenerate and verify its exact source projection; bind that immutable view plus
receipts to the request; atomically switch all five listed consumers; then
delete or make unreachable every global/repository fallback. Missing
scope/revision/digest or an inexact projection must fail before record
repository access. P7b does not complete that ownership flip, mixed-revision
serving proof, V1 route cutover, or any production traffic migration.

**Validation**

- `(cd nexuscrm-metadata && GOWORK=off go test ./... -count=1)`: PASS.
- `(cd nexuscrm-metadata && GOWORK=off go vet ./...)`: PASS.
- `(cd nexuscrm-metadata && GOWORK=off go test -race ./catalogsnapshot -count=1)`:
  PASS.
- `(cd backend && GOWORK=off go test ./... -count=1)`: PASS.
- `(cd backend && GOWORK=off go vet ./...)`: PASS.
- `(cd backend && GOWORK=off go test -race ./internal/boundary -count=1)`:
  PASS.
- `scripts/ci/test-issue-tracker-runtime-conformance.sh`: PASS against the
  reviewed fixture revision `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`.
- `scripts/ci/check-runtime-process-boundary.sh`: PASS.
- `(cd tools/boundarygate && GOWORK=off go run . -root ../..)`: PASS with zero
  forbidden edges, zero contract runtime leaks, and zero new dialect tokens.
- `scripts/scan-public-core-boundary.sh`: existing scan exits non-zero on
  current-tree hits in shared Salesforce flow code, testdata packs, and the
  OAuth handler. None is a P7b-changed path; P7b adds no tenant payload or new
  hit and does not modify the baseline to hide them.
- `jq empty docs/refactor/serviceification/runtime-metadata-snapshot-p7b.json`
  and `git diff --check`: PASS.
### 2026-07-13 — P1x sealed issue-tracker fixture cleanup

**Run ID:** `p1x-sealed-issue-tracker-cleanup-20260713`

**Decision**

- The issue-tracker is not an App candidate. Its only retained role is the
  sealed CI consumer under `nexuscrm-app-packs/tests/conformance/issue-tracker`
  and the byte-identical NexusCRM snapshot used to execute the consumer against
  the real `ContractService -> PersistenceService.Retrieve` path.
- Delete every artifact that can be installed, built, published, routed,
  deployed, or rendered as an issue-tracker product. Do not replace those
  surfaces with a generic compatibility abstraction.
- Keep the semantic fixture provenance at
  `ec2ed3b2f0501d749f7a2b2eac1a86bbeba506b8`. P1x changes repository shape,
  not the reviewed point-read vectors, so the snapshot revision and checksum do
  not move merely because the app-packs repository checkout advances.

**Current-tree inventory and disposition**

- Retained fixture: app-packs `tests/conformance/issue-tracker/`; NexusCRM
  `tests/conformance/issue-tracker-runtime/`, the build-tagged records adapter
  conformance, capacity evidence, and the P4 cross-process test. These are
  tests only and create no command, route, manifest, image, AppService,
  AppInstallation, or production module requirement.
- Obsolete pack/frontend: app-packs `issue-tracker-standard/`, including its
  installable pack manifest and frontend source package, plus the repository
  frontend source-index entry and release-shaped contract test.
- Obsolete dialect: app-packs `dialects/dialect-issue-tracker/` and the NexusCRM
  distribution-module require/replace and CI build target.
- Obsolete distributions: NexusCRM `backend/distributions/issuetracker/`,
  `backend/distributions/coexistence/`, `cmd/issue-tracker-server`, the combined
  CRM/issue command and setup build tags, positive first-party product
  registration, and Docker source copy. The distribution appsource bridge
  retains only a negative identity tombstone so an already-persisted active row
  is rejected rather than silently reclassified as a companion pack.
- Obsolete delivery/install input: the L2 `issuetracker` and combined build-tag
  selections and generated distribution helper, and the controlplane
  issue-tracker pack-oracle corpus.
- Obsolete frontend proof: the NexusCRM external-App runtime script, issue
  source-component/import locks, and CI invocation. CRM remains the only
  production frontend source selected by NexusCRM.
- Obsolete documentation: live repository maps and runnable validation lists
  that still described an issue-tracker dialect/product. Historical evidence
  may name the retired shape only when it is explicitly non-normative.
- Accidental production dependency: the issue dialect in the production
  distribution module and Docker closure. Both are physically removed; no
  production dependency retains the fixture module.

**Dependency-ordered removal**

- `nexuscrm-controlplane#29` merged as
  `bc3c1406d1324da46bfdab073f6dd2e9929ae859`; it removed the obsolete pack
  oracle.
- `nexuscrm-pingcap-pack#3897` merged as
  `847e850e4f0529239ff5f7646f53721f1712ac6b`; it removed issue/combined
  deployment selections.
- `nexuscrm-contract#23`, `nexuscrm-api-host#12`, and
  `nexuscrm-connectors#136` aligned their repository maps so no L0 guide claims
  that a release dialect still exists.
- `nexuscrm-app-packs#386` supplies the producer deletion at
  `3c699bf9ffc787f23dd0364112e779344d615830`. NexusCRM main previously pinned immutable older
  app-packs commits, so producer deletion cannot break a branch-following
  consumer. The NexusCRM P1x change advances both checkout pins to this
  main-lineage commit while deleting every old consumer in the same change.

**Guards and evidence**

- The app-packs fixture boundary rejects any issue-tracker-named path outside
  the sealed fixture, any issue-tracker token in production/release controls,
  any production fixture dependency, and any command/manifest/image entrypoint.
  Its `go list -deps -test ./...` check includes test dependencies and future
  fixture subpackages.
- The NexusCRM cross-repository gate verifies the reviewed checksum, compares
  the four semantic files byte-for-byte, runs all app-packs fixture Go tests,
  checks ordinary test closures and production command closures, and rejects
  the retired pack/dialect/distribution/image paths. It also proves the sole
  retired-name tombstone cannot grow into a dialect or fixture dependency and
  that no other production Go source contains issue-tracker vocabulary.
- `scripts/ci/test-issue-tracker-runtime-conformance.sh` passes through the real
  records adapter with typed scalar, null/absent, FLS omission, missing/RLS
  masking, forbidden, cancellation, and deterministic ordering outcomes.
- `tests/integration/l0-completeness/crm-source-backed-gate.sh` preserves the
  former matrix gate's still-valid CRM install/idempotence/source-loss/drift and
  controlplane runtime-recovery proofs. Only the manufactured second-product
  matrix is removed; production CRM recovery coverage remains required by the
  `source-backed-runtime-gate` CI job.
- Boundarygate reports 0 forbidden edges, 0 contract runtime leaks, and 0 new
  dialect-token files. Existing public-core scan hits are unrelated current-
  main baseline facts; P1x adds none.

**Review correction**

- The first review exposed one failure class behind two symptoms: deletion had
  removed evidence together with the obsolete product shape. The active-pack
  identity was still needed as negative evidence for safe startup, and the old
  two-pack runner still contained independent CRM recovery evidence.
- The correction keeps facts at their observing owners. The L1 distribution
  coverage list owns the retired identity tombstone and fails before CRM
  validation; it does not keep any payload executable. The CRM-only TiDB gate
  owns source-backed install/recovery evidence and no longer claims multi-App
  neutrality.
- This does not restore compatibility paths. A stale active row must be
  explicitly deactivated; there is no handler, pack source, frontend, dialect,
  command, installer choice, or migration adapter capable of serving it.

**Exact-main ownership correction**

- Merge-SHA run `29258596389` exposed that `FirstPartyProductPackNames` had two
  incompatible consumers: Runtime composition needs current plus retired names
  for fail-closed coverage, while frontend alignment needs only current product
  names. Putting the tombstone directly in the positive list made CI demand a
  frontend manifest for a product that P1x had correctly deleted.
- Split the L1 facts instead of weakening frontend alignment:
  `FirstPartyProductPackNames` remains the positive current-product inventory;
  `RetiredFirstPartyProductPackNames` owns negative tombstones; CRM
  `KnownProductAppPacks` composes their union. The generic Runtime contract and
  frontend parser are unchanged.
- Fail if a retired tombstone enters any positive product/release inventory, or
  if Runtime composition omits the tombstone and reclassifies a stale active
  row as a companion pack.

**Exact-main dependency correction**

- Merge-SHA run `29259715493` passed frontend alignment, then failed the
  external-module SSoT check because `backend/distributions` selected
  `nexuscrm-contract v0.1.7` while the owning `backend/go.mod` pin remained
  `v0.1.5`.
- The selected version is not optional: app-packs commit
  `3c699bf9ffc787f23dd0364112e779344d615830` contains the current CRM dialect,
  whose published module contract requires `v0.1.7`. Lowering only the direct
  distribution requirement would not change Go MVS selection and would hide
  the real dependency.
- Advance the backend SSoT to the already-published contract `v0.1.7` and keep
  `backend/distributions` aligned. This is a version-ownership correction; it
  adds no interface, wire type, Runtime behavior, or fixture dependency.
- Re-ratify the P6-D oracle and ADR at exact contract commit
  `917886fc6920e97f8ed93615629865a53cde8407`. The reviewed `v0.1.5..v0.1.7`
  diff changes approval/controlplane surfaces but no Runtime delegation,
  selected-root, or wire package; the executable timing algebra remains the
  same. The oracle now fails whenever its approved tag differs from the backend
  owner pin.
- Fail if any consumer selects a contract version different from the backend
  SSoT, or if a source-pin advance changes the selected module graph without an
  explicit owner-pin update.
- Do not rewrite R1a's immutable `c9736c2d...` release-inventory JSON as though
  that older revision consumed `v0.1.7`. Its Markdown companion now labels the
  post-audit delta explicitly; the live pin fact remains owned only by
  `backend/go.mod` and R1b must read that owner.

**Failure conditions**

- Fail if any issue-tracker pack, dialect, frontend, command, build tag,
  distribution, install choice, deployment target, image input, or App-domain
  lifecycle fact returns.
- Fail if fixture semantics drift without a reviewed snapshot/revision update,
  if a production or ordinary-test closure imports the fixture, or if the real
  Runtime conformance stops executing.
- A future real non-CRM App requires N1 product evidence and a separate App
  decision. It must not promote or wrap this fixture.

### 2026-07-13 — A1c non-serving Catalog App authority closure

**Run ID:** `a1c-catalog-app-authority-20260713`

**Released evidence tuple**

- `nexuscrm-contract#22` merged as
  `77290832e809df5cafa6696e48dfc54b83c564f7` and was released as `v0.1.8`.
  It owns the neutral App-authority manifest, package, component, dependency,
  capability, grant, binding and Runtime security-projection value contract.
- `NexusCRM#3644` merged as
  `9762c00227ed9a8acbbb6528560a98be44dc2cae`. It supplies the narrow Runtime
  security-projection persistence port that Catalog may call; the port does not
  issue a serving lease or author an AppInstallation.
- `NexusCRM#3645` merged as
  `ecb7a11b4d854ca32f284e561bec9866df317887`. It removed the last
  layer-specific package-override terminology before the authority was built.
- `nexuscrm-controlplane#31` merged as
  `a269af03582be1ef3a2e28dc80f77e7675c35378` to restore the released contract
  pin before the feature PR. `nexuscrm-controlplane#32` then merged as
  `e6cf83d3a19786b45567f240e17407dd343531f4` and was released as `v0.1.21`.
- `nexuscrm-pingcap-pack#3899` merged as
  `b4d1af196250969cdeef7c4f9e0e02c5c86cd039`. It owns the test2-only workflow;
  NexusCRM and controlplane do not own tenant deployment credentials or
  environment selection.

**Authority and lifecycle decision**

- `nexuscrm-controlplane/catalogapp` is the sole new durable authority for
  publishers and keys, App identity and transfer audit, immutable App releases,
  dependencies, namespaces, capabilities and components, per-org
  AppInstallations, operation journals, handler/frontend bindings, removal
  receipts and the finite legacy-conversion journal.
- Catalog stores active and candidate release, namespace, workload, capability,
  dependency and security-projection facts separately. A lifecycle operation
  first commits its candidate and expected projection digest; Runtime publishes
  one complete newer projection; Catalog records the exact acknowledgement;
  only then may completion flip the active tuple. A failed operation retains
  its fence and must be explicitly resumed or replaced.
- ServiceRelease and FrontendRelease are independent binding planes. They can
  change for the active AppRelease without fabricating a Catalog revision or an
  AppRelease transition. The AppInstallation row serializes standalone binding
  candidates with an App-release lifecycle operation.
- Frontend artifact content is not an identity. `FrontendBindingID` is immutable
  audit identity, while nullable active, standalone-staging and operation keys
  select the one current owner or attempt. Failure and activation release the
  attempt key, allowing an exact artifact rollback to create a new immutable
  audit row rather than overwrite history.
- Handler ownership epochs are fencing tokens and are never reused. A failed
  staged handler burns its epoch; an exact retry observes the failed transition;
  a replacement advances the epoch so a stale service owner cannot regain
  authority.
- The conversion journal owns only a sealed legacy-incarnation to App-operation
  mapping. The referenced App operation is the sole lifecycle `State` and
  `Step` owner. Each operation also records `DIRECT` or `LEGACY_CONVERSION` as
  its initiation kind, so an otherwise identical org/App/idempotency tuple
  cannot let one entry point capture the other.
- Conversion accepts caller-supplied, already-verified legacy facts and a signed
  mapping receipt. Catalog neither reads nor mutates `_System_InstalledPack`.
  The old authority remains serving until R1b and P7c/S1 complete the explicit
  ownership flip and deletion ledger.

**Failure-class corrections during implementation**

- The first frontend model used permanent artifact content as a natural key.
  That confused immutable audit identity with current ownership: failure could
  wedge the row, and an exact rollback could not create a new attempt. Splitting
  immutable binding identity from nullable attempt/active selectors fixed the
  whole retry, failure and rollback class; TiDB constraints now enforce the
  legal combinations.
- The first conversion journal duplicated operation lifecycle state. That made
  two sources of truth possible. The journal now stores only immutable mapping
  identity and joins the App operation for current state and step.
- A plain lifecycle request and legacy conversion could initially reuse the
  same durable operation through the unique idempotency tuple. The durable
  initiation kind closes this authority-confusion path; real TiDB coverage
  proves that a direct PREPARE followed by conversion conflicts without
  publishing a conversion mapping or binding.
- Handler/frontend activation originally admitted a lock-order inversion against
  operation staging. Every path now locks the AppInstallation before the
  binding row, so the installation remains the serialization owner.
- Early test2 runs repeated the full DDL setup for every scenario and exceeded
  the environment's useful test window. The harness now creates one
  marker-owned scratch database/schema per run and isolates scenarios with
  unique org/App identities. This changes test setup only, not authority
  behavior; the `always()` cleanup removes only that run's marker-owned
  databases.

**Reproducible validation**

- Exact-head standard and adversarial Codex Review for controlplane PR #32
  completed in run `29262545529`: both approved with zero blockers and zero
  suggestions.
- The final pre-merge head passed the complete real-TiDB test2 workflow in run
  `29262568543`.
- The merged controlplane revision passed the exact-main workflow in run
  [`29264622412`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29264622412).
  The workflow checked out
  `e6cf83d3a19786b45567f240e17407dd343531f4`, required that revision to be
  contained in `main`, used contract `v0.1.8`, and passed
  `TestCatalogAppTiDB` plus lifecycle recovery/removal, identity/namespace/grant
  conflicts, concurrent CAS/conversion rollback and frontend-only binding CAS.
- Local controlplane validation passed package tests, integration compilation,
  race, vet, every affected non-pack module, builds and `git diff --check`.
  The app-packs pack modules also passed against the updated released pin.
- A fresh external Go module, with no local path or workspace replacement,
  successfully ran
  `go get github.com/pingcap-inc/nexuscrm-controlplane/catalogapp@v0.1.21` and
  `go test github.com/pingcap-inc/nexuscrm-controlplane/catalogapp -count=1`.
  This proves the published module graph is consumable rather than only working
  inside the source workspace.
- NexusCRM backend boundaries and boundarygate remain green with zero forbidden
  edges, zero contract runtime leaks and zero new dialect-token files. The
  public-core scanner still reports unrelated current-main baseline/test-fixture
  hits; A1c adds no tenant payload or new hit and does not widen a baseline.

**Non-serving proof and next gate**

- No NexusCRM or controlplane production command, startup path, route, image,
  manifest, Gateway handler, Runtime metadata read or request-admission path
  imports or selects `catalogapp`. `EnsureSchema` and lifecycle execution remain
  explicit caller operations. A1c therefore proves a durable neutral authority,
  not a production App installation or serving cutover.
- P7c/S1 remains blocked until A1c, P7b and #3624 R1b evidence are all present.
  R1b must publish the immutable CRM App artifact with signed provenance and an
  exact L2 BOM binding. The legacy-installation conversion receipt belongs to
  the later cutover, not to release identity.
  P7c/S1 must then bind one exact trusted call and immutable metadata view,
  validate the Catalog/Runtime receipts, switch all consumers atomically and
  delete the repository/global fallback. Only that phase may claim one serving
  read path or mixed-revision exclusion.

## 2026-07-13 — V1a-M record-access metadata closure

**Evidence reopened**

- The inventory was derived from NexusCRM `main` at
  `964150890e86fccbea8268621bb76ea5551fb90f`, not from the tracking issue or
  the P7b selected-root model. The real current path is
  `ContractService.BatchRecordAccess -> PersistenceService.RetrieveManyRaw ->
  PermissionService.BatchCheckRecordAccess`, with an optional
  `ResolveObjectTypes` scan when the object is not supplied.
- P7b's selected-root projection is insufficient for this capability. Current
  outcomes also read RequiredWritePermission, derived Edit/Delete policy,
  ControlledByParent edges, sharing rules, LockRule and the metadata binding
  that disables generic Delete.
- Approval instances, record shares/team rows, role/group membership,
  effective permission grants (including values projected into SystemPermission
  virtual fields) and the record payload are not Catalog facts.
  They remain with the Runtime database, SecurityEpoch, or TrustedCall owner.

**Root design decision**

- A profile-less union view would make every consumer receive the largest
  metadata surface and would make future deletion/parity gates ambiguous.
  Catalog projection now requires an internal profile. `selected-root-read/v1`
  preserves the P7b subset; `record-access/v1` adds only this capability's
  immutable closure. The profile is compiler configuration, not a Task B API,
  protobuf, RPC or L1 semantic type.
- The profile and new closed fields require artifact v2. There is no implicit
  v1 default because P7b is still non-serving; accepting an unprofiled artifact
  would turn missing ownership into a silent policy choice.
- Object-unspecified record access currently probes every live object. The
  record-access profile therefore requires the complete same-revision
  object/read-table mapping. The mapping is Catalog-owned; the actual record
  probes remain dynamic Runtime DB work.
- Current `FindMany` hydrates a full raw row before RLS and access evaluation.
  Sharing criteria and formula text do not yet share one compiler-owned
  normalized dependency IR. V1a-M keeps the complete runtime field shape and
  records formula projection pushdown as a later internal optimization instead
  of claiming an incomplete dependency set is exact.

**Closed engineering proof**

- The compiler fails closed on missing/unknown profile, incomplete object
  closure, mixed revision, ambiguous/unsupported policy expressions, missing
  field/object references, inherit cycles, invalid LockRule and duplicate
  generic-delete blockers. Canonical encoding, source digest, artifact digest,
  schema generation and engine generation remain independently checked.
- The generic delete-blocker capability string now has one Go owner under
  shared constants; permissions, records registration and the projection
  compiler consume it.
- A parity fixture runs the same PermissionService over legacy effective
  metadata and one bound RuntimeMetadataView. Derived policy, LockRule,
  approval lock, sharing rule/group state, ControlledByParent and generic
  delete blocking produce identical Read/Edit/Delete/Locked results. The bound
  provider has no MetadataService or metadata repository fallback.
- The executable JSON inventory fixes every required fact to exactly one of
  CatalogRevision, SecurityEpoch, Runtime database or TrustedCall. Boundary
  tests anchor every claim to current code and reject Catalog facts marked
  excluded or dynamic facts marked projected.
- The same executable inventory fixes the later ownership flip to seven current
  edges: adapter schema, RetrieveManyRaw shape, permission-grant projection
  shape, RecordRepository schema fallback, object-type candidate enumeration,
  PermissionService metadata provider and its sharing-rule TTL cache. This
  prevents P7c/S1 + V1b from declaring completion while one hidden shape read
  or process cache remains.
- Production and command-test dependency closures still exclude
  `metadata/catalogsnapshot` and future subpackages. Runtime commands,
  assembly, process and manifests do not select the v2 schemas or
  `record-access/v1` profile.

**Next ownership flip**

- This phase does not create a second serving metadata path. P7c/S1 + V1b must
  bind one exact view after TrustedCall admission, switch the real record-access
  assembly, delete MetadataService/repository/global shape fallbacks (including
  live object enumeration), and prove stale/missing/mixed receipts fail closed.

## 2026-07-13 — V1a-C Neutral Record-Access Contract

**Current-tree evidence and decision**

- Re-derived the real route at NexusCRM
  `964150890e86fccbea8268621bb76ea5551fb90f`, then revalidated its complete
  serving call path at current main
  `4364749f93847e9fe63d8ef67de7bce6301da5d2`, and nexuscrm-app-packs
  `03435cd941f25e983c90dddd836dcbd1ae1fccea`. The production CRM frontend calls
  `POST /ui-api/record-access`; this is a real product consumer, not an
  architecture-test invention.
- The explicit-object path performs the L1 object-Read gate, schema lookup, one
  raw batch repository read, RLS/sharing omission, object/row CRUD evaluation,
  approval-lock probe, metadata LockRule evaluation and generic delete
  suppression. The object-unspecified path additionally resolves record types
  and currently fans out once per type inside L1.
- Current observable limits are zero to 200 submitted IDs and the generic 25
  MiB HTTP buffering cap. The latter is not a business limit. Current code has
  no record-access-specific numeric deadline, semantic byte, concurrency or
  response limit.
- Defined `runtime.records.evaluate-access.v1` as one coarse neutral operation.
  Optional object resolution is Runtime-owned; results expose only
  Read/Update/Delete/Locked and one receipt set. Missing, RLS-denied, unresolved
  and forbidden inferred groups are indistinguishable omissions. Operational
  failures are all-or-nothing.
- The semantic contract accepts an empty batch as a no-op with receipts, so the
  one coarse operation can represent the complete input domain. RPC count is a
  separate L1 composition fact: accepted non-empty requests make exactly one
  call, while the current empty success and rejected input make zero.
- Reused the approved 64-byte physical object-name and 255-byte outer record-ID
  envelopes without copying selected-root's 2-second attempt limit. A native
  deadline is required, but a record-access numeric cap remains unapproved.
  The 200-entry and identifier bounds already constrain encoded request and
  response sizes below the existing 128 KiB receive and 2 MiB send caps, so no
  separate semantic byte limit was invented.

**Failure-class corrections during implementation**

- The first semantic draft rejected an empty batch even though the current
  public route accepts it. Making empty a valid Runtime no-op closes the
  semantic input-domain gap without requiring the production route to invoke
  Runtime for a request that reads no fact.
- The first Nexus evidence draft then incorrectly promoted semantic validity
  into a composition requirement and changed the app-authority guard from zero
  to one RPC for empty input. That would have made a current local success fail
  on Runtime unavailability. The owning invariant is now precise: exactly one
  RPC for accepted non-empty input, zero for empty/rejected input, and no
  per-record or per-type L1 fan-out. Contract capability and route composition
  are related but are not the same owner.
- An early shared-facts extraction accidentally suggested that selected-root's
  2-second attempt cap applied to every Runtime capability. The cap remains in
  `recordsv1`; only protocol, receipts, safe failures and physical identifier
  ceilings moved to `commonv1`.
- The initial record-access status decoder required typed details for every
  outcome except cancellation and deadline. Real gRPC connection failures and
  message-size rejection occur before a handler can attach details, so
  `UNAVAILABLE` became `INTERNAL_FAILURE` and could not follow the reviewed
  retry rule. Record-access now admits only the finite undetailed transport set
  that is unambiguous and non-disclosing; stale/incompatible and missing remain
  fail-closed without capability-bound detail.
- A top-level `MISSING` initially remained representable because the shared
  error enum supports selected-root. Record-access validation and wire mapping
  now reject or downgrade it to internal: missing and denied records exist only
  as identical result omissions.
- The first response validator checked ordering, uniqueness and readable-only
  projection without accepting the original request. That allowed a buggy or
  compromised server to return a readable ID outside the submitted batch,
  including for an empty request. Response validation now requires the request
  and rejects every uncorrelated result before L1 projection.
- Request validation deliberately preserves the current missing-entry behavior
  for empty and whitespace-only identifiers. The first correlated-response
  check excluded only the empty string, so a whitespace-only submitted value
  could still reappear as a readable result and disclose policy state. Response
  validation now rejects every identifier whose trimmed form is empty, while
  the request continues to accept and omit those entries. Shared receipt
  validation also rejects an empty expected capability, closing the same
  fail-open class at the capability boundary.
- The first wire status refactor bound receipts to the capability but still
  decoded every shared invalid-component and limit-kind enum. That would have
  let a selected-root `SELECTED_ROOT_FIELD` or `SELECTED_FIELDS` detail cross
  the record-access boundary despite a matching top-level category. The
  record-access mapper now owns a closed detail grammar: only its object,
  record, deadline and response components; exact object/record/count maxima;
  deployment-owned admission; its two stale axes; and an exact capability-bound
  compatibility range. Undetailed transport exhaustion remains representable
  because gRPC can reject a message before attaching typed detail.
- The wire README still copied the nested module's old root version even after
  `wire-go/go.mod` moved to the reviewed semantic release. Updating that copied
  number would only postpone the next drift. The README now points to `go.mod`
  as the sole live pin owner; release evidence records immutable historical
  tags separately.

**Release and proof**

- The semantic root and wire are separate Go release boundaries. Publishing a
  wire module against unpublished root source would require a local `replace`
  or an unreviewed pseudo-version, so V1a-C deliberately uses two sequential
  contract PRs: reviewed root release first, then a wire PR pinning that public
  version.
- Immutable neutral vectors cover CRUD differences, owner/sharing/RLS,
  approval and LockRule locks, generic delete blocking, mixed types, masking,
  duplicates, ordering, cancellation, deadlines and one-over bounds. They do
  not pretend the selected-root issue-tracker fixture already runs this
  different capability; real-adapter execution belongs to the next phase.
- The semantic root reviewed head
  `391475690e385b5da3102b6bf6059f92fec5e97d` merged as
  `57b368d7da978a4c1ab28ea8a2a677909fdc4878` and is released as `v0.1.9`.
  The wire reviewed head
  `b0437c237c7e2c2dc30f0a19f639b3e851b6829a` merged as
  `dcdb3300598aa895c44c92dc26314f07f1db4130` and is released as
  `wire-go/v0.1.2`.
- Exact-head Contract runs `29271834601` and `29273720678` passed. Codex Review
  runs `29271832849` and `29273718826` both recorded standard and adversarial
  APPROVE. Their final workflow step failed only while enabling auto-merge
  against GitHub's transient `UNSTABLE` state; the aggregate verdicts remained
  immutable `ACCEPT` and the reviewed heads were merged unchanged.
- A fresh external module with no workspace or `replace` downloaded both tags
  and passed tests for the semantic package, wire mapping and generated Runtime
  package. Exact commands and the full call/semantic matrix are recorded in
  `v1a-c-record-access-contract.md`.

**Next gate**

- Do not mark the route moved. V1 must still implement the real Runtime adapter,
  run the CRM and capability-specific non-CRM matrices, bind one `TrustedCall`
  and immutable metadata view through P7c/S1, fence one active route owner and
  delete the legacy fan-out without a fallback.
## 2026-07-13 — R1b immutable CRM AppRelease and L2 BOM

**Current-tree authority decision**

- R1b re-derived the delivery path from the A1c Catalog implementation and the
  live L1/L2 trees. `appauthority.Package` remains the sole AppManifest,
  metadata bundle and AppReleaseDigest owner. The controlplane
  `releaseartifact` package is only a canonical delivery envelope plus detached
  publisher proof; it does not define another App, release, installation or
  registry.
- The first CRM publication is `io.nexuscrm.sfcrm@0.2.0`. Its AppReleaseDigest
  is `sha256:28ff98fbdf13d52f5dcdda362d3cdcb0d9bd012cefe218a98ebb642eabf6e802`;
  its MetadataBundleDigest is
  `sha256:880a7d3785854feaf14a663500b5d63366ffa8b1ef8c661a3d3cbef6bdc3b47c`.
  It contains the exact 54-component CRM metadata input and the minimum A1b
  record-access capability declaration. It contains no service image because
  CRM does not yet have an independently released AppService.
- Provenance is detached from release identity. The publisher attestation binds
  the exact release tuple, source commit, source-tree digest, builder and
  contract version. Signing rebuilds from Git object bytes at the declared
  commit, not from a mutable worktree; false revision claims, dirty source,
  duplicate JSON facts and check/read races fail closed.
- The L2 serviceification BOM is a thin immutable binding over one NexusCRM
  commit, one released Catalog importer, and exactly one CRM publication
  commit/file tuple. It owns no App fact and does not replace the current
  production `release-tuple.json`. There is no optional service artifact entry.

**One import path, still non-serving**

- `catalogapp.ImportReleaseArtifact` validates operator-owned publisher trust
  at signing and verification time, verifies Ed25519 provenance and the closed
  host compatibility envelope, then delegates to the existing A1c
  `RegisterRelease` owner. Exact replay retains the first durable verification
  time; a different proof for the same immutable release conflicts.
- The external CRM evidence enters the real Catalog TiDB suite only as exact
  package, attestation and public-key bytes supplied by the L2 workflow. L0
  never imports L1/L2 packages or learns their repository layout. The suite
  verifies, imports, reads back and exactly replays the release through A1c.
- This is publication/import evidence, not a serving flip. No Runtime command,
  route, image, manifest, AppInstallation selector or metadata read path gained
  a CRM release dependency. The legacy source-backed CRM serving path remains
  authoritative until the atomic P7c/S1 + V1b cutover deletes it.

**Closed engineering proof**

- Controlplane PRs #38 and #40 merged the neutral carrier/import path and the
  exact-replay correction. The final reviewed controlplane head was
  `00bf1239cd3b02cab812641d749df48a2642b849`; merge
  `249c25397c6b6cbb0effdcfc7c1371f231763c58` is published as `v0.1.22`.
  Exact-head review run `29272823357` approved both standard and adversarial
  passes.
- App-packs PR #397 final reviewed head
  `db5eb9b4c367be50d24368c2c2de9f4099da13f3` merged as
  `7c20038c771172fb81697a1b74eeee4d34268830`. Its builder tests prove
  deterministic identity, historical-source rebuild, exact Git provenance, all
  current component-kind ownership, missing/duplicate/unowned rejection and
  detached signature verification.
- L2 PR #3914 final checked head
  `c14902f117df846552cc64deff102d6d69046325` merged as
  `5ae34e2ff6a86fe5e3cc975043609e582a82985b`. Its BOM gate checked out
  all three repositories at the recorded immutable identities, required the L1
  publication commit to be contained in app-packs `main`, rebuilt and verified
  the committed release, checked the importer module version and rejected
  duplicate, missing, mutable or tampered BOM facts.
- Exact CRM publication import passed the real test2 TiDB Catalog workflow in
  run `29279718646`; the exact-main BOM gate passed in run `29279655652`.
  Both runs were non-serving and used scratch Catalog databases only.

**Failure-class corrections**

- The first artifact importer assigned a new `VerifiedAt` on exact retry, which
  made durable replay conflict with itself. The durable first verification time
  now remains part of the stored proof and exact later verification is an
  idempotent read.
- The first publication test compared version `0.2.0` with the moving current
  pack. That would have forced future metadata changes to rewrite an immutable
  historical release. Reproducibility now rebuilds each publication from its
  attested historical source revision; a new source requires a new release.
- Merely validating that a provenance revision looked like a SHA still allowed
  a publisher to claim commit B while signing source A. The publisher now
  materializes manifest and component inputs directly from commit B's Git
  objects and compares the resulting canonical package before signing.
- App-packs does not duplicate the controlplane codec to avoid private-module
  CI credentials. The L2 exact-BOM gate owns the cross-repository rebuild and
  cryptographic verification, where the released L0 module graph is available.
- The first L2 exact-head run verified the BOM and then failed because Go used
  a cross-user module-cache lock under the self-hosted runner home. Both R1b
  workflows now disable the setup-go shared cache and use run-owned build,
  module and temp directories; a workflow-boundary test prevents regression.

**Next gate**

- R1b removes the artifact-identity prerequisite for P7c/S1 + V1b, but it does
  not by itself unblock that cutover until the remaining V1a contract/adapter
  units are complete. R2 may publish independent service/frontend artifacts and
  R3 may evolve reconciliation without changing App-domain ownership.
- The earlier A1c wording that R1b must publish a "signed legacy mapping" was
  too broad. R1b publishes signed release provenance and the immutable BOM.
  The owner-controlled legacy installation conversion receipt remains a
  P7c/S1 + V1b cutover input, not an AppRelease or BOM identity fact.

## 2026-07-13 — F1-I production IdentityContext exchange

**One authority path**

- F1-I adds one neutral `IdentityContext.Exchange` operation. Its semantic
  request contains only AppInstallationID plus requested Runtime capabilities;
  its response contains only an opaque delegation plus authoritative expiry.
  Workload, Org, principal, session, Connected App, audience, epochs, grants,
  key and lifetime remain issuer-derived facts.
- api-host authenticates the workload before it captures the end-user bearer.
  The bearer uses the dedicated sensitive
  `x-nexuscrm-end-user-authorization` metadata key, never protobuf. The standard
  gRPC `authorization` key remains untouched for existing workload authorities.
  api-host removes the sensitive key before every Authority and business
  handler call; Runtime Identity shadows the private bearer value before every
  later authority, repository, ID-provider and key-provider call.
- Runtime Identity is the only production issuer. It reuses AuthService's
  exact-current public bearer validation and bearer-free durable
  session/user/Connected App/AuthEpoch read, the existing workload binding,
  one Runtime-security issuance snapshot, the approved P6 capability
  intersection, and `delegationv1.Sign`. There is no fallback Org, grant,
  issuer, key, session database or token grammar.
- The Runtime-security issuance snapshot derives Org, audience, SecurityEpoch,
  lease and the exact AppInstallation/workload grant from one repository
  statement selected by infrastructure replica ID. AppInstallation and
  requested capabilities are the only caller selectors; absent or cross-Org
  bindings are masked as the same forbidden outcome.
- The operator key provider supplies one active Ed25519 private key and explicit
  overlap public keys. The production issuer enforces the P6-D 60-second
  lifetime, 5-second verifier skew, 65-second planned overlap and zero
  compromised-key overlap. Source and process-boundary guards reject generated
  startup keys and static Org/installation/capability grants.

**Executable proof and remaining serving boundary**

- The generated gRPC walking slice validates a real public JWT through
  AuthService and TiDB, issues one delegation, and presents it to the existing
  Runtime `SecurityAdmission`. Admission installs the existing immutable
  `TrustedCall` before the selected-root adapter runs. The test proves the raw
  bearer is absent from Records and incoming metadata, advances durable
  AuthEpoch to reject the old delegation, and proves the next exchange binds
  the new epoch rather than minting stale authority.
- Negative evidence covers malformed/missing credentials, revoked sessions,
  inactive users, disabled Connected Apps, unknown workloads, wrong/cross-Org
  installations, ungranted capabilities, stale epochs, expired leases,
  authority and key-provider outages, every cancellation boundary, concurrent
  exchanges, unique DelegationIDs and current/overlap/retired/compromised keys.
  No pre-sign denial reaches token signing or Records.
- Runtime public OAuth scopes still require an explicit composition-owned map
  to neutral Runtime capabilities. F1-I makes that authority a required port;
  it deliberately supplies no broad default and does not encode CRM scope names
  in L0. The first serving AppService/cutover must bind that port from the
  approved App/OAuth policy.
- `backend/cmd/runtime-api/main.go` remains on the rejecting non-serving
  bootstrap. F1-I adds a production-capable assembly seam and CI walking slice,
  not a route, image readiness claim, tenant deployment or traffic switch.

**Failure-class corrections**

- The first api-host implementation constructed Records failures before
  invoking the identity mapper. Although both semantic packages alias the same
  common failure type, transport-owned failures now explicitly use
  `commonv1`, and identity decoding is guarded for both transport failures and
  legacy Authorities.
- The first sensitive carrier reused standard `authorization`. That would have
  hidden or overwritten credentials owned by published workload Authority
  implementations. A dedicated key now carries only the end-user credential;
  tests prove standard workload authorization remains visible and every
  reserved-key occurrence outside Exchange fails closed, including malformed
  and multi-valued input.
- A successful authority call that returned impossible state was initially
  classified like an outage. Issuance now validates the complete P6 claims
  grammar before consulting the signing provider. Owner errors remain
  `UNAVAILABLE`; impossible successful projections are `INTERNAL` and never
  reach signing.
- Leaving Exchange on an arbitrarily long caller deadline while sharing
  admission with Records allowed stalled identity calls to monopolize every
  slot. Copying Records' two-second semantic cap would have invented Identity
  evidence. api-host therefore keeps a separate explicit host attempt cap:
  zero disables Exchange, and an enabling Runtime composition must bind a
  positive value. The caller's shorter deadline still wins; no F1-I value is
  represented as an approved Identity SLO.
- The first issuer treated an empty ConnectedAppID as an impossible successful
  session projection. AuthService deliberately accepts legacy/first-party
  sessions whose app, scope, and grant facts are all empty, so that check
  manufactured a new authority invariant and misclassified a valid owner fact
  as `INTERNAL`. The issuer now preserves the empty fact and always delegates
  the authorization decision to the required `SessionCapabilityAuthority`;
  no default capability is invented, and an empty policy result remains
  `FORBIDDEN`.
- The initial F1-I consumer bump re-ratified the P6-D contract tag but left the
  api-host evidence at its historical `v0.3.0` value. The executable oracle now
  pins and compares both consumed releases: contract `v0.1.10` and api-host
  `v0.5.0`. The historical exact-main P6-D run remains labeled as the original
  implementation evidence rather than being presented as an F1-I CI run.

**Released contract evidence**

- nexuscrm-contract semantic PR #26 reviewed head
  `2801d60547c4bfd1246ea64e07812882d27f21b5` merged as
  `c01105baf6345bad21045530994f8da15e9ff988` and is released as `v0.1.10`.
  Wire PR #27 reviewed head
  `85b528734b95c7eb86e52f8827445b0807c5683c` merged as
  `2f19f7d6feed36d418f0da3dd58026a7c2740725` and is released as
  `wire-go/v0.1.3`. Contract runs `29282191385` and `29283339088` passed;
  their review runs recorded standard and adversarial approval. A clean
  external module downloaded and tested both releases without a `replace`.
- nexuscrm-api-host PR #13 reviewed exact head
  `7a0f7cbd830dbcf4adcabe09e2747b783b80c8c0` and merged as
  `77c66b28d48b4089682c918a2e82c226a27281eb`; annotated tag `v0.5.0`
  resolves to that merge. Build/test run `29286900840` and workflow-shape run
  `29286902342` passed. Review run `29286900945` recorded standard and
  adversarial approval with zero blockers; its final GitHub job failed only
  because that repository does not enable auto-merge, so the already reviewed
  exact head was merged manually. A clean external module consumed
  nexuscrm-contract `v0.1.10`, wire-go `v0.1.3`, and api-host `v0.5.0`
  together without a `replace`.

**NexusCRM merge and exact-main evidence**

- NexusCRM PR #3659 reviewed exact head
  `41e8385a93b5970033f785e24531d485a00a614d` and merged as
  `81db62a3793f5ef540ebcada0058756a09e6f3e7`. Exact-head Codex Review run
  `29289227784` passed after standard and adversarial review with no remaining
  blockers.
- Exact-main `go-ci` run `29294773021` executed that byte-exact merge SHA by
  `repository_dispatch` and passed all three jobs: build/test/seal, backend and
  independent Runtime image smoke, and the source-backed Runtime gate. Inside
  the TiDB-backed job, step `Runtime API real-repository and identity-exchange
  gates (#3555 P3/F1-I)` passed. This is the executable proof that a real
  AuthService session and Runtime security projection can issue a delegation
  over generated gRPC, that the existing SecurityAdmission accepts it, and
  that the selected-root adapter executes without receiving the raw bearer.
- F1-I closes the production-capable issuer and transport walking slice only.
  `backend/cmd/runtime-api/main.go` remains non-serving. The first serving
  AppService composition must still provide the approved session-scope to
  neutral Runtime-capability mapping and an explicit positive Identity attempt
  cap as part of the later atomic ownership cutover.

## 2026-07-14 — main health and automatic exact-main CI handoff

**Observed failure and evidence correction**

- NexusCRM `main` at `5580649a8e0a913bad1d2088516ef4634917f740`
  failed `TestP1cM2eValidEnvelopeCandidate`: the reviewed capacity golden
  expected 135 canonical system-catalog string `Id` owners while the live
  catalog contained 137. PR #3663 added exactly two such owners,
  `_System_ConnectedPrincipal.Id` and
  `_System_ConnectedAuthorizationRequest.Id`, both `VARCHAR(255)` primary
  keys. Its three other connected-delivery tables deliberately use
  `ConnectedPrincipalId` or `JobId` as their primary key and do not contribute
  to this count.
- The golden therefore moves deliberately from 135 to 137. A focused live
  catalog test pins the five connected-delivery primary-key shapes and proves
  that exactly the two named tables contribute canonical `Id` owners. The
  selected-field, identifier and request/response envelope candidates do not
  change.

**Root cause and owning-layer fix**

- PR #3663 was auto-merged by the Codex Review workflow using repository
  authority. GitHub suppresses ordinary `push` workflow recursion for changes
  produced with `GITHUB_TOKEN`; merge SHA `5580649a8...` consequently had zero
  Actions runs and zero check-runs. `go-ci.yml` accepted a manual
  `repository_dispatch`, but no owner emitted it. A manual dispatch recovered
  evidence for one revision and did not close the mechanism gap.
- The first attempted repair split one lifecycle between two owners: Codex
  Review armed auto-merge while a second workflow acknowledged, waited,
  verified, dispatched CI and tried to undo the auto-merge on failure. Review
  exposed the resulting failure class through mutable source-head evidence,
  cleanup retries and same-head waiter cancellation. These were not independent
  edge cases; the abstraction required distributed ownership of one merge.
- The final shape removes that split. Codex Review only dispatches a versioned
  approval command after both passes approve and the reviewed head/base remain
  current. The trusted default-branch workflow is the single owner of merge and
  CI dispatch. It repeatedly invokes the GitHub squash-merge API with the exact
  reviewed SHA as the compare-and-merge precondition; a pending required check
  is ordinary bounded retry, while a changed head fails immediately. There is
  no auto-merge request, receiver acknowledgement, cleanup mutation or waiter
  ownership transfer. Both Review and the merge owner require the PR base to be
  `main`; exact-main authority never merges a PR targeting another branch.
- The merge owner writes the reviewed head and unique handoff ID into the
  squash commit body. The GitHub merge response, PR `merge_commit_sha`, first
  parent, exact trailers and reachability from `main` form one immutable proof.
  A workflow rerun resumes from that proof. Same-head review reruns may coexist,
  but only the handoff whose trailers are in the winning merge can continue;
  all others fail harmlessly without canceling or undoing another owner.
- After proving the merge, that same workflow emits `go-ci-main` with the exact
  merge and parent SHA. The test workflow independently revalidates the proof
  before exposing one SHA to its complete graph. Every job explicitly checks
  out and records that resolved SHA; ambient `github.sha` is not the tested
  revision. Normal push and handoff runs for the same merge share one
  concurrency key. GitHub PR, merge, commit, compare and dispatch operations
  use bounded retry for their applicable transient failures.
- The resolver is executable helper logic rather than an inline YAML state
  machine. Its tests cover malformed payload, stale and closed heads, required
  checks not ready, timeout, wrong merge identity, idempotent resumption,
  competing same-head handoffs and success. Normal human pushes use the same
  exact-SHA resolver and remain supported.

**Bootstrap and completion gate**

- The installing PR cannot use workflow behavior that is not yet on the
  default branch. After it merges, one explicit bootstrap `go-ci-main`
  dispatch is required for that PR/head. A temporary resolver exception pins this
  proof exception to PR #3665, requires its unchanged reviewed head and a
  `bootstrap-*` receipt, and must be deleted in the immediate closure PR. This
  is evidence recovery for the installation only, not the operating model.
  That closure PR must demonstrate the automatic trailer-backed handoff.
- PR #3665 reviewed head `c6a76280edbe1bc7fef9b46ee89a723549f6b7bc`
  merged as `94d6425d36cb1f50b2b28f8876ea270c7168e9ef` after exact-head
  Standard and Adversarial approval. Bootstrap run `29313511018` resolved and
  checked out that exact merge, and its image smoke passed, but build/test/seal
  failed before Go execution because the frontend deleted-suite guard only
  resolved a PR base ref. `repository_dispatch` has no PR event payload even
  though the exact-main resolver had already supplied parent
  `1dffcdb415a48f7978fac01aca63e1ad0f1b05ec` as
  `GITHUB_EVENT_BEFORE`. The guard now treats that resolved immutable parent as
  its first authority, fails closed on a malformed or unavailable explicit
  SHA, and uses the historical mutable-ref fallback only when no exact CI base
  was supplied. The build checkout fetches two commits so the resolved parent
  is locally available.
- Automatic canary PR #3669 reviewed head
  `6e8bfac88a6043f796a327dc918aec7fcf36134a` was merged by handoff run
  `29314242297` as `1fbe80354fc77f7aac72d0beac3f09cfb3dbd297` with the exact
  reviewed-head trailers. Its automatically dispatched `go-ci` run
  `29314256794` passed resolver, full build/test/seal, image smoke and the
  source-backed Runtime gate. Re-running the older #3665 bootstrap then exposed
  a compatibility fact: exact checkout correctly executes the old checker from
  `94d6425d...`, so the new explicit-SHA code is intentionally unavailable.
  The trusted test harness therefore binds the already verified parent to the
  ephemeral checkout's `origin/main` ref before any repository test executes.
  This does not change the tested tree; it gives both legacy ref-based guards
  and current explicit-SHA guards the same immutable diff authority.
- Compatibility-base PR #3670 reviewed head
  `66997d2a1d3151ad52562a915a608c52a65899f9` was merged automatically by
  [handoff run `29316483056`](https://github.com/pingcap-inc/NexusCRM/actions/runs/29316483056) as
  `b835e6614ff24871d0f3a6245b326f992e5b017f`, with exact reviewed-head and
  handoff trailers. Its automatically dispatched
  [`go-ci` run `29316498396`](https://github.com/pingcap-inc/NexusCRM/actions/runs/29316498396)
  passed resolver, full build/test/seal, image smoke and the source-backed
  Runtime gate. This proves the steady-state owner can merge, attest, dispatch
  and validate a current reviewed head without a manual step.
- Final
  [installation recovery run `29316566430`](https://github.com/pingcap-inc/NexusCRM/actions/runs/29316566430)
  resolved the original #3665
  merge `94d6425d36cb1f50b2b28f8876ea270c7168e9ef`, bound its immutable parent
  `1dffcdb415a48f7978fac01aca63e1ad0f1b05ec` as the compatibility diff base,
  and passed all four jobs. That is byte-exact evidence for the installing
  merge, including full build/test/seal, image smoke and the source-backed
  Runtime gate; it does not certify a newer tree in its place.
- With installation evidence recovered, the one-PR bootstrap proof exception
  is removed. Every repository-dispatch `go-ci` request must now carry a merge
  whose immutable trailers prove the exact reviewed head and handoff owner.
  The workflow-shape guard prevents the installation exception from returning.
  No manual dispatch, receipt, auto-merge cleanup or mutable branch head is an
  operating dependency.
- Main health and the automatic exact-main handoff are therefore complete.
  V1a record-access adapter/client is the next serviceification slice and is
  not implemented here. V1b production cutover has not started.

## 2026-07-14 — V1a record-access Runtime walking slice

**Current-tree ownership result**

- The first real CRM capability remains the C0-selected
  `POST /services/data/:version/ui-api/record-access` operation. V1a does not
  switch that route. It adds the dormant, capability-complete path needed to
  prove the boundary before P7c/S1 + V1b performs one atomic ownership flip.
- The real Runtime owner is the existing `records.ContractService`, not a new
  capability plane. Its new `EvaluateRecordAccess` method keeps optional-object
  type resolution and the per-object `BatchRecordAccess` evaluations behind
  one coarse call. The legacy three-method `contract.RecordAccessReader` and
  `contract.Deps` remain unchanged.
- `runtimeassembly.NewRecordsReadRuntime` now constructs one shared read-only
  graph for selected-root and record-access: one MetadataService, one
  PermissionService, one PersistenceService, the query repository and the
  record repository. The real FolderAccessService is wired against the same
  Runtime database. Construction starts no EventBus listener or worker.
- `RecordsServer.EvaluateRecordAccess` requires the existing `TrustedCall`,
  invokes the real adapter, emits only readable submitted results in stable
  record-ID order, and attaches record-access capability receipts. The one
  SecurityAdmission chain derives the required capability from api-host's
  private verified method context; it does not add a second method registry or
  interceptor path.

**Transport and admission root-cause correction**

- The first api-host draft let record-access inherit an arbitrary caller
  deadline. Because admission happens before Authority and the handler, an
  authenticated workload could then occupy every process admission slot for an
  unbounded time. Simply copying selected-root's two-second cap would have
  invented record-access evidence that V1a-C explicitly did not approve.
- Adding a record-access field to the released `runtimegrpc.Config` then
  created a different compatibility break: downstream positional struct
  literals would no longer compile. The final public shape preserves the exact
  three-field Config and existing constructor signatures and enables the new
  method only through a sealed option.
- The option is an explicit maximum admission lease, not a semantic deadline.
  An admitted request keeps its caller-owned deadline byte-for-byte; a caller
  asking to hold a scarce slot longer than the configured operational lease is
  rejected before Authority/admission/handler as typed
  `RESOURCE_EXHAUSTED/ADMISSION`. No host-created `DeadlineExceeded`, default
  duration or production-approved record-access SLO is introduced. Without a
  positive lease the method remains `UNIMPLEMENTED` and is not advertised.

**L1 and non-CRM proof**

- The sfcrm Runtime client owns only current route normalization and projection.
  Empty, rejected and untyped normalized-empty input stays local with zero RPC;
  every accepted non-empty request makes exactly one injected neutral Runtime
  call. Typed IDs remain exactly as submitted, while the untyped branch trims,
  drops blanks and collapses duplicates exactly as the current route does.
- The client validates each response against the original normalized request.
  This correlation is security-significant: an unrequested result would reveal
  record existence/access and fails closed as an internal protocol failure.
- The existing CI-only issue-tracker fixture now consumes the immutable
  contract-owned record-access vectors; it did not gain a pack, AppRelease,
  route, command, image or deployment. NexusCRM executes all 11 vectors against
  the real `ContractService -> PersistenceService -> PermissionService` adapter,
  including CRUD differences, RLS/missing masking, approval and metadata locks,
  generic delete blocking, mixed type resolution, forbidden groups, ordering,
  cancellation and in-flight deadline expiry.
- The sealed fixture guard compares every copied file, includes future fixture
  subpackages and test dependencies, and proves that Records, backend commands,
  CRM distribution, production images and normal test closures do not depend
  on the fixture.

**Walking-slice scope and gate**

- The source-backed Runtime test starts a separate Runtime OS process, uses the
  generated api-host record-access client and dormant sfcrm client, verifies
  delegation for the distinct capability, and reaches the real TiDB-backed
  Records adapter in one RPC. The existing point-read clients remain in the
  same process test; the two capabilities share one graph and one security
  chain rather than parallel hosts.
- `backend/cmd/runtime-api` still invokes `AssembleNonServingBootstrap`.
  Production route composition, RuntimeMetadataView binding, serving receipts,
  AppRelease selection, route ownership fencing and deletion of the in-process
  fan-out all remain P7c/S1 + V1b work. V1a is therefore boundary proof, not a
  production traffic claim.
- Local evidence passed backend and records full tests/vet, targeted race,
  module boundary and Runtime process guards, sealed fixture production-closure
  checks, both real-adapter fixture matrices, dormant L1 distribution compile,
  JSON validation and `git diff --check`.
- NexusCRM PR #3673 reviewed exact head
  `234b39dcfb1894d1a60533b1ee103eac72606c83`; standard and adversarial review
  approved with no blockers in run `29325817117`. The trusted handoff merged
  that exact head as `7a1815b55bc74db2233690fb0f941fccf565ec4f`.
  Exact-main go-ci run `29327198843` passed all four jobs. Its TiDB-backed
  source gate executed the sfcrm and CI-only issue-tracker clients through a
  separate Runtime OS process, generated gRPC client, TrustedCall admission,
  the real Records adapter and TiDB. V1a is therefore closed; production route
  ownership, serving-view binding and fallback deletion remain P7c/S1 + V1b.

**Published transport evidence**

- nexuscrm-api-host PR #14 reviewed exact head
  `b40e09e5b685349574bbd5f1bbfd95d2b213eb7e`; both standard and adversarial
  passes approved with zero remaining blockers in run `29324453545`. The job's
  final failure is the repository's known post-approval auto-merge handoff,
  not a review rejection. The head was merged manually as
  `a6532715967f223ca58d7cf8bf8e6b935ba6c6df` after build/test and
  workflow-shape CI passed, and annotated module tag `v0.6.0` resolves exactly
  to that merge.
- nexuscrm-app-packs PR #399 reviewed exact head
  `7043cb6c048ac8b618418ecf2e852026d38aeed3`; standard and adversarial review
  both approved with no blockers in run `29324005315`. Dialect seals, sealed
  fixture shape and frontend-source CI passed. The trusted handoff merged that
  head as `53445ca7357a6529e15a6626843a83e2ccd28408`; the NexusCRM fixture provenance
  and official source pin now name that immutable main-lineage revision.

## 2026-07-14 — D1 test-stack foundation: ownership before deployment

**What current-tree implementation proved**

- The independent Runtime API composition now starts from operator-owned
  workload credentials and signing keys, the real TiDB repositories, the
  Catalog installation/binding authority, content-addressed Catalog artifacts,
  one prepared SecurityState, and one immutable RuntimeMetadataView. It enables
  only `runtime.records.evaluate-access.v1`; the historical selected-root and
  non-serving bootstrap compositions remain test/transition paths and are not
  advertised by this distribution.
- SecurityAdmission installs the trusted identity first. ServingAdmission then
  derives org and AppInstallation only from that identity, matches the one
  active Catalog HandlerBinding, validates SecurityEpoch, and installs one
  private request scope containing the exact view and receipts. The engine's
  serving metadata reader has no repository or process-global cache fallback.
  Sharing-rule caches are bypassed for this immutable request-bound mode so a
  later request cannot reuse an older revision under the same object name.
- Reconciliation may read Catalog and the artifact store; request admission
  performs only an atomic prepared-state read. Failed reconciliation removes
  readiness for new requests, while an in-flight request retains the exact view
  pointer it already captured. The gRPC handler obtains its receipts from that
  same scope rather than a parallel receipt source.
- D1 deployment descriptors contain only Runtime API. The no-work Runtime
  worker image remains historical build compatibility and is not deployed.
  The new Runtime distribution has no L1 import and no legacy backend fallback;
  the existing production monolith and CRM route remain unchanged.

**Root causes corrected before release**

- The Catalog compiler was immutable, but its three production metadata inputs
  (`GetAllSchemas`, `GetAllRelationships`, and `GetSharingRules`) still queried
  the repository pool directly and ignored the transaction already carried by
  `context.Context`. A reconcile could therefore assemble one artifact from
  multiple database snapshots. These reads now reuse the existing persistence
  transaction seam; a single-connection regression test would deadlock on the
  old path and proves that the complete snapshot read stays inside one
  transaction. No second metadata reader or compiler was added.
- Catalog and persistence both need the Runtime security projection value, but
  neither is its semantic owner. Keeping the DTO in persistence made
  controlplane's published package depend on a local `v0.0.0` core module whose
  root `replace` directives disappear for downstream consumers. Copying the DTO
  into Catalog would instead create two incompatible Go types and two owners.
  The root fix places the neutral, stdlib-only value in nexuscrm-contract and
  has both packages consume that one identity.
- The contract review workflow originally approved the code and then failed
  while trying to enable auto-merge from inside its own required check. That is
  a state-machine ownership error, not a product-code failure. Contract PR #28
  therefore made review read-only and added a workflow-shape guard; the exact
  approved merge `00d5c63d0e1ef4d7b7a2fbd789c510018e0f0fa4` is published as
  `v0.1.11`.
- Existing guards encoded two physical accidents as invariants: a persistence
  struct definition and an exact whole-module contract tag. They now guard the
  real facts instead: persistence aliases the neutral contract owner, while
  P6-D's approved delegation/timing evidence remains at `v0.1.10` and permits
  the separately reviewed additive `v0.1.11` projection DTO release.
- The earlier Runtime image smoke expected a live-but-not-ready bootstrap. Once
  the real command became fail-closed serving composition, preserving that
  behavior would require a hidden authority or generated key. The smoke now
  proves the command refuses incomplete authority, and deployable test topology
  must supply all signed serving inputs.

**Decision and remaining gate**

- Reopening the complete test-environment DoD found an observability ownership
  gap before deployment: AppService could count Identity and Records RPCs, but
  L2 had no authoritative way to prove a rejected Runtime request stopped
  before the repository or to compare SQL/pool cost. Inferring shared-TiDB
  activity in the deployment script would be both racy and a database backdoor.
  Runtime now exports bounded, App-blind repository-call, repository-stage SQL,
  complete-RPC SQL counters (Identity and Records remain separate), plus the
  `database/sql` pool snapshot from its existing admin metrics endpoint. The
  SQL observer carries only nested counters in context;
  it never records SQL text, arguments, org, App, object, record, or user facts.
  L2 remains a consumer of these owner metrics rather than becoming a second
  records/database observer.
- This NexusCRM change implements the Runtime-owned portion of D1/P7c-S1. It
  does not itself complete D1. Completion requires independently reviewed and
  immutable Gateway and sfcrm AppService images, the thin L2 BOM/deployment,
  and a real test-environment run through Gateway → AppService → Identity →
  SecurityAdmission → ServingAdmission → RecordsService → TiDB.
- The final test run must prove parity and the negative matrix, one Records RPC
  for accepted non-empty input, zero for empty/rejected input, no mixed
  revision, fail-before-repository behavior, cancellation/restart/drain, and
  legacy-versus-new resource evidence. Production ownership remains legacy
  until a separate atomic V1b decision.
- A second full-DoD audit found that Runtime-only observation still could not
  satisfy the required legacy-versus-new SQL comparison. The legacy backend
  already publishes its shared `database/sql` pool, but it had no request-bound
  statement count. The generic HTTP middleware now binds the same nested SQL
  counter to each matched request and publishes only a bounded method/route
  aggregate. It does not know CRM objects or SQL contents. The D1 runner can
  therefore compare the existing record-access route with the new Runtime
  operation without querying TiDB diagnostics, guessing from shared activity,
  or adding an L1-specific observer. A test2 legacy deployment that does not
  expose this reviewed metric fails the D1 precondition rather than silently
  producing incomplete evidence.
- Repeated review concern around the Runtime image smoke exposed a broader
  gate-ownership gap. The serving startup contract had been updated in the
  post-merge Docker harness, but no PR-time boundary check required that
  harness to carry the token, signing-key, TLS, serving-config and current
  record-access capability together. The Runtime process boundary gate now
  enforces that closed input set and rejects the old world-readable token and
  selected-root bootstrap assertions. A subsequent runtime audit also found
  that the first fixture preparation attempted to run `/bin/sh` inside the
  deliberately shell-free distroless Runtime image. CI now prepares an
  owner/non-world-readable bind mount for the image's declared UID `10001` and
  starts the unchanged distroless image as its default user. Image shape,
  secret permissions, startup contract and publication gating therefore form
  one executable chain instead of four separately maintained claims.
- The first exact-main run after adding Runtime image publication failed before
  source tests because the workflow-shape guard equated the invariant with
  exactly three validation jobs. Publication correctly introduced a fourth
  exact-SHA checkout, so the hard-coded count rejected the safer graph. The
  guard now enumerates every job after `resolve-exact-main` and requires each
  to depend on the resolver, checkout its output, and verify HEAD. The owned
  fact is exact-revision participation, not the current number of jobs; future
  graph growth therefore fails only when a job escapes that invariant.
- Exact-main run `29360359037` then reached the real TiDB security gate and
  exposed a clock-ownership mismatch in `RenewReplicaLease`. The lease expiry
  was computed from one database `CURRENT_TIMESTAMP(6)`, but the returned
  `AuthorityNow` came from a later statement, so a correct 30-second lease was
  reported as roughly 29.999 seconds and the authoritative-duration invariant
  failed. The first attempted correction moved the clock into the locking
  security-state read. Review then exposed the broader failure class: current
  time can be evaluated before either a state-row or duplicate lease-row lock
  wait, so moving the sample earlier made contention consume the new lease.
  The repository now first owns both rows, then performs a final database-time
  expiry update and derives the returned authority base from that durable
  expiry. No caller/process clock or second database clock enters the decision.
  The real-TiDB gate deliberately holds the replica lease row longer than the
  requested lifetime and requires the renewal to return with a fresh lease;
  unit tests alone cannot prove this lock/time ordering invariant.

## 2026-07-20 — P7b mutation-view closure review: executable completeness

**Run ID:** `p7b-mutation-view-closure-20260720`

**What I did**

- Reopened the complete record-mutation consumer path after PR #3827 review:
  request-bound `MetadataService`, Records insert/update/bulk/derived-rollup
  validation, component-scoped AutoNumber facts, Runtime artifact opening, and
  Flow RecordCreate/RecordUpdate field-mapping execution.
- Replaced the record engine's display-oriented validation-rule getter with a
  mutation-specific read. Legacy composition still reads the existing
  immutable metadata cache; request-bound composition requires an exact
  `record-mutation/v1` view and fails before write execution if the view is
  missing or has another profile.
- Closed compiler and reader validation over AutoNumber object/field identity,
  component-derived `DefaultedOnCreate`, and every Flow field-mapping group
  referenced by the selected Flow closure.
- Re-derived the record-type and identifier-rule consumers instead of treating
  their existing empty result as sufficient. Record-type reads are now
  error-bearing through Records, Permissions and the neutral contract adapter;
  all mutation-execution getters share one exact-profile guard. The audit also
  proved that IdentifierRule has no production consumer or repository
  authority: its table was retired and identifier generation is now owned by
  `_System_Field.auto_number_*`. The new Runtime artifact surface was removed
  instead of preserving a second, dead source of truth.

**What failed and why**

- A missing request-bound view was represented as an empty validation-rule
  list. That value is safe for optional display/read consumers but unsafe for
  writes because “no rules exist” and “the authority is absent” became
  indistinguishable.
- The Catalog object carried the union of schema-decorator output while
  AutoNumber rules were component scoped. Copying the union before component
  selection leaked one installation's derived create-default bit into another
  installation using the same object.
- Compiler input checked AutoNumber field binding, but a validly re-digested
  Runtime artifact was checked only for object membership. Likewise, Flow
  metadata could reference a mapping group whose rows were removed by exact
  component selection; the existing applier treats an empty group as a
  successful no-op.
- Record types had a value-only service method, so repository failure, missing
  request-bound authority and a legitimate empty list all became the same
  value. The permission access surface and Records `RecordTypeId` validator
  could therefore weaken behavior instead of rejecting an incomplete
  execution view. IdentifierRule initially appeared to have a similar profile
  bug, but reopening its author and readers showed the entire projected path
  was compatibility residue rather than a current mutation requirement.

**What I learned / decision**

- A mutation projection is usable only when it is complete enough to execute,
  not merely canonical and correctly digested. The same closure invariant must
  run when Catalog compiles a projection and when Runtime opens externally
  supplied artifact bytes.
- “Empty” remains a valid business value only after authority/profile binding
  succeeds. Missing authority is an infrastructure failure and must not share
  the same API as optional metadata reads.
- That rule is an execution-boundary property, not a ValidationRule special
  case. Record types, validation rules, trigger subscriptions and Flow
  definition/version resolution now use one profile-exact helper; the retired
  IdentifierRule API is rejected outright in request-bound Runtime. Boolean
  trigger-presence checks retain their documented fail-safe `true` behavior.
- Component-owned derived facts must be removed from the global effective
  shape before the selected component set is re-applied. Object membership is
  not capability ownership.

**Proof and next**

- Tests cover missing and wrong-profile mutation views, all Records production
  validation-rule call sites, cross-installation AutoNumber isolation, hostile
  valid-digest AutoNumber field mismatch, missing RecordCreate and RecordUpdate
  mapping closure, and successful compilation after the owning mapping
  component is selected.
- Metadata and Records full tests, full race tests and vet pass. Boundarygate
  remains at zero forbidden edges, zero contract Runtime leaks and zero new
  dialect tokens; `git diff --check` passes.
- This remains non-serving P7b mechanism. P7c/S1 still owns request admission,
  active revision selection and the atomic production read-path switch; D2B
  assembly must consume the reviewed view rather than recreate any fallback.
- Before API/Worker composition, P7c/S1 must also remove the backend-owned
  duplicate context resolver and bind through the metadata module's neutral
  `WithRuntimeMetadataView` / `RuntimeMetadataViewFromContext` seam. P7b does
  not perform that serving ownership flip.

## 2026-07-20 — D2B mutation authority correction: provenance is not behavior selection

**Run ID:** `d2b-mutation-effective-behavior-authority-20260720`

**What I did**

- Reopened the complete ownership chain before merging the reviewed P7b
  mutation closure: A1c legacy-pack conversion, AppRelease component lineage,
  Catalog compilation, Runtime projection, controlplane reconciliation and the
  durable worker's `RuntimeWorkBinding` verification.
- Compared the proposed per-fact component association with both L1 package
  facts and L2/admin-authored effective metadata. I also tried to prove
  object-subset projection with an executable test instead of relying on the
  earlier least-privilege claim.

**What failed and why**

- The controlplane proposal derived a synthetic tuple by treating
  `AppCapabilityID` as `ComponentID`, `RuntimeCapabilityID` as
  `DefinitionVersion`, and the AppRelease digest as a component content digest.
  Those are distinct authorities. The real AppRelease component mapping is
  validated during conversion, while the current conversion journal persists
  only the mapping digest; it does not authorize reconstruction of an
  individual component identity from capability identifiers.
- Requiring every effective mutation fact to have one App component owner
  cannot represent L2/admin-authored rules. More importantly, using that
  component to filter execution would let a mutation initiated through one App
  omit another App's or an administrator's active validation, Flow or derived
  rule for the same effective object.
- The earlier tests proved internal consistency of the synthetic association,
  not semantic ownership. They could pass while encoding the wrong authority.
- The attempted object-subset test failed for a separate, explicit reason:
  `record-mutation/v1` currently requires the full same-revision schema closure
  for engine and policy resolution. Current code therefore does not prove an
  App-specific least-privilege object projection for mutation.
- The first authority correction removed the serialized scope field while
  retaining the v3 schema name. Exact-head review correctly blocked that shape:
  the immediately preceding merge could already have persisted canonical v3
  Catalog artifacts, and strict decoding would classify their known field as
  corruption after restart.

**What I learned / decision**

- AppRelease component lineage is an authoring and release-provenance fact;
  effective Runtime behavior selection is a CatalogRevision fact. They must
  not be collapsed into one identifier or one filtering rule.
- A mutation view now contains the complete effective behavior for its
  same-revision object closure. `DefinitionInput` and
  `DefinitionComponentIDs` remain available for genuine immutable definition
  lineage, but effective mutation facts neither require nor manufacture such a
  definition.
- AppInstallation and capability remain security/admission and receipt facts.
  Durable asynchronous correctness is bound by the existing exact
  `RuntimeWorkBinding`: CatalogRevision, SchemaGeneration, EngineGeneration,
  CatalogSnapshotDigest and RuntimeMetadataViewDigest. No synthetic App
  component is needed to make a worker retry deterministic.
- The present mutation profile's full-schema requirement is an explicit
  limitation, not evidence of a broken service boundary. If a future product
  needs App-level resource isolation, Catalog must own an explicit
  resource/grant scope model. Component provenance must not be reused as that
  authority.
- Artifact grammar and semantic authority are separate compatibility concerns.
  The corrected compiler emits v4 without component scopes. Legacy v3 Catalog
  artifacts remain strictly readable as migration sources; their scope rows
  are validated but ignored for projection, which regenerates complete v4
  behavior and does not carry the ambiguous legacy definition selection into
  the mutation Runtime view. Legacy v3 mutation Runtime views fail closed
  because missing facts cannot be reconstructed from filtered view bytes.
  Legacy effective-metadata v2 inputs compile forward to v4 after removing the
  retired association and mutation definition selection.

**What next**

- PR #3827's approved head merged as
  `42904efdf8f719e2ef735ec238517549cb0f9817` before this evidence was
  reopened. Publish this correction as a separate exact-main follow-up and do
  not let controlplane or engine assembly depend on the short-lived synthetic
  ownership shape.
- Remove synthetic definition selection and component stamping from
  controlplane reconciliation. Publish mutation views from the complete
  effective CatalogRevision closure while preserving exact durable-work
  receipts.
- Rebase the Records/worker substrate on the corrected metadata API and then
  prove the first real mutation family end to end. Do not claim App-specific
  object-scope isolation until a separately owned Catalog resource-scope model
  and executable cutover exist.

## 2026-07-20 — D2B engine delivery unit: transaction owner, not process owner

**Run ID:** `d2b-mutation-engine-substrate-split-20260720`

**What I did**

- Reconstructed the all-in-one D2B branch from current code after the mutation
  metadata authority correction, then separated the transactionally coupled L0
  engine substrate from Runtime process composition, images, manifests,
  publishers, Catalog activation and the L1 AppService.
- Rebased the substrate directly onto the corrected metadata merge
  `37c0bf7aac88b661a7b54bb192cc9b5406af0a2c` and published it as PR #3833.
  The original integration PR #3813 was closed as superseded so it cannot
  remain a competing delivery or implementation path.
- Re-derived the versioned Go dependency graph for controlplane PR #162 rather
  than preserving the planned repository order.

**What failed and why**

- PR #3813 mixed two ownership domains. Records, Persistence, Automation,
  Events and Audit participate in one database transaction and durable
  handoff, but process assembly and artifact delivery do not. Image, manifest
  and Catalog review findings therefore repeatedly obscured the engine
  invariant and made every correction reopen unrelated surfaces.
- The planned order `metadata -> controlplane -> engine` was not executable.
  Controlplane consumes the new Persistence durable-work types, so merging it
  first would require a branch-only workspace or local replace. Conceptual
  producer/consumer direction was mistaken for the actual released-module DAG.
- The first local validation inventory covered the four largest engine modules
  but omitted independently changed Events, Kernel, DB and Shared modules.
  Passing through an upstream consumer was not sufficient proof; each changed
  module was added to the standalone test and vet matrix.

**What I learned / decision**

- Review size is determined by one invariant and one rollback owner, not by a
  Go-module boundary or a raw file count. Mutation effects, durable command
  outcome, required outbox rows and worker fencing remain one engine owner
  because an intermediate merge must never allow a mutation without its
  obligation or a worker disposition without its fence.
- Runtime API/Worker composition and release delivery are independent owners.
  They follow the engine and Catalog producer; they do not travel inside the
  transaction substrate PR.
- The executable order is now `metadata -> engine -> controlplane -> process
  composition`. Controlplane must consume the exact merged engine owner with
  normal module pins and no replace directive before it is reviewable.
- A superseded integration branch is evidence, not authority. Closing #3813
  after #3833 existed restored one implementation and delivery path.

**Evidence and next**

- Metadata correction #3832 passed exact-main run `29768185833`, including the
  full build/test seal, source-backed Runtime gate and Docker image smoke.
- On the #3833 substrate, standalone full tests and vet pass for Records,
  Persistence, Audit, Automation, Events, Kernel, DB and Shared. Full race
  suites pass for Records, Persistence, Audit, Events and Automation; backend
  full tests and vet pass.
- Boundarygate reports zero forbidden edges, zero contract Runtime leaks and
  zero new dialect tokens; `git diff --check` passes. The diff adds no Runtime
  command, process assembly, image, manifest, workflow or publisher.
- Next: obtain exact-head review for #3833; after merge, repin controlplane
  #162 to the exact merged engine revision and validate it without workspace
  substitutions. Only then land the small Runtime API/Worker composition owner
  and run the separate-process real-TiDB failure/recovery matrix. None of these
  facts marks D2B complete or switches production traffic.

### Follow-up: the durable queue must have one clock authority

**What failed**

- A final review of the record-event queue found that stale-processing lease
  recovery already used TiDB time, but retry visibility, enqueue ordering,
  claim/heartbeat timestamps and terminal timestamps still used the API or
  worker process clock. A skewed or restarted process could therefore make a
  retry visible early or late, leave crashed work unreclaimable until the
  database clock caught up, or disagree with another worker about FIFO order.
- Candidate discovery and the atomic claim separately copied the eligibility
  predicate. Tests accepted arbitrary timestamp arguments, so they proved the
  SQL shape without proving the clock owner.

**Decision and proof**

- TiDB is now the sole authority for every durable queue transition that
  controls ordering, retry eligibility, claim lease recovery or completion.
  These writes use `CURRENT_TIMESTAMP(6)` and both candidate discovery and
  compare-and-transition claim call one shared database-time predicate.
- SQL regression tests reject host timestamp parameters for enqueue, heartbeat,
  claim, retry and completion and assert the exact database-time lease/retry
  offsets. Persistence full tests, vet and the full race suite pass.
- Process-local `time.Now` remains only for non-durable stage timing. The later
  separate-process TiDB matrix must still exercise clock-skew/restart behavior;
  this correction prevents the engine contract from depending on that test
  environment's host-clock alignment.

### Follow-up: command preflight, transactional authorization and durable result authority

**Run ID:** `d2b-mutation-replay-policy-authority-20260720`

**What I did**

- Reopened the complete accepted-command replay state machine after review
  demonstrated that a later Catalog policy denial could encounter an already
  durable independent operation result.
- Traced initial command preflight, the mandatory policy recheck inside each
  DML transaction, the production repository's `PENDING -> COMPLETED` slot
  transition, command/slot lock ordering and semantic response validation as
  one protocol rather than independent error-handling branches.
- Re-reviewed the Runtime rollup queue's durable identity and readiness claim,
  and compiled the CRM distribution boundary affected by the context-aware
  metadata lookup change.

**What failed and why**

- The first implementation tried to terminalize every slot when a replay-time
  policy evaluation denied the command. Production correctly rejected an
  attempt to overwrite a `COMPLETED` slot, rolling back the terminalization and
  leaving the command accepted. The in-memory test store had hidden this by
  accepting writes to any slot state.
- The released semantic validator treated every `POLICY_DENIED` as the cause of
  a zero-effect command preflight. That rejected the mandatory transaction
  recheck's legitimate outcomes: an independent response retaining committed
  siblings, or an atomic response retaining the denial cause with rollback
  markers.
- Merely admitting those outcomes initially made `success + POLICY_DENIED +
  PROCESSING_HALTED` valid. Exact-head review correctly rejected that ambiguous
  grammar. A second review also found that the semantic mapping fence had not
  advanced with the new response grammar.
- Revision-bound rollup rows persisted complete Runtime metadata lineage but
  hashed only the business source/parent/field tuple. `INSERT IGNORE` could
  therefore suppress a new-revision obligation behind stale or poison work.
  Readiness also verified only a subset of columns used after startup.
- Standalone CRM distribution validation exposed two boundary issues: one
  caller still used the old context-free field lookup signature, and an
  aggregate route test equated “JSON object” with the concrete
  `map[string]any` type even though the neutral named record-map carrier has the
  same wire representation.

**What I learned / decision**

- `POLICY_DENIED` is a reason, not one execution phase. A fresh command keeps
  the public zero-effect command preflight, while every mutation transaction
  independently rechecks Catalog policy to close TOCTOU. `PROCESSING_HALTED`
  remains exclusive to the preflight grammar.
- The command row is the shared mutex for preflight and every independent slot
  transition. Once any slot is durable, replay skips command preflight,
  preserves every immutable completed payload and resumes only pending slots
  under transaction-local authorization.
- Every terminal response is semantically validated before its durable command
  transition. Corrupt pending/completed slot state fails closed without a
  business effect or terminal write, and the memory store now enforces the
  production pending-only transition.
- Independently produced rollup work is identified by the business obligation
  plus Catalog's canonical complete `RuntimeWorkBinding`. This keeps retries
  idempotent within one exact lineage without letting an older lineage suppress
  a newer one. Runtime readiness owns the complete queue schema used by enqueue,
  claim, reconcile, retry, repair and observation.
- In-process Go carrier types are not HTTP grammar. Distribution tests should
  assert the serialized object shape without forcing an unnecessary conversion
  inside the neutral contract.

**Evidence and next**

- Contract PR #49 passed both exact-head reviews and merged as
  `ea2e1d9da55b7732b9390092887598cd07934834`; semantic root `v0.1.24` publishes
  mapping revision 5 without changing protobuf fields.
- Wire pin PR #50 passed both exact-head reviews and merged as
  `09a5242cfc64980f4da23408488d7b35f8d98105`; `wire-go/v0.1.9` is the matching
  independently consumable mapper release.
- With those published pins, every changed Go module passes `GOWORK=off go test
  ./...` and `go vet ./...`; the CRM distribution passes with the `crm` build
  tag against app-packs main `416775b91b2ed7903ae7bee49bdb0c08ee382e14`.
  Records, Persistence, Audit, Automation and Events pass their full race
  suites. Boundarygate remains zero forbidden edges, zero contract Runtime
  leaks and zero new dialect tokens.
- Next: push one coherent #3833 head, obtain exact-head review and merge the
  engine owner. Only then may controlplane and Runtime process composition pin
  the exact merged engine; this evidence does not claim process assembly,
  separate-process TiDB proof or a production ownership flip.

### Follow-up: disclosure policy belongs to the Runtime use-case adapter

**What I did**

- Reopened the complete delete call path after exact-head review found that the
  new Runtime missing/RLS mask had changed the existing in-process
  `PersistenceService.Delete` response from its established 403 to 404.
- Separated the unchanged CRUD/RLS/FLS execution owner from the use-case's
  disclosure projection. The legacy entry point and the Runtime adapter now
  invoke one delete implementation and the same permission checks, but supply
  explicit outcomes for missing records, object CRUD denial and row/RLS denial.
- Made the records-owned persistence adapter the only production implementer of
  `RuntimeRecordMutationEngine`; its value-returning constructor prevents a
  typed-nil production configuration. A raw `PersistenceService` can no longer
  be supplied accidentally as the Runtime engine.

**What failed and why**

- The first substrate implementation changed the shared persistence method to
  emit `NotFoundError` for both downstream permission checks. That made the new
  contract vector pass, but silently changed every legacy production consumer
  of `PersistenceService.Delete`. A Runtime contract projection had leaked
  below its ownership boundary.
- The same shape also left a delete race: if object resolution succeeded and
  the row disappeared before the delete read/lock, the legacy idempotent `nil`
  result could become a false successful Runtime operation.
- Adversarial review found the same ownership class in the dedicated record
  event queue: `BackfillRecordKeys` was the sole row operation that did not
  apply the scoped repository's Org/AppInstallation/RuntimeCapability
  predicate. A scoped worker could select and rewrite another scope's legacy
  row, changing that scope's FIFO identity.

**What I learned / decision**

- Authorization execution and information-disclosure projection are different
  facts. Records/Permissions remain the sole authorization path; the coarse
  Runtime use-case adapter owns whether an already-classified denial is
  `ACCESS_DENIED` or `MISSING_OR_DENIED`.
- Legacy delete remains idempotent for a missing row and returns the exact
  existing `Forbidden` response for record denial. Runtime delete instead maps
  a missing row or row/RLS denial to one indistinguishable not-found condition,
  while preserving object CRUD denial as a permission condition.
- This is an adapter boundary, not a second authorization path: no permission
  check, record lookup or DML implementation was copied.
- A scoped repository promise applies to maintenance as well as dispatch.
  Backfill candidate selection and its compare-and-update now both use the same
  complete scope constraint; constraining only one side would still permit a
  row-ID collision or selection/update race to cross ownership.

**Evidence and next**

- Focused tests execute the real `PersistenceService` through both entry
  points, assert the exact legacy status/code/message, assert missing/RLS
  masking versus object-CRUD distinction, and run the complete mutation service
  projection. Records full tests, vet and targeted race tests pass.
- The backend P6-D compatibility guard initially rejected the new root-contract
  pin as designed. The exact `v0.1.23..v0.1.24` diff contains only
  `recordmutationv1` semantic/mapping changes and does not touch
  `delegationv1`, `identityv1` or `recordsv1`; the guard now pins reviewed merge
  `ea2e1d9da55b7732b9390092887598cd07934834` instead of accepting an open-ended
  version range.
- A cross-scope SQL regression test proves that both backfill statements bind
  Org, AppInstallation and RuntimeCapability, including the update of a row ID
  that may also exist outside the worker's authority. Persistence full tests,
  vet and race pass.
- Next: rerun repository boundary and affected-module checks, push the corrected
  #3833 head and restart both exact-head reviews. This correction still does not
  assemble a Runtime process or switch production traffic.

### Follow-up: integration fixtures are consumers of the ownership schema

**What I did**

- Reopened the Audit TiDB integration setup after exact-head review showed that
  its private `_System_RuntimeWorkOwnership` table still described the older
  fence shape.
- Added the required `OperationId` column and centralized authority installation
  in one fixture helper. Ownership-flip probes now advance the operation identity
  together with deployment digest and epoch, matching the control-plane-owned
  state transition instead of creating an internally inconsistent authority row.
- Searched the current tree for every hand-authored ownership-table DDL and
  INSERT. This Audit fixture is the only test-owned physical duplicate; the
  production mutation path remains `RuntimeWorkOwnershipControl`.

**What failed and why**

- `RuntimeWorkOwnershipRepository.CheckReady` correctly made `OperationId` part
  of the required schema, but the independently maintained TiDB integration
  fixture did not evolve with that authority contract. The test therefore would
  fail before reaching its lease, fencing, effect and repair assertions.
- This was not a Runtime behavior failure. It was schema-consumer drift in a
  test environment that intentionally creates its own isolated tables.

**What I learned / decision**

- Test DDL is a real consumer of an owned schema and must fail closed when it
  drifts; relaxing readiness to accommodate the fixture would have weakened the
  production invariant.
- Integration probes that mutate authority state must update the complete
  authority tuple. `OperationId` is durable replay/conflict identity, not an
  optional readiness-only column.
- The fixture remains local to the Audit integration test because importing a
  release/control-plane schema installer into an L0 engine test would reverse
  ownership. The executable readiness check is the parity guard at this module
  boundary.

**Evidence and next**

- Audit full tests, vet and full race pass. The integration file compiles and its
  isolated TiDB cases remain gated by the existing external TiDB environment;
  no local TiUP cluster was started.
- `rg` finds no other hand-authored ownership-table DDL or INSERT in the current
  tree outside this fixture and the persistence-owned control implementation and
  tests.
- Next: push the fixture correction, rerun both exact-head reviews, then require
  merged-head CI before the engine owner can be consumed by controlplane or
  Runtime process composition.

### Follow-up: ownership readiness must prove uniqueness, not only columns

**What I did**

- Extended the same-schema audit from the stale Audit fixture to the database
  constraint that makes `_System_RuntimeWorkOwnership` a single authority.
- Reused one Persistence-owned exact-index verifier for Runtime ownership,
  command-lifecycle retention and Audit replay/retention readiness, deleting the
  duplicate Audit implementation.
- Added fail-closed probes for a missing, non-unique or unexpectedly composite
  authority key.

**What failed and why**

- Ownership readiness verified every required column and then queried the active
  row, but never verified the Catalog-declared `PRIMARY KEY (WorkFamily)`.
  A damaged or partially applied table with duplicate work-family rows could
  pass column readiness. Different deployments could then each match and lock a
  different active row, defeating the fence that all worker effects rely on.
- This is the broader failure class behind schema-fixture drift: column presence
  is not enough when an index or foreign key carries behavior.

**What I learned / decision**

- A readiness gate must prove every physical constraint that its algorithm uses
  for correctness. For ownership, exact uniqueness and column order are part of
  the authority contract; for command retention, the exact bounded-scan index
  and cascade rule are part of lifecycle readiness.
- The verifier belongs in Persistence as generic physical-schema mechanism.
  Individual work families still declare which named index they require; no
  queue or App-specific schema authority moved into the helper.

**Evidence and next**

- Persistence and Audit full tests, vet and full race pass. The new regression
  rejects missing, non-unique and composite authority keys, and ownership
  `CheckReady` itself is proven to stop before authority admission when the
  primary key is absent.
- Next: combine this root correction with any remaining exact-head review facts,
  push one new reviewed head, and require the external TiDB readiness path plus
  merge-SHA CI before downstream composition.

### Follow-up: command expiry and DML share one locked execution boundary

**What I did**

- Reconstructed the durable command state machine after exact-head review found
  that the early `LoadUnexpired` read was the only execution-window check.
- Added a database-clock expiry decision inside every preflight and business-DML
  transaction after locking the command row and the complete operation slot set.
- Made expiry route through the existing interrupted-command terminalizer, which
  preserves committed independent positions, closes only pending positions, and
  creates a fresh terminal proof window without replaying caller DML.

**What failed and why**

- A request could read an unexpired ACCEPTED command, then wait in metadata
  preflight or on a database lock until `OutcomeExpiresAt`, and still execute.
  The lifecycle worker could concurrently discover the same expired command.
  Although both paths eventually locked the command row, the execution path did
  not use the post-lock database time to decide which side owned the boundary.
- An entry-point timestamp check would still be wrong: process time is not the
  durable clock, and an operation-slot wait after an early check could cross the
  expiry boundary.

**What I learned / decision**

- `OutcomeExpiresAt` is an execution-admission boundary for new business DML,
  not permission to delete accepted idempotency state. The transaction that
  holds both command and operation locks reads TiDB time immediately before the
  effect. At equality, expiry wins and no DML runs.
- Execution and lifecycle now use the same command-row-first lock order. If
  execution acquires the lock while still unexpired, it may commit atomically;
  if it acquires it at or after expiry, it performs no effect. If lifecycle wins
  first, execution observes the durable terminal response.
- Atomic commands additionally reject any non-PENDING or payload-bearing slot
  before executing their first effect. Independent commands may retain an
  already completed prefix, but never resume a pending sibling after expiry.

**Evidence and next**

- Deterministic database-time tests advance the clock between preflight and DML.
  The independent case commits exactly one prefix operation and terminalizes
  the pending sibling; the atomic case performs zero inserts. Both produce a
  durable COMPLETED response with a fresh terminal retention window.
- Records targeted tests, full tests, vet and full race pass locally.
- Next: retain the current adversarial-review evidence window, combine any
  additional consequential blocker with this state-machine correction, then
  push one exact head and rerun the full gate.

### Follow-up: a locking query and a durable claim both need explicit owners

**What I did**

- Audited every new D2B ownership-fenced transaction rather than patching only
  the Audit method named by review. Runtime ownership activation,
  record-command retention, record-event repair, rollup repair, Audit lease,
  Audit effect and Audit repair now all use the existing Persistence-owned TiDB
  pessimistic transaction primitive before issuing `FOR UPDATE`.
- Replaced the record-event worker's row-ID-only lease with an explicit durable
  `OutboxClaim {EventID, Token}`. Each scoped claim or reclaim writes a fresh
  UUID token; heartbeat, completion, poison and retry transitions require that
  exact token together with status and Runtime work scope, then clear it when
  the attempt ends.
- Added `ClaimToken` to the Catalog-owned `_System_RecordEventOutbox` schema and
  the independent-worker readiness columns. Repair clears stale claim identity
  when an operator deliberately requeues a failed row.

**What failed and why**

- `database/sql.BeginTx` does not establish TiDB pessimistic mode. A subsequent
  `SELECT ... FOR UPDATE` therefore did not by itself prove the blocking lock
  semantics assumed by the deployment fence. The code declared a stronger
  invariant than its transaction owner provided.
- The queue treated `(row ID, scope, status=processing)` as claim identity. If a
  worker paused past lease expiry, another worker could reclaim the same row,
  but the first worker could later heartbeat or finalize the second worker's
  claim. Deployment `OwnershipEpoch` cannot distinguish two claims made by the
  same active deployment.

**What I learned / decision**

- Transaction mode is part of the correctness contract of a locking query. The
  owner that opens the transaction must establish it; stores must not infer
  pessimistic behavior from SQL syntax.
- Deployment ownership and attempt ownership are different facts. The former
  fences releases; the latter fences one time-bounded execution. A durable
  per-claim token is the minimum generation identity for the latter.
- The claim handle is explicit in the neutral outbox repository seam rather
  than hidden in process memory or context. This makes every state transition
  carry the proof it is authorized to mutate and prevents future subpackages
  from accidentally falling back to row-ID-only completion.

**Evidence and next**

- Persistence regression tests prove a stale token cannot heartbeat, complete
  or retry a reclaimed scoped record event while the current token can.
  Transaction tests require the pessimistic session setup and cleanup around
  each new fence/repair/retention path.
- Shared, Kernel, Persistence, Events, Records and Audit module tests pass
  locally after the change. Remaining validation is full affected-module vet
  and race, backend composition, boundarygate and exact-head review.
- Next: complete those gates, publish one coherent exact head, then run the
  external TiDB worker path before declaring D2B engine substrate complete.

### Follow-up: transaction context is not locked authorization evidence

**What I did**

- Traced Runtime mutation policy evaluation through the real
  `PersistenceService` update and delete pipelines after review found a
  pre-DML authorization read.
- Changed the production Runtime mutation adapter so update and delete require
  an authorization callback that is invoked only after Persistence has loaded
  the root row with its existing `GetLock` path. A missing callback now fails
  closed.
- Moved delete's root-row CRUD/RLS and protected-record decisions onto that
  same locked row image. The initial read remains planning evidence only for
  the established parent-before-child lock order.

**What failed and why**

- The Runtime service re-read the record after opening a transaction, but used
  ordinary `Retrieve`. With TiDB snapshot and current reads, sharing a
  transaction context does not make an unlocked read serialize with a later
  `SELECT ... FOR UPDATE`. A concurrent writer could therefore change a policy
  field between authorization and DML.
- `relatedExists` was evaluated through the generic read-only Query seam. Even
  a locked root row cannot serialize a concurrent insert into the related
  table, and TiDB does not provide the missing generic predicate/gap-lock
  contract here. Treating that query as authoritative would retain the same
  failure class on a different row.

**What I learned / decision**

- A transaction boundary and an authorization boundary are not interchangeable.
  Row-dependent authorization belongs to the component that owns the current
  row lock, and the authorized image must be the same image used by validation,
  events and DML.
- The non-serving D2B substrate no longer accepts a generic policy Query.
  `relatedExists` therefore fails closed until Catalog metadata and Persistence
  define an explicit coordination protocol that serializes the relevant child
  writes. This does not create a second metadata or authorization path.
- The managed-email `OwnerPack` ownership rejection remains an early
  generic-CRUD fail-fast gate and is also rechecked from the locked root image.
  The existing RecordShare parent lock gate is cross-row and is not falsely
  claimed as fixed by locking the share row; it needs an explicit parent-lock
  owner before that legacy mechanism can make a stronger concurrency claim.

**Evidence and next**

- Regression tests prove an unlocked preflight image may say `Open` while the
  locked image says `Locked`; update and delete both reject without DML.
  Production-adapter coverage also proves the authorization callback observes
  the repository lock before it sees the row, and nil authorization fails
  closed.
- Delete tests prove protected-record state is decided from the locked image.
  A `relatedExists` policy without serialized evidence is rejected rather than
  evaluated through an unsafe query.
- Next: before any route whose policy uses `relatedExists` can cut over, the
  Catalog/Persistence owners must define and test the child-write coordination
  rule. D2B must not restore the generic Query shortcut.

### Follow-up: a claim token is generation identity, not live attempt authority

**What I did**

- Reopened the complete scoped record-event claim lifecycle after exact-head
  review challenged the resume-after-expiry window: short claim transaction,
  heartbeat, effect admission, subscriber transaction, retry/poison
  bookkeeping and competing reclaim.
- Captured the generated refresh SQL. It constrained ID, processing status,
  claim token and Runtime work scope, but contained no lease predicate.
- Made Persistence own one complementary database-time lease boundary. Reclaim
  accepts `LastModifiedDate < cutoff`; refresh/effect admission accepts
  `LastModifiedDate >= cutoff`. Both use the same lease duration and TiDB
  `CURRENT_TIMESTAMP(6)`.
- Required a failed effect transaction's separate bookkeeping transaction to
  reacquire the live claim before changing retry or poison state. The original
  effect transaction's refresh and row lock were rolled back with that
  transaction and cannot authorize later bookkeeping.

**What failed and why**

- The durable token fix prevented a worker from mutating a claim after another
  worker had reclaimed it, but did not cover the interval after lease expiry
  and before that reclaim. A paused worker still had the matching token and
  could refresh `LastModifiedDate`, revive its expired attempt and win the row
  race against a legitimate reclaimer.
- Deployment ownership could not close this gap: two attempts from the same
  active deployment share the same ownership epoch. Existing tests covered a
  rotated token and a lost deployment, but not an expired unrotated token.
- Retry/failure bookkeeping opened a new transaction after an effect
  transaction failed. Without a second live-claim admission it could mutate
  queue state using authority that had been rolled back or expired.

**What I learned / decision**

- Scoped attempt authority is the conjunction of generation token, Runtime
  work scope, processing status and a live database-owned lease. No one field
  is sufficient by itself.
- Persistence owns this fact because it alone can atomically compare the lease,
  renew it and acquire the row lock. Events consumes the result; it does not
  infer liveness from process time or token presence.
- Once effect admission succeeds inside the pessimistic transaction, its row
  lock is held through subscriber DML and terminal status. A later standalone
  transaction must reacquire live authority instead of borrowing that rolled
  back proof.
- The legacy unscoped single-process repository retains its existing behavior;
  this closes the independently deployed scoped-worker state machine without a
  second capability plane or serving cutover.

**Evidence and next**

- Exact SQL tests prove live and expired scoped refresh outcomes, complementary
  database-time predicates and unchanged legacy refresh SQL.
- Events tests prove a non-live claim cannot reach subscribers and that retry
  bookkeeping revalidates after the effect transaction loses its proof.
- Persistence and Events full tests, vet and race pass. Shared and Kernel full
  tests and vet pass; backend full tests and vet pass. Boundarygate reports zero
  forbidden edges, zero contract Runtime leaks and zero new dialect tokens;
  `git diff --check` passes.
- Next: push one exact head and rerun Codex Review. The external TiDB worker
  recovery matrix remains the later composition proof.

### 2026-07-21 — D2A Related-List Counts Ownership Slice Closed

**What I did**

- Migrated `POST /ui-api/related-list-counts` through the released sfcrm
  AppService and neutral Runtime related-counts capability without changing the
  route response or adding a fallback.
- Published and independently verified the Runtime, AppService and Catalog
  owner images, then published signed resolved BOM
  `sha256:305c4ab88cf9efd0e04e23bb1d8aee5f48927b5ed01b9d202d371f1b576fa809`.
- Ran the exact-main stack on test2. Run
  [29845167957](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29845167957)
  produced sanitized v3 evidence artifact
  `serviceification-d1-test2-29845167957-1`.
- Closed the L1 ownership fact in app-packs #493, merged as
  `ff803124be991131153074381cf256fbeedb1d7b`.

**What I found**

- One HTTP batch crosses one Identity RPC and one Records RPC. Two requested
  parents produce two bounded Runtime admissions and two repository calls;
  empty and over-boundary requests are rejected locally without remote work.
- The new path preserves legacy response bytes semantically, deterministic
  ordering, parent-reference fallback, and missing-parent masking. Cancellation,
  Runtime/AppService/Gateway restart, and no-fallback outage checks pass.
- Catalog publishes three capability-specific Runtime metadata views and
  receipts from one snapshot: query, related-list counts and record access.

**What failed**

- The first new-BOM run exposed Catalog v3's obsolete exactly-two-capability
  assumption. The Catalog owner now accepts the complete supported plural set
  while unknown, duplicate and partial shapes still fail before mutation.
- One test2 attempt lost its TiDB connection during unchanged metadata DDL.
  The same source tuple had succeeded twice and the identical rerun passed the
  failure window, so no product code was changed for that environment event.
- The next run completed every service assertion but the final sanitizer
  rejected safe boolean key `parent_record_id_fallback` because it reserves
  the `record_id` token. L2 #4293 renamed the evidence-only fact to
  `parent_reference_fallback` without weakening the sanitizer.

**What I learned / decision**

- BOM grammar, Catalog input grammar and evidence grammar have separate owners;
  one version must not be derived from another merely because they participate
  in the same release.
- Capability count is not a Catalog semantic. Catalog validates each explicit
  supported binding and emits one receipt per capability; L1 AppRelease owns
  which supported capabilities compose the CRM release.
- A real-path success can still fail evidence publication. Evidence schema and
  sanitization therefore belong in the same executable closure, after the
  service assertions and before a family is marked proven.
- The observed local test2 latency is evidence, not an approved production SLO.

**Next**

- Keep this family closed unless a parity, ownership or no-fallback regression
  falsifies the proof. Continue D2A with the next coherent production read
  family; do not reopen the related-count contract or publication framework.

### 2026-07-21 — D2A Notification Read Ownership Model

**What I did**

- Audited both live notification read consumers: the product UI route
  `GET /product/notifications` and the Salesforce-compatible v59-v63
  `GET /connect/notifications` routes.
- Reused the existing neutral query RPC and query capability. No notification
  RPC, App capability, AppRelease, or Runtime engine path was added.
- Added one closed query operand that asks Runtime to resolve the verified
  `TrustedCall` principal. The request carries no principal identifier, and
  Runtime lowers the selector to an internal literal only after admission.

**What I found**

- AppService only owns an opaque end-user bearer and must not decode identity.
  Runtime already owns the verified principal, so caller-side principal
  injection would put the fact in the wrong layer and create a spoofable second
  identity path.
- Both notification surfaces are projections over the same bounded query:
  explicit root fields, recipient equality, descending creation time, and a
  fixed limit. Connect additionally selects unread rows and masks query failure
  as its existing empty-feed compatibility behavior.
- The existing CRM AppRelease already declares the query capability. The
  notification family changes the L1 service implementation and Gateway route
  table, not the released Runtime capability set.

**What failed / learned**

- The first contract PR combined root semantics with wire-go while wire-go
  still pinned the previous root tag. That shape could not produce independently
  consumable Go modules. The release was split into root `v0.1.26`, followed by
  a wire-only release pinned to that exact root.
- The durable rule is that a nested module must compile against an already
  published dependency. Cross-module source changes in one repository are not
  one atomic release.
- Reopening the product route's failure behavior found a pre-existing Runtime
  classification-order bug: bounded FLS admission overload implements both
  `RetriableCondition` and `InternalCondition`, but Runtime checked the latter
  first. The process boundary therefore changed the monolith's retriable
  `503 + Retry-After` into a non-retriable 500. Runtime now preserves typed
  retriable conditions before generic internal classification for all record
  read classifiers, and the L1 error projector carries the neutral retry delay
  into the HTTP header. This is an owner-layer fix, not a notification special
  case.
- The first retry fix used `errors.As`, which searched through an outer internal
  owner and across joined failures. That would have turned fail-closed internal
  conditions into retryable `UNAVAILABLE` and amplified load. Runtime now uses
  the shared owning-condition classifier: a transparent wrapper may expose the
  retriable owner, an outer internal owner wins, and any joined internal branch
  fails closed. Retry delay is read only from the same unique retriable owner.

**Next**

- Publish the wire carrier, pin Runtime and AppService, publish exact owner
  images, then run both real routes through Gateway, AppService, Runtime and
  TiDB with parity, RPC-count, recipient-isolation, outage and sanitized
  evidence gates before closing the family.

### 2026-07-22 — D2A Recycle-Bin Runtime Owner

**What I did**

- Added the neutral `ListRecycleBin` gRPC adapter to the independently serving
  Runtime API. It calls the existing records `ContractService` seam and keeps
  CRM URLs, duplicated wire aliases and Salesforce errors in L1.
- Bound the request to the existing immutable record-query metadata projection,
  trusted user, SecurityAdmission and ServingAdmission. No recycle-specific
  metadata store, repository, permission path or L1 import was introduced.
- Pinned the reviewed releases that own the capability: contract `v0.2.1`,
  wire-go `v0.1.12` and api-host `v0.14.0`.

**What I found**

- The existing records engine already owns tombstone enumeration plus CRUD,
  RLS and FLS visibility. Runtime needs only one coarse adapter and the existing
  query projection; Catalog remains the owner that selects that projection for
  an installed capability.
- The first real TiDB integration shape can reuse the production read graph and
  request-bound metadata view. It requires one gRPC request and one visibility
  query for the single-object fixture; multi-object fan-out remains bounded and
  deterministic inside the existing engine.

**What failed / learned**

- Backend full tests initially failed because the P6 dependency-evidence guard
  did not recognize the new root-contract and api-host releases. Exact repository
  diffs proved that they add only the recycle semantic/method/client surface and
  do not change delegation, identity, lease or P6 timing semantics. The guard
  now pins the reviewed merge SHAs instead of being weakened or bypassed.
- The isolated worktree does not contain the coordinated app-packs checkout at
  the relative path expected by the distributions module. Reproducing the CI
  checkout layout made that module's test and vet pass; the source change did
  not need a replacement-path workaround.

**Evidence and next**

- Backend full tests and vet, distributions tests and vet, targeted race tests,
  Runtime integration compilation, dependency pin checks, boundarygate and
  `git diff --check` pass. The real TiDB integration remains the source-backed
  CI/test2 proof; local execution correctly skips without the declared TiDB
  environment.
- Next: merge and publish this Runtime owner, add the minimal Catalog mapping to
  the existing query projection, activate immutable CRM `0.5.0`, then perform
  the L2 no-fallback ownership flip and exact signed test2 proof. This entry does
  not claim the recycle family or D2A complete.

### 2026-07-22 — D2B Mutation Composition Split at the Serving Boundary

**What I did**

- Reopened the first Runtime mutation API review against current `main` and
  traced every durable side effect produced by the existing Records graph.
- Extracted the request-bound Records/Permissions/Metadata/Automation engine
  composition into a non-serving Runtime assembly change. It defines one graph
  for future API and Worker owners but adds no process caller, route, listener,
  capability selector, or production activation.
- Moved the dormant graph into the dedicated
  `runtimeassembly/recordmutation` package. An executable dependency-closure
  guard proves that neither L0 commands nor product distribution commands
  contain that package before the later Runtime API ownership flip.

**What I found**

- A successful mutation can atomically create four durable work families:
  audit history fanout, command lifecycle, record-event dispatch, and rollup
  delta. Enabling the API before those ownership/schema closures exist would
  accept a command whose required effects cannot be completed.
- The engine graph and the serving process have different owners. The graph is
  reusable L0 composition; the API owns ingress and admission; each Worker role
  owns one fenced durable consumer. Keeping all three in one PR created a
  circular dependency and made a producer-only process look complete.

**What failed / learned**

- Unit, race, boundary and gRPC tests proved request semantics but did not prove
  capability closure. The strict review correctly found that audit and record
  events could be persisted without a deployed consumer. This was an ordering
  defect in the migration plan, not a reason to embed workers in Runtime API.
- The first split still exported a `NewServingRecordsMutationRuntime` and called
  the graph warmup `Prepare`. That allowed a caller to mistake engine validity
  for serving readiness even though the graph cannot observe the four durable
  work-family schemas and fences. The serving constructor was removed; the
  remaining operation is explicitly `PrepareEngine`. Runtime API will be the
  sole owner of the later ingress/readiness decision.
- The independently composed engine initially omitted Connected App refresh
  token revocation even though the monolith wires it into the record mutation
  transaction. This is not an HTTP-host concern: disabling or deleting a
  Connected App must revoke its durable tokens in every mutation composition.
  The shared graph now wires the existing L0 OAuth repository and an executable
  guard prevents that security lifecycle from disappearing silently.
- Process readiness must prove durable schema and an activated ownership tuple
  for every work family a mutation may create. It must not require transient
  worker-process liveness: an active durable queue may safely survive a worker
  restart, while missing authority or schema must fail closed before serving.
- The first non-serving guard inferred graph selection from source substrings.
  A constructor assigned to a variable could bypass that claim, and the graph
  was compiled into the already-serving `runtimeassembly` package even without
  a caller. Non-serving status is now structural: the graph has its own package
  and production command dependency closures exclude it. This is both simpler
  and stronger than teaching a text scanner every Go call form.
- The first graph adapter also repeated active Flow lookup that already belongs
  to `MetadataService.GetRuntimeActiveFlowVersion`. Runtime now supplies that
  canonical method directly to `FlowExecutor`; the adapter retains only the
  distinct mutation projections it owns. The request-bound view remains the
  sole source, but there is no competing method that can drift in trimming,
  profile validation, or failure semantics.
- The graph dependency value originally exposed the connection, raw database,
  transaction manager, and repositories as independently replaceable fields.
  A caller could therefore assemble repositories and raw writes on one pool
  while the transaction manager committed another. The dependency value is now
  opaque: its sole builder derives the complete database-backed closure from one
  Runtime-owned connection, and the engine derives its transaction manager and
  raw handle from that same connection. A structural regression test rejects
  any newly exported dependency field.
- The first dependency-closure guard ran the default distribution build only.
  The real CRM server and CRM setup entry points are build-tagged, so that test
  could miss a future production import. The guard now enumerates both tagged
  CRM commands explicitly. It uses `go list -e` only to tolerate an absent
  optional L1 sibling in a standalone core worktree; normal tagged build tests
  continue to own source/build validity.

**Next**

- Merge the non-serving engine graph and metadata adapter, then land the
  independently fenced Worker consumers. Rebase the Runtime API to the merged
  graph and enable serving only after it verifies the complete four-family
  durable-work closure. L1 capability activation and L2 test2 remain later
  ownership flips; this entry claims none of them complete.
### 2026-07-22 — D2B payload/lifecycle Runtime Worker core

**What I did**

- Reopened the current Runtime work authority instead of copying the abandoned
  all-in-one D2B branch. The existing Persistence-owned
  `RuntimeWorkOwnershipControl` remains the only activation/CAS writer; the
  independently deployed worker receives and verifies an exact authority tuple.
- Replaced the no-work worker composition with two real, payload-only roles:
  fenced audit-history fanout and expired record-command terminalization/
  cleanup. Both use the existing durable stores and the process-owned
  Start/Ready/Drain/Stop/Wait lifecycle, a role-specific bounded DB pool and
  bounded queue policy.
- Added queue/age/attempt/failure/in-flight and pool metrics, an audit poison
  compare-and-set repair job, strict role configuration, and dependency guards
  that exclude Runtime API assembly, controlplane, L1 dialects, Catalog
  selection and ownership authoring from the worker composition.
- Added a real-TiDB integration probe for ownership transfer, stale-worker
  exclusion, durable recovery by the next epoch, audit effect idempotency,
  terminalization of an expired accepted command, and command/operation cascade
  cleanup. Ordinary CI compiles the probe; D2B test2 must execute it.

**What failed / learned**

- The first file import accidentally targeted the shared main worktree because
  the patch tool does not inherit a shell command's worktree. Those six newly
  created files were removed immediately and recreated with absolute paths in
  the dedicated D2B worktree; no pre-existing main-worktree change was touched.
- The older branch put all four worker families, mutation API composition and
  metadata binders in one large review unit. The real ownership boundary is
  smaller: audit and command lifecycle consume no Catalog view, while record
  events and rollups interpret one immutable mutation view. Splitting there
  reduces review size without weakening the final D2B DoD.
- Worker startup must not create its own authority. A self-activating process
  would let two releases each claim legitimacy and would turn an operator CAS
  into a startup side effect. Startup now fails closed when the database row,
  release digest or epoch is missing or stale; test/deployment code must invoke
  the separate control owner explicitly.
- Review exposed that two CI guards still described the retired no-work worker,
  and that the process had no proof connecting its executable to the release
  digest in the durable fence. These were one transition-identity problem, not
  three independent bugs. An OCI digest cannot be embedded in the binary whose
  bytes create that digest, so the durable chain is now layered: the process
  compares its linked exact source revision with deployment config; the
  repository-owned signature/provenance maps that source revision to the OCI
  digest; and `_System_RuntimeWorkOwnership` fences that digest plus deployment
  and epoch. The image smoke verifies the first binding before its deliberate
  no-database failure, while exact-main CI executes the real TiDB fence/recovery
  probe. Direct self-assertion of an OCI digest remains forbidden because it
  would be circular rather than stronger evidence.
- A later review found that an ownership transfer could leave an idle old
  worker Ready indefinitely: family transactions verified the fence only when
  work existed. This was not an audit-specific queue bug. Deployment health and
  durable effect authority are two related but distinct invariants. The common
  worker composition now continuously verifies the same Persistence-owned
  authority tuple, withdraws readiness and cancels the family controller on
  loss, while every durable effect retains its transaction-local fence. The
  real-TiDB probe transfers ownership with an empty queue before publishing the
  recovery item, so health no longer depends on incidental work arrival.
- The source build and dependency guard passed while the deny-by-default
  Runtime Docker context omitted the new `runtimeworker` package. Reopening the
  complete image closure also found existing `runtimeidentity` and
  `runtimeproduction` omissions. The fix is not a worker-only allowlist entry:
  the process-boundary gate now derives every backend package in both Runtime
  command graphs and requires the Docker context to admit an explicit parent
  subtree. This makes a future source/image closure drift fail in the cheap
  boundary job before the trusted publisher runs.
- The real-TiDB worker probe now checks the actual live, ready, version,
  capability and worker/pool metrics surfaces after authority-backed startup.
  Image-only smoke still owns labels, embedded schemas, source revision and
  fail-closed startup; it cannot honestly claim Ready without TiDB and an
  activated ownership row.

**Evidence and next**

- Backend full tests and vet, targeted race tests, worker integration-tag
  compilation, boundarygate and `git diff --check` pass. The exact-main workflow
  now runs the tagged TiDB recovery test instead of merely compiling it.
  Boundarygate reports
  zero forbidden edges, zero contract Runtime leaks and zero new dialect-token
  hits.
- Next: complete review of this worker-core slice, then add the metadata-bound
  record-event and rollup roles against the same current Runtime mutation view.
  D2B remains in progress until exact owner artifacts are published, pinned in
  L2 and the separate-process real-TiDB test2 recovery/no-fallback matrix passes.
### 2026-07-22 — D2A Recycle-Read Ownership Slice Closed

**What I did**

- Merged and published the independent Runtime and Catalog owners, immutable CRM
  `0.5.0` AppRelease, and sfcrm AppService implementation for
  `GET /recyclebin`.
- Reused the established owner publishers and signed L2 BOM path. The decisive
  resolved BOM is
  `sha256:e7de62f8b02a7de9284d8ac7da21f27526ffba08965f9ee15f3d29dafef3a9c9`;
  it pins Runtime
  `sha256:079113233de476dc50004bfa042c76bac4ebaa52f3cfb956f148ced2743c7eec`,
  Catalog
  `sha256:d9f74487ba2db65147873545b591c3e04c1b47be52f8322995ed729c2a1dd70e`
  and AppService
  `sha256:44651ad9921ed525367972173caa286dbcf959340ba309ad9d5c39f68f3844cb`.
- Ran the exact signed Catalog-first stack against an isolated real TiDB
  database. Test2 run
  [29912105907](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/29912105907)
  passed publication verification, metadata materialization, the walking slice,
  evidence validation and cleanup.

**What I found**

- The completed route crosses Gateway, sfcrm AppService and one neutral Runtime
  recycle-listing RPC. The sanitized evidence records two accepted requests,
  two identity RPCs, two records RPCs, two repository calls, deterministic
  ordering and byte parity. Two local rejections caused zero remote work.
- Catalog reconciled the installed recycle capability to one immutable Runtime
  metadata view and compatibility receipt before serving. The evidence records
  `fallback=false`; the legacy endpoint was a reference oracle and never a
  recovery path.
- The AppRelease remained `0.5.0`: it already owned the metadata and capability
  fact. The final repair changed only AppService HTTP framing, so publishing a
  new exact AppService image and repinning the L2 BOM was sufficient. Inventing
  `0.5.1` would have coupled executable release identity to an unchanged
  metadata release.

**What failed and why**

- The first decisive run passed all prechecks and service assertions until the
  recycle response byte comparison. The first 683 bytes were identical; the
  AppService response had one trailing newline because the shared writer used
  `json.Encoder.Encode`, while the legacy Gin route used `json.Marshal` plus
  `Write`.
- This was an L1 HTTP projection defect, not a Runtime, metadata, security,
  database or service-boundary defect. The repair is route-local for all
  recycle success and error responses. The shared writer remains unchanged so
  mutation routes outside this D2A family are not silently pulled into scope.
- One exact-main AppService publisher correctly stopped before the stable tag
  when `main` advanced after the candidate was built. Re-running the existing
  exact-main handoff at the new commit produced the immutable image. No IAM,
  signer workflow or publication-state redesign was required.

**What I learned / decision**

- Byte parity is a real compatibility requirement. Replacing the comparison
  with semantic JSON equality would have hidden an observable wire change.
- The correct unit of progress is one complete capability family: exact owner
  artifacts, Catalog binding, independent processes, real database, failure
  behavior, no fallback and machine-readable closure. Component completion
  alone is not closure.
- Fix the fact at its owner. A one-byte L1 framing defect did not justify a new
  Runtime contract, global response writer, release model or publisher.

**Evidence and next**

- The sanitized v5 artifact is
  `serviceification-d1-test2-29912105907-1`; it contains no record payloads or
  tenant identifiers. Its recycle section proves parity, deterministic order,
  bounded RPC/repository counts, local rejection and zero fallback.
- App-packs #511 changes only the recycle-read machine-closure entry and its
  anti-overclaim test from activation pending to proven. Other D2A families
  remain explicitly planned; this result does not claim all synchronous reads
  are migrated.
- Next: merge that closure evidence and select the next coherent family from
  the live machine inventory. Do not reopen the recycle contract or publication
  framework unless new evidence falsifies an invariant proved here.

### 2026-07-22 — D2B metadata-bound Runtime Worker replay

**What I did**

- Rebuilt the record-event and rollup worker roles on the current non-serving
  mutation graph before publishing a review branch. Each process accepts one
  exact deployment fence and one Org/AppInstallation/capability scope; each
  durable row carries the exact Catalog snapshot and Runtime metadata-view
  digests and revision/generation lineage that its effect must interpret.
- Added a binder that opens only those immutable content-addressed artifacts,
  verifies source lineage, projection profile, compatibility and process scope,
  then installs the already-opened view on the effect transaction. It has no
  active/latest selector, Catalog repository, TTL authority or fallback.
- Reused the Persistence-owned work-family authority for process health,
  transactional effects, poison disposition and compare-and-set repair. The
  worker can verify and lock an activation but cannot create, select or transfer
  one.
- Made producer activation process-owned and executable. A dormant mutation
  graph produces nothing; a process must first prove the audit, record-event
  and rollup schemas plus one canonical active owner for every family before it
  can enable the graph-local Audit V2 emitter.
- Extended the exact-main TiDB gate to execute both the payload/lifecycle and
  metadata-bound worker probes, and require an explicit pass event for each.

**What failed / learned**

- Replaying the older worker commits exposed three integration-history defects:
  a pre-package-split mutation-runtime type, the retired repair/config shape,
  and a standalone distribution dependency check accidentally evaluated under
  the root Go workspace. They were corrected against the current owner APIs;
  the distribution check now explicitly uses `GOWORK=off`.
- The first boundary update recognized only the exact mutation-graph package
  and scanned only files in the worker package root. That would let future graph
  or worker subpackages escape the claimed production closure. The guards now
  classify the complete graph subtree, recursively inspect future worker
  subpackages, and allow only `runtimeassembly/recordmutation` beneath the
  shared assembly parent. The older process-boundary shell check used substring
  matching and therefore misclassified this allowed child as the forbidden
  parent; it now rejects the parent and every other sibling explicitly.
  `cmd/runtime-worker` is the one permitted production consumer; Runtime API,
  legacy server/setup and distribution commands remain denied.
- The role document did not need a second JSON-schema authority. The checked-in
  Runtime Worker schema describes environment variables and the role-file path;
  strict role v1/v2 decoding and validation belongs to `runtimeconfig.LoadWorker`.
  A positive schema-v2 file-load test now proves that real ownership instead of
  duplicating it.
- The initial JSON test pipeline used `go test | tee` without explicitly
  enabling `pipefail`. A package-level failure after individual test pass events
  could otherwise become false-green evidence on a shell that reports only
  `tee`'s status. The gate now preserves the test process exit status and also
  checks the two exact test pass events.

**Evidence and decision**

- The clean replay passes full backend, Persistence and Records tests/vet;
  targeted race tests; Runtime Worker integration-tag compilation; the Runtime
  process boundary; boundarygate with zero forbidden edges, zero contract
  Runtime leaks and zero new dialect-token hits; workflow parsing; and
  `git diff --check`.
- No local TiUP/TiDB cluster was started. Those checks prove compilation and
  in-process invariants, not real database behavior. The formal branch must be
  rebuilt from the mutation graph's squash-merged `main`, and exact-main CI must
  execute artifact loss/restore, poison/repair, ownership transfer, stale-owner
  exit and real rollup effects against TiDB before this worker slice is closed.
- D2B remains in progress. Runtime mutation transport/admission, L1 capability
  activation, signed owner artifacts, L2 BOM repinning and separate-process
  no-fallback test2 remain later ownership flips.

### 2026-07-22 — D2B worker startup and retention review blockers

**What I did**

- Reopened both exact-head review blockers as lifecycle-owner failures instead
  of patching the named lines. The database blocker applied to every process
  composition that already had a startup context; the retention blocker
  required a worker-owned maintenance state machine, not an unfenced delete.
- Added one context-aware database connection constructor that captures the
  immutable driver/DSN specification, applies the requested pool bounds and
  uses the caller context for the first network probe. Runtime API, Runtime
  production, Runtime Worker and tenant-overlay setup now use it; the legacy
  singleton entry point remains compatible.
- Added explicit record-event processed-row retention and cleanup cadence to
  the strict worker role. A separate bounded maintenance tick calls the
  existing scoped cleanup inside the same transactional ownership fence as
  dispatch. Other work families reject those settings.

**What failed / learned**

- The worker previously called a context-free constructor that performed an
  unconditional first `Ping`, then performed a second `PingContext`. The
  visible 15-second startup context therefore guarded only redundant work. A
  lifecycle deadline is meaningful only when the first side effect consumes
  it.
- Calling cleanup on every queue poll would have traded table growth for idle
  fenced-transaction pressure. Queue polling and retention maintenance share
  one process and fence but require independent cadences.
- Processing a durable row is not the end of queue ownership. A real worker
  capability also owns bounded terminal retention; otherwise successful
  operation creates an unbounded database obligation.

**Evidence and next**

- Unit tests prove first-probe cancellation, strict retention/cadence parsing,
  work-family ownership, maintenance ownership loss and bounded pass behavior.
  The real-TiDB exact-main probe now inserts an expired processed record-event
  row and requires the independently fenced worker to delete it.
- After local full, vet, race, boundary and tagged-test compilation pass, push
  the corrected #3898 head. Exact-main TiDB execution remains the closure gate;
  this review repair does not claim D2B completion.

### 2026-07-22 — D2B durable execution envelope closure

**What I found**

- The record-event worker reopened an exact Runtime metadata view but installed
  only that view on the effect transaction. The rollup worker installed both
  the view and the queryable `RuntimeWorkScope`. These are two consumers of the
  same durable binding and therefore must not reconstruct different execution
  contexts.
- Current Records event writers derive the scope again from the bound view, so
  the review's specific claim of an already-reproduced silent Flow-event loss
  was too strong. The shape was still unsafe: any scope-aware nested effect
  outside that one writer would receive an incomplete context.

**Root fix and proof**

- The worker now has one prepare/install path. It verifies immutable artifacts
  before the effect transaction, derives the scope from the verified durable
  binding, and installs the complete `RuntimeMetadataView + RuntimeWorkScope`
  envelope in both record-event and rollup paths.
- Events now models the installer as a fallible operation. A partial or failed
  envelope installation fails before publish rather than silently publishing
  with missing execution state.
- Tests prove a context restored from an encoded record-event binding can write
  to a matching scoped queue, and prove installer failure produces no publish.
  Events and Backend full test/vet, targeted race tests, the Runtime process
  boundary and `git diff --check` pass locally.

**Decision and next**

- The D2B Runtime Worker boundary remains correct. The missing invariant was
  complete durable execution-context restoration, owned by the binder rather
  than by individual downstream effects.
- Before the Runtime mutation API ownership flip, inventory CRM product
  lifecycle capabilities that the legacy CRM composition registers. A Runtime
  graph must either own their neutral mechanism or fail closed outside launch
  scope; it cannot silently omit an L1 lifecycle effect.

### 2026-07-22 — D2B Runtime mutation admission reconstruction and L1 activation gate

**Exact current-tree evidence**

- NexusCRM base: `937cc7d669493170bb44282ab745d6544207eefe` (`D2B: run
  metadata-bound work in fenced Runtime Workers (#3898)`).
- L1 dialect source inspected: app-packs
  `c199067fa7fc549449f1a207c9d70de9c8624a8b`.

**What I did**

- Rebuilt the former mutation-API proposal from the current `main` tree rather
  than rebasing its obsolete composition. The independent Runtime API now
  retains the existing read graph and conditionally constructs the coarse
  mutation graph from the same Runtime-owned TiDB connection.
- Added the released neutral `RecordMutationsService` adapter, stream
  admission, immutable-view binding, trusted-call installation, request caches,
  status adaptation and closed-operation SQL metrics. The process registers the
  mutation service only when the serving selection explicitly includes the
  neutral capability and a positive capability-owned outcome retention.
- Reopened the complete durable-output closure. The initial reconstruction
  verified audit, record-event and rollup workers but omitted the command
  outcome lifecycle worker. Added one Persistence-owned command-schema verifier
  and require the canonical active `runtime.record-command-lifecycle.v1` owner
  before a mutation graph can enable its producers.
- Re-derived the legacy CRM lifecycle path instead of assuming the generic
  mutation engine was complete: the CRM distribution registers
  `crm.approval-process-child-sync` for `after_create_in_tx` and
  `after_update_in_tx`; the L1 dialect decodes `ProcessDefinition.Metadata` and
  materializes `ProcessNode`/`ProcessAction` rows in the caller transaction.

**What failed / root cause**

- The old proposal replaced the read graph with a mutation graph. That would
  have removed existing query-history and recycle-bin behavior. It also treated
  a common database connection as proof that all capability-specific metadata
  should share one graph.
- Current tree disproves that assumption. The mutation metadata resolver
  rejects the record-access projection and its schemas; the two capability
  profiles are deliberately least-privilege and cannot be combined without
  creating a broader read authority or a second fallback.
- A generic Runtime cannot import the L1 callback above, and its built-in
  lifecycle registry does not implement the CRM capability. Routing ordinary
  CRM mutations to Runtime today would therefore silently omit a required
  in-transaction product effect. That is an L1 activation blocker, not a
  reason to encode `crm.*` behavior in L0.

**Decision — default answer, proof, failure condition, alternative and DoD**

- **Default answer:** compose separate capability-profiled graphs from one
  Runtime-owned connection; do not create a second database authority and do
  not activate the CRM mutation route yet.
- **Proof method:** the Runtime process test drives one client-streaming RPC
  through P6 trusted delegation and P7 serving admission, proves a bound
  mutation receipt and rejects missing or mismatched delegation before the
  executor. The mutation metadata test proves a record-access view cannot enter
  the mutation graph. The producer closure now proves command schema plus all
  four canonical durable worker-family owners before activation.
- **Failure condition:** a deployment can serve when any command-recovery owner
  is absent; a graph can read a different capability profile or repository/global
  fallback; or CRM `ProcessDefinition` create/update stops materializing its
  child projection. Any of these blocks D2B activation.
- **Alternative:** an L1 compiler may lower the declared lifecycle behavior into
  a genuinely generic, revision-bound L0 primitive and prove the same atomic
  child effects. An object-specific Runtime exception or a legacy in-process
  fallback is not an alternative.
- **Final DoD for the activation gate:** the launch-scope L1 mutation metadata
  has no legacy callback dependency; Runtime interprets the compiled neutral
  artifact under one bound view and transaction; current CRM route parity proves
  child materialization; and the test distribution has no legacy fallback.

**Evidence and next**

- Backend and Persistence full-module test/vet pass. Persistence full race and
  focused Runtime API/mutation-assembly race pass. The Runtime production,
  worker and dependency-boundary tests pass; boundarygate reports zero forbidden
  edges, zero contract Runtime leaks and zero new dialect-token hits; and
  `git diff --check` passes. No local TiUP/TiDB was started.
- This branch is an uncommitted current-main reconstruction, not D2B closure:
  it has no L1 route flip, owner artifact, BOM repin or test2 evidence.
- Next: preserve the generic API/worker mechanism; open the L1 lifecycle
  compilation/activation design only after its current consumer and exact
  transaction-parity proof are specified. Do not claim that a new Runtime RPC
  alone migrates CRM mutation traffic.

### 2026-07-22 — D2A record-view attachment-source matcher closure

**What I did**

- Re-derived the attachment-decoration prerequisite from the current
  non-serving record-view metadata closure, rather than reviving the obsolete
  D2A route proposal. The old closure named only a source object and its file
  reference field; it did not declare how a value in an authorized root field
  identifies a source row.
- Added a V6 CatalogSnapshot/RuntimeMetadataView grammar. A V6 resolver has a
  closed, AppRelease-owned matcher: either the complete source record
  identifier or one bounded byte segment. Its byte offset remains a
  request-scoped L1 proof because its position in a product value varies per
  record. The compiler validates canonical ordering, rejects a
  missing/unknown/unbounded matcher, and reuses the existing 255-byte
  record-identifier contract rather than inventing a new limit.
- Preserved V5 decoding only for audit of historical artifacts. A V5
  record-view view exposes neither source resolver nor source-field authority,
  so an older artifact cannot accidentally become serving-capable.

**What failed / root cause**

- The earlier record-view design treated an opaque L1 source value as if it
  were always a complete record identifier. That loses the distinction between
  a full identifier and an identifier embedded in a product-owned URL or other
  value grammar. It would let a future Runtime either guess the grammar or
  query with an unproven value.
- Moving URL parsing or a product-specific prefix into L0 would merely shift
  the ownership error. The source-value proof remains opaque and byte-accurate
  at the L1/Runtime wire boundary; Catalog owns only the neutral relation that
  says which exact portion may identify the source row.

**Decision and next**

- This change is metadata grammar only: it adds no serving Runtime adapter,
  route, database lookup, L1 parser, or fallback. The next Runtime owner must
  require the V6 binding, prove the FLS-authorized field and exact byte offset,
  apply the matcher before any source/file lookup, and make every invalid or
  hidden proof perform zero lookup with an opaque unavailable outcome.
- The App compiler must supply the declared matcher as an AppRelease fact.
  Only after that compiler pin and the Runtime proof are complete may the L1
  record-view family replace its legacy decoration path and be activated in an
  L2 test2 run.

**Evidence**

- Built from NexusCRM `0675cf62cb2646e3d90e6a6679665cf81ad0934c`.
  `nexuscrm-metadata` full test/vet, the catalogsnapshot race test,
  boundarygate tests and `git diff --check` pass. The public-core scan still
  reports only its pre-existing unrelated baseline hits; it reports no changed
  catalogsnapshot path.

### 2026-07-22 — D2B Runtime Catalog serving read and source-pinned walking-slice closure

**Run ID:** `d2b-catalogread-source-tuple-20260722`

**What I did**

- Reopened Runtime production composition and removed its dependency on
  `catalogapp.Authority`, which is Catalog's lifecycle/write owner. Runtime now
  consumes only Catalog's `catalogread.Reader` and obtains an installation plus
  active HandlerBinding as one statement-consistent serving tuple.
- Updated the tagged separate-process integration fixture to seed Catalog's
  authoritative test schema and read that tuple through `catalogread`, instead
  of retaining deleted two-read test seams. An inactive installation is now
  explicitly proven unavailable.
- Reconstructed the L1 source inputs for the walking slice from the platform's
  only source lock: official pack/dialect source is
  `f6470f7130d5cf9893e57123891d6721ae154c13`; independently released sfcrm
  AppService source is `047f9c119132ee3f36483bf5efb5051c79a60851`.
- Made `test-runtime-l1-walking-slice.sh` require both inputs, verify each
  checkout's exact revision, and prove that the official-pack checkout is the
  same physical directory used by the distribution module's relative L1 Go
  replacements. It also rejects an AppService path whose declared Go module is
  not the sfcrm AppService module.

**What failed / root cause**

- A local tagged-build attempt used the ordinary D2B app-packs worktree rather
  than the two source-locked inputs. It failed because that old dialect source
  did not contain the formula-validating ApprovalProcess decoder now required
  by the platform. That was not a Runtime or Catalog failure.
- The real harness flaw was broader: its AppService override was explicit, but
  the dialect and wire dependencies came implicitly from
  `backend/distributions/go.mod` relative replacements. A caller could combine
  an arbitrary official pack checkout with a separately pinned AppService and
  discover the invalid tuple only in Go compilation.
- The current L2 D1 BOM is intentionally not a pre-merge test input for this
  change: its Runtime source is `f7a5ccafa81ded828d901cf1ab71d5506f38d881`,
  while this work is based on a later NexusCRM revision. It becomes relevant
  only after owner artifacts are published and the BOM is repinned.

**Decision and next**

- Two app-packs revisions remain separate authorities; do not collapse them to
  one SHA. The harness must instead prove their explicit pair before compiling
  or serving the slice.
- The pre-merge proof is exact-source tagged compilation and the source-tuple
  guard. The security-preserving real-TiDB separate-process gate is exact-main
  only because it checks out private L1 sources and executes code with its
  credentials. Do not weaken that trust boundary merely to run PR-head code on
  test infrastructure.
- Evidence: the valid source pair reaches only the expected missing-TiDB
  precondition; wrong official-pack and AppService revisions fail before Go;
  the tagged distribution package compiles with the valid pair; Runtime API,
  boundary, appsource and process-boundary tests pass; `bash -n` and
  `git diff --check` pass. No local TiUP/TiDB was started.
- Next: land the exact #3925 head, require exact-main's source-backed real-TiDB
  gate, then publish the Runtime API/Worker owners and repin the L2 BOM for the
  D2B no-fallback test2 proof. This does not mark D2B or a production route
  cutover complete.

### 2026-07-23 — D2B exact-main Worker scratch-schema closure

**Run ID:** `29964589474`

**What I found**

- Exact-main's Runtime Worker gate reached the two real-TiDB recovery tests and
  failed before either Worker was assembled: the scratch-schema bootstrap
  requested `_System_UserRole`, which does not exist in the canonical schema
  catalog.
- `_System_UserRole` is not a canonical system table. `UserRole` is instead a
  generic L0 identity primitive, but the Worker integration's only bound
  mutation view declares `Parent` and `Child`, and its durable recovery
  scenarios have no role-hierarchy work path. The hand-written table list had
  therefore named a nonexistent system table, not established that a real
  identity-table dependency belonged in this narrow fixture.

**Root fix and proof**

- Removed the nonexistent pseudo-system table rather than blindly renaming it
  to `UserRole`. The latter is a generic identity primitive, but adding it to
  this narrow fixture would still create an untested dependency solely to
  silence a startup probe.
- Centralized the scratch fixture's canonical `_System_*` physical-table
  closure in one test-only helper. A normal (non-TiDB) test now requires every
  requested table to both exist in `schemacatalog.SystemTableDefinitions` and
  begin with `_System_`. The tagged real-TiDB test consumes that same closure,
  so an unknown system table or a future unproven non-system dependency fails
  locally before the expensive D2B workflow.

**Evidence and next**

- `GOWORK=off go test ./internal/runtimeworker`, its
  `runtime_worker_integration` build, both corresponding race runs, and
  `GOWORK=off go vet ./internal/runtimeworker` pass on the correction;
  `git diff --check` passes. No local TiDB was started.
- The correction does not prove TiDB recovery by itself. After exact-main
  reruns the two original recovery tests successfully, D2B still requires the
  owner-artifact publication, role-derived L2 BOM resolution, trusted-signer
  activation, and the signed no-fallback test2 proof before closure.

### 2026-07-23 — D2B Worker producer-closure evidence correction

**Run ID:** `29966995341` on `f3eb01153aa5c5fd9e9848f5383bc9bac765e515`

**What I did**

- Read the completed exact-main `source-backed-runtime-gate` failure and
  traced `runtime-worker` assembly to the durable producer activation check.
- Re-derived the complete output closure from the shared mutation graph rather
  than treating the first reported missing family as the only prerequisite.

**What failed / root cause**

- The real-TiDB metadata-worker test activated the record-event and rollup
  owners, then deliberately proved that an absent audit owner blocked startup.
  It activated audit and incorrectly assumed that the closure was then
  complete. The graph correctly also requires the independently fenced
  `runtime.record-command-lifecycle.v1` owner, so assembly failed before the
  event worker started.
- The log also contained a non-fatal `UserRole` preload warning. The previous
  entry incorrectly called `UserRole` an L1 CRM product table. The platform
  architecture classifies `User`, `Profile`, `Group`, and `UserRole` as generic
  L0 identity primitives. That warning is not the gate failure: the tested
  system-context event and rollup work does not use role hierarchy data. It is
  evidence that the legacy permission constructor still performs an eager,
  best-effort role-cache preload; it must not be hidden by reintroducing the
  nonexistent `_System_UserRole` pseudo-table.

**Root fix and proof**

- The integration test now proves the owner chain explicitly: absent audit
  blocks assembly; after audit is active, absent command lifecycle blocks it;
  only after all four canonical owners are active may the event worker start.
  This keeps the production readiness rule authoritative and prevents a test
  from declaring a partial closure complete.
- This is a fixture/readiness-proof correction, not a new Worker capability,
  metadata authority, or fallback. The generic identity preload behavior is
  recorded separately because it is not exercised by this no-user work family.

**Next**

- Run the same tagged test against the declared test2 TiDB after exact-main
  validates this correction. Only then may the Runtime API/Worker source be
  published, followed by the signed BOM repin and final no-fallback D2B proof.

### 2026-07-23 — D2B exact-main workspace module-graph seal

**Run IDs:** `29968817855` on `036e1d0bb952c47fcc18a74257a2edad5d6a1047`
and `29969323450` on `f494ec41883d7867be0cdd23606db29eb68ba809`

**What I found**

- Both exact-main `go-ci-main` runs failed in `build-test-seal` before the
  source-backed Runtime Worker/TiDB gate could run. The failing command was
  `scripts/ci/check-workspace-module-graphs.sh`, whose per-module
  `GOWORK=off go list -mod=readonly -deps -test ./...` rejected a stale module
  graph with `updates to go.mod needed`.
- This was not a missing `go.sum` line and not a Runtime Worker failure.
  The D2B contract upgrade had already made the workspace select
  `github.com/pingcap-inc/nexuscrm-contract v0.2.8` through local `records`.
  `automation` and `metadata` import that contract directly, and `analytics`
  reaches it through the local engine graph, but each still declared the older
  `v0.1.24` requirement. Go's readonly canonical-module rule correctly
  rejected that inconsistent release closure. The second failure proves that
  #3934's Worker producer-closure correction neither caused nor masked the
  release-closure defect.

**Root fix and proof**

- Normalized only the three modules that the exact CI predicate proved stale:
  `nexuscrm-analytics`, `nexuscrm-automation`, and `nexuscrm-metadata`. Their
  contract requirement/checksum now record the already-selected `v0.2.8`.
  No Runtime behavior, metadata authority, worker lifecycle, or dependency
  direction changed.
- The original `scripts/ci/check-workspace-module-graphs.sh` passes after the
  normalization, as does its equivalent readonly dependency traversal for all
  `go.work` modules. Targeted test and vet runs pass for all three modified
  modules, and `git diff --check` passes.
- Do not use a broad local `go mod tidy` as a substitute for this proof. The
  repository's CI-only L1 conformance fixture deliberately resolves a private,
  source-pinned module unavailable in this standalone local checkout; a broad
  tidy would conflate that test harness topology with the exact readonly
  production module-closure failure. The CI predicate identifies and verifies
  the actual ownership boundary.

**Next**

- Merge this small module-seal correction and require a new exact-main run to
  reach the real TiDB Runtime Worker gate. Only a passing gate unblocks owner
  artifact publication, signed L2 v6 BOM resolution, and the final test2
  no-fallback proof; none of those stages is implied by this module fix.

### 2026-07-23 — D2B record-event claim clock and poison-repair root cause

**Evidence:** exact-main runs `29970218730` and `29979019545` on the current
Runtime Worker recovery gate.

**What I did**

- Traced the actual durable state machine rather than treating the two-second
  test timeout as a Catalog or worker-assembly failure:
  `ClaimEvent -> RefreshOutboxProcessingClaim -> prepare immutable metadata ->
  terminal/retry transition`.
- Compared the Runtime's database-clock lease expression with the physical
  `_System_RecordEventOutbox.LastModifiedDate` shape and with the repair
  receipt predicate.

**What failed / root cause**

- The worker uses a microsecond database clock and a two-second lease, but the
  record-event outbox declared `LastModifiedDate` as second-precision
  `DATETIME`. A refresh in the same stored second matched the live claim but
  wrote the same value. TiDB correctly returned `RowsAffected=0`; the Runtime
  incorrectly interpreted that no-op write as a lost claim and safely abandoned
  the attempt. The row was then reclaimed after its two-second lease, leaving
  it `processing` with retry count zero.
- The terminal-failure path separately dropped the exact failed-attempt count.
  Runtime poison repair compares both the observed error and attempt count, so
  a first-attempt immutable-artifact failure could not be repaired safely even
  if it had reached `failed`.
- The first attempted correction still let the service author that count from
  the `RetryCount` returned by candidate discovery. Codex Review on
  `d8f8e6d8fba7a8b890fc360285fe9a0ee088c79c` exposed the broader state-machine
  defect: discovery can precede another worker's claim/retry transition, and
  invalid-payload branches still bypassed the new terminal operation. A
  caller-supplied count was therefore not a durable receipt even when the final
  SQL update was token-fenced.

**Root fix**

- The schema authority now requires `DATETIME(6)` with
  `CURRENT_TIMESTAMP(6)` for the record-event lease clock. Runtime readiness
  rejects the legacy shape, so an old database cannot silently serve the new
  Worker binary.
- Refresh uses a database-only strictly advancing expression
  (`GREATEST(now, last_modified + 1 microsecond)`), making a matched CAS
  observable even at equal database clock precision. It does not use a process
  clock or weaken the claim token/status/scope predicates.
- `ClaimEvent` now reads the durable retry count from the successfully claimed
  row through the same executor and returns it in the claim receipt. The
  service no longer carries the earlier discovery count into execution.
- Retry and terminal failure no longer accept caller-authored counts. Their
  fenced persistence operations increment `RetryCount` in the database while
  storing the next status and error. Retry exhaustion is selected from the
  claim receipt, and invalid payloads use the same terminal operation. This
  makes the exact count, error and state one database-owned transition while
  preserving the receipt needed by compare-and-set poison repair.
- The generic schema reconciler owns the compatible `DATETIME -> DATETIME(6)`
  widening. It admits only non-narrowing precision changes within the same
  `DATETIME` or `TIMESTAMP` family; family changes and narrowing remain explicit
  migrations. That change is owned and released from `nexuscrm-controlplane`,
  not hidden in Runtime startup.
- `nexuscrm-controlplane` consumes the catalog through a published Core kernel
  pin. Its generic reconciler deliberately does not duplicate the new table
  definition. After the Core artifact containing this schema change is
  published, a normal exact-pin update must release controlplane against that
  artifact; only that release can apply the physical DDL in test2.

**Proof and limits**

- Unit tests cover the full DDL, Runtime readiness rejection of the old shape,
  strictly advancing refresh SQL, durable count capture after claim, stale
  discovery versus current claim, atomic retry/terminal increments, malformed
  payload terminalization, and exact terminal attempt count. Affected shared,
  kernel, persistence, records, events and runtime-worker tests/vet pass
  locally; focused persistence/events race tests pass; no local TiDB was
  started.
- The control-plane reconciliation unit tests pass. Its whole local suite has
  unrelated environmental failures because this worktree lacks the official
  app-packs root, plus an existing L0-reference inventory expectation; neither
  failure reaches this reconciliation path.
- This is still not D2B closure. The next proof is: publish the Core schema
  artifact, update and publish the controlplane exact Core pin, then perform
  the owner-artifact upgrade against test2 TiDB and rerun the unchanged real
  recovery test. Only after that can the signed L2 BOM and no-fallback D2B
  test2 proof proceed.

### 2026-07-24 — Launch-candidate recovery: local-first composition proof

**Source tuple used for discovery**

- NexusCRM: `b3d488f0bb9793ec9eae11b6e6cd32caa92fe06d`
- nexuscrm-controlplane: `c087262603153bab7ff2d7eae4827ebf56d5e000`
  plus the local composition correction described below
- nexuscrm-app-packs checkout:
  `3b0bd55c53ec82316097f8a7ae4e0c647035f0dd`
- local TiDB: existing `nexuscrm-public-core-tidb-8` container, port `4001`;
  clean validation database
  `nexuscrm_serviceification_latest_20260724`

**What I did**

- Stopped treating every new repository `main` as the next release candidate.
  D2A and D2B remain parallel implementation tracks, but they now converge
  through one frozen L2-owned source tuple.
- Used local deployment as the functional discovery loop and CI as the
  compilation, boundary, exact-source, and publication admission loop.
- Ran the complete release lifecycle from an empty database:
  `apply-metadata -> repair -> apply-packs -> install-pack -> verify-pack`.
- Ran the real Runtime API/TiDB cross-process read and identity-exchange
  admission tests, plus the Runtime Worker fence, restart, durable event,
  poison-repair, and rollup tests against local TiDB.

**What failed / root cause**

- New Permission State direct-writer enforcement correctly rejected
  controlplane bootstrap repositories that had no coordinator. The defect was
  not a missing retry or a D2B metadata classification problem. Controlplane
  had several production repository compositions, but did not own one shared
  `TransactionManager -> PermissionStateAuthority ->
  PermissionStateDirectWriterCoordinator` graph.
- The first composition correction accidentally created a second
  `TransactionManager` over the same `*sql.DB` inside the pack service graph.
  Persistence correctly rejected the nested transaction because transaction
  identity belongs to the manager, not merely to the database handle.
- `ApplyMetadata` originally performed managed schema metadata writes before
  the Permission State singleton existed. Wiring the coordinator alone would
  therefore only change the failure from "missing coordinator" to "missing
  singleton".
- After updating to current NexusCRM `main`, Runtime Worker integration tests
  failed to discover linked contract/api-host versions. Production binaries
  contain those dependency records, but Go deliberately omits dependency
  modules from `.test` binary build information. This was an evidence-harness
  defect, not a production Runtime failure.
- Boundarygate also treated the `sobject` substring inside neutral identifiers
  such as `crossObject` as a new dialect leak. At the same time, a new
  automation path did use the legacy `models.SObject` vocabulary. The two
  facts had to be separated: remove the real L0 leak and correct only the
  ambiguous scanner token.

**Root fix and proof**

- controlplane now owns one reusable Permission State direct-writer
  composition. Bootstrap and standalone backfill create it from their one
  connection; the pack service graph reuses its already-existing exact
  transaction manager. Schema, metadata, permission, and user repositories
  bind to the same coordinator.
- `ApplyMetadata` now creates physical L0 DDL, reconnects, initializes the
  Permission State singleton, and only then runs compatibility-preserving
  managed schema convergence. A second reconnect preserves TiDB schema-cache
  behavior before later reconciliation.
- Runtime production still derives capability identity only from its linked
  executable. The real-TiDB test build uses an explicit factory and a
  build-tag-only integration constructor; there is no mutable global factory
  and no production API for synthetic build information.
- Automation uses neutral `models.Record`; the compatibility helper remains
  only for existing callers. Boundarygate keeps legacy substring freezing for
  all unambiguous dialect tokens and uses identifier/CamelCase boundaries only
  for `sobject`.
- Clean local lifecycle result: `ApplyMetadata` completed all 8 steps,
  `repair` completed all 39 steps, runtime packs applied, the official CRM
  pack installed, and source-backed verification passed.
- After the controlplane owner branch rebased onto `2a6c5c6` (the bounded
  polymorphic-backfill scan change), both real-TiDB permission-state bootstrap
  integration tests passed again. The release-lifecycle evidence remains tied
  to the frozen source tuple above rather than silently relabeling a newer
  owner revision as already proven.
- Latest-source Runtime proof passed:
  `TestRuntimeAPIRealQueryRepositoryCrossProcess`,
  `TestIdentityExchangeTokenIsAcceptedByExistingRuntimeAdmission`,
  `TestRuntimeWorkerRealTiDBFenceRecoveryAndLifecycle`, and
  `TestRuntimeMetadataWorkersRealTiDBExactArtifactsRepairAndEffects`.
  Controlplane full `go test ./...` and `go vet ./...`, Runtime
  Permission-State/Worker race tests, focused approval tests, and boundarygate
  all pass locally.

**Decision**

- Local deployment is the fast, authoritative discovery loop for lifecycle,
  service behavior, ownership, restart/fencing, and no-fallback correctness.
  CI remains authoritative for repeatable code checks, module/boundary policy,
  exact-source identity, and signed publication. Test2 confirms the already
  proven immutable candidate; it is not the first ordinary functional run.
- Do not publish separate D2A and D2B full BOMs or repin after every merge.
  Freeze one tuple, admit a newer revision only for a demonstrated launch
  blocker, and update the tuple atomically.

**Next**

- Merge and release the controlplane composition fix.
- Pin that released controlplane version in NexusCRM, then run the same local
  lifecycle without any local `replace`.
- Publish one immutable L2 launch candidate, run signed test2 confirmation,
  and proceed to the launch-required D3 acceptance set. Unrelated later-main
  work remains outside that candidate.

### 2026-07-24 — R0 exact-main health before launch-candidate freeze

**Evidence**

- The release candidate was deliberately refreshed once to NexusCRM
  `1832430f9324f0d0fc00152adff33b1d8376bf68` to close current-main health
  before freezing. Exact-main CI first failed the standalone module-graph seal
  and the backend CRM Docker build with a read-only `go.mod` update request.
- `nexuscrm-connectors v0.2.1` had advanced the selected contract graph while
  `backend` and `backend/distributions` still directly pinned
  `nexuscrm-contract v0.2.9`. Both module owners now pin the published
  `v0.2.10`; the resulting direct `golang.org/x/sys` requirement reflects an
  existing backend import rather than a new dependency.
- The full backend suite then exposed two hidden gates. The formula boundary
  ratchet counted two new marker words in explanatory comments, not two new
  compatibility branches; the comments now use neutral prior-state
  terminology and the ledger remains unchanged. The P6 evidence gate correctly
  rejected the new contract pin until it was reviewed: the four P6-sensitive
  package trees are byte-identical between `v0.2.9` and `v0.2.10`, and
  contract PR #75 exact-head Standard/Adversarial review approved release
  merge `c2016a175ab7f379f5f4f8ac28c357ff53b5dd54`.

**Proof and decision**

- Workspace module graph, private release pins, boundarygate, Runtime API and
  Worker builds, CRM distribution build, and complete backend, automation,
  kernel, and distribution tests pass locally. Boundarygate reports zero
  forbidden edges, zero contract runtime leaks, and zero new L0 dialect hits.
- This R0 change restores one publishable NexusCRM baseline; it does not add a
  capability or reopen D2A/D2B. After exact-main CI and both Runtime
  publishers pass, this merge SHA becomes the NexusCRM member of the single
  L2-owned launch tuple. Later `main` commits stay out unless they fix a
  demonstrated launch blocker.
- The first exact-main run for merge `fc74c890ff392978f234643f2ee2ace8b59b40d6`
  then exposed a validation omission: the local R0 suite had not run the
  independent `nexuscrm-records` module. Its aggregate envelope evidence still
  expected 143 managed string-ID owners even though #4012 deliberately added
  `_System_PackGeneration.Id` and `_System_PackActivation.Id`. The follow-up
  records those two concrete owners alongside the `143 -> 145` aggregate
  update; it is schema evidence correction, not a new launch capability.

### 2026-07-24 — R0 controlplane release pin exposed the legacy outbox lease clock

**Evidence and root cause**

- The merged controlplane composition fix is published as
  `nexuscrm-controlplane v0.2.10`, resolving to merge
  `1f32293f26bec50b0025e767e0e91743c9104c88`. NexusCRM's backend and
  distribution module graphs were moved from `v0.2.9` to that exact release;
  both read-only dependency closures resolve.
- With the former pin, the automation integration harness stopped in
  `InitL0` because the required Permission State writer coordinator was
  absent. With `v0.2.10`, initialization correctly completed and exposed two
  after-save workflow failures that had previously been masked.
- The failures shared no formula-engine cause. Trigger subscriptions were
  present, the write transaction enqueued `record.created` / `record.updated`,
  and the manual drainer claimed each row, but no after-save EventBus
  subscriber ran.
- `_System_OutboxEvent.LastModifiedDate` was still `DATETIME` while the pull
  worker repository renews a fresh claim with a one-microsecond,
  database-owned advance and uses `RowsAffected` as the compare-and-transition
  result. TiDB truncated that advance to the same second, returned zero changed
  rows, and the worker treated its own live claim as lost before publish.
  `_System_RecordEventOutbox` already had the required `DATETIME(6)` shape;
  the legacy queue had been left behind when the common claim lifecycle began
  serving both tables.
- A broader local automation-package run initially used the developer
  app-packs checkout at `e786489922a6826d0dd8e9ca63bb47c4ee4f07fc`,
  while this NexusCRM revision pins the official CRM source to
  `1ad078b876614e6d1ff9472035b11ae3cc5c1ac1`. That mixed source identity
  surfaced unrelated future L1 composition diagnostics. Local validation must
  materialize the exact source pin just as CI does; a moving app-packs checkout
  is not release evidence.

**Root fix and proof**

- The managed schema now declares
  `_System_OutboxEvent.LastModifiedDate DATETIME(6) NOT NULL DEFAULT
  CURRENT_TIMESTAMP(6)`. The schema-catalog test applies the database-owned
  lease-clock invariant to both outbox tables, preventing a future
  second-precision regression.
- The published controlplane reconciler already proves that `DATETIME` to
  `DATETIME(6)` is a safe temporal precision widening and emits the required
  `ALTER TABLE ... MODIFY COLUMN` for existing installations. Greenfield
  `InitL0` creates the correct shape directly.
- The focused real-TiDB proofs now pass:
  `TestWorkflow_OnAllChanges_MyVariableCurrentCrossObjectAssignment_EndToEnd`
  executes its cross-object after-save update, and the frozen positive control
  in `TestWorkflow_EntryConditionOnActiveFormula` observes its formula value
  and fires. The schema-catalog package test also passes.
- The two focused real-TiDB tests do not consume the moving L1 checkout and
  therefore remain valid proof of the generic outbox defect and fix.

**Decision and next**

- Do not revert the controlplane release or patch the formula tests. The pin
  corrected the owning composition and revealed an independent queue-schema
  invariant violation.
- PR #4031 merged the pack-generation ID evidence while this local validation
  was running. Keep the controlplane pin and outbox precision correction in one
  follow-up R0 PR based on that exact merge; do not split the pin from the
  schema invariant. Run the persistence/events, backend/distribution, and
  boundary gates once, then merge one reviewed head and pay for one exact-main
  Runtime publisher cycle. Any CRM source-backed local proof must use the exact
  app-packs pin, never a branch-moving checkout.

### 2026-07-25 — R0 mutation closure used the working monolith as the differential oracle

**Claim and evidence**

- The failed environment run had already proved that the legacy mutation route
  accepted the same Account request. The legacy process therefore became the
  behavioral oracle: when the same bearer, session, database, metadata, and
  request succeeded through the old Runtime image but failed through the
  candidate, the defect belonged to a newly crossed process or representation
  boundary rather than to Account business semantics.
- TiDB stores `_System_RuntimeRecordCommand.Receipts` as JSON and may normalize
  object-key order and whitespace. The Runtime repository compared the returned
  bytes to the caller's input bytes, so a semantically identical receipt was
  rejected as a changed serving binding. Receipt comparison now decodes with
  `UseNumber`, requires one complete JSON value, and compares deterministic
  canonical JSON. Exact large numbers remain exact, malformed JSON fails
  closed, and a genuinely changed revision remains fenced.
- L1 JSON decoding supplies exact numeric values, and Runtime lowers decimal
  fields to `schemautil.ExactDecimal`. The kernel validator already accepted
  that neutral value, but metadata's duplicate numeric validation path accepted
  only Go primitive numbers. Currency value `0` therefore worked in the
  monolith's `float64` path and failed after the RPC boundary. Metadata
  validation now accepts `ExactDecimal` and evaluates bounds with `big.Rat`
  rather than losing precision through `float64`.
- Exact-head review then found the same carrier gap in the pluggable
  `Validator="range"` owner: its default branch silently skipped non-primitive
  numeric carriers. The owning kernel validator now recognizes
  `ExactDecimal`, compares it against the metadata-authored decimal spelling,
  and both generic numeric type validators fail closed for a malformed carrier
  even when no range is configured. Registry-, schema-, and MetadataService-
  level tests prove that a value just above `0.1` cannot pass through
  binary-float expansion or through either validation read path.

**Local full-stack proof**

- Candidate source `79b11d1e29c0420ee06ba03a9fadb249fe6afef7`
  ran as a separate sfcrm AppService, Runtime API, four Runtime Worker roles,
  Catalog snapshot, and TiDB database. Catalog's real compare-and-set owner
  moved all four work families to candidate ownership epoch 2.
- Typed create returned 201 and preserved false, zero, empty string, explicit
  null, date, and datetime. An identical command replay returned the same
  record ID without another row; reuse with a different request digest returned
  400. Update preserved omitted versus explicit null, delete hid the record,
  and all candidate command rows reached `COMPLETED`.
- Record events for create, update, and delete reached `processed`; audit outbox
  depth returned to zero. Runtime API and record-event Worker restarts retained
  the survivor record and processed the next event under the same fenced owner.
  After the old Runtime container was stopped, the candidate path still
  returned 200 while the old path returned 503, proving zero fallback.
- A local 401 initially appeared only in the host-built candidate. The product
  code was not at fault: macOS interpreted TiDB's timezone-free `DATETIME`
  session expiry in Europe/Amsterdam while the proven Linux containers run
  UTC. Running the local candidate with `TZ=UTC` made old and new paths both
  return 200. This is a runner-environment invariant, not a reason to weaken
  session expiry or add application compensation.
- A copied disposable database also contained views whose definitions still
  named the source database. Those views were rebound in the disposable
  fixture; no product DDL-recovery behavior was added. Metadata change remains
  independently owned and is not coupled to record mutation.

**Decision and next**

- Keep the monolith available as a differential oracle until launch cutover,
  but never as a serving fallback for the new path. Every parity failure first
  asks whether the same input succeeds through the legacy owner, then narrows
  the delta to serialization, trusted identity, metadata binding, transaction
  handoff, or durable work ownership.
- Local and CI must run one functional runner with the same frozen BOM, images,
  metadata, UTC container semantics, and assertions. Local execution owns
  application debugging and the complete walking slice; CI is the final
  clean-environment, publication, and deployment confirmation.
- The two product fixes are the only candidate code changes. After module,
  boundary, and exact-source checks pass, publish one Runtime API/Worker pair
  from this source, repin one L2 tuple, and run one final environment
  confirmation rather than resuming patch-and-redispatch loops.

### 2026-07-25 — R0 froze one release train and closed the signed-image loop locally

**Frozen release identity**

- NexusCRM PR #4054 merged as
  `03813e690ca4b5b192ee4013eaf95003d9bca6ca`; exact-main Go CI and both
  Runtime publishers passed from that source.
- L2 PR #4517 merged as
  `1de6f81de151d8ae8341ecfa9cd477fa959e6175`. The resulting desired BOM is
  `sha256:7b92d90c94a9129fca95a7c76edcaedde6384334c60fdaa1f93124ab1e5b5c68`.
  No later `main` commit is part of this candidate.
- Publication run `30151815917` signed and independently verified the one
  immutable resolved BOM:
  `ghcr.io/pingcap-inc/nexuscrm-serviceification-bom@sha256:ed56e7ad7bcbed02cfecf64c9b979f99700ae8f62aa6dcfd1940a329d4c259b2`.
  It resolves Gateway, sfcrm AppService, Runtime API, Runtime Worker,
  Catalog, frontend, AppRelease, and L2 metadata to exact source and digest
  identities.

**Exact signed-image local proof**

- The published images, not host-built substitutes, ran as separate Gateway,
  sfcrm AppService, Runtime API, and four Runtime Worker processes against the
  preserved disposable TiDB and immutable Catalog views. Catalog's production
  compare-and-set path moved all four work families from ownership epoch 4 to
  epoch 5 and rendered Worker configs from the resolved BOM.
- An authenticated Salesforce v63 create traversed
  `Gateway -> sfcrm AppService -> Runtime API -> TiDB` and returned 201.
  Query returned the same record with false, decimal zero, empty string,
  explicit null, date, and datetime semantics preserved. Two updates and a
  delete completed; all four commands scoped to the run reached `COMPLETED`,
  create/update/delete events reached `processed` with zero retries, and the
  run-scoped audit queue drained.
- Runtime API and record-event Worker were restarted. The same Gateway and
  AppService continued to read and mutate through the restarted owners.
  The delete followed the platform's soft-delete contract (`IsDeleted=1`).
  A process scan found no legacy backend, Runtime, or Gateway serving process;
  the request path therefore had no legacy fallback.
- Final local container identity readback matched the BOM digests byte for
  byte. Runtime and all Workers were healthy. The local closure manifest is
  retained under the disposable run root
  `/private/tmp/r0-signed-30151730697/evidence/local-closure.json`; it is local
  evidence, not a repository or production artifact.

**What failed, and what stepping out changed**

- macOS's system `curl`/LibreSSL reported `bad decrypt` against the Gateway.
  OpenSSL 3 established TLS 1.3 successfully, and the existing Linux test
  container returned HTTP 200. The wrong assumption was that the macOS client
  had to be the release oracle. Linux/OpenSSL matches the target environment;
  no product TLS code or cipher policy was changed for a local client defect.
- A first drain assertion counted every historical command in the reused
  disposable database and reported two old `ACCEPTED` fixture rows. Scoping
  evidence to this run showed all four new commands completed. The wrong
  assumption was that a reused database was globally empty. Worker behavior
  was not changed to satisfy an invalid assertion.
- Runtime's HTTP readiness returned 200 while Docker marked the container
  unhealthy. The local composition had placed the admin listener on container
  port 29091, but the published image contract checks container port 9091.
  Mapping host `29091 -> container 9091` made both readiness and image health
  pass. This was corrected in the local composition, not in application code.
- Security review correctly rejected extracting the pre-existing bearer from
  TiDB. The proof instead minted a new synthetic local JWT, installed only its
  verifier/current value in the disposable session row, and used a new local
  signing secret. No existing credential was read or reused.

**Decision and next**

- Local execution is now the application-functional gate. Environment run
  `30153836723` consumed the exact published BOM above and reached the D2B
  idempotency assertion, but the assertion's helper combined curl's HTTP status
  and the semantic JSON count on stdout. A real count of `2` was observed as
  two lines, `200` and `2`, and was falsely reported as a duplicate Runtime
  record. No product, image, metadata, or BOM change was required.
- L2 PR #4519 captures and validates the transport status inside
  `gateway_account_count`, exposes only `totalSize` to command-substitution
  callers, and adds an executable stdout-contract regression. It merged as
  `d087f63dd2d33f9b0790d2246b5a30619d5320cb`; release-tuple checks correctly
  proved no republish or repin was needed.
- Environment run `30154779946` used that runner revision with the same
  immutable resolved BOM and advanced beyond idempotency. It then rejected the
  supposedly valid position of the independent composite. Reproducing the
  exact request locally showed the product was correct: active Account
  metadata requires `Website`, `Industry`, `NumberOfEmployees`,
  `RecordTypeId`, and `Region__c`, while several positive D2B probes still
  authored only `Name` or `Name` plus `Description`. The same drift affected
  independent and atomic composite, recovery-survivor creation, post-commit
  ambiguity, and the otherwise-valid command-digest conflict probe.
- The owning fix is one metadata-derived positive Account fixture helper, not
  another composite assertion or product compatibility path. Every positive
  D2B create now consumes that helper; the intentional required-field negative
  fixture remains incomplete. L2 PR #4520 adds an executable shape contract
  and proves locally that the exact signed stack returns independent results
  `[true,false]` with one committed row and atomic results `[false,false]` with
  zero committed rows. Product images, metadata, and the frozen BOM remain
  unchanged.
- The first stdout bug and the broader Account-shape drift were two
  expressions of one execution-boundary error: CI had been asked to discover
  application-fixture correctness that the local runner should already own.
  Future runner changes must execute their helpers and the affected business
  path locally before dispatch. CI's job is to confirm that the already-proven
  tuple survives the clean deployment environment.
- Environment run `30155897322` exposed that this rule had still not been
  applied to the final composite artifact. The new Account-shape helper was
  executable, but its callers used `jq --slurpfile` without `-n` or another
  primary input. The non-interactive runner therefore wrote an empty request
  and Gateway correctly returned HTTP 400. The earlier local probe used a
  similar hand-written jq command with an input file, so it proved product
  semantics while silently replacing the broken runner assembly step.
- L2 PR #4521 moves the complete independent/atomic request construction into
  one executable helper, validates the resulting JSON before HTTP, and makes
  unit coverage generate and parse both final artifacts. Those exact helper
  outputs—not manually reconstructed substitutes—returned HTTP 200 through the
  frozen signed local stack with `[true,false]` independent results and
  `[false,false]` atomic results. Composite HTTP failures now also retain a
  sanitized response-shape/error-code diagnostic before cleanup.
- Before redispatch completed, a one-click-wider audit of the remaining D2B
  assertions found a second owner-boundary error. The concurrent-command proof
  sent raw Salesforce `Idempotency-Key` value `d2b-concurrent-conflict`, then
  queried `_System_RuntimeRecordCommand.CommandId` with that same wire value.
  sfcrm AppService deliberately hashes the wire value to
  `sfcrm-idem-<sha256>` before crossing the L1/L0 boundary, so Runtime never
  stores the raw key. The active environment run was canceled before paying
  for this deterministic later failure.
- L2 PR #4522 merged as
  `36834e23703194186b252f47e1422934a21cb8c4`. It gives that mapping one
  runner helper, uses the raw key only at the HTTP owner and the derived
  neutral identity only at Runtime SQL inspection. The exact helper readback
  against the disposable signed-stack database observed zero raw matches and
  one completed neutral command. A full search found no other Runtime command
  SQL using raw wire identity.
- R0 closes only when the next environment run succeeds and tracking issues
  record the same immutable identities. Any further failure must first be
  classified as environment, deployment, assertion, or a contradiction of the
  local proof; it must not restart endpoint-by-endpoint debugging or reopen the
  launch tuple by default.

### 2026-07-25 — The complete frozen stack passed locally; CI is no longer the application debugger

**What the complete local runner proved**

- L2 PR #4524 merged as
  `8445799b9bc5942236a7333f5705ccc40a379dbc` after exact-head review of
  `ae57ce5fb7a78e89371b8f82e9de1829fd90bd75`. It adds one repository-owned
  command that executes the same D1/D2
  application harness in a pinned Linux toolbox against a fresh, marker-owned
  local TiDB database. Its only BOM input is the immutable OCI digest: the
  wrapper pulls that digest and the inner runner extracts its embedded JSON.
  An arbitrary host file cannot be paired with the evidence reference. The
  runner does not rebuild a substitute Gateway, AppService, Runtime, Worker,
  or Catalog.
- Fresh local run `1784992005-1` completed with exit status zero after that
  artifact-binding correction. The exact
  frozen path
  `Gateway -> sfcrm AppService -> Runtime API -> TiDB` passed the semantic call
  matrix, D2A query/queryAll/queryMore/history, related counts, notifications,
  recycle-bin reads, D2B create/update/delete/composite behavior, cancellation,
  ambiguous completion, admission failure, restart, and zero-fallback checks.
- The four Runtime Worker roles then passed durable crash recovery, atomic
  effect-and-ack, forced duplicate delivery, ownership-epoch fencing, poison
  repair, database-unavailable fail-closed behavior, queue/in-flight
  convergence, and graceful drain. The official v6 evidence validator passed
  inside the runner, and the outer command retained only the sanitized evidence
  file after removing containers, credentials, the scratch database, and the
  private run root.
- The complete changed-owner regression module passed: 75 tests, zero failures,
  with two platform-conditioned skips. Bash syntax, Python compilation, and
  `git diff --check` passed. The broader L2 suite still has unrelated existing
  Python-3.9 and tenant-fixture failures; they are not evidence against this
  runner and are not folded into R0.

**Root cause discovered only by running the whole topology**

- The previous harness left the exact-source reference monolith alive while it
  started independently deployed Runtime Workers against the same scratch
  database. The monolith starts its own audit outbox worker. Killing the new
  audit Worker therefore did not leave a pending row: the legacy worker
  consumed it first, and the harness incorrectly reported that the committed
  Runtime mutation had produced no durable work.
- This was not an audit producer bug and not a reason to weaken the recovery
  assertion. It was a topology/ownership violation in the test itself: two
  processes could act on one work family. The runner now completes the explicit
  reference parity and performance phase, terminates and verifies absence of
  the marker-owned reference process/listener, and only then starts the four
  fenced Runtime Worker roles. Sanitized evidence records
  `reference_backend_retired_before_worker_ownership=true`.
- A later repeat exposed a second evidence-owner defect. The runner waited for
  one metrics scrape to report `in_use_connections=0`, then performed a second
  scrape for the persisted pool snapshot. A transient connection between those
  two observations made the recorded snapshot equal one. The fix is not a
  larger sleep: the snapshot that enters evidence is now generated repeatedly
  within the bounded convergence window and accepted only when that same
  snapshot reports zero. This removes the check-then-capture race.

**Root cause discovered by reviewing the local adapter as a trust boundary**

- The first local command accepted two independent inputs: an OCI digest string
  for evidence and an arbitrary resolved-BOM file for execution. A successful
  run could therefore claim digest A while testing file B. This was not a
  missing checksum on the JSON; an OCI index digest cannot be reconstructed
  from that payload alone. The wrong system shape was a second, weaker artifact
  read path. The file input was deleted. Local and hosted execution now both
  derive the BOM bytes from the one exact OCI digest.
- The same adapter had encoded Docker Desktop facts as if they were the
  portable contract: supplementary group 0 for the mounted socket and
  `/private/tmp` when `TMPDIR` was absent. The wrapper now derives the real
  socket GID, retains group 0 only for Docker Desktop compatibility, and uses
  a validated `${TMPDIR:-/tmp}` root. This keeps host adaptation at the wrapper
  boundary instead of leaking platform exceptions into the application runner.
- Scratch connection parsing treated every value other than `true` or `1` as
  false. A malformed value such as `bogus` could therefore enter the explicit
  insecure-local exception. `TIDB_TLS` is now the closed enum `true|false`;
  empty retains the hosted secure default, and every other value fails before
  a database connection or recovery record is created.
- The correction passed the 75-test changed-owner module, shell and Python
  syntax checks, and the complete fresh local walking slice above without
  changing any product image, metadata revision, or BOM identity.

**Local/CI responsibility and security decision**

- Local mode is explicit and fail closed. Only `127.0.0.1`, `localhost`, or
  `host.docker.internal` may use the non-TLS/empty-password developer TiDB
  exception. Hosted test2 continues to require TLS and a non-empty password.
  Local mode pulls and extracts the exact BOM digest and then runs its exact
  service-image digests; hosted CI remains the owner of signature/provenance
  verification, IAM, test2 TLS, and real deployment identity.
- Local output lives below a mode-0700 session root. Success evidence is copied
  out only after the official validator succeeds; a failed validation cannot
  leave a success-named artifact. Run credentials and the scratch database are
  removed by the same owner-scoped cleanup contract.
- The operating rule is now executable rather than aspirational:
  application/fixture/topology debugging closes locally on the exact frozen
  tuple; CI confirms that the locally proven candidate still works in the real
  publication and test2 environment. A hosted failure must be classified
  against the local proof before any product or BOM change is considered.

**Next**

- Hosted test2 run `30163778669` was dispatched from exact merged main
  `8445799b9bc5942236a7333f5705ccc40a379dbc` while retaining the frozen BOM
  `ghcr.io/pingcap-inc/nexuscrm-serviceification-bom@sha256:ed56e7ad7bcbed02cfecf64c9b979f99700ae8f62aa6dcfd1940a329d4c259b2`.
- That run passed registry/signature/provenance checks, exact AppRelease and L2
  resolution, a fresh metadata build, Catalog reconciliation, the D1 semantic
  matrix, every D2A family, and D2B create/update/composite behavior. It then
  failed only at the single-record DELETE parity assertion. The runner reported
  neither the legacy nor Runtime status, so the result proves the failure
  boundary but not its owning path.
- The complete local runner was repeated from the exact merged main—not the
  pre-merge PR head—as run `1784995215-1`. It passed the same DELETE assertion
  and the rest of the walking slice through evidence validation and cleanup.
  This falsifies deterministic source, pack, metadata, BOM, and product-shape
  drift. The remaining unknown is hosted environment or timing.
- L2 PR #4526 adds no retry and changes no product behavior. It preserves the
  failing DELETE owner, both HTTP statuses, response shape, and stable error
  codes through the existing payload/identifier-free diagnostic. That
  environment probe is required before any application change can be
  justified.
- Full clean install and focused diagnosis are different products and should
  not share one cost profile. The release gate must continue to create a fresh
  database and run the complete walking slice. A repeated environment-only
  failure should instead use a BOM/L2-revision-bound immutable metadata
  baseline, start only the services required by the failing capability, and
  repeat a focused probe with status, latency, and error classification. Once
  the root is fixed, one final clean-install run remains the acceptance proof.
  Do not make every diagnostic iteration pay for a 20-minute metadata rebuild.
- If hosted confirmation passes, record its run/artifact identity in #3555 and
  #3624 and mark R0 complete. Do not republish images or repin the BOM.
- If it fails, compare the failed stage with local run `1784990599-1`. Treat a
  difference in registry, signature, TLS, IAM, runner host, or deployment
  cleanup as hosted-environment work; reopen application behavior only if the
  same frozen runner reproduces the contradiction locally.

### 2026-07-25 — Hosted latency exposed a physical object-discovery path that local TiDB hid

**Evidence that changed the diagnosis**

- Diagnostic hosted run `30165365397` used the same frozen BOM and passed
  artifact, metadata, Catalog, and process startup. It failed 12 seconds after
  the semantic matrix began because the untyped record-access request returned
  HTTP 504. This is application-path latency, not publication or deployment
  failure.
- The last successful hosted D1 evidence gives the decisive comparison for the
  same logical record-access operation: the typed path used 25 repository SQL
  statements, while the untyped path used 263. Its 20 typed samples already
  had about 2.03 seconds p50 on the remote TiDB path, versus about 151
  milliseconds for the legacy in-process reference. The hosted environment did
  not create a random new failure; it amplified an existing query-shape defect.
- Current-tree tracing showed the owner. Untyped
  `EvaluateRecordAccess` called `FindObjectTypesForIDs`, which enumerated the
  137 live `_System_Object` rows and probed every physical object table, eight
  at a time. Runtime DELETE was worse: even though the single-record L1 request
  carries `ObjectID`, preflight and execution each called `GetRecordType`, so
  one DELETE repeated the same full-table reverse discovery twice. Hosted run
  `30163778669` spent about 20 seconds in the D2B delete group before reporting
  `single delete parity failed`; the code path now explains that earlier
  observation without treating it as a separate bug.

**Step-out conclusion**

- The false assumption was that a record's object identity must be proven by
  searching physical data. Object shape and Salesforce KeyPrefix identity are
  Catalog facts; record existence and authorization are data-plane facts.
  Combining them made route cost grow with the number of installed objects and
  bypassed the request-bound metadata authority the split architecture had
  already established.
- The correct read path is hybrid, not a universal KeyPrefix rewrite. A known
  unique Salesforce prefix is resolved from the immutable request-bound
  metadata view. An explicit mutation `ObjectID` is already the operation
  boundary. The subsequent access check or locked DML remains the sole proof of
  row existence and authorization. UUID/opaque identifiers have no prefix
  identity and retain the existing physical reverse lookup for compatibility.

**Owning fix and regression boundary**

- Record-access now resolves prefixed IDs from metadata and sends only
  unresolved opaque IDs to physical discovery. Runtime DELETE uses an explicit
  `ObjectID` directly, uses Catalog identity for objectless prefixed collection
  deletes, and falls back only for objectless opaque IDs. Execution no longer
  repeats object discovery before its authoritative locked delete.
- Identifier shape is classified before prefix lookup. A canonical UUID remains
  opaque even if its first three hexadecimal characters happen to equal a live
  KeyPrefix; a regression fixture pins that collision case. This preserves the
  generic-record compatibility promised by the hybrid path instead of treating
  “starts with a known prefix” as sufficient evidence.
- The sealed mutation engine names the remaining escape hatch
  `ResolveOpaqueRecordObject`; it no longer exposes a general `GetRecordType`
  method that invites the hot-path scan to return.
- Focused tests prove mixed prefixed/opaque behavior, zero physical probes for
  normal prefixed record access, zero reverse lookups for explicit or
  Catalog-resolved deletes, one compatibility lookup for an opaque objectless
  delete, mismatch masking, cancellation, and no second lookup during
  execution. Records and metadata tests/vet, records race tests, Runtime
  assembly/API tests, and boundarygate pass locally.

**Next**

- Merge and publish this NexusCRM owner fix, repin only the Runtime API/Worker
  identities required by the source change, and run the complete local
  walking slice against that one frozen candidate before a final hosted test2
  run. Do not enlarge timeouts: that would preserve an object-count-dependent
  request shape and conceal the same failure until the next metadata expansion.

### 2026-07-25 — Review reopened two current blockers; adjacent truths remain out of R0

**Truth / current scope / blocker decision**

- Exact-head review of the first identity fix found two demonstrated defects.
  First, the record-access Runtime view did not serialize `KeyPrefix`, so the
  production request-bound metadata service could not use the new resolver.
  Second, the explicit-object DELETE shortcut could evaluate an authored policy
  before proving that the target was visible, allowing a nonexistent row to
  produce `POLICY_DENIED` and authored text instead of
  `MISSING_OR_DENIED`. Both defects are true, belong to the R0 record-access /
  mutation claim, and would make that claim false; both are current blockers.
- The legacy approval reverse lookup, the typed-path SQL count, and an eventual
  storage-level selected-field optimization are also real, but they do not
  invalidate this frozen launch slice. They remain follow-up work rather than
  expanding PR #4066. The local release-image build's private deploy-key
  requirement is a publication-environment fact, not an application blocker.

**Corrected owners**

- `KeyPrefix` is now an explicit least-privilege fact in record-access Runtime
  metadata view schema V8. Historical record-access views remain readable but
  are not serving eligible. Runtime admission requires V8, so an old view
  cannot silently fall back to physical discovery. Mutation continues to
  receive the same Catalog fact in its existing write closure; Catalog remains
  the only source.
- Update/delete policy preflight now reads the target once from its already
  known object through a mutation-specific persistence adapter. That adapter
  preserves the established disclosure split: object CRUD denial is explicit,
  missing and RLS-hidden rows are masked, and authored policy sees no row until
  that gate passes. The loaded row is reused for conditional preflight; locked
  DML remains final authorization and TOCTOU authority. A target access failure
  remains scoped to its operation, so an independent-batch sibling is not
  accidentally halted; only an authored command-preflight policy denial keeps
  command-wide behavior.
- A first attempt reused generic read `Retrieve`. Full regression evidence
  rejected that design because it converted an explicit object-DELETE denial
  into a missing result. The implementation was replaced rather than changing
  the test: read authorization and mutation authorization are not the same
  boundary.

**Evidence**

- Record-access projection tests prove V8, exact `KeyPrefix` reconstruction,
  and historical-view non-serving behavior. Runtime metadata service tests
  prove `ResolveObjectForRecordID` from the actual bound record-access view.
- Mutation tests prove missing update/delete targets cannot expose authored
  policy text, while the existing missing, RLS-hidden, object-CRUD-denied, and
  locked-row TOCTOU matrices retain their established outcomes.
- Full metadata and records tests pass; both modules pass `go vet`; Runtime API
  and mutation assembly tests pass with real loopback process boundaries.

**Next**

- Run race and boundary/release checks, amend PR #4066, and require exact-head
  review. After merge, publish and freeze the one corrected Runtime/Catalog
  candidate, run the complete local walking slice, and use hosted test2 only as
  the final publication/deployment proof.

### 2026-07-25 — KeyPrefix ownership must fail closed at every immutable boundary

**Review evidence and root cause**

- Exact-head adversarial review found that record-access Runtime view schema V8
  rejected blank `KeyPrefix` values but accepted two objects with the same
  exact prefix. `MetadataService`'s legacy live identity index already rejected
  that state, while the request-bound Runtime reader returned the first matching
  object in canonical order. This was a true R0 blocker: record access or an
  objectless delete could be routed to the wrong physical object.
- The defect was not a Records or DELETE conditional. The V8 projection moved
  the Catalog-owned identity fact into a new immutable read path without moving
  its uniqueness invariant. That created two consistency standards for one
  fact. Object ordering then accidentally became identity authority.

**Owning correction**

- One case-sensitive KeyPrefix ownership validator now guards Catalog
  compilation, Catalog artifact opening, and Runtime view opening. Exact
  duplicates fail closed; Salesforce-distinct values such as `00K` and `00k`
  remain separate identities.
- Runtime validation applies the same rule to the V8 record-access projection
  and the existing mutation projection. A hostile but correctly digested
  artifact therefore cannot make either request-bound read or objectless
  mutation choose an object by array order.
- No per-call fallback, waiver, timeout, or Records-layer duplicate check was
  added. Catalog remains the identity owner and Runtime consumes only a
  validated immutable view.

**Evidence and scope control**

- Complete `catalogsnapshot` tests and vet pass. New regression tests cover
  source compilation, case-sensitive distinct prefixes, tampered record-access
  V8 artifacts, and tampered mutation artifacts. Focused Records tests for
  request-bound access and Catalog-resolved delete behavior also pass.
- A full local metadata-module run reached seven Flow lifecycle integration
  fixtures because local TiDB was available; those fixtures seed an
  intentionally ownerless `Probe_Flow` and are rejected by the existing flow
  ownership admission rule. The KeyPrefix diff does not touch that path. It is
  a real test-fixture issue, but it is not part of the R0 identity claim and was
  not folded into this PR.

**Next**

- Amend PR #4066 and require exact-head review. After merge, keep the existing
  launch sequence: publish the corrected owner images, freeze one BOM, prove
  the complete walking slice locally, and use hosted test2 only for final
  environment and deployment confirmation.

### 2026-07-25 — Exact-main rejected an impossible identity fixture, not Runtime identity

**Failure classification and root cause**

- The frozen NexusCRM merge passed build/test/seal, image smoke, and the earlier
  source-backed TiDB gates, then failed the F1-I identity-exchange integration
  with `UNAUTHENTICATED`. The same exact test and source SHA reproduced locally
  against TiDB.
- Runtime Identity was behaving correctly. The fixture generated a JWT with an
  empty profile and role while its durable `User` row named both. Strong
  authorizing-session admission intentionally rejects that mixed identity
  instead of combining caller-signed and current durable subject facts.
- This is a current release blocker because it prevents the exact-main code gate
  and owner publishers, but it is not evidence that the independently serving
  identity path or deployment topology is wrong. Production login-issued
  bearers already carry the session-bound profile and role.

**Owning correction and evidence**

- The integration fixture now uses one profile/role constant set for both the
  signed bearer and durable user row. Production validation was not relaxed,
  no compatibility path was added, and no CI retry was used to hide the
  deterministic mismatch.
- The exact failed CI command now passes locally against TiDB, covering both
  the real-repository cross-process read and identity-exchange-to-admission
  path. The focused identity test also passes independently.

**Next**

- Complete the relevant backend tests, race/vet and boundary checks, merge one
  focused root-fix PR, then repin only the NexusCRM source identities in the
  frozen L2 candidate. Resume Runtime image publication, resolved-BOM signing,
  complete local walking-slice proof, and one final hosted test2 run.

### 2026-07-25 — R0 closure requires one compatible source graph and local application proof

**Outcome**

- The corrected R0 candidate is frozen at NexusCRM
  `4a4bcd8dc325055b6da80ecd78055e3061b39f50`, controlplane
  `fc029a06f1a5d1167b96c32365204b3dac22b215`, L2 metadata
  `a1d29fbf8166c2cada1fb86f7b90c17d8af27213`, and L2 publication authority
  `3d468cd789a7b6981ef0f41e80293d89cff4cde7`.
- The signed resolved BOM is
  `ghcr.io/pingcap-inc/nexuscrm-serviceification-bom@sha256:0cc31323fd74faa0b3af8bd567d2b00085243503ddfee3d6f4409f5cedd6c4b2`.
  It binds the corrected Catalog, Runtime API, Runtime Worker, Gateway,
  sfcrm AppService, frontend, AppRelease, and exact L2 metadata revision.
- Final local run `1785028885-1` completed the repository-owned walking slice
  against TiDB 8.5.0 after all harness corrections. Its independently
  revalidated sanitized v8 evidence has SHA-256
  `8541958449fb0aa2a68c0acfcbd24514271c11503b0e07404bed2d9bb88c1f84`.
  The run used the unchanged signed BOM, crossed every earlier hosted failure
  stage, and proved the complete D1/D2A/D2B path.

**What the repeated failures actually meant**

- The first local launch attempt did not expose an application defect. The
  local wrapper entered a credential-isolated runner without first caching
  every digest-pinned job and service image. The wrapper now extracts and
  validates the exact BOM on the host, validates each role's fixed owner OCI
  repository, and caches every referenced digest before entering the runner.
- The next local attempt correctly rejected a Catalog artifact whose Runtime
  view was not an exact projection. Runtime used the corrected NexusCRM source
  graph, but Catalog was still compiled against an older graph. Catalog and
  Runtime being individually green was insufficient; both artifacts and the
  metadata builder must share one compatible system-schema graph.
- After Catalog was rebuilt from the corrected graph, the L2 lineage still
  named the older metadata-builder Runtime. The missing
  `_System_ConnectorIngressControl.FenceEpoch` fact proved that metadata
  lineage is the third member of the same compatibility closure. Rebuilding
  the final pack from the exact NexusCRM and app-pack inputs changed lineage,
  not final pack bytes.
- A resolved BOM cannot refer to the merge commit that will contain itself.
  The publication DAG was therefore closed in two immutable steps: first land
  exact metadata lineage, then land the BOM tuple that consumes that already
  existing commit. This is an immutable-DAG constraint, not a reason to use a
  mutable branch or tag.
- Hosted run `30175434603` passed publication, provenance, metadata, Catalog,
  all D1/D2A/D2B application journeys, and reached the final Worker recovery
  proof. It then failed because the harness rendered the next role-config set
  directly over existing mode-`0400` files. Native Linux correctly rejected
  truncating a protected file. The macOS Docker bind mount used by the first
  local run did not faithfully enforce that mode, so local application proof
  was green while one deployment-filesystem invariant remained unproved.
- The owning fix is not a permission relaxation. The harness now renders both
  the complete next Catalog activation-output set and Worker role-config set in
  fresh sibling directories, validates and protects them, then publishes only
  the newly fenced owner's receipt and config with same-filesystem renames. The
  stale Worker remains bound to the old config inode. The shared JSON writer
  also explicitly rejects owner-nonwritable output on every filesystem, so
  macOS local verification now catches this failure class without relying on
  host permission semantics.
- After that fix passed locally, hosted run `30177441182` reached the D2A
  related-list-count route and received a non-`200` response. The same frozen
  BOM passed the complete local run, and the preceding hosted run had passed
  this route, so this was not evidence of a source/BOM regression. It did expose
  a current observation-boundary defect: the route retained its private
  response only inside the run directory, emitted no sanitized status/shape
  evidence, and cleanup correctly removed the private response and service
  logs. The run therefore could not distinguish transport, Gateway, AppService,
  Runtime, or a stable CRM error owner.
- The route now reuses the existing versioned failure sanitizer and records
  only stage, HTTP status, JSON shape, and bounded stable error codes; response
  messages, payloads, and identifiers remain excluded. Complete local run
  `1785019583-1` passed after this diagnostic change, with independently
  revalidated evidence SHA-256
  `77df0e6537c15377cc3185da0e5a9259283b405c78bf27397e6cbc9121947fe6`.
  A further environment run is useful only because it can now return
  decision-grade evidence instead of becoming a blind retry.
- Hosted run `30178501583` then failed with a sanitized HTTP `504` on the
  untyped record-access probe immediately after all processes reported ready.
  The same immutable candidate had passed that route locally, and earlier
  hosted runs had reached much later stages. The failure site moving from
  related-list counts to record access disproved a capability-specific source
  regression. The broader faulty assumption was that process readiness implied
  stable launch traffic readiness. It did not exercise cold Identity exchange,
  capability-specific Runtime admission, or the first SQL path inside the
  existing five-second route budget.
- The owning correction is a bounded launch-readiness barrier, not a longer
  route deadline or a retry hidden inside application semantics. Before
  measured assertions, the shared runner requires two consecutive successful
  side-effect-free cycles through every read family actually exposed by the
  current v6 release: typed and untyped record access, query, related-list
  counts, notifications, and recycle-bin reads. The barrier has a 90-second
  outer bound, preserves existing per-request deadlines, resets on transient
  failure, and fails immediately on malformed or semantically divergent
  responses.
- The desired AppRelease advertises record-view capabilities, but the v6
  Gateway intentionally keeps record-view routes disabled; v7 is their separate
  activation stage. Under the truth-scope-blocker test this is a real future
  surface, but not an R0 v6 blocker. The barrier therefore derives its exact
  required family set from the resolved BOM instead of silently broadening the
  launch claim.
- Adding required readiness evidence to the existing v6 schema would have
  silently changed a versioned contract. The current candidate instead emits
  `nexuscrm.serviceification-d1-test2-evidence/v8`; the validator preserves
  historical v6 compatibility and requires the new barrier only for v8.
- Exact-head review then found that the first v8 validator accepted an
  impossible proof with one attempted cycle but two consecutive successes, and
  accepted elapsed time beyond the 90-second bound. It also found that the
  runner invoked the barrier for non-v6 releases whose historical evidence
  schemas did not own it. Both findings were valid. The corrected validator
  enforces `consecutive_successful_cycles == 2`,
  `2 <= attempted_cycles <= elapsed_seconds + 1`, and
  `0 <= elapsed_seconds <= 90`; the runner and emitted field are scoped only
  to the current v6/v8 launch candidate. This avoids both a false proof and an
  accidental rewrite of v1-v7 evidence semantics.
- A second review found that the initial one-to-one resolved-BOM v6 → evidence
  v8 mapping still rejected historical evidence v6 before its retained
  validator ran. The actual compatibility model is one resolved release schema
  with a bounded evidence-reader window: resolved-BOM v6 accepts historical
  evidence v6 under the original rules and current evidence v8 under the same
  D2B rules plus mandatory readiness proof. The current runner still emits only
  v8. Both fixtures and the exact final local artifact pass the corrected
  validator.
- A third review found that the 90-second loop deadline did not bound an
  individual request if the Gateway accepted a connection and then stalled.
  Readiness calls now pass the remaining barrier time to `curl --max-time`.
  Ordinary semantic calls retain their existing behavior, while a stalled
  readiness request can no longer consume the serialized workflow's global
  timeout. The complete separate-process stack passed again after this change.
- A fourth review found that the notification capability exposes two public v6
  aliases while the first barrier exercised only one. Both
  `product/notifications` and `connect/notifications` are now independently
  named, requested and required by the v8 evidence contract. Each successful
  cycle therefore contains seven requests across six exact route entries,
  including both typed and untyped record-access requests.
- A fifth review found that Python numeric equality allowed JSON `7.0` to
  satisfy the expected integer request count `7`. The validator now requires an
  exact non-boolean integer type as well as the exact count. The floating-point
  counterexample is a regression test; the exact final v8 artifact still
  validates.
- A sixth review challenged integer-resolution Bash `SECONDS` as the evidence
  duration source. The producer now measures start and finish with
  high-resolution nanoseconds and rounds a positive sub-second duration upward
  to the integer evidence grammar. The Bash/curl deadline remains the safety
  owner; a no-op-sleep regression proves sub-second evidence cannot become
  zero. The complete separate-process stack passed again with the final
  producer.

**Local proof and ownership**

- The full local run built exact metadata, installed a fresh isolated database,
  reconciled Catalog first, and started independently digest-pinned Runtime
  API, sfcrm AppService, Gateway, and four Runtime Worker roles.
- It proved record access, query/queryAll/queryMore/history, related-list
  counts, notifications, recycle-bin reads, create/update/delete, composite
  behavior, typed/null/omitted values, cancellation, ambiguous outcomes,
  admission failure, crash-before/after-commit, fencing, idempotency, poison
  repair, restart, graceful drain, queue convergence, and zero in-flight work.
- The reference monolith was retired before Worker ownership checks.
  Runtime/AppService outage probes failed closed and the evidence records
  `fallback: false`; the reference path was a parity oracle, never a serving
  fallback.
- Local evidence owns application semantics. Hosted test2 owns only the
  remaining real-environment proof: external registry/signature verification,
  native Linux filesystem/container semantics, test2 TLS and credentials,
  isolated TiDB deployment, and cleanup of marker-owned resources.
- Final local readiness evidence reports two attempts, two consecutive
  successful cycles, seven requests per cycle across the six exact v6 route
  families, and three seconds elapsed. The remainder of that same run proves
  read parity, mutation, durable command acceptance, cancellation, ambiguous
  outcomes, process restart, all four fenced Worker roles, poison/repair,
  graceful drain, and zero legacy fallback.

**Decisions**

- Freeze one launch tuple. A newer `main` commit does not enter R0 unless it
  fixes a demonstrated launch blocker and the complete tuple is repinned.
- CI must not rediscover application behavior. Another hosted dispatch is
  allowed only if the exact candidate first passes the complete shared runner
  locally.
- A PR's auto-merge path must not be treated as evidence when the relevant BOM
  gate is not required. Exact producer, signer, local-runner, and environment
  evidence are the release proof.
- Replaced local BOM/Catalog candidates and experimental Catalog images were
  deleted after the corrected signed tuple passed locally. The exact current
  images and TiDB fixture remain available for reproducibility.

**Next**

- Merge the launch-readiness barrier after its exact-head gates, then dispatch
  one final hosted test2 with the unchanged signed BOM. Record the immutable
  result in both tracking issues and do not broaden the launch scope. If it
  passes, R0 is closed and the same digests are the only D3/D4 candidate.

### 2026-07-26 — Hosted setup exposed an idle advisory-lock session, not an application failure

**Evidence and root cause**

- The post-readiness hosted run
  [`30183144639`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/30183144639)
  failed before Catalog, Runtime, Gateway, AppService, or any application
  assertion started. The setup owner successfully completed approximately
  79 seconds of metadata application, then its dedicated
  `nexuscrm_controlplane_apply` advisory-lock connection returned
  `unexpected EOF`. The following repair owner waited on the server-side stale
  lock and eventually failed with `result=0: invalid connection`.
- Metadata mutation used ordinary pool connections while the advisory-lock
  connection remained idle. The test2 TLS/proxy path could therefore reclaim
  that connection during a long apply. The old release path ignored the result
  of `RELEASE_LOCK`, so the successful mutation and failed ownership release
  were incorrectly reported as one successful operation.
- This is a setup ownership-lifecycle failure. It is not a CRM capability,
  Runtime RPC, Worker, DDL-recovery, or metadata/business-semantics failure.
  The monolith's working application behavior remains a useful oracle but does
  not exercise this new separate-process lock-session lifetime.

**Owning correction**

- [`nexuscrm-controlplane#229`](https://github.com/pingcap-inc/nexuscrm-controlplane/pull/229)
  merged as `3e69ea1d982e44a6abb7ffb720b0d73743557511` and was released as
  `v0.2.16`.
- A 15-second keepalive owns the dedicated advisory-lock session; every ping
  has a five-second bound. Keepalive failure invalidates the minted owner scope
  and is joined into the operation result.
- Apply verifies that the same owner scope is still active immediately before
  writing the trusted completed marker. Release uses an independent five-second
  context, reads the actual `RELEASE_LOCK` result, and requires exactly `1`.
  Operation, keepalive, release, and close errors are preserved together.
- Tests cover keepalive loss and non-owner release results. Focused lock,
  ApplyAllIfNeeded, reapply, tenant, ambiguous-activation, race, vet, Catalog
  publisher, and diff checks pass.

**Scope decision**

- Arbitrary loss of a lock session during an already running legacy DDL
  statement exposes a real pre-existing distributed-fencing limitation. Fully
  fencing uncancellable DDL requires a separate mutation-substrate design.
- It is not the observed hosted blocker: the hosted mutation completed, then
  the idle lock session and ignored release result broke handoff. Under the
  truth/current/blocker test, R0 fixes the demonstrated lock-session lifecycle
  and records the broader fencing problem without making the launch candidate
  absorb a new DDL architecture.
- [`NexusCRM#4069`](https://github.com/pingcap-inc/NexusCRM/pull/4069)
  merged as `4eda21cfad02787d219441c3ee50ae4fea9f0d2d`, pinning `v0.2.16`
  in both Runtime-owned module graphs. Runtime and Catalog can therefore be
  republished from one compatible dependency graph.
- The L2 release tuple remains bound to the already-built metadata lineage at
  NexusCRM `4a4bcd8d…`; it is not the serving Runtime source owner. The desired
  serviceification BOM alone advances Runtime, frontend input, and Catalog.
  Keeping these facts separate prevents a serving repair from falsely
  rewriting immutable pack-build history.

**Execution boundary**

- The corrected desired BOM passed 179 focused BOM, rendering, candidate,
  test2, evidence, and system-schema tests locally. The unchanged release tuple
  passed pack-hash and remote-identity validation.
- The complete shared local walking slice remains the application proof owner.
  A final hosted dispatch is permitted only after the newly signed resolved BOM
  passes that same runner locally. Hosted CI then proves registry signatures,
  test2 TLS/proxy behavior, cloud credentials, isolated TiDB, and cleanup; it
  must not become another application-debug loop.

**Republished candidate and complete local proof**

- The immutable serving-owner repin landed through
  [`nexuscrm-pingcap-pack#4537`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/pull/4537)
  at merge `8153b176da639ff1430120758aeb75ccf789b467`. Its corrected
  claim is explicit: advance serving owners and retain immutable metadata
  lineage. Desired-BOM, release-tuple remote/pack-hash, and 179 focused
  BOM/render/candidate/test2/evidence/system-schema tests pass.
- Owner publication completed with these exact identities:
  - Runtime API:
    `ghcr.io/pingcap-inc/nexuscrm-runtime-api@sha256:06c34dee14b04da37155b749ec326f90c1a041443a57bd71192aae9ccc70f868`
  - Runtime Worker:
    `ghcr.io/pingcap-inc/nexuscrm-runtime-worker@sha256:0ef4b2b3cd2be40f17fdde90145c74e5773069914f8675f1fce177fa90f04c07`
  - Catalog:
    `ghcr.io/pingcap-inc/nexuscrm-controlplane-catalog-reconcile@sha256:02ca654646365cb346b45883d23c82d6798af99b490177e6a2048e26e303cce1`
  - frontend:
    `ghcr.io/pingcap-inc/nexuscrm-frontend-assets@sha256:b6979d4c41595527157b67630f7c8cd290249bf01c222939ea198b8cc5446261`
- The two-phase L2 producer and signer published
  `ghcr.io/pingcap-inc/nexuscrm-serviceification-bom@sha256:73ea5a386ef6cd7c455f20cff268bf9bd606304dec6fe69577a8f1d1d07a6758`,
  binding those owners to the unchanged Gateway, sfcrm AppService, AppRelease,
  and L2 metadata identities. Its canonical desired-BOM binding is
  `sha256:a27e1a3d6aaabfdde7b8483dd75d447e28e2f0f225bb1484c34e5bb292c6d45a`.
- Complete local run `1785036532-1` started from the exact signed BOM and an
  isolated TiDB 8.5.0 database. It passed the v8 launch-readiness barrier,
  every D1/D2A/D2B read and mutation journey, cancellation and ambiguous
  outcomes, Runtime/AppService/Gateway restarts, four independently fenced
  Worker roles, crash-before/after-commit, poison repair, graceful drain, queue
  convergence, and zero in-flight work.
- The reference monolith was retired before Worker ownership. Outage probes
  failed closed, and the evidence records `topology.fallback=false`.
  Independently revalidated sanitized evidence has SHA-256
  `c335930d368a16c72cae8bbff0694e06644f7bd3063836ce18645c4fb6849608`.
- Hosted run
  [`30186418265`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/30186418265)
  was the first single environment attempt for this identical resolved BOM;
  its result and resulting local-proof correction follow.

**Hosted Worker handoff exposed a local-proof filesystem mismatch**

- Run `30186418265` proved the corrected long metadata apply, Catalog
  reconciliation, Runtime API, sfcrm AppService, Gateway, every D2A read
  family, D2B mutation/fault behavior, legacy retirement, cancellation,
  restart, and the first four independently fenced Worker owners. It failed
  only after Catalog advanced the audit Worker ownership epoch and the stale
  owner stopped.
- The replacement Worker start tried to truncate its existing workload token.
  The first start had already protected that inode as `0400`, so the native
  Linux test2 filesystem correctly rejected the write. The expected empty HTTP
  replies earlier in the log belonged to deliberate crash/ambiguity probes and
  were not the failure.
- The complete local runner had used the same script, BOM, containers, and
  business path, but stored its run root on a macOS directory mounted through
  Docker Desktop. A discriminating probe showed the toolbox reporting mode
  `0400` while the backing macOS file remained `0600`; the second write
  therefore succeeded locally. A Linux process does not imply Linux filesystem
  semantics when its state is a host bind mount.
- [`nexuscrm-pingcap-pack#4538`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/pull/4538)
  first used atomic inode replacement to reproduce the hosted filesystem
  semantics. Reopening the broader ownership model showed that this was a
  technically valid fix for the write error but the wrong lifecycle: it kept
  an invented coupling between Worker restart/ownership transfer and workload
  credential rotation.
- Current NexusCRM `main`
  `4eda21cfad02787d219441c3ee50ae4fea9f0d2d` makes the owners explicit.
  `_System_RuntimeWorkOwnership` plus `WorkFamily`, `DeploymentID`,
  `WorkerReleaseDigest`, and monotonically increasing `OwnershipEpoch` fence
  claim, renewal, completion, and process readiness. The Worker has no business
  RPC identity channel; its projected token is a deployment mount and is not
  the work-ownership authority.
- The corrected #4538 lifecycle therefore provisions one protected token per
  Worker role once, before any Worker starts. Initial start, ordinary restart,
  and ownership-epoch replacement only read that token. A second provisioning
  attempt fails closed and cannot change the bytes. Credential rotation, if
  later required by a real deployment identity contract, is a separate
  operator action and is not smuggled into the ownership-transfer test.
- The corrected exact tree passed review and merged as
  `ab86b2695e216ad4762293df6cb23c7a44935671`; its merge tree is byte-identical
  to the locally proven PR tree
  `cd44b04a00cd43da24d007a58fe62519043dc270`.
- The local runner still places all execution state on a marker-owned native
  Docker volume mounted at the daemon's own mountpoint; only sanitized evidence
  crosses back to the macOS output directory. It removes that volume only after
  the inner runner proves cleanup completed; otherwise the marker-owned volume
  remains as the recovery record.
- Review then exposed a second ownership error in the wrapper itself: the local
  run ID used second-resolution time, so concurrent invocations in the same
  second could share a session root, cleanup marker, and volume name. The
  inspect/create sequence did not make that identity atomic, and a marker left
  by one invocation could authorize another invocation's deletion.
- The corrected wrapper atomically creates a unique numeric run/session ID,
  adds an independent random invocation nonce to the native volume, and checks
  owner, kind, and nonce labels before every removal. A foreign or stale volume
  is retained rather than deleted. The executable regression test proves an
  exact label match permits removal and a mismatched invocation nonce cannot
  call `docker volume rm`.

**The first corrected full rerun exposed a separate ambiguity-probe race**

- The exact frozen-BOM rerun passed metadata authority, the reference oracle,
  Catalog, Runtime API, sfcrm AppService, Gateway, every D2A family, D2B
  mutation semantics, reference-monolith retirement, and initial startup of all
  four fenced Worker roles. It then failed before Worker handoff with
  `post-commit ambiguity probe did not commit`.
- The failure was not a Runtime or token regression. The test-only TLS
  blackhole forwarded the request, but stopped reading and closed its upstream
  as soon as the curl client hit its five-second timeout. On slower local
  execution this converted the intended “commit, then lose the response” order
  into “caller disconnect cancels before commit.” The test was racing the
  application and could claim the wrong fault order.
- The root fix makes the blackhole parse and drain exactly one complete upstream
  HTTP response without forwarding any response bytes. A client disconnect no
  longer cancels that upstream. The public query and idempotent replay still
  prove the durable commit and absence of a duplicate effect.
- The complete runner suite now has 81 passing tests (two environment skips),
  including guards that starts cannot provision Worker tokens and that the
  ambiguity proxy no longer exits its upstream drain on `client_closed`.
- Exact PR tree `cd44b04a00cd43da24d007a58fe62519043dc270`
  at head `877ebccee52e17050e4807c14e297eff02e9ca1a` then completed native-volume
  run `523045245447384984-1`. The unchanged frozen BOM passed every D1/D2A/D2B
  journey, post-commit ambiguity, stale-epoch replacement at active epoch 2,
  poison repair, queue convergence, zero in-flight work, graceful drain, zero
  fallback, evidence validation, scratch cleanup, and volume cleanup.
  Independently hashed sanitized evidence is
  `95b6e8169471373eef4f7c6943516a6defb527045ad93aad76318240bc5a2a09`.

**Decision**

- “Same local runner” now means the same script, fixtures, BOM, images,
  topology, and filesystem permission semantics. macOS bind mounts remain
  appropriate only for read-only source and sanitized output, not for mutable
  service state whose correctness depends on Linux ownership or modes.
- Ownership fencing and credential lifecycle are separate state machines:
  Catalog/Runtime DB epoch transfer protects durable effects; deployment
  provisioning protects the credential file. A restart or ownership transfer
  must not implicitly rotate credentials.
- This proof-harness correction has no release-tuple impact and must not mint a
  second launch candidate. At this point, local application closure was proven
  for the exact PR tree; the remaining evidence gap was one hosted test2 with
  the same unchanged BOM, environment validation, and marker-owned cleanup.

### 2026-07-26 — A local green run did not prove the fault injector

Hosted test2
[`30189948588`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/30189948588)
used merged L2 source `ab86b2695e216ad4762293df6cb23c7a44935671`
and the unchanged frozen BOM. It passed release resolution, metadata build,
Catalog activation, Runtime API, sfcrm AppService, Gateway, every current D2A
family, D2B mutation semantics, reference-monolith retirement, and independent
startup of all four Worker roles. It then failed at
`post-commit ambiguity probe did not commit`.

The failure falsified the claim that the prior local green run had completely
proved this fault order. The services and release tuple were the same, but the
test-only response blackhole still streamed request bytes on a background
thread and inferred commit from a second public query. It had no explicit proof
that one complete HTTP request reached Gateway or that Gateway returned the
upstream success response. “Same images, script, and topology” was therefore
insufficient: a network fault test also needs an explicit event-order
protocol. Faster local scheduling happened to produce the intended order;
hosted scheduling exposed the missing handshake.

The right owner is the harness, not Runtime or Gateway. The correction in
[`nexuscrm-pingcap-pack#4540`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/pull/4540)
passed exact-head review with zero blockers and merged as
`28b1806ba31308caa71e75e867512f31fc8bf065`; its merge tree is byte-identical
to locally proven head `54659aba36b6cc8ed9e81be36797d15cf6321879`.
The correction is test-only:

- buffer and validate exactly one bounded `Content-Length` request before
  contacting Gateway;
- forward that complete request independently of caller lifetime;
- drain one complete upstream response and atomically record its HTTP status;
- return zero response bytes to the caller;
- require `response_received` with HTTP 201 before record-visibility and
  idempotent-reconciliation assertions;
- publish sanitized fault phase/status on failure;
- execute the real TLS proxy in local tests instead of treating source-string
  assertions as behavioral proof.

The corrected harness passed 184 local BOM/harness tests (two environment
skips). The final exact source tree then completed local native-volume run
`331619409174230470-1` with the unchanged BOM. D1, all current D2A reads, D2B
mutations, explicit post-commit ambiguity, cancellation, restart/no-fallback,
four fenced Worker roles, stale-epoch rejection, poison repair, graceful drain,
queue convergence, zero in-flight work, evidence validation, scratch cleanup,
container cleanup, and volume cleanup all passed. Its sanitized evidence
SHA-256 is
`4afc2d2da237ea247acdf0da3505cf32804e8dc0825c2c1480e42373effaad32`.

**Decision:** a complete local runner passing once proves that candidate run,
not that every test instrument is deterministic. A fault injector must have its
own executable protocol test and observable event-order receipt. CI remains the
final environment proof only. Hosted run
[`30191552760`](https://github.com/pingcap-inc/nexuscrm-pingcap-pack/actions/runs/30191552760)
was that single proof. It ran merge
`28b1806ba31308caa71e75e867512f31fc8bf065` with the unchanged frozen BOM and
passed the walking slice, evidence validator, exact evidence upload, result
report, and marker-owned cleanup.

The independently downloaded artifact
`serviceification-d1-test2-30191552760-1` (`8629007861`) validated against the
same resolved BOM. Its sanitized evidence SHA-256 is
`cf91605e0a7a6dd991571a924b945c90a1f0064d5cc21555709fd44c1de8a861`.
It records cancellation, zero in-flight convergence, zero fallback outages,
caller timeout after commit, ambiguity reconciliation, one durable command and
effect, stale-epoch rejection at active epoch 2, poison repair, graceful drain,
queue convergence, and zero Worker in-flight work.

**Closure evidence decision:** the immutable R0 evidence package is complete.
Issue [#3555](https://github.com/pingcap-inc/NexusCRM/issues/3555) remains the
sole owner of live R0 and next-phase status. The technical next step is the
bounded launch-surface closure and machine-readable `MIGRATED` versus
`EXPLICITLY_DISABLED/DEFERRED` classification, not another R0 candidate
publication.

### 2026-07-27 — M1 recovery: focused tests hid composition failures

PR #4098 reached review after focused Runtime API and repository tests, but
before the complete backend suite, official `InitL0`, released dependency
approval gates, and D1 admission behavior had been exercised together. Review
then surfaced symptoms one at a time: Flow operational errors became
`InvalidArgument`, new resource-class pools expanded the process concurrency
limit, owner cancellation looked like caller cancellation, and readiness did
not prove durable identity indexes.

The shared cause was a proof boundary smaller than the release claim. Two
implementation shortcuts reinforced it:

- synchronous Flow replay and asynchronous Worker command ownership shared one
  journal even though their transaction and lifecycle rules differ;
- resource-class isolation was modeled as additive pools rather than stricter
  bounds inside the released process admission limit.

Recovery returned each invariant to its owner:

- api-host now acquires one process lease plus an optional stricter class lease
  atomically; unconfigured D1 methods retain one shared pool;
- `_System_RuntimePlatformCommand` remains the asynchronous command inbox,
  while `_System_RuntimeFlowInvocation` owns synchronous Flow acceptance,
  execution, completion, and restart replay in one caller transaction;
- Runtime classifies an engine cancellation as caller cancellation only when
  the request context itself is canceled;
- startup schema verification checks required physical types and the exact
  ordered unique indexes that make replay identity authoritative.

The first complete backend run immediately found another fact that focused
tests had missed: both Runtime tables declared `MEDIUMBLOB`, but the official
schema resolver rejected that type, so `InitL0` could not create the candidate.
The correct fix was not another fallback or text encoding. The command contract
allows opaque payloads larger than `VARBINARY` can hold, so the schema owner now
admits the single exact `MEDIUMBLOB` physical type while continuing to reject
arbitrary blob variants and SQL suffixes. The real `InitL0` scheduled-Flow
integration test then passed.

The release gates found three more omissions after bootstrap was green:

- the workspace build concealed stale standalone module graphs, so Files,
  Automation, Analytics, Audit, Events, and Records did not all resolve the
  same released contract revision with `GOWORK=off`;
- the new Runtime Flow transaction boundary was replay-safe, but its two
  production call sites were absent from the frozen pessimistic-transaction
  owner inventory;
- `_System_RuntimeFlowInvocation` was present in the generated managed-table
  set without an explicit scope-floor ownership entry.

These were not reasons to redesign M1. They were evidence that a release is the
module graph, owner ledgers, and declared scope as well as the new handlers.
The fixes align the affected modules to one released contract, classify the two
transaction owners with their replay rationale, and declare the neutral
synchronous invocation table in the scope floor. No compatibility read path or
new service was added.

Several full-suite failures were then reproduced in a clean latest-main
worktree before deciding ownership: the Automation integration package
timeout, REST OAuth `InitL0` bootstrap-store panic, Flow lifecycle fixture
owner-tuple failures, generated permission-writer inventory drift, drop-table
fixture coordinator failures, selected-root evidence drift, and the
public-core scan's existing hits. Those failures remain main-owned baseline
work; absorbing them into #4098 would hide M1's actual claim and recreate the
scope trap. Exact M1 tests plus every unaffected test in those modules still
run independently.

**Decision:** no M1 PR enters review again on focused evidence alone. Before
review, freeze the dependency heads and run the complete backend, official
bootstrap, D1 admission/security gates, TiDB restart/revision tests, boundary
gate, race suite, and distribution build. A red release-approval gate means the
owner release is not yet consumable; it must not be allowlisted ahead of a
reviewed merge and tag. #4098 remains an M1-A substrate delivery and must not
claim Catalog production enablement, L1 cutover, or final M1 closure.

### 2026-07-27 — M1 recovery: test authority handoffs and external side effects

The next exact-head review found two defects even after the individual
endpoint, repository, TiDB restart, and schema tests were green:

- Catalog correctly defines `flow-execution/v1` as a mutation-capable
  projection with additional caller-visible root authority, but the shared
  Runtime mutation graph and seven metadata consumers repeated a stricter
  `record-mutation/v1` equality check. A correctly admitted Flow could select
  its root, yet nested Flow resolution and mutation metadata reads failed
  inside the real engine graph.
- the durable upload journal fenced its database transition but not its
  object-store side effect. An expired `PREPARED` owner and its reclaimer wrote
  the same deterministic key. The stale writer could therefore overwrite the
  winner's body even when its later database transition lost.

Both failures crossed ownership boundaries that the earlier tests stopped
before: admission profile into the actual engine dependency graph, and
database lease ownership into an external object write. The correction owns
each fact once:

- `RuntimeMetadataView` now exposes the Catalog-owned predicate for complete
  mutation execution metadata. The shared graph consumes that predicate while
  serving admission continues enforcing the exact RPC capability profile.
  A Runtime endpoint regression executes a real parent/subflow chain from one
  immutable Flow projection; read-only profiles remain rejected.
- every upload claim/reclaim receives an opaque owner token and its own
  immutable attempt object key. `MarkBlobStored` compares the token and stores
  the exact winner key; cleanup compares and deletes only the same token/key.
  A forced stale-writer interleaving proves that the winning metadata, checksum,
  and object body cannot diverge. Runtime readiness now rejects a Files
  capability whose journal lacks the token column or authoritative unique
  idempotency index.

**Decision:** happy-path state-machine tests are insufficient when a request
hands authority to another layer or writes outside the transactional store.
For each such handoff, add one adversarial test that pauses immediately before
the side effect, changes or reclaims authority, then resumes the stale actor.
The proof must assert the final authoritative read path, not merely that the
stale actor received an error. Do not model a lease expiry, cancellation, or
post-write CAS as an object-store fence.

### 2026-07-27 — M1 recovery: shared grammar is not shared execution ownership

The next review found that the previous Flow projection correction was still
incomplete. `flow-execution/v1` and `record-mutation/v1` correctly share the
same immutable mutation metadata grammar, but Catalog also reused the D2B
record-event Worker's activation policy for both profiles. That policy rejects
every action except subflow calls. The new neutral `acceptCommand` handler was
therefore registered and bound in Runtime but no Flow containing it could
produce an activatable Catalog view.

The implementation also installed the complete
`PlatformFlowActionCallRegistrations` set in the independent Runtime graph just
to obtain `acceptCommand`. That set includes email, notification, connector,
approval, and other handlers whose dependencies and ownership are not present
in this Runtime composition. Catalog happened to reject them, but the serving
graph still claimed a broader action closure than it owned.

The correction separates two facts:

- mutation-capable profiles share metadata consumed by the record/Flow engine;
- each serving role has its own fail-closed executable-action policy.

The record-mutation worker remains subflow-only. The foreground FlowExecution
profile additionally permits only the neutral `acceptCommand` mechanism, and
only when its view selects Catalog-owned command registration authority.
Runtime registers exactly that command action rather than the broad platform
set. Compiler tests prove the authorized case, missing-registration rejection,
record-worker rejection, and hostile-view rejection; a real Runtime endpoint
test executes the compiled Flow and reaches durable command acceptance.

**Decision:** never use one broad registry to acquire a single owned handler,
and never infer execution ownership from a shared data grammar. For every
request-bound projection, test both what metadata it can represent and what
the specific serving role may execute.

### 2026-07-27 — M1 recovery: reconstructed seams are not production composition

The first Flow command endpoint test manually assembled a server, executor,
transaction runner, and capture accepter. It proved the handler contract but
was described too broadly as real serving composition. Re-running the journey
through official `InitL0`, `AssembleProductionWithConnection`, a TCP gRPC
client, and TiDB exposed two facts that the reconstructed test could not:

- the test client requested a five-second deadline while the host advertised a
  one-second Flow admission lease, so the host correctly rejected it before
  execution;
- the production adapter passed the foreground execution to the legacy default
  Flow mode, which persisted `FlowInterview`, even though
  `_System_RuntimeFlowInvocation` owns synchronous acceptance, execution,
  completion, and restart replay.

The deadline mismatch was a harness error; expanding the service limit would
have hidden it. The ownership mismatch was a production adapter defect. The
adapter now marks only the private synchronous Flow RPC as a transient
interview execution. Scheduled, timed, paused, and resumed work continues to
use `FlowInterview` under the Worker/lifecycle owner. The real test proves one
Catalog-bound request produces one completed Runtime invocation and one
accepted command, produces no `FlowInterview` row, and replays the same result
after process reassembly without duplicating either durable row.

**Decision:** a handler-level test may claim handler semantics only. A
production-composition claim must use the official bootstrap, production
assembler, real transport, authoritative store, and restart path. When two
durable records describe one execution, decide the lifecycle owner before
adding metadata or fixtures; do not make both records authoritative to satisfy
the test.

### 2026-07-27 — M1 recovery: a clean lifecycle journal cannot make an unbounded transaction safe

The next review invalidated the local design rather than finding another
isolated implementation bug. The private `ExecuteFlow` adapter put invocation
acceptance, the complete Flow graph, all business writes, and the terminal
result into one caller-owned pessimistic transaction. Separating that
synchronous journal from the asynchronous command inbox fixed lifecycle
ambiguity, and making Flow interviews transient removed the duplicate execution
record, but neither decision proved that the transaction itself was bounded.

The immutable projection rejected waits, scheduled ownership, and unsupported
actions. It did not compile or prove loop and subflow fan-out, read/write/lock
budgets, or whole-callback replay safety. This was not a hypothetical edge
case: the launch-required standard quote/order Flows read an arbitrary
line-item collection and create one child record per item. Rejecting all loops
would therefore reject the frozen launch contract, while adding the complete
bounded-atomic/durable-step compiler and execution model to #4098 would absorb
the Flow Runtime V2 epic into an already oversized substrate PR.

The production replacement is a maintenance-window, stop-the-world upgrade.
That removes the need for old and new Flow writers to overlap or negotiate a
rolling handoff. It does not change the steady-state safety requirements of
the new Runtime: after startup, every Flow still needs an explicit bounded
atomic or durable execution profile with exact-version replay semantics.

After removing the Flow wrapper, the direct `AcceptCommand` production walking
slice found two test-composition mistakes that handler tests could not expose.
The process capability list must not repeat a gRPC capability as a
permission-state evidence fact, and `AcceptCommand` is a workload-consumer
method: the authenticated Integration workload plus its durable security and
Catalog binding is the authority, so the client must not attach an end-user
delegation token. The corrected real-TiDB test starts the official production
assembler over TCP, accepts opaque bytes once, restarts the process, replays
the same receipt without a second row, and confirms Records is registered on
the same endpoint.

**Decision:** remove the foreground Flow serving path, its speculative
`_System_RuntimeFlowInvocation` table, Flow-only Catalog projection, and
Flow-to-command action bridge from #4098. Keep the additive neutral contract
release and the independent `AcceptCommand` inbox, but do not register or
advertise `ExecuteFlow` until the separate M1 Flow delivery proves the official
launch roots against the Flow Runtime V2 execution profiles. M1 remains blocked
on that delivery; the work is not reclassified as M2. A stop-the-world cutover
must drain and fence the previous owners before starting the new tuple, and a
rollback must perform the same fence in reverse.

**Lesson:** when repeated review findings move from idempotency, error mapping,
and lifecycle ownership to the size of the transaction itself, stop repairing
the adapter. Re-run the truth-scope-blocker test against real launch metadata.
A cleaner journal around an unsafe unit of work is still the wrong substrate,
and leaving it disabled does not make a difficult-to-reverse schema and serving
contract correct.

### 2026-07-27 — M1 recovery: a vertical cut must remove reverse inventories and every secondary owner path

The first review after removing foreground Flow found two defects that the
focused recovery suite and the independent local audit missed.

First, the deleted Flow server and its removed record-mutation transaction
wrapper still appeared in the records-owned pessimistic transaction inventory.
The implementation had removed the serving paths but had not run the reverse
ownership inventory that exists specifically to prove the set of transaction
owners is exact. The exact inventory test failed on both stale entries; review
reported only one.

Second, durable Files claimed that `BLOB_STORED` repair belonged to a scheduled
Worker, but the foreground retry path still exposed `ResetMissingBlob` and
could replace `OwnerToken` and `ObjectKey`. `_System_File` metadata is written
before the journal is marked complete, so a stale reconciler could write
metadata for its old attempt after another foreground request changed the
journal owner. Adding owner predicates only to the final transition would
detect the race after the harmful metadata write.

The correction removes foreground `BLOB_STORED` reassignment entirely. A
missing stored blob now fails closed with a retriable repair-required result
and leaves state, owner token, object key, and digest byte-for-byte unchanged
for the scheduled Worker owner. Completion compares the exact owner token,
object key, and complete digest. Real TiDB plus filesystem tests prove stale
completion is rejected and a missing stored blob cannot create metadata,
re-upload, or mutate the journal.

**Superseded decision:** this pass initially allowed `PREPARED`/`FAILED`
foreground reclaim and made the owner tuple immutable only after
`MarkBlobStored`. The 2026-07-28 failure-injection pass below proved that
reclaim could discard the only cleanup key for a pre-transition object. The
current decision is stricter: no foreground owner/key rotation in any
nonterminal state; cleanup must terminate the old attempt first.

**Lesson:** deletion is not complete when forward references disappear. Run
the owning reverse inventories and full release gates after every vertical cut.
For cross-system state machines, enumerate every operation that can change the
owner tuple; if metadata and the journal cannot commit atomically, eliminate
secondary foreground ownership rather than relying on a fence checked after
the external side effect.

### 2026-07-27 — M1 recovery: focused endpoint tests did not prove the shared error boundary

After the Files ownership correction, the standard review found no blocker but
the adversarial review showed that normal engine conditions were still emitted
as gRPC `Internal`. The new Platform handlers all called
`classifyPlatformEngineError`, but that helper delegated to the pre-existing
Record Access classifier. That classifier owns a different error vocabulary:
it recognizes record-access failures and selected HTTP-shaped errors, while
Files and other neutral engines return transport-independent
`DomainWireError` conditions. `DomainWireError` deliberately has no
`HTTPStatus` method, so malformed idempotency requests and missing/deleted files
fell through to `FailureInternal`.

The handler tests had proved successful requests, caller cancellation, and one
typed retriable error. Report's missing test stopped at the immutable Catalog
view and never made its engine return a not-found condition. Files represented
an absent engine result directly and never exercised the real
`DomainWireError` returned by `ContractService`. Those tests were individually
correct but did not prove the cross-product of engine error ownership,
capability-approved semantic detail, and final gRPC status.

The correction removes the Record Access reuse and gives the private Platform
boundary one neutral classifier. Each handler supplies only the
invalid-component value approved by its capability contract. The classifier
preserves request-context cancellation, distinguishes owner-side context
sentinels, respects infrastructure ownership before domain errors, maps the
reviewed neutral domain kinds, and fails closed for unknown, limit-incomplete,
or ambiguously joined errors. The regression suite now covers every current
caller capability, the neutral error taxonomy, wrapper and joined-error
ownership, and the actual Catalog/Search/Report/Files handler status.

Full backend verification also exposed two pre-existing harness facts instead
of producing a clean all-packages claim. The CRM automation fixture does not
register the product-dialect host primitive required by the already-pinned
official pack, and the REST integration fixture calls official `InitL0`
without wiring the mandatory Controlplane bootstrap-state port. Neither file
nor pin differs from the PR base. These are recorded as baseline failures and
are not absorbed into the Platform error-boundary fix.

Running the complete Records owner package, rather than only its named M1
inventory gate, found one additional PR-owned defect: the two new system tables
changed the selected-root capacity evidence, but its frozen golden still
claimed 149 string-ID owners instead of 150. That stale evidence was corrected
and the complete Records race suite was rerun. By contrast, the Kernel
permission-writer inventory and Persistence permission/drop-table fixture
failures reproduce in files unchanged from `main`; they remain explicit
baseline harness gaps rather than being hidden or pulled into M1.

**Decision:** a shared private serving boundary is not ready for review until
its error contract is tested as a matrix:

- every engine-owned neutral condition and infrastructure ownership shape;
- every capability-approved invalid component and final gRPC category;
- caller versus owner cancellation, transparent wrapping, mixed/joined errors,
  unknown future conditions, and fail-closed disclosure;
- at least one real handler path for each classifier caller.

Focused happy-path tests may establish local mechanism only. They cannot support
a merge-readiness claim for a shared boundary, and a standard-review approval
does not replace this matrix or an independent adversarial pass.

### 2026-07-28 — M1 recovery: a mutable attempt key cannot also be durable cleanup authority

The next adversarial review found that a successful object-store `PutObject`
followed by a failed `MarkBlobStored` could leak an attempt object. The first
recovery had made attempt keys owner-specific and deleted a stale writer's
object after an explicit lost-ownership result. That closed stale overwrite,
but the journal still had only one mutable `OwnerToken/ObjectKey`. Foreground
reclaim replaced both fields before the old artifact was durably cleaned.

The missing local matrix was broader than the review example. Fault injection
reproduced the same orphan class when:

- the object store committed but returned an error;
- the object store returned an inconsistent size receipt;
- `MarkBlobStored` did not commit;
- the process stopped after `PutObject` and before `MarkBlobStored`;
- a stale owner's best-effort delete failed once.

The committed-but-response-lost `MarkBlobStored` control case already
reconciled correctly through `BLOB_STORED`. This proved that deleting on every
transition error would be unsafe: it could remove a blob referenced by a
committed journal transition.

Removing foreground reclaim exposed a second expression of the same ownership
problem. The cleanup worker previously deleted a `PREPARED` object and its row
in one pass. An expired writer whose `PutObject` completed after that delete
could still recreate the object after its only durable key had disappeared.

**Decision:** expired `PREPARED/FAILED` rows are never rotated by foreground
acceptance. Cleanup is two-pass: the first pass writes a
`FAILED/CLEANUP_CLAIMED` tombstone and deletes any visible object while
retaining the exact owner/key; only a later pass after a quiet period deletes
the object again and removes the row. Upload `PutObject` receives a deadline
that ends before cleanup eligibility, and the claim quiet period adds another
bounded interval. A retry remains fail-closed until terminal cleanup removes
the old authority.

The first implementation of that decision still relied on a false adapter
assumption: the filesystem `ObjectStore` accepted a context but ignored it.
The S3 adapter propagated cancellation, while the filesystem adapter could
continue copying after the journal lease's writer deadline. The filesystem
adapter now copies only into its private temporary file, returns promptly on
cancellation, closes/removes that file, and leaves no path that can rename a
late reader result into the durable key. Its race test blocks a real reader,
cancels the call, then releases the reader and proves that no object becomes
visible after `PutObject` returned.

Real TiDB plus filesystem tests now cover crash-before-transition,
expired-in-flight retry, cleanup tombstone retention, a late `PutObject` whose
immediate stale-owner delete fails, terminal cleanup, and successful retry
with a new attempt key.

**Lesson:** an external side effect is not owned merely because its key appears
in a mutable row. Every key that may have committed must remain durably
discoverable until deletion is confirmed after the writer's bounded lifetime.
When a test proves stale-writer cleanup only with a successful `DeleteObject`,
inject cleanup failure and process stop before declaring the ownership loop
closed.

### 2026-07-28 — M1 recovery: passing scope into an authority is not a disclosure proof

A final reverse evidence audit mapped every new production path to three
separate proof layers: engine behavior, gRPC policy/handler behavior, and
production composition. That audit found that Catalog consumer handlers passed
the admitted Org/AppInstallation/workload scope into the injected authority,
but trusted the returned view without rechecking its scope. A buggy authority
could therefore return a correctly digested immutable view for another
installation or integration workload and the serving boundary would serialize
it. The same boundary also did not independently enforce the caller's minimum
revision against the returned lineage.

The correction validates the authority result immediately before disclosure:
App views must match the admitted Org and AppInstallation; Integration views
must additionally match the admitted workload; and both must meet the
request-bound revision floor. Mismatched scope fails as disclosure-safe
`Internal`, while stale authority fails as the existing Catalog
`FailedPrecondition`. Unary and watch paths share the same checks.

The audit also removed an unused report-by-ID pagination seam. Runtime report
execution already consumes the exact immutable definition selected from the
request-bound Catalog view; retaining a second helper that loaded a definition
by ID from the legacy metadata service created an unowned alternative read path
with no consumer.

Generated clients now cross a real TCP Runtime process for Catalog, Search, and
Files, proving registration, host policy, Security/Serving admission, mapping,
and handler dispatch. The first Catalog process test failed because it attached
an end-user delegation token to a workload-consumer method. The production host
correctly rejected that identity-class mixture. The test was fixed to model the
two authentication shapes separately; production policy was not weakened.
Search received a successful multi-object/global-limit projection test, both
Catalog watch families now prove cursor advancement only after a successful
send, and deterministic file IDs are proven stable within and distinct across
Org/AppInstallation/idempotency scopes. A real TiDB integration test also
proves that production composition constructs Search, Report, and Files
adapters together on the existing Runtime process.

**Decision:** every new serving capability needs an evidence row for all three
layers. Separate handler and engine tests are useful but do not prove that the
released generated client can reach the handler through the real process.
Conversely, a process test with an injected engine does not prove production
assembly. M1-A must prove both boundaries; the final enabled Catalog producer,
exact BOM, Worker roles, and complete D3 walking slice remain M1-C/owning-release
evidence rather than being pulled into this substrate PR.

**Lesson:** authorization is a property of the value disclosed, not merely the
arguments sent to its source. Revalidate scope and revision at the last trusted
boundary. Also treat a test rejected by security as possible evidence that the
test modeled the wrong caller class before changing production admission.

### 2026-07-29 — M1 Track A: exact root selection is not an immutable execution closure

The first Flow adapter selected the root `FlowVersion` from the request-bound
Runtime view and compared every transitive child with the authorized
`AppMetadataView`. That looked revision-bound at the serving boundary, but the
legacy executor's native subflow and `actionType=flow` paths still called
`ExecuteFlow` by name. They reopened the live active-version query and could
execute a child from a different metadata revision.

The first real TiDB walking slice did not catch this because its Flow had only
one assignment. Strengthening the same test to execute a pack-owned
root-to-child chain found a second process-local dependency: the child still
required the legacy standard-action context marker even though the private
Runtime boundary had already authorized the complete exact closure against the
installation-scoped AppMetadata view.

**Decision:** the private Runtime seam passes the complete exact Flow closure
into the executor. Root and descendants resolve only from that unexported
request context; a missing member fails closed and never reopens the live Flow
tables. The prepared-closure context is also the only executor path that may
skip the legacy protected-standard-Flow marker, because the Runtime handler has
already verified every member against the same authorized AppMetadata view.
All legacy and public in-process `ExecuteFlow` callers retain the existing
protection behavior.

The bounded-atomic proof also initially rejected declared collection variables
but still allowed a RecordUpdate/Delete `InputReference` to point at an
arbitrary request input. The executor accepts runtime slices on that path and
would fan out writes without a static bound. V1 now permits these mutations only
when the target reference is produced by a `GetFirstRecordOnly` lookup. It
continues to reject Wait, Screen, Loop, external action, collection lookup,
dynamic parent prelock, graph cycle, missing child, and any closure whose
reachable-element total exceeds the fixed execution budget.

The launch gate first calls the engine's existing atomic validator for action,
Wait, trigger, and parent-prelock safety, then applies only the stricter
launch-specific closure and execution-bound proof. This avoids creating a
second atomic-safety compiler whose rules could drift from Runtime.

The real process test now proves nested exact-closure execution, one Runtime
gRPC endpoint, the actual Flow engine and TiDB transaction, durable completion,
process stop/reassembly, and replay of the same ExecutionID, outputs, and
revision/generation receipts.

**Lesson:** "the root is exact" is weaker than "the execution is exact." Trace
every recursive engine edge and make it consume the same immutable authority.
When removing a process-local authorization hook, do not synthesize its old
marker across the boundary; replace it only with the neutral authorization fact
owned by the new boundary, and prove a protected descendant through the real
serving composition.

### 2026-07-29 — M1 Track A: test harness success can be false evidence

Several local commands produced misleading status during Track A verification:

- the real Catalog consumer test reported package PASS after skipping because
  sandbox policy denied the TiDB connection;
- a package-discovery pipeline exited zero after `go list` failed, because the
  final pipeline process succeeded;
- Controlplane inherited a stale parent `go.work` entry for a deleted worktree;
- its producer test failed correctly against the released old Core compiler but
  passed when linked to the candidate compiler;
- an uncached parallel backend suite made multiple packages run official
  `InitL0` against the same local TiDB, causing apply-lock contention and
  teardown failures unrelated to Track A;
- the first Flow receipt schema change updated the authoritative schema
  catalog but omitted the shared managed-table inventory and scope floor, so
  focused Persistence/Kernel tests passed while schema-management protection
  remained incomplete;
- the first production capability list allowed `ExecuteFlow` without also
  selecting `AppMetadataView`. The handler correctly required that authority,
  but the composition root still accepted and advertised a Flow-only
  configuration whose delegation verifier could never authorize a call;
- the Core candidate passed its focused Runtime, Shared, Metadata, Persistence,
  and Kernel suites but exact-main Records stopped before the remaining release
  matrix because the new Flow transaction owner was absent from the
  pessimistic-owner inventory. The same gate also exposed the already-landed
  Files finalization owner and the new Runtime Flow invocation table's
  selected-root schema-owner count. A locally green subsystem is not a
  releasable Core commit until the cross-module inventories derived from its
  source and schema have been run.
- the Automation module still had a clean-main Connector test-fixture defect:
  three historical-terminal TiDB fixtures omitted
  `_System_ConnectorRemoteIdentityLane`, so the current reservation path failed
  before those tests reached their assertions.

**Decision:** evidence records distinguish executed PASS from SKIP, require
pipeline failure propagation, use an explicit temporary Core/Controlplane
workspace for pre-release producer verification, and run shared-InitL0 backend
packages serially when the cache is cold. The Connector production behavior
remains outside Track A, but its stale real-TiDB fixtures block the same
exact-main release and are repaired in the release-health follow-up by creating
the already-canonical lane table; no Worker or Connector runtime behavior is
pulled into that PR. A new `_System_` table is one atomic change across the
schema catalog, `AllSystemTableNames`, and
`scope-floor.md`; the complete shared module is part of that evidence, not an
optional downstream check. The Flow-to-AppMetadata serving requirement is
validated at the production composition root before credentials, database
connections, or endpoint registration. A handler-local authorization check is
necessary but cannot make an impossible production configuration valid; this
single frozen dependency does not justify a generic capability-dependency
framework.

The release ownership also forces a minimum sequence rather than one
cross-repository pseudo-PR: Core must merge and publish its Catalog compiler and
Flow runtime first; Controlplane then pins that release and publishes the
producer; a final small Core pin/composition update consumes the reviewed
Controlplane release. Core has two independently resolved serving module
graphs, `backend` and `backend/distributions`; advancing only the first leaves
the CRM distribution on the old producer even when focused Runtime tests pass.
The final pin therefore advances and verifies both graphs as one release
candidate.

A real-TiDB run also exposed a legacy backend integration fixture that manually
recreates only part of the CRM distribution. Adding its first missing dialect
requirement merely revealed further L1-owned action-handler and OAuth ownership
requirements. That partial patch was reverted: Track A proves the app-blind
Runtime through its tagged real-repository suite, while Track C/D3 must exercise
the real CRM distribution rather than growing a second test-only L1
composition inside Core.

**Lesson:** `exit 0`, package `PASS`, and a focused green test are not evidence
until the intended path actually ran against the intended candidate. Verify the
candidate revision, dependency pins, environment, and non-SKIP assertions
before using a result in a PR claim.
### 2026-07-29 — M1 Worker closure: do not turn parallel work into a false metadata owner

The first B1 composition attempt correctly added scheduled occurrence leases,
safe pre-effect reclaim, an ownership-aware transaction commit fence, and a
controller-owned scheduler lifecycle. It then made an incorrect shortcut:
scheduled discovery reopened `record-mutation/v1`. That projection
intentionally contains only trigger-reachable Flow closure and excludes
scheduled roots. Extending it to all scheduled roots would also be wrong
because one AppInstallation-scoped Worker must not silently acquire another
installation's schedules.

The invalid production wiring was removed while preserving the generic B1
mechanism. At that review point B1 correctly waited for Track A to freeze the
Catalog-owned exact scheduled Flow view/binding instead of borrowing another
projection. Track A subsequently published that authority; the current B1
composition reopens it exactly. The same evidence audit prevented B2 from
selecting a Flow from current metadata, inferring a handler from command kind,
or reopening a latest repository. B2 must likewise consume the exact command
execution binding now published by Track A.

The first focused TiDB command returned `ok`, but verbose output proved the
integration test had skipped because sandboxed loopback access was denied. The
test was rerun with explicit access to the existing healthy local TiDB and
passed without skip. The expanded case now covers readiness rejection of
legacy active rows, atomic effect/completion, safe reclaim, mixed
state/revision rejection, restart, poison, and stale-owner rollback. Full
automation failures were reproduced on detached exact
`origin/main` and belonged to the existing connector fixture missing
`_System_ConnectorRemoteIdentityLane`; they were not hidden or counted as
green.

**Decision:** B1 and B2 remain one delivery wave but not one forced diff. Land
the functional B1 scheduled work family with its production composition first,
then land B2 as a separate work-family PR in the same Runtime Worker artifact.
Neither PR may claim launch usability before both work families and the full D3
walking slice pass.

**Lesson:** parallel execution does not erase dependency direction. When the
only way to make a parallel PR appear complete is to borrow a nearby
projection, infer routing, or accept a skipped integration test, stop and
expose the owner dependency instead.
### 2026-07-29 — M1 B1 review recovery: a reclaimable row is not enough if the cursor can outrun acceptance

The first B1 restart proof tested the reservation repository directly: an
expired row with no committed effects could be reclaimed, while a row with a
separately fenced effect became uncertain. That proof was true of the first
implementation but also exposed an avoidable split ownership model; the final
implementation instead places effect and completion in one reservation-owned
transaction. Independently, the real
Scheduler marked an occurrence accepted immediately after spawning its worker
goroutine. A process could therefore advance the scan cursor and stop before
the goroutine created any durable row; on restart there was nothing for the
otherwise-correct reclaim code to discover.

The first response—enumerating expired reservation rows—closed only the
post-claim crash window. Reopening the whole path exposed a second edge: a
record-scoped scheduled occurrence can contain many input rows, so sequential
claims can leave a partially durable occurrence if the process stops midway.
Keeping the cursor behind in memory does not help because that cursor is lost
with the process.

B1 now treats acceptance as the owner boundary. It canonicalizes the complete
bounded input set and writes all per-input occurrence rows in one TiDB
transaction under the active work-family ownership fence. Only a successful
batch commit lets discovery advance; effects still execute asynchronously.
The persisted canonical inputs are versioned and size-bounded, and expired
rows are discovered independently of the schedule time window. Recovery first
reopens the exact Flow definition/version authority, then reclaims and executes
the stored inputs. Missing or mixed authority remains fail closed.

The real TiDB test now proves two inputs are both durable before the first
effect starts, controller cancellation leaves both pending, a new Scheduler
process rediscovers both without re-querying mutable inputs, and both complete
on attempt two. A separate synchronization test pins that discovery cannot
return success—or update its accepted-progress map—while the atomic acceptance
transaction is blocked.

**Decision:** cursor progress is an acknowledgement of durable acceptance, not
of goroutine admission. Restart recovery and schedule discovery remain separate
read paths for separate facts: discovery creates new occurrence identity;
recovery consumes already accepted rows. No unbounded historical schedule
rescan or legacy fallback was added.

**Lesson:** test the handoff, not just the queue primitive. A lease table can be
perfectly reclaimable while the caller still loses work before enqueue. Also,
when one logical occurrence expands into multiple durable units, acceptance
must be atomic or have its own durable manifest; an in-memory cursor cannot
repair a partially persisted batch after process loss.

### 2026-07-29 — M1 B1 final local proof: Git cleanliness and mock DML are not production evidence

The first B1 implementation added a generic transaction-manager commit hook and
a lease-renewal goroutine. Both were symptoms of putting ownership outside the
operation that owns the reservation. The corrected store starts one pessimistic
transaction, locks the work-family ownership row and exact attempt row, invokes
the existing bounded exact-version Flow inside that transaction, writes
`COMPLETED`, and only then commits. Reclaim uses the same lock order, so it
cannot pass an active effect transaction even when wall-clock lease time has
elapsed. This removed the generic commit hook, renewal race, and a new global
SchedulerService transaction owner; the remaining reservation owner is
classified at its actual boundary.

The first simplified version still called the fail-closed transaction manager
directly. The full records race gate rejected that owner, and code inspection
showed the deeper mismatch: the prepared Flow seam requires the existing
records-owned pessimistic replay boundary so late parent-prelock discovery can
restart the complete callback. B1 now adapts the reservation owner to
`RunInPessimisticTransactionWithOutcome`; exact attempt validation, Flow effect,
and `COMPLETED` remain one replayable callback, while commit/rollback ambiguity
stays non-terminal. Merely adding the new call site to the inventory would have
hidden a real transaction-semantics bug.

The first production-composition test also logged successful RecordUpdate calls
while every target row stayed unchanged. The scheduler was not at fault: the
Catalog fixture had omitted `Updateable=true`, so the real records engine
correctly reduced the writes to no-ops. After restoring the true field
capability, the same path reached audit V2 and failed because the worker scratch
schema omitted its canonical `_System_FieldHistorySequence` dependency. Keeping
the real field capability and adding the existing audit table to the fixture
closure made the walking slice prove successful DML, rollback after a later Flow
fault, timed execution, future-row exclusion, and controller drain.

**Decision:** B1 proof uses the real production composition, immutable Catalog
view, writable-field capability, records engine, audit producer, transaction
manager, reservation-owned transaction, and TiDB schema. Mock assignment tests
remain useful for parser diagnosis but cannot satisfy the release claim.
Existing unrelated connector and schema-DDL fixture failures are disclosed
separately and are not pulled into the B1 diff.

**Lesson:** after every rebase, run the owning inventories and the real walking
slice before calling the branch conflict-free. A log that says "updated" is not
an effect receipt; assert the database row and the durable work state together.

### 2026-07-29 — M1 B1 exact authority must survive beyond Flow-plan preparation

The scheduled recovery binder correctly reopened each durable row's immutable
Catalog view and used it to select an exact Flow root/subflow closure. It then
returned only the closure. The Scheduler effect context still inherited the
Worker-startup view, so a row accepted under Catalog revision A could execute
revision-A Flow code while Records and nested durable effects resolved metadata
from revision B.

The fix makes the exact closure and its already-verified context installer one
prepared execution envelope. Both fresh and recovered paths must carry that
envelope through lease claim to the effect boundary, where the row view is
installed before the records-owned pessimistic transaction. The automation
engine remains Catalog-implementation-blind, and artifact I/O still finishes
before claim/effect ownership.

The first proposed regression discriminator changed a field's physical column
between revisions without changing `SchemaGeneration`. That would have made the
test sensitive, but it modeled an invalid publication and was removed before
commit. The final TiDB proof uses two semantically compatible Catalog revisions
and observes the durable binding emitted by the successful record effect. With
the installer temporarily disabled, the test failed and showed the startup
revision digest; with the fix restored, it passed and preserved the durable
row's digest through the nested record-event handoff.

**Decision:** exact durable authority is an execution-envelope fact, not merely
a planning input. Recovery remains allowed across compatible Catalog revisions;
missing or incompatible artifacts still fail closed.

**Lesson:** a test must not manufacture an impossible authority state just to
make a bug observable. Prefer an existing durable receipt or downstream binding
that reveals which authority the real effect actually used.

### 2026-07-29 — M1 B1 nested work needs both exact revision and the consuming capability

The revision-A execution-envelope fix preserved the scheduled Flow view through
the business effect, but installed that Flow capability as the primary Records
view. Records therefore emitted the nested record-event row under
`runtime.flow.execute.v1`. The dedicated record-event Worker is correctly
partitioned under `runtime.records.mutate.v1`, so it could not claim the row.
The initial production-composition assertion decoded the queued binding and
proved its revision, but stopped before running the downstream consumer.

The corrected envelope opens two already-published projections from the same
immutable Catalog snapshot: the exact Flow view owns root/subflow closure, and
the exact record-mutation view owns DML plus nested-work routing. The Worker
derives only the sibling projection's content address from that snapshot and
then requires the corresponding artifact to exist in the Catalog publication
store. Missing publication fails closed; no current/latest selector or
repository fallback was added. The nested row now carries the mutation binding
as primary and the Flow binding as `flowWork`.

The real TiDB production-composition test now starts the record-event Worker
after the scheduled effect and requires that exact row to reach `processed`.
It also asserts both bindings and their shared lineage. Full focused Runtime
Worker gates and their race run remain green.

**Decision:** a nested durable handoff preserves one Catalog lineage while
switching to the capability owned by its consumer. Exact revision and correct
work-family routing are independent invariants, and both must be encoded.

**Lesson:** do not stop a cross-process test at “the queue row exists.” Follow
the durable handoff through the real scoped consumer and assert terminal
progress; otherwise a perfectly immutable row can still be permanently
unclaimable.

### 2026-07-29 — M1 B1 entry proof must start before the durable row

The first B1 production test proved recovery, reclaim, exact Flow authority,
transactional effect plus completion, and downstream record-event consumption.
It nevertheless inserted every scheduled reservation directly. The actual
production discovery path used a repository-free Runtime MetadataService whose
scheduled-Flow enumeration still read the legacy process cache. A real Worker
could therefore recover an existing row but could never discover and durably
accept a newly due Catalog schedule.

The same blind spot existed on the timed path. Its test called the durable
producer directly, while the production record-trigger listener accepted a
timed-only entry and then passed the Flow to the generic executor. The
executor's scheduled-path fallback immediately ran the same entry that the
Worker would run again when due. Preparing the timed worker plan also inherited
the record-trigger-root marker even though the prepared root deliberately has
that trigger removed.

The correction enumerates scheduled roots only from the request-bound immutable
Runtime view, lets startup recovery finish before ordinary discovery ticks, and
binds the discovery interval to the existing Worker poll interval. Timed-only
roots transfer their sole entry to the durable owner; roots with an immediate
connector still execute that connector now. Scheduled-plan validation uses a
fresh Flow runtime state while preserving caller cancellation. The first
discovery scan uses a fixed one-minute lookback rather than the poll interval:
short test/ownership polling must not shrink the bounded restart window, while
V1 still makes no unbounded downtime catch-up promise.

Stopped upgrade exposed a separate ownership detail. Making the new scope
columns nullable was necessary for the retained legacy writer, but retaining
its old composite unique index was not: that index would collide across valid
AppInstallation scopes. Instead, the retained writer maps its existing exact
identity into the same digest column with a legacy domain separator. The one
digest index remains authoritative, while active rows without scoped authority
fence the new Worker until the stopped ownership switch terminalizes them.

**Decision:** B1 tests must cover all three production handoffs:
Catalog schedule to durable acceptance, record event to timed acceptance, and
retained writer to the final shared schema. The Catalog walking slice requires
the discovered occurrence to reach its terminal failed status, not merely that
a row appears.

**Lesson:** deep proof after a queue boundary cannot establish that production
can enter the queue correctly. Start each walking slice at the real authority
handoff, and challenge proposed compatibility fixes against the new scoping
invariant before adopting them.

The strengthened production test initially appeared to hang: readiness was
green, no effect had committed, and several candidates remained unchanged.
TiDB processlist showed the real state. One scheduled effect held the
work-family ownership row inside its business transaction, while the other
effects correctly waited on the same `FOR UPDATE` fence. Cold TiDB and race
instrumentation allowed that intentional serialization to outlive the old
eight-second test budget; a repeat completed normally and released the lock.
The production fence was retained, and only the real walking-slice deadline was
raised to a bounded thirty seconds. Weakening the lock to make a test faster
would have reopened ownership transfer during an in-flight commit.

Finally, the first digest bridge used delimiter-concatenated authority fields.
Although launch-produced IDs do not contain those control delimiters, the
public key constructor did not enforce that assumption. Both legacy and scoped
digests now use length-delimited components and separate domains, preserving
one unambiguous identity for every accepted tuple.

### 2026-07-29 — M1 B1 first durable acceptance owns an occurrence across Catalog revisions

`ClaimBatch` initially required an existing row's immutable metadata binding to
equal the Worker process's current binding. That reversed the authority
relationship after Catalog publication advanced: a completed occurrence from
revision A was rejected when revision B rediscovered the same schedule window.
Discovery then treated the rejection as an incomplete handoff, retained its
cursor, and could retry that historical occurrence indefinitely instead of
reaching later work.

The occurrence identity remains the stable scoped definition/version/window/
input tuple. Only a new acceptance uses the current Catalog binding. Once any
row exists, that durable batch is authoritative: active rows recover with their
persisted exact binding, terminal rows are duplicates, and every row in the
batch must carry one valid binding for the same scope. Missing, cross-scope, or
mixed persisted authority still fails closed. No revision was added to the
identity and no historical row is rebound.

The real-TiDB state matrix now covers reserved, completed, failed, and poisoned
revision-A rows rediscovered by a revision-B service, followed by acceptance of
a later revision-B occurrence. The full production-composition test initially
lost its ten-second lease on a heavily shared TiUP cluster; the same exact head
passed in 28.592 seconds on an isolated TiUP TiDB/TiKV cluster. That separated
environment contention from the lifecycle fix without weakening the lease or
the production ownership fence.

**Decision:** current Catalog authority selects only newly accepted work; the
first durable acceptance owns the execution binding for that occurrence
forever.

**Lesson:** design durable work tests as a state-transition matrix, especially
across authority revisions. A same-revision restart test cannot prove
rediscovery behavior after publication advances, and a shared-database timeout
must be reproduced in isolation before changing production timing semantics.

### 2026-07-29 — M1 B1 stopped upgrade needs a temporal fence, not an invented identity mapping

The retained legacy writer could write the final reservation schema, but its
unscoped occurrence digest intentionally differed from the new
AppInstallation-scoped digest. The initial stopped-upgrade test proved that
active legacy rows fenced ownership transfer and that terminal rows eventually
unblocked readiness. It did not combine that handoff with the new Scheduler's
one-minute startup lookback. A recently completed legacy occurrence could
therefore fall inside the new scan while remaining invisible under the scoped
identity, allowing the same scheduled effect to run twice.

Legacy rows contain no authoritative AppInstallation mapping. Rebinding them,
querying them as a global serving fallback, or dropping scope from the new
identity would all manufacture authority or reintroduce cross-installation
collisions. The stopped-upgrade bridge now uses the fact the deployment model
does own: time. Active legacy work must first drain, and recent terminal legacy
rows keep the new Worker unready for one complete startup-lookback window. Once
that quiescence gate passes, the first new scan cannot cross the old ownership
boundary. Missing terminal timestamps fail closed.

The same review exposed a verification gap: a backend TiDB fixture hand-authored
the reservation table without the digest column now written by the retained
legacy service. The scheduled-reservation integration set now uses the final
digest column and unique index, while its separate warm-upgrade fixture remains
deliberately old and exercises the official reconciliation/backfill path.

**Decision:** phase-one stopped upgrade separates legacy and scoped occurrence
domains with an enforced temporal fence. It does not guess a scope for legacy
receipts and does not add a compatibility read path.

**Lesson:** schema compatibility and semantic handoff are separate claims.
Upgrade tests must cross the ownership boundary with the new process's actual
startup window, and retained-writer tests must run against the exact final
schema they claim to support.

### 2026-07-29 — M1 B1 substrate presence is not capability activation

The first production composition installed the timed-path producer
unconditionally in every record-event Worker. Its readiness check correctly
required an active scheduled-flow owner, but that made merely shipping the B1
code change the startup contract of already-enabled record-event deployments.
The substrate-only release therefore could stop unrelated event processing
before the stopped-upgrade operation had activated B1.

The repeated B1 review findings shared one implementation failure class: the
work was tested after its durable handoff while the entry and activation
boundaries remained implicit. Catalog discovery, record-event timed acceptance,
and the stopped legacy handoff are different producer/composition boundaries
feeding one scheduled consumer family. One consumer authority does not make
those producers one owner.

`_System_RuntimeWorkOwnership` cannot activate this cross-role handoff by
itself. It owns one work-family deployment generation, but does not identify
the scheduled consumer's AppInstallation scope or prove that its exact
receipt-pinned composition reached readiness. Treating any global scheduled
owner as activation allowed a differently scoped or failed candidate to make
the record-event Worker accept rows that its scheduled Worker could not claim.

B1 therefore keeps timed production disabled in record-event production
composition. Ordinary record events preserve their existing path; admitted
timed metadata receives an explicit rejecting handoff and cannot enter the
generic scheduled-connector fallback. The generic timed producer and scheduled
consumer remain tested mechanisms. M1-C must install their exact cross-role
handoff only after the candidate has one matching scoped scheduled composition;
B1 does not invent another readiness registry or global feature flag.

**Decision:** capability code may be present while the capability is disabled;
a global work-family owner is not sufficient authority to enable a scoped
producer. B1 owns the mechanism and fail-closed default. M1-C owns exact
candidate activation.

**Lesson:** every independently deployable mechanism needs an explicit
code-present/authority-active/composition-enabled state model before
implementation. Test both the disabled non-regression path and the enabled
walking slice through the real serving composition; queue-level tests alone
cannot prove either state. That disabled proof must include metadata for the
new capability, not only an unrelated event: a nil producer and a rejecting
producer have different semantics when the existing Flow executor owns a
legacy fallback. Test fixtures must also use the canonical trigger-subscription
row identity (`Update`, not the authoring-level `CreateAndUpdate`) and a record
ID whose key prefix reaches that row, or the walking slice never enters the
claimed trigger. Enabled mechanism tests must not be described as production
activation proof until the exact cross-role candidate composition exists.

### 2026-07-29 — M1 B1 recovery must dispose the durable attempt before scanning onward

Recovery originally tried to reopen immutable Flow authority before reclaiming
the expired row. A missing or corrupt artifact therefore produced only a log:
the attempt did not advance, the row remained at the front of the limited
recovery scan, and enough bad rows could starve healthy work indefinitely.
Failure classification existed at the Catalog boundary but had no durable
consumer.

Recovery now claims the exact row first. Invalid claimed inputs or immutable
authority are recorded against that fenced attempt: permanent failures
terminalize as failed, while retryable failures retain their lease and durable
error until the existing bounded reclaim/poison lifecycle advances them. A
lease is refreshed under the same ownership/token CAS immediately before the
effect transaction. The scheduled work-family ownership row serializes effect
commits, so production uses one execution lane instead of aging nominally
parallel leases behind the same fence; durable acceptance remains batched.

The real TiDB tests place more unavailable-authority rows than the recovery
capacity ahead of a healthy row and prove the healthy row completes while each
bad row receives a durable retry disposition. The scheduled production test
starts from Catalog discovery and proves the scheduled Worker, exact Flow
effect, and downstream binding. The generic timed producer has its own
transactional TiDB proof; the cross-role production activation remains M1-C
owned as recorded above.

The full Runtime Worker dependency gate initially found an unrestricted
Controlplane dependency and prompted an overcorrection: startup stopped reading
Catalog's active HandlerBinding and trusted only the candidate's exact receipt
pin. That removed a writer dependency but also made deployment config the
serving selector; a self-consistent unpublished or stale receipt could run.
The corrected boundary allows exactly Controlplane's released writer-free
`catalogread` facade in the scheduled authority composition. Startup reads the
active installation/binding tuple in one statement, requires the candidate pin
to equal that binding's receipt, verifies the complete receipt tuple and exact
immutable artifacts, and requires both Catalog and Runtime work-family epochs
to agree. All other Controlplane packages remain forbidden. M1-C owns the exact
candidate assertion; Catalog remains the active selector, receipt and artifact
author. The real ownership-transfer test proves an epoch-matching receipt that
Catalog has not activated still fails closed.

The same proof exposed a database-time boundary bug. `UTC_TIMESTAMP` is a
timezone-free SQL `DATETIME`, while the production MySQL DSN parses it in local
time. Calling `Time.UTC()` shifted an already-UTC wall clock a second time.
Scheduled lifecycle code now reinterprets scanned database wall components as
UTC for comparisons while preserving database-wall values for SQL parameters.

The final exact-head review found one remaining entry-ownership ambiguity.
Timed publication allowed a non-empty top-level `StartElementReference`, while
the generic executor treats that field as a callable-flow fallback whenever
`Start.Connector` is absent. If the top-level reference named the timed
connector, the listener could durably accept the due occurrence and then
execute the same element immediately. The launch profile now expresses an
immediate record-triggered entry only through `Start.Connector`; Catalog
publication rejects a timed Flow with `StartElementReference`, and the listener
independently rejects that shape before durable acceptance. Normal
`Start.Connector` plus one timed connector remains supported.

**Decision:** every recoverable row first becomes one fenced durable attempt;
pre-effect authority failures receive a durable bounded disposition, and only
one scheduled effect per work-family owner enters the commit fence at a time.

**Lesson:** a queue consumer is not proven by inserting directly into its
queue. Any later production-activation claim must start at the real producer
composition. Include bad rows ahead of good work, and verify database time
semantics with the same DSN timezone behavior used in production.

### 2026-07-30 — M1 B1 discovery must separate permanent rejection from retryable authority

Recovery already classified immutable-authority failures after a durable row
was claimed, but fresh Catalog discovery treated every pre-acceptance failure
as retryable. A scheduled root whose immutable launch shape can never satisfy
the bounded Worker profile (for example Wait or Screen) therefore kept the
global scan cursor behind the same occurrence forever. Later scheduled roots
could execute in that tick, but future discovery remained pinned to the invalid
window.

Fresh discovery now uses the same authority classifier as recovery. A
permanently ineligible immutable occurrence is rejected fail closed and counts
as completed discovery progress; it never becomes a fake reservation.
Temporarily unavailable content-addressed authority remains retryable and keeps
the cursor in place. The focused regression puts a permanent invalid root
before a healthy root and proves both are visited and discovery completes; a
separate retryable case proves the cursor still does not advance.

**Decision:** only executable inputs become durable scheduled work. Permanent
pre-acceptance rejection advances discovery without inventing a queue row;
retryable authority unavailability retains the cursor.

**Lesson:** a cursor gate cannot use one boolean for capacity, transient
authority, and permanent metadata eligibility. Classify before deciding
progress, and test a bad item ahead of healthy work rather than only the bad
item in isolation.

### 2026-07-29 — M1 B2: acceptance must preserve recoverable authority, not only an opaque digest

The Platform-command inbox originally persisted the admission receipts and a
trusted scope digest, but that was insufficient for a separate Worker process.
The Worker could not recover the admitted Org, AppInstallation, workload,
capability, or exact immutable Catalog view without reconstructing identity
from process-local state. B2 now writes those explicit authority fields and the
encoded Runtime work binding in the same acceptance transaction. A bounded
execution-scope digest is derived from the explicit Org/AppInstallation/
capability tuple only for the TiDB claim index; the repository recomputes it
before every insert, and the explicit columns remain the recoverable truth.

The first schema attempt indexed the three explicit `VARCHAR(255)` scope
columns and exceeded TiDB's 3072-byte index-key limit. Replacing recoverable
authority with a digest would have hidden the source facts, so the correction
kept both: explicit authority for verification and one deterministic digest
for bounded selection. A later full kernel test caught a separate local edit
mistake where those columns had been inserted into the adjacent record-command
table. Focused repository tests did not parse the embedded catalog and therefore
could not detect it. The neighboring table was restored, and the kernel schema
gate is now part of the local proof set for this change.

Platform-command execution uses a scoped lease and a store-owned pessimistic
transaction. The registered neutral executor performs only bounded Runtime
effects inside that transaction; the store writes `COMPLETED` and its opaque
result before the same commit, so the executor cannot omit or bypass the
terminal transition.
Expired `PROCESSING` rows are therefore safe to reclaim. Retry, permanent
failure, poison, exact-observation repair, cancellation, ownership loss, and
unknown commit outcomes remain distinct; an uncertain caller never authors a
second disposition. Provider external effects remain Integration-owned and
are not admitted through this executor.

A real TiDB run then found that a newly accepted command was invisible to
`Claim` for the local timezone offset. Acceptance had relied on the table's
`CURRENT_TIMESTAMP` default for `AvailableAt`, while claim eligibility compared
against `UTC_TIMESTAMP`. Those are different wall-clock authorities whenever
the database session is not UTC. Acceptance now writes availability and audit
timestamps explicitly from `UTC_TIMESTAMP` in the same insert. The integration
test fixes its TiDB session at `+08:00`, so UTC correctness no longer depends
on the developer or CI host timezone.

The final composition audit reopened all current owning repositories rather
than treating contract fixtures as launch semantics. Contract examples named
`runtime.flow.execute`, Core tests named `integration.ingress`, and the
Controlplane producer fixture named `provider.event`, but no AppRelease or
Integration consumer publishes a launch-owned command kind/payload executor.
The shared Runtime Worker assembler therefore accepts the Platform-command
family only when its release composition supplies a non-empty neutral executor
registry; the normal binary fails before database access when that owner is
absent. A real TiDB composition test supplies a test-only registry and proves
accept, claim, retry, permanent failure, missing-executor poison, same-
transaction effect/completion, restart non-replay, and the formal repair path.
The test executor is evidence for the mechanism, not a launch command.

A final rebase audit found that the earlier substrate had also made Runtime API
command acceptance depend on any active global Platform-command work-family
row. That row carries neither AppInstallation scope nor proof that the matching
executor composition is running, and durable acceptance must remain available
while a correctly configured Worker restarts. The premature API gate and its
test-only global owner were removed. Runtime API still verifies the exact inbox
schema and acceptance-time immutable view; the scoped Worker independently
fails closed on ownership, binding, and executor readiness before claiming or
committing effects. The real Runtime TCP gRPC restart test deliberately creates
no global ownership table, so this false coupling cannot return unnoticed.

The complete Runtime Worker boundary gate then found that the processor file
imported `catalogsnapshot` and handed the raw metadata view to every executor.
That both widened the executor surface and left the exact view outside the
effect transaction context used by the existing Records graph. Composition now
wraps the verified view behind a two-operation authority: authorize the exact
kind/version and bind the already-open view to the transaction context. The
processor and release executor no longer receive a Catalog artifact type, and
the executor test requires the exact view to be present through the one
metadata context seam before an effect can run.

**Decision:** keep the B2 state machine and release-owned neutral executor
registry, but do not wire a production executor until the frozen launch
command kind and payload shape have a real owner. Temporarily unavailable
content-addressed artifacts retry under the bounded attempt lifecycle; corrupt
or out-of-scope bindings and missing release executors poison fail closed.
There is no latest-view, legacy-process, inferred-routing fallback, or global
owner shortcut for AppInstallation-scoped acceptance.

**Lesson:** a digest is useful for a bounded index, but it is not recoverable
authority. Persist the source identity and exact immutable binding at the
handoff transaction, derive query accelerators from them, and verify the
derivation at the owning repository. Also run the catalog owner test whenever
editing adjacent entries in a large schema file; a focused consumer test cannot
prove that the authoritative publication source was changed correctly. A
generic post-hoc commit hook is also the wrong ownership shape for a durable
worker effect: the state store must open the transaction, lock the work-family
and exact attempt, run the bounded effect, and author the terminal result
itself. Finally, immutable authority failures need the same permanent versus
retryable classification used by B1; treating a temporarily unavailable
artifact as corrupt would poison healthy accepted work during a store outage.
Queue timestamps need the same discipline: never mix session-local
`CURRENT_TIMESTAMP` defaults with UTC claim predicates.

### 2026-07-30 — M1 B2: stopped cutover must quarantine authority that cannot be reconstructed

The first B2 schema added explicit Org, AppInstallation, caller workload,
Runtime capability, immutable binding, and execution-scope columns as required
fields on the already-published Platform-command inbox. That was a correct
steady-state row shape but an incomplete cutover. Pre-B2 `ACCEPTED` rows carry
only a scope hash; the source authority cannot be recovered from that hash.
On real TiDB, additive `NOT NULL` text columns do not necessarily reject those
rows: the DDL succeeds and preserves them with empty values. The original
schema verifier checked only columns and indices, while the Worker's later
malformed-row query was AppInstallation-scoped. An empty-Org legacy row could
therefore escape readiness, make Runtime API start, and turn a legitimate
idempotent retry into an unexplained conflict.

The production upgrade is stopped, not rolling. The correction does not add a
dual read, fabricate a binding, or make API acceptance depend on Worker
liveness. Both Runtime API and the Platform-command Worker already call the
same physical-schema verifier before enabling the capability; that verifier
now also performs a global inventory of every row's reconstructable authority.
Any legacy or corrupt row is preserved and quarantined in place, and capability
activation fails with an explicit operator disposition error. Old command
writers must already be stopped, and the legacy inbox must be drained or
archived before either new owner can start.

The same audit found an avoidable physical type migration: B2 had changed
`ResultPayload` from `MEDIUMTEXT` to `MEDIUMBLOB`, which the existing
Controlplane reconciler correctly refuses as an unsafe automatic conversion.
The table retains its published `MEDIUMTEXT` column. New opaque result bytes
are stored using strict, prefixed Base64 and decoded at the repository boundary,
preserving binary semantics without creating a second schema migration.

A real TiDB regression now starts from the exact pre-B2 table and an accepted
legacy row, applies the additive columns and replacement claim index, proves
the row remains preserved but both owners' shared readiness rejects it, then
proves the same schema becomes ready only after explicit stopped-window
disposition. Separate tests preserve arbitrary non-UTF-8 result bytes through
the text representation and keep normal accept/restart/execute/repair behavior
green.

**Decision:** B2 supports one stopped cutover. Pre-B2 rows with unrecoverable
authority are a global fail-closed activation gate, not input to a backfill.
Rolling old/new writers, automatic deletion, inferred metadata, and
compatibility reads remain out of scope.

**Lesson:** a `NOT NULL` schema declaration is not proof that historical rows
have meaningful authority after additive TiDB DDL. Test the old physical table
with real data, then gate service activation on semantic completeness across
the whole inbox. When the upgrade model is stopped, preserve and expose
unresolvable work for explicit operator disposition instead of building a
permanent compatibility path.

### 2026-07-30 — M1 B2: immutable metadata without its routing scope is an incomplete execution envelope

The first Platform-command binder reopened and verified the exact immutable
command view, but its transaction hook installed only that view. The mutation,
scheduled-Flow, and rollup paths already used the shared `installExecution`
helper, which installs both the view and the verified `RuntimeWorkScope`.
Platform-command had bypassed that helper. A release-owned executor could
therefore authorize its command correctly but fail to route nested durable work
to the accepted Org/AppInstallation/capability partition.

The command binder now derives the scope from the same verified
`RuntimeWorkBinding`, carries it with the command authority, and uses
`installExecution` inside the effect transaction. Binding can return an error;
an installation failure rolls the transaction back and the executor is not
called. A focused test uses the real scoped record-event outbox consumer to
prove that the command transaction contains the complete envelope rather than
checking only a context value.

The same review reopened the new execution-scope digest. Its original NUL
separator was ambiguous because `RuntimeWorkScope.Validate` permits arbitrary
non-empty strings, including NUL. The hash input is now length-delimited, so
distinct source tuples cannot collapse into the same byte sequence. This
changes no published command row: B2 has not shipped, and pre-B2 rows lack this
digest and remain under the stopped-cutover quarantine gate.

**Decision:** every durable effect path installs the exact immutable view and
its verified routing scope through one shared helper before executing business
mechanism. Query digests use unambiguous length-delimited source encoding.

**Lesson:** verifying each authority fact is insufficient if the execution
context drops one fact at the final transaction seam. Prove the full envelope
with a real downstream consumer, and make composite digest encodings
injective-before-hash rather than relying on a delimiter validation does not
forbid.

### 2026-07-30 — M1 B2: activation must verify derived routing authority, not its syntax

After the stopped-cutover quarantine was added, the shared Runtime API/Worker
schema gate rejected missing authority and malformed execution-scope digests.
It still accepted any 64-character lowercase hexadecimal value. `Accept`
recomputed that digest from Org, AppInstallation, and Runtime capability, and a
configured Worker's scoped readiness compared it with its expected digest, but
the global activation gate did neither. A valid-looking corrupt row could
therefore pass Runtime API startup while remaining outside every Worker's claim
partition.

This was the fourth review round because tests continued to construct rows
through the correct writer. They proved that accepted rows had the right
digest, not that activation rejected a durable row whose derived accelerator
disagreed with its source authority. The broader assumption was wrong: checking
that all source columns exist plus checking an accelerator's syntax does not
prove that the row is routable.

The shared activation inventory now streams every durable row, validates its
source `RuntimeWorkScope`, recomputes the digest through the same Go `Digest`
implementation used by acceptance and Worker construction, and compares the
stored value exactly. A valid-looking mismatch is quarantined by both serving
roles before activation. The stopped-cutover contract remains unchanged:
Runtime does not guess authority, rewrite the row, add a dual read, or create a
background migration owner.

**Decision:** the explicit Org/AppInstallation/capability columns are
authority; `ExecutionScopeDigest` is only a derived claim accelerator. The
single shared activation gate must prove that derivation globally before
either producer or consumer starts.

**Lesson:** every persisted derived routing key needs three proofs: the writer
derives it from source authority, the reader constrains claims by it, and the
activation gate recomputes it from persisted source facts. Tests must inject a
syntactically valid but semantically wrong derived value; happy-path rows from
the correct writer cannot prove this invariant.

### 2026-07-30 — M1 Track B: a cross-module owner requires the owning global inventory gate

The merged B2 command executor correctly used the records-owned pessimistic
transaction boundary, but its new direct owner was not added to Records'
repository-wide parent-lock transaction-owner inventory. Local preflight ran
complete Backend, Persistence, Automation, and Kernel suites, but omitted the
complete Records module. Exact-main CI therefore found the only failure after
merge: the new owner was unclassified even though its callback contract had
already been designed and tested as replay safe.

The repair classifies the Platform-command callback as a pessimistic retry
owner: one immutable accepted command is replayed while its exact lease and
work-family epoch are locked, and only bounded Runtime effects may occur before
effect plus terminal result commit together. Provider/external effects remain
forbidden. No transaction behavior changes.

**Decision:** every Track B change that consumes a Records transaction boundary
must run the complete Records module locally, including its repository-wide
owner inventory, before review.

**Lesson:** passing the module that implements a new owner is not proof that
the module governing the shared primitive recognizes it. Derive verification
from both sides of a cross-module boundary: implementation behavior in the
consumer and global ownership inventory in the provider.

### 2026-07-30 — M1 Track B: dependency gates must encode facade exceptions exactly

The B1 scheduled Worker intentionally reads Catalog's active selection through
the released, read-only `catalogread` facade. The architecture boundary test
allowed that exact package while rejecting every other Controlplane package,
but the shell release gate still searched the complete dependency snapshot for
the Controlplane parent path. Once the Worker imported the allowed facade, the
substring check rejected it as though it were a lifecycle owner. This remained
hidden until exact-main reached the Runtime process-boundary gate.

The release gate now evaluates dependencies one package at a time. It permits
only the exact `catalogread` package and continues to reject the Controlplane
root and every sibling or descendant package. Runtime Worker behavior,
metadata authority, and the forbidden-owner policy do not change.

**Decision:** Catalog active selection remains Catalog-owned. Runtime Worker
may consume the one released read-only serving facade, but may not import
Controlplane lifecycle, publication, reconciliation, or write ownership.

**Lesson:** a package-level architecture policy cannot be implemented with
parent-path substring search once it has a deliberate narrow facade exception.
Keep executable gates aligned with the source-level owner inventory, and run
the exact release script locally after changing a production command closure.

### 2026-07-30 — M1 release closure: a cross-repository contract change is incomplete until its consumer pin advances

The connector identity-handoff Runtime change intentionally replaced a static
`standard_action_protected` descriptor guard with a stricter action-owned
authorization that also admits one exact frozen connector companion
FlowVersion. The matching L1 descriptor repair was reviewed and merged in
`nexuscrm-app-packs#550`, but NexusCRM still selected the preceding app-packs
revision. Exact-main therefore combined the new Runtime registration with the
old L1 descriptor and failed source-backed pack resolution.

The repair advances only the official checkout and CRM per-product source lock
to the reviewed app-packs merge. It does not weaken Runtime authorization,
copy L1 metadata into L0, or change a production deployment. Older accepted
installed revisions remain governed by the existing bounded transition window.

**Decision:** the Runtime registration and L1 descriptor remain separately
owned, but a candidate is releasable only when the NexusCRM source lock selects
their reviewed compatible pair.

**Lesson:** for cross-repository contract changes, “both PRs merged” is not the
terminal state. The consumer revision pin and a clean source-backed resolution
test are part of the same delivery closure and must be included in local
preflight before exact-main publication.

### 2026-07-30 — M1-C: cross-capability effects need one atomic serving set, not a startup-time metadata lookup

The first M1-C pass treated Flow-to-Records execution as though the Flow view
were sufficient authority. Reopening the complete transaction path showed the
bilateral dependency: `ExecuteFlow` retains the Flow view as its primary
execution authority, but nested Records DML must resolve the mutation sibling;
both direct mutation and Flow-owned DML may then create durable automatic-Flow
work and therefore need the Flow sibling as an auxiliary binding. Using either
request's primary view for both purposes would mix capability authority, while
looking up a current Flow view when the Worker consumes the row would mix
Catalog revisions.

Runtime API now prepares Flow and mutation together or rejects the serving set.
Admission binds the primary view, mutation sibling, and auxiliary Flow view
from that one atomically published set. Durable record-event rows carry the
exact mutation and Flow bindings from the request; the Worker does not select
current/latest metadata.

Timed handoff remains disabled by default. The record-event role can activate
the existing B1 producer only when deployment supplies the exact scheduled
role document. Assembly requires the same Worker artifact/source,
Org/AppInstallation, immutable store, and compatibility generations, then
checks that exact ownership tuple and Catalog's active receipt before enabling
the handoff. A global scheduled owner is deliberately insufficient. This is a
stopped-candidate activation assertion, not a second scheduler, metadata
authority, rolling-upgrade protocol, or new work-family contract.

Rechecking the parallel M1-C implementation exposed one remaining time-of-check
gap: startup verification alone does not stop a live record-event Worker from
accepting another timed occurrence after scheduled ownership transfers. The
production producer now binds the exact scheduled owner and locks it inside the
same record-event effect transaction as reservation acceptance. The unfenced
constructor is private to Automation tests, and a real-TiDB transfer test proves
that stale ownership creates no reservation. The existing scheduled
`WorkerRole` remains the deployment assertion; no parallel v4 role schema is
needed for this stopped candidate.

The decisive test starts before the durable row: one real mutation gRPC call
crosses the sole Runtime API listener, persists a row with both immutable
bindings, and the API process stops before independently composed record-event
and scheduled roles complete the timed Flow through TiDB. Before any
record-event Worker starts, it asserts that the direct mutation is visible but
no timed reservation or effect exists, ruling out a synchronous/process-local
fallback. It also proves that a mismatched scheduled deployment cannot
activate. Existing tagged production
composition covers Files/ObjectStore, API restart/replay, Worker reclaim,
ownership transfer, cancellation/drain, and selected-root/query/mutation
non-regression.

The implementation was locally green before its new walking slice was included
in the exact-main workflow regex. That was incomplete release evidence: CI
could pass without running the proof named by the PR. The source-backed Runtime
gate now explicitly requires the M1-C test and the existing production
Files/ObjectStore composition test to run and pass.

**Decision:** capability-specific views remain distinct, but every request and
durable row that crosses capabilities captures all required views from one
immutable serving set. Timed Flow activation reuses the exact scheduled
WorkerRole plus Catalog receipt; no fallback or new authority is introduced.

**Lesson:** inspect both directions of a nested engine call before declaring an
authority closure complete, and verify that release CI selects each test that
supports the delivery claim. A locally passing tagged suite is not exact-main
evidence if the workflow regex silently omits it. Also distinguish readiness
from effect authority: a startup check proves only one instant, while an
ownership transfer must be fenced in the transaction that creates the durable
effect.

**Next:** after M1-C is reviewed, merged, and published, M2 owns sfcrm and
Integration client cutover. D3 then freezes the isolated exact BOM; D4 alone
owns the stopped production replacement. M1-C does not alter production
routing, metadata, Flow state, BOM, images, or deployed services.

### 2026-07-30 — M2 permissions: immutable app metadata is not dynamic user authority

The first M2 consumer audit separated two facts that the legacy in-process
composition could read through one object graph. The curated CRM search order
is immutable AppRelease metadata, while effective object read access is dynamic
Runtime-owned security state. AppMetadataView can author the former but cannot
become a second permission source; RecordAccess is record-scoped and cannot
stand in for the complete object/field projection.

The narrow closure adds one app-blind effective-permission capability to the
existing private Platform endpoint. Runtime reads the existing Permission
engine through the admitted request transaction, binds the result to the same
PermissionState and serving receipts, and limits object/field disclosure to the
authenticated AppInstallation's immutable RuntimeMetadataView. The sfcrm
adapter retains the frozen Salesforce response keys in L1. Its search-scope
route reads the published `metadata.pack.search-scope-order.v1` component,
requests effective permissions with that Catalog revision as a floor, requires
exact Catalog/schema/engine/security receipts, and preserves the authored order
while filtering unreadable objects. Missing, malformed, stale, mixed, or
wrong-App authority fails closed; there is no repository or global fallback.

Two pre-review checks found assumptions that focused unit tests had missed.
First, the Catalog projection carries the published neutral component kind
`metadata.pack.search-scope-order.v1`, not the source-pack descriptor name
`search_scope_order`. Second, registering a Runtime service is insufficient:
the capability must also enter the existing ServingAdmission closed set or the
real TCP request is rejected before reaching the engine. Tests now use the
published component identity and cross the actual gRPC listener.

The initial wire delivery also omitted an immutable compatibility image. The
old `platform-api-v1` baseline correctly remains byte-immutable, so the additive
service receives its own `effective-permissions-v1` Buf image, immutability
guard, breaking check, and generated schema row. Updating the old image would
have erased the historical N baseline; leaving the new service outside every
image would have allowed a future incompatible deletion.

The first host draft also classified the complete permission projection as
`ShortUnary`. That confused transport shape with work shape: the unary RPC can
aggregate several permission families and thousands of field rows. It now uses
the existing `HeavyWork` resource class, so the process-wide cap still owns
total concurrency while the established heavy-work pool isolates fast control
RPCs. Re-running the real Runtime process tests after changing the
classification caught two fixtures that enabled the capability without its
pool; they now prove fail-closed resource ownership instead of bypassing it.

**Decision:** AppMetadata remains the sole immutable app-fact authority and the
Permission engine remains the sole dynamic access authority. L1 may combine
their revision-bound projections for a product response but may not recreate
either fact, query Runtime repositories, or fall back to the legacy process.

**Lesson:** derive consumer closure by fact ownership, not by the legacy method
that happened to return the final response. Validate against published
component identities and the real admission path, and create an immutable
compatibility row whenever an additive RPC first ships. Classify new RPCs by
their worst admitted work and response bounds, not merely by unary versus
streaming transport.

**Next:** release the semantic/wire/API-host chain, pin the production Runtime
and sfcrm AppService to those reviewed releases, publish the matching CRM
AppRelease, then prove the exact-image M2/D3 walking slice. This work does not
change production routing, metadata, Flow state, BOM, or deployed services.

### 2026-07-30 — M2 search: authorize and order at the owning Runtime boundary

The current-tree consumer audit showed that sfcrm's remaining search-scope
helper combined three independently owned facts: Catalog authors the
AppInstallation's eligible object order, Runtime permissions decide which of
those objects the viewer may read, and Runtime MRU owns only viewer-specific
ordering. Moving that helper across the process boundary did not require a new
Search engine or a generic metadata RPC. It required one coarse, app-blind
operation on the existing RecordSearch service that returns only authorized
object identities.

The production adapter intersects the request with the request-bound immutable
RuntimeMetadataView before it reaches dynamic authority. Runtime then loads the
admitted Permission State, candidate-bounded object-read decisions, and viewer
MRU inside one Runtime transaction. The response carries the request's Catalog,
schema, engine, and security receipts; missing or changed authority, MRU
failure, cancellation, and stale Catalog floors fail closed. There is no
repository/global default and no legacy/process-local fallback.

An early draft reused the full effective-permissions projection even though the
consumer needs only object-read access. That would have coupled search ordering
to unrelated field, custom-permission, and system-permission work. Reopening
the live owner found the smaller `ListObjectPermissions` projection, but I
incorrectly assumed its `CanRead` field was equivalent to Runtime's final read
decision. It is an administrative grant projection: for ordinary objects it
does not reapply `RequiredReadPermission`, setup gates, or derived
`AccessPolicy`. The resulting RPC could therefore disclose a gated object
identity even though the existing Search path and record/query engines denied
read access. The correct narrow seam is the existing
`CheckObjectPermissionWithUser(Read)` decision, evaluated only for the
Catalog-bounded candidates; no new permission algorithm or projection is
needed.

The first real-TiDB fixture manually approximated authority tables. It passed
the happy path but hid canonical foreign-key and required-field rules; replacing
it with the schema catalog immediately exposed the mismatch and the missing
ObjectPermissions identity. The final test uses canonical authority tables,
the production Runtime assembly, real TCP gRPC, and real TiDB permission/MRU
reads. It proves that an MRU-favored unreadable object is omitted while readable
objects retain viewer order. After the administrative-projection mistake was
found, the same test added two granted objects denied respectively by
`RequiredReadPermission` and derived `AccessPolicy`; both must remain absent.

Two process lessons also surfaced. A release pin is incomplete until every
version-sensitive security and distribution gate acknowledges the exact
reviewed release; focused Runtime tests did not exercise those gates. Separately,
an exact-main boundary failure introduced by an adjacent merged PR was restored
in its own two-line PR instead of being hidden in this delivery. This preserved
the Search truth scope while unblocking artifact publication.

**Decision:** keep Search scope ordering on the existing private
RecordSearchService and one Runtime endpoint. Catalog eligibility,
Permission-engine read access, and MRU ordering retain separate owners but are
combined once, transactionally, by Runtime. L1 continues to own labels, URLs,
key prefixes, Salesforce response shape, and the authored eligible order.

**Lesson:** choose the narrowest existing owner seam that proves the consumer
fact, and verify the seam's implementation rather than inferring semantics from
an “effective” or `CanRead` name. Administrative permission grants are inputs,
not necessarily the final Runtime access decision. Build integration fixtures
from canonical schema publication, not a handwritten approximation. Never type
an evidence SHA from memory: obtain it from Git and mechanically compare it
with the PR body before review.

**Next:** merge and publish this Runtime closure, then replace sfcrm's
process-local search-scope call with the private Platform client. Continue the
remaining current-tree consumer audit before opening another RPC. D3 still owns
the exact-image, multi-process zero-fallback proof, and D4 still owns the stopped
production replacement.

### 2026-07-31 — M2 Integration: split serving identity before command execution

The first bounded-command delivery accumulated static multi-workload identity,
Catalog disclosure admission, command authority, immutable Flow execution,
record-mutation closure, and real launch-route eligibility in one PR. The
individual concerns were mostly required somewhere in M2, but the delivery
claim became circular: it excluded the Integration Service while also making
Integration-side pre-acceptance rejection a Core merge gate. Repeated review
findings were evidence that several owner boundaries had been combined, not a
reason to keep extending the same PR.

The first extraction attempt still included `AcceptCommand` authority. Compiling
that slice alone exposed a hidden dependency on the not-yet-landed command
Catalog model and execution context. The correct first independent owner is
smaller: one existing Runtime endpoint authenticates a closed static set of
sfcrm and Integration workloads, while AppMetadata and IntegrationBinding
disclosure are scoped to the exact workload and AppInstallation. It changes no
command, Worker, Flow, record-mutation, connector, or provider-effect behavior.

The extracted admission initially retained a startup-time `SecurityEpoch`
snapshot. That recreated a second security-version owner and would reject every
otherwise-valid Catalog consumer call after the existing SecurityAdmission
renewed. The fix is deletion, not synchronization: SecurityAdmission remains
the sole current epoch owner; Catalog consumer admission consumes its trusted
call and owns only workload, capability, and AppInstallation scoping.

The first review then found a simpler whole-composition omission: the v4
serving document was accepted by the parser and production assembler, but the
existing Permission State startup gate still recognized only v2/v3. Every real
v4 process would therefore stop before serving. The correction does not add an
upgrade protocol; it classifies v4 under the same already-required Permission
State v1 mode and tests both current serving schemas through the gate.

**Decision:** keep the original large branch only as implementation evidence
and deliver M2 Core as independently testable owner slices. The first slice is
static multi-workload authentication plus Catalog disclosure admission. Command
metadata/admission, bounded execution, and Integration consumption follow in
dependency order.

**Lesson:** code that is eventually required is not automatically part of the
current PR. Rebuild each extracted change on current `main` and compile it
without the reference branch; an undeclared dependency is direct evidence that
the proposed slice is not independent. When an existing admission layer already
owns a changing security fact, downstream admission must consume that decision
rather than snapshot or refresh the same fact. A new configuration schema is
not locally complete until every startup discriminator that receives it has
been audited and executed; parser acceptance alone is insufficient.

**Next:** review and release the static Catalog-consumer slice, then extract the
command Catalog authority without Worker execution. After that, land the
bounded-command Worker substrate and the Integration-side durable handoff as
separate owner deliveries. Production routing, metadata, BOM, Flow state, and
deployed services remain untouched until the stopped replacement milestone.

### 2026-07-31 — M2 command authority: project one Catalog fact, not the Worker

The first extraction copied the full command-execution commit from the large
reference branch. That brought connector mapping shape, inbound authorship,
record-mutation projection, and their schema versions into what was supposed to
be the command authority slice. Those facts are eventually needed by the
bounded Flow executor, but they are not required to define which workload may
execute which exact immutable definition.

The narrowed compiler change adds only the workload, command kind/payload
version, exact definition/version, and trusted owner namespace to the existing
command projection. An unbound N-1 command registration remains byte-compatible
under Catalog v9/Runtime view v11. A bound registration uses Catalog
v10/Runtime view v12, and those new fields fail closed if relabeled under the
older schemas. No Flow, connector mapping, mutation adapter, Worker, or serving
behavior enters this slice.

The binding is not a second metadata authoring source. Controlplane derives it
from the same revision-bound `IntegrationBindingView` inputs already carrying
`RuntimeCommandBinding` and `OwnerPack`; Runtime receives a purpose-specific
immutable projection. A follow-on Controlplane release must publish that
projection before command admission or Worker execution can consume it.

**Decision:** ship the command execution authority as a small Catalog compiler
version boundary. Keep connector/mutation closure with the later bounded
executor that actually consumes those facts.

**Lesson:** when extracting a large implementation, split by the fact the owner
must publish, not by the commit that first happened to contain it. Every new
artifact field needs both a new-version positive proof and an old-version
rejection proof; otherwise an N-1 label can silently acquire N semantics.
Composite authority identities must also be unambiguous for every admitted
opaque value: enforce the contract's byte and UTF-8 bounds, reject control
characters, and use length-framed keys instead of delimiter concatenation, so
two different authorization tuples cannot alias.
Artifact versions must follow the facts in the consumer's selected projection,
not merely the superset schema of the source snapshot. A v10 Catalog may contain
both bound and unbound commands; an AppInstallation selecting only unbound
commands must still receive the unchanged v11 view, while only a projection
that actually carries an execution binding becomes v12.

**Next:** release this Core compiler shape, then pin it in Controlplane and
derive bindings from the already-published Integration route selection. Only
after that producer is available should `AcceptCommand` admission consume the
new authority.

### 2026-07-31 — M2 command admission: consumer authority is not an L1 handler

The first command-admission sketch reused `ServingAdmission` by constructing a
command `HandlerBinding`. That made direct server tests pass, but it described a
topology that cannot exist for the independent Integration consumer:
`AcceptCommand` is a consumer-only handoff and sfcrm does not own a command
handler for it. A fabricated handler would duplicate Catalog authority and
turn a test fixture into a production prerequisite.

The production shape instead keeps the existing admission owners in order.
`SecurityAdmission` authenticates the current workload and SecurityEpoch. A
command-consumer admission then reads the current revision-bound
`IntegrationBindingView`, verifies that it and the immutable command projection
come from the same Catalog snapshot and generations, and creates the private
Runtime execution scope. `ServingAdmission` accepts that already-proven
consumer scope without manufacturing an L1 handler. Before durable persistence,
`AcceptCommand` checks the exact Flow definition/version and trusted owner
namespace against both views.

The consumer path deliberately accepts only the definition-selecting
`runtime.flow.execute` command. Ordinary kind/version-only commands retain
their published handler-owned behavior when command-consumer admission is not
selected. Production configuration already forbids selecting the same Runtime
capability twice, so this delivery does not invent a mixed handler/consumer
mode or a rolling-upgrade protocol.

**Decision:** authorize independent Integration command handoff from one current
Catalog consumer view plus one same-source immutable Runtime projection. Do not
add a handler, second metadata owner, repository fallback, Gateway, service, or
Worker behavior. Publish the already-reviewed producer as Controlplane
`v0.2.29` and pin that exact release in both backend modules in the same
consumer delivery; no fallback or mixed-version serving mode is added.

**Mistake:** the original local proof injected a private execution scope
directly and therefore hid that production composition had no owner capable of
creating it for a consumer-only command. Focused server tests were necessary
but insufficient because they did not exercise the real interceptor chain.
The first PR head then repeated the same evidence gap one level higher: it
tested a directly compiled command snapshot while the official backend module
graph still pinned the pre-producer Controlplane release. A merged producer
commit is not available to production composition until it has one immutable
tag and every owning backend module pins that tag.

**Lesson:** admission tests must begin with the real caller role and production
composition, not a convenient internal context value. When one request needs a
current authorization decision and immutable execution material, compare their
lineage at the boundary and fail before durable ownership on any missing, stale,
or mixed revision. Preserve existing handler semantics by making the new
consumer mode an explicit deployment selection rather than broadening every
command call.

**Next:** publish this admission slice after the complete Runtime/TiDB
composition is green. Then implement bounded Platform-command Worker execution
as a separate owner delivery; non-atomic lifecycle and provider effects remain
outside this PR.

### 2026-07-31 — M2 bounded command execution: share proof, not accepted sets

The large reference implementation made the synchronous Runtime API and the
Platform-command Worker call one selector whose accepted set included bounded
loops. That reused code but silently broadened the already-published M1
`ExecuteFlow` behavior. The corrected shape shares one app-blind eligibility
engine with explicit profiles: the synchronous profile preserves the frozen M1
set, while the Worker profile may accept only engine-validated atomic shapes
under a hard transaction execution budget and pre-effect filtered-write bound.

The Worker reopens the command, Flow, and mutation views from the durable row's
one exact Catalog snapshot. It checks workload, definition ref, and exact Flow
version before execution, installs the mutation and Flow views only inside the
owned transaction, and commits the Flow effect, terminal command state, and
canonical result together. Unknown commit outcomes, cancellation, and ownership
loss are never converted into a second disposition. Provider effects and
`PushToConnector` remain absent from the Worker graph.

Current launch evidence also contains an atomic Flow action that requests an
identity-convergence dependency. Atomic execution alone does not provide the
durable wait/wake lifecycle needed to own that outcome. The Worker profile
therefore rejects such a Flow statically; this substrate must not accept it and
turn the dependency into a permanent command failure. Its lifecycle remains a
separate M2 owner delivery rather than expanding this PR.

**Mistake:** an early adapter treated connector owner keys like schema names and
used case-insensitive matching. Controlplane and Catalog preserve those owners
as opaque byte-exact authority, so folding them at the final consumer could let
two distinct connector owners share mapping or authorship facts.

**Decision:** preserve one eligibility implementation but two explicit behavior
profiles; keep all metadata reads revision-bound; register only neutral
platform connector actions; compare opaque connector owners byte-for-byte.

**Lesson:** reuse is safe only when the invariant is shared. Different public
accepted sets must remain explicit even if their validation mechanics are the
same. An authority identity must retain the same equality semantics at every
producer and consumer boundary; a final case-folding adapter can undo an exact
upstream fence.

**Next:** merge this bounded substrate only after exact-head boundary, race,
real TiDB, restart, cancellation, and production-composition gates pass. Prove
the retained atomic launch routes across repositories afterward; keep the
non-atomic Integration journey under its separate durable lifecycle owner.

### 2026-07-31 — M2 bounded command review: admission must describe the exact executor

The first bounded Worker head reused the package-level atomic validator. That
validator intentionally knows the full platform action registry, while the
role-scoped Worker installs only the connector actions it owns. The resulting
accepted set was wider than the executable set: an atomic-safe platform action
such as `sendNotification` could pass selection and then fail permanently after
durable claim because no Worker handler existed.

The same adapter also returned a raw engine error before reading the typed Flow
outcome. The engine deliberately returns both for failures such as execution
budget exhaustion so the caller can roll back while retaining stable terminal
classification. Discarding the outcome changed a proven permanent failure into
an unclassified retry/poison path.

**Decision:** selection accepts an explicit atomic validator. The synchronous
API keeps the published platform validator; the Platform-command Worker passes
the exact configured `FlowExecutor` validator. The Worker classifies a typed
failed outcome before falling back to a raw engine error, while owner
cancellation remains authoritative.

**Mistake:** the implementation aligned selection with a global capability
catalog rather than the concrete role graph, and treated Go's `(result, error)`
pair as mutually exclusive even though this engine uses both by contract.

**Lesson:** a role may admit only what its exact executor can run. At adapter
boundaries, inspect the callee's real result/error contract instead of applying
generic Go conventions; typed failure evidence and rollback errors can be
simultaneously required.

**Next:** run the complete backend, automation, boundary, race, production
composition, and real TiDB Worker gates on this exact head before requesting a
fresh review. Do not add missing actions to the Worker merely to widen its
accepted set.

### 2026-07-31 — M2 bounded command review: direct record writes need positive cardinality proof

The Worker profile originally rejected direct record-reference writes only
when their root was already declared as a collection. That negative check was
not a boundedness proof: an Assignment can copy a multi-row lookup result into
a scalar-named reference, after which a direct RecordUpdate or RecordDelete
would bypass the engine's filtered-write row bound.

The retained launch Flows do not require that shape. Their direct deletes use
exact outputs of `GetFirstRecordOnly` lookups, while their potentially multi-row
writes use the existing filtered path with the engine-owned row limit.

**Decision:** direct InputReference updates and deletes are accepted only when
the reference itself is a statically proven single-record lookup output. All
other direct references fail closed; bounded batch writes remain on the
filtered path. Do not add a general Assignment provenance analyzer to this PR.

**Mistake:** the first proof asked whether a reference was known to be a
collection instead of asking whether it was proven to contain one record. An
unknown reference was therefore treated as safe.

**Lesson:** safety gates must be positive proofs at the effect boundary.
Negative classification is not enough when aliases can hide the property being
bounded. Check the real launch metadata before expanding an analyzer: when the
launch contract already uses a narrower safe form, reject everything else.

**Next:** rerun the exact retained launch fixtures plus the official Runtime,
TiDB, restart, cancellation, and source-backed release gates. Keep the
pre-existing generic collection-write implementation outside this delivery;
the new Worker profile must make it unreachable.

### 2026-07-31 — M2 bounded command review: transaction bounds include the terminal result

The bounded Worker initially proved Flow execution and mutation bounds but did
not model the final executor/store handoff. The command store already rejected
empty or larger-than-1-MiB terminal results inside the effect transaction. That
error rolled back deterministically, but the processor could not distinguish it
from an unknown commit outcome, so it left the command PROCESSING for lease
reclaim and eventual poison.

This was another instance of the same review failure class: admission and
execution were checked locally while the complete durable owner contract was
not enumerated end to end. Static output-size analysis is neither required nor
useful here because output size is a runtime fact and the store must retain its
defensive bound for every executor.

**Decision:** the persistence owner returns one stable typed invalid-terminal-
result error after a proven rollback. The generic Platform-command processor
maps that error to permanent FAILED, so every present and future executor gets
the same behavior. The Flow adapter also classifies missing execution identity
and non-canonical output as deterministic permanent failures. Unknown commit,
lease loss, ownership loss, and cancellation retain their existing state-based
reconciliation behavior.

**Mistake:** verification stopped at engine result production and assumed any
successful `[]byte` result could be persisted. The storage result contract was
treated as an implementation detail instead of part of the transaction owner.

**Lesson:** bounded execution includes the complete durable envelope: admitted
input, executable capability, effect cardinality, execution budget, terminal
result encoding/size, and disposition classification. At each interface, list
which failures are deterministic, retryable, or outcome-uncertain and prove the
mapping rather than relying on the generic shape of an `error`.

**Next:** run the production-composed real-TiDB Worker test with an oversized
result that writes an effect first; prove the effect rolls back, the command
becomes FAILED on attempt one, and restart does not replay it. Then rerun the
full release gates on the exact head before requesting review.

### 2026-07-31 — M2 bounded command review: a handler registry is not a dependency closure

The production mutation graph installed the whole connector platform action
registry after selection had been corrected to validate against the exact
executor. That made selection and dispatch agree, but it did not make the
executor complete. Several registered remote-identity actions require the
connector durable store and installed contract/profile resolvers, none of which
this revision-bound Runtime graph owns. Finalization also requests outbound
mappings, while the preceding Catalog delivery deliberately publishes only the
inbound mapping authority required by this slice.

**Decision:** the record-mutation Runtime graph installs only
`applyConnectorFieldMappings`, whose complete dependency closure is already the
same revision-bound mutation view plus Records persistence. Remote-identity
registration, finalization, resolution, transfer, and dependency lifecycle
remain absent until their owning M2 delivery supplies their complete immutable
authority and durable dependencies. A composition test enumerates every other
connector platform registration and proves this executor rejects it. The
mutation adapter also rejects outbound mapping queries explicitly instead of
turning an authority mismatch into an empty result. The unconsumed connector
authorship resolver and setter are removed from this PR rather than retained as
a half-wired promise for the later lifecycle owner.

**Mistake:** the previous correction equated “handler is registered” with
“capability is executable.” The test reinforced that mistake by proving only
that dispatch reached a handler and produced a parameter-validation error; it
never exercised a successful production dependency path.

**Lesson:** a role-scoped action capability is the handler plus all authority,
storage, transaction, and resolver dependencies needed to complete it. Exact
registry validation is necessary but not sufficient. Positive production
composition tests must reach terminal success, and an intentionally partial
role must advertise only its dependency-complete subset.

**Next:** rerun the same exact-head backend, automation, race, boundary, and
real-TiDB Worker gates, including a successful inbound mapping effect and an
unauthorized no-effect row. Keep remote-identity lifecycle closure as the next
explicit M2 owner slice; do not recreate live-repository or outbound-metadata
fallbacks in this PR.

### 2026-07-31 — M2 bounded command review: positive cardinality facts must be unambiguous

The positive direct-write proof still assumed that one normalized Flow
reference had only one producer cardinality. Flow metadata can give the same
output reference to a `GetFirstRecordOnly` lookup and a collection lookup. The
selector then found the reference in its single-record set, while the runtime
value could be the collection written by the other producer; the existing
executor intentionally treats that value as a collection update or delete.

**Decision:** a direct Platform-command RecordUpdate or RecordDelete reference
is eligible only when it is present in the single-record producer set and
absent from the collection producer set. Conflicting producer cardinality
fails closed before durable execution. The generic executor and the separate
scheduled-Flow selector are not redesigned in this PR.

**Mistake:** after replacing a negative collection check with a positive
single-record check, the proof stopped one step too early and treated producer
membership as unique authority. Two individually true facts can still conflict;
the effect boundary needs one unambiguous fact.

**Lesson:** positive proof includes conflict detection. When metadata supplies
derived facts from several producers, acceptance must establish both the
required fact and the absence of a contradictory fact on the exact key used by
the effect. Fix that invariant at selection, rather than adding runtime caps or
changing execution semantics.

**Next:** rerun the selector race suite and the production-composed real-TiDB
Flow-command restart test on the exact head. If both remain green, request a
fresh review; keep remote-identity lifecycle and other worker profiles in their
own owner deliveries.

### 2026-07-31 — M2 bounded command review: cardinality authority must remain stable until the effect

Rejecting contradictory lookup producers was still insufficient. A Flow
Assignment can replace the same ContextData key after a single-record lookup,
and direct RecordUpdate/RecordDelete intentionally iterate a collection value.
The selector therefore proved what one producer declared, not what the effect
would consume.

**Decision:** direct Platform-command record writes require the GetFirst lookup
to be the only root-value authority for that reference. The selector now
fail-closes when any other metadata element can write the same context key:
variable initialization, lookup field output, root Assignment, record-create
output, loop alias, subflow/action output, formula, or decision result. Dotted
field assignments remain allowed because they mutate the already-proven record
without replacing its cardinality. This is a static admission correction only;
the executor, synchronous API profile, contract, and Integration route scope do
not change.

**Mistake:** the previous fix compared lookup producer sets but did not derive
the complete write inventory for the exact runtime key. That left a second
write path capable of invalidating the admission fact before the effect.

**Lesson:** an admission proof over mutable interpreter state must cover the
fact's lifetime, not only its origin. Enumerate every writer at the owning
boundary, permit only mutations that preserve the proved property, and reject
ambiguous ownership before durable work is accepted.

**Next:** run the complete exact-head selector, race, backend, boundary, and
real-TiDB Worker restart gates. Keep route-specific action lifecycle and the
independent Integration Service in their planned follow-on M2 owner slices.

### 2026-07-31 — M2 bounded command review: keep authority and effect scopes distinct

The selected root and subflow closure was not the complete effect closure. A
record write synchronously publishes record-trigger events, and the trigger
listener executes the active trigger Flow with the same transaction context.
That indirect Flow shares the element budget, but its direct collection
RecordUpdate/RecordDelete loops did not have a row bound. The trigger root is
also a separate invocation selected from the request-bound mutation view; if it
inherits the command root's prepared closure, its own native subflows fail as
missing even though they are present in that same immutable Runtime view.

**Decision:** the Platform-command Worker keeps the M1 prepared-closure
authority model. The command projection has already bound the authorized
workload to the exact root name and version; the same-revision Flow view selects
the complete immutable closure. The Integration binding owner namespace may
intentionally differ from an L2 companion Flow's `DefinitionOwnerPack`, so it
is not a replacement Flow-ownership check. The bounded prepared entrypoint
consumes the existing protected-Flow gate once for the selected root: an exact
companion-version marker authorizes the intentional cross-pack root, while a
mismatched protected root fails before any element executes. Descendants remain
owned by the exact prepared closure and are not re-authorized through the
legacy per-call marker model. The entrypoint also adds the existing 200-row
pre-effect limit to direct collection update/delete.
The listener preserves that context, so synchronous trigger Flows inherit the
same limit and the existing shared 1,000-element budget. At trigger dispatch it
drops only the unrelated command closure after proving that request-bound
Runtime metadata owns the new root; trigger descendants continue through that
immutable view and no repository fallback is possible. The ordinary prepared
entrypoint remains unchanged for Runtime API and scheduled Flow roles. We do
not suppress record triggers or compile live trigger metadata into the selected
command closure.

**Mistake:** the earlier proof treated exact selected metadata as the complete
effect closure. A later correction then confused the Integration binding owner
with the Flow owner and re-enabled the legacy protected-Flow authorizer for
every member of the prepared closure. Besides rejecting the real connector-pack
to L2-companion shape, that created a growing delegation problem for native
subflows, `actionType=flow`, and deeper descendants. Removing the authorizer
entirely then hid the fact that the protected root gate had not explicitly
consumed the exact companion-version proof already stamped from Catalog. The
synthetic fixture originally hid the identity error because it used one string
for both owners.

**Lesson:** exact selection, authorization, owner namespace, and bounded effects
are different invariants. Follow synchronous side effects far enough to find
inherited work, but consume each proof at its owning boundary: Catalog selects
the exact root/closure, the protected-Flow gate checks the root's exact
companion marker once, and descendants cannot escape the prepared closure.
Fixtures must model cross-pack companion ownership when production does;
otherwise a same-pack shortcut looks safe.

**Next:** run the full automation/backend, affected race, boundary, build, and
real-TiDB Worker gates on the exact head, then request fresh review. Keep
trigger redesign, scheduled-Flow policy, dependency lifecycle, and non-atomic
Integration ownership outside this substrate PR.

### 2026-08-01 — M2 bounded command review: dependency closure must still match the real journey

Repeated blocker fixes made the local implementation safer while silently
breaking the delivery claim. The production mutation graph originally
registered the complete connector action set even though it did not own the
remote-identity stores and resolvers. Removing that over-advertised set was
correct, but retaining only `applyConnectorFieldMappings` also removed
`setRetriable`. Both retained current-tree bounded-atomic launch profiles use
that dependency-free retry marker; the narrowed graph therefore rejected the
actual candidates while synthetic mapping tests stayed green.

**Decision:** the Platform-command graph installs exactly two dependency-complete
connector mechanisms: revision-bound inbound field mapping and the in-memory
retry marker. A successful Flow result carrying `IsRetriable=true` is not a
command success: the owned effect transaction rolls back and the durable
command returns to retry disposition. Remote-identity lifecycle actions remain
absent. Direct native subflows and `actionType=flow` children are also rejected
by this command profile. The current launch candidates do not require them,
and immutable visibility of a child is not an authorization grant. The bounded
entrypoint enforces the same single-root invariant even if a caller bypasses
the selector. Runtime API and scheduled Flow retain their existing child-flow
paths.

**Mistake:** the prior correction optimized for making one blocker disappear
instead of re-running the real route matrix. It conflated “this action has no
complete dependency closure” with “all actions in the same registry must be
removed,” and later conflated same-revision prepared visibility with delegated
child authorization. Both produced a cleaner local story but a false M2 claim.

**Lesson:** after narrowing a capability boundary, replay the exact consumer
journeys before accepting the new boundary. Dependency completeness is
per-action, not per-registry. Immutability proves what code will execute; it
does not prove who authorized a different protected root. When no launch
journey needs a broader shape, reject it instead of inventing a new authority
model inside the current PR.

**Evidence:** a temporary, non-committed cross-repository probe used the exact
private launch-metadata revision. The production-equivalent selector accepts
both retained bounded-atomic profiles, rejects the profile with a deferred
identity-convergence dependency, and rejects the non-atomic profile. The probe
and detached worktree were removed after the run; no private fixture or
revision identity was copied into public Core.

**Next:** prove retry rollback/disposition through the production Worker with
real TiDB, rerun the full exact-head release gates, and only then request fresh
review. Identity-convergence, non-atomic execution, Integration Service, D3,
and production remain separate owners.

### 2026-08-01 — M2 bounded command review: declaration is not execution provenance

The Platform-command selector previously treated a `GetFirstRecordOnly`
declaration as proof that a direct record-reference Update/Delete consumed the
lookup result. That was only a shape proof. Canonical inputs are present in the
Flow context before execution, so a reachable write could consume a caller
value under the same reference while the declared lookup was disconnected,
bypassed by another branch, or followed through its fault connector.

**Decision:** direct record-reference writes in the Platform-command profile
must have authoritative lookup provenance. For every path from the Flow start
to the write, a `GetFirstRecordOnly` lookup producing that reference must have
taken its success connector first. A lookup fault path clears the proof. The
existing single-record and competing-writer checks remain in place. The M1
synchronous API profile is unchanged.

**Mistake:** the earlier fix addressed cardinality and competing writers but
still inferred runtime authority from a metadata declaration. A node-dominance
check alone would also have been incomplete because a fault connector executes
the lookup element without producing a trusted result.

**Lesson:** eligibility for a system-actor effect must prove where the runtime
value came from, not merely that a compatible producer exists somewhere in the
metadata. Follow success and fault control flow separately whenever the proof
depends on an element result.

**Evidence:** focused tests reject a disconnected producer, a branch that
bypasses the producer, and a lookup fault path that reaches the write. Both
retained bounded-atomic profiles from the exact private launch revision still
pass the production selector. The temporary cross-repository probe and worktree
were removed; no private fixture or revision identity was copied into public
Core.

**Next:** rerun the exact-head backend/race/boundary and production Worker
gates, then request one fresh review. Do not extend this proof into a general
Flow compiler, new delegation model, or rollout requirement.

### 2026-08-01 — M2 identity convergence: publish authority before enabling execution

**Decision:** close the retained atomic journey in owner order. Catalog first
publishes one AppInstallation-scoped, revision-bound finalization authority:
the exact installed connector contract generation, matching authorship
binding, outbound mapping closure, and the non-secret profile identity needed
to freeze durable remote-link state. The record-mutation Runtime view carries
that closure under an explicit schema version and returns detached values. It
contains no provider response, credential value, callout permission, or second
metadata read path. Catalog also rejects mixed contract generations per owner
and conflicting profile or mapping facts for a shared Runtime selector, so an
adapter can never choose between two authorities. Runtime action enablement and
durable command wait/wake remain later slices that must consume this view.

**Mistake:** the earlier broad implementation tried to enable the remote
identity actions while the independent Runtime graph still lacked their
metadata authority. Removing the actions fixed the false execution claim, but
it also made it tempting either to reopen the live pack repository from
Runtime or to combine Catalog publication, Worker lifecycle, and Integration
composition in one recovery PR.

**Lesson:** a missing action dependency is not evidence that the engine must be
rewritten. Follow the existing engine to the first unavailable authority, put
that fact in its real owner, and make mixed generations fail closed. Profile
and credential identities can be required for durable state without granting
external I/O; provider effects remain Integration-owned. “Stricter” is not
automatically safer during a read-path cutover: Catalog must preserve the
existing profile defaults and the outbound resolver's exact outbound-before-
bidirectional ordering because both participate in durable fingerprints.

**Evidence:** Catalog/effective/view round trips select V12/V11/V14 only when
the new authority is present. Tests reject missing or mixed authorship,
contract/profile generation drift, and schema downgrade; public getters are
deeply detached. Metadata build, vet, full tests and race tests pass, as do the
automation and backend suites, CRM-tagged Runtime composition tests, and the
module boundary gate with zero new violations.

**Next:** publish the exact authority from Controlplane, then wire only the
existing no-HTTP finalization mechanism to the immutable Runtime view and add
durable dependency blocking/wake. Keep non-atomic execution, independent
Integration serving composition, D3, and production routing outside this PR.

### 2026-08-01 — M2 finalization review: completeness includes executable coordinates

**Decision:** the V12 finalization-authority validator now rejects a blank or
non-canonical create path, update path, or response-ID path alongside the
existing identity, profile, mapping, authorship, and generation checks. These
three fields are mandatory in the neutral connector contract parser and are
copied into durable remote-link state, so an immutable authority is not
complete without them.

**Mistake:** the first validator reconstructed the ownership and revision
invariants but audited only selector identity and field closure. It relied on
the normal pack parser having produced the contract and missed that Catalog
artifacts must also reject malformed direct input independently. That allowed
an unusable durable contract shape to pass publication validation.

**Lesson:** when a new immutable view claims to carry a complete execution
authority, compare every persisted execution coordinate with the owning source
contract, not only the fields used to select it. Reuse existing validators for
mapping and association closure, and add only the missing required scalar
checks at the Catalog artifact boundary; do not use the finding to redesign
connector execution or broaden rollout requirements.

**Evidence:** the focused Catalog test now clears each of the three required
coordinates independently (including whitespace-only input) and proves every
case is rejected. The full catalogsnapshot suite and diff check pass.

**Next:** rerun the metadata race and boundary gates, publish the corrected
exact head, and then resume the separate Controlplane producer PR.

### 2026-08-01 — M2 Runtime finalization: activate one action at the owning role

**Decision:** the Platform-command Worker activates only the existing
`finalizeConnectorInboundRemoteIdentity` no-HTTP action. Its adapter resolves
one complete authority from the record-mutation `RuntimeMetadataView` already
bound to the command attempt; contract, profile generation and outbound
mapping closure are returned together. Missing authority fails closed and
never falls back to the legacy split repositories. The Runtime API graph keeps
the frozen M1 action set, and `PushToConnector`, provider callouts and the other
connector lifecycle actions remain absent.

**Mistake:** the first real-TiDB run reached the production action and failed
because the Runtime Worker scratch-schema inventory still described the old
work path. It omitted the four existing canonical tables used by strict
finalization: remote-identity lane, remote link, push attempt occupancy and
inbound evidence. Focused adapter tests could not expose that composition gap.
The first adapter file also imported metadata outside the Worker's explicit
composition-file allowlist; the full backend boundary gate caught it and the
adapter was moved into the existing `metadata_binding.go` owner.

**Lesson:** enabling an existing engine mechanism also changes the process's
physical dependency closure. Prove the exact production graph against its
canonical schema, not only the resolver and action handler. Add only tables
actually reached by the new role; a missing test fixture is not permission to
install the wider connector service or invent new schema. Keep new metadata
adapters inside the already-audited composition boundary instead of expanding
the boundary allowlist for file-layout convenience.

**Evidence:** unit tests prove the returned authority is detached and
revision-bound, missing authority does not use the legacy resolver, and the
Worker installs finalization while still rejecting `PushToConnector`, register,
resolve and transfer actions. The real-TiDB production Worker walking slice
updates the record, consumes inbound evidence, creates one finalized remote
link and completes the command in one owned transaction; restart leaves the
terminal attempt and both durable rows unchanged. Focused race, vet and Runtime
process-boundary gates pass.

**Next:** finish the exact-main backend/release gates and review this narrow
Core composition PR. Dependency `BLOCKED`/wake remains a separate slice using
the published typed V2 command payload; non-atomic execution, Integration
serving composition, D3 and production routing remain outside this PR.

### 2026-08-01 — M2 dependency wait: keep one durable command owner

**Decision:** the V2 Runtime Flow command may publish one fully scoped identity-
convergence dependency after its owned TiDB effect transaction has definitely
rolled back. The existing Platform-command row then moves from `PROCESSING` to
`BLOCKED`, retains its immutable Catalog binding and normal attempt number, and
becomes due for the same Worker after one minute. Replaying the exact atomic
Flow re-runs its current record lookup; success commits the business effect and
terminal command result together. A changed dependency, incomplete/tampered
scope, or wait beyond 30 minutes fails closed. V1 and synchronous ExecuteFlow
eligibility remain unchanged.

**Mistake:** the first evidence pass treated an older local pack checkout as if
it described the launch route and nearly turned a missing authority into a new
resolver/service. Reopening the latest L2 Flow showed that the dependency is a
conditional terminal marker: the next exact replay naturally rechecks whether
the record has converged. Full module-graph verification also exposed that a
contract bump in persistence requires the actually affected workspace modules
to advance their exact pin; focused package tests alone did not prove that
release closure.

**Lesson:** before adding a lifecycle owner, prove that the current durable
owner cannot express the state transition. Here it can, so a second queue,
scheduler, resolver, provider action, or rolling-upgrade protocol would create
duplicate ownership. Persist a dependency only after transaction rollback is
known, preserve the original blocked timestamp across replay, and keep raw
selectors out of sanitized error text. Run the standalone module graph and
exact production composition locally before review, not after CI finds pin or
fixture drift.

**Evidence:** real TiDB tests prove attempted effects roll back before
`BLOCKED`, restart preserves state and attempt, due replay commits one terminal
effect, dependency drift and hash tampering fail closed, and the bounded wait
expires without consuming the ordinary retry budget. The full persistence and
race suites, all tagged Runtime Worker integration tests, full backend tests,
affected module tests/vet, boundarygate, Runtime process boundary, and module-
graph seal pass. The test TiDB playground and exact app-packs worktree were
removed after verification.

**Next:** review and merge this Core substrate, publish its nullable schema
through Controlplane for the stopped-upgrade path, then have Integration emit
V2 only for the retained atomic Contact/Lead journey and prove the exact route.
The non-atomic workflow owner, remaining sfcrm closure, D3 and production
routing remain separate work.

### 2026-08-01 — M2 Integration serving: compose existing owners, do not invent another runtime

**Decision:** the independent provider-facing process is a thin production
composition over the released connector supervisor, ingress receipt router,
Integration-owned handoff receipt, and one TLS Platform client. It verifies the
one-way WorkItem cutover, current revision-bound `IntegrationBindingView`, exact
Org/AppInstallation/workload scope, and handoff schema before opening ingress.
It does not import Runtime, Flow, record or L1 engines; provider effects remain
in Integration, while `AcceptCommand` remains the only L0 business handoff.

**Mistake:** the first lifecycle draft used buffered error channels as if they
were broadcast completion facts. `Wait` and `Shutdown` could consume the same
single value, leaving the other caller blocked. The HTTP-listen failure path
also waited for a router goroutine while holding the mutex that the goroutine
needed to publish its result. A connection-level Runtime readiness check alone
was also weaker than the serving claim because it did not prove the configured
consumer still had Catalog binding authority. The first configuration pass
also invented a stricter `0600` regular-file rule instead of reusing the
repository's established workload-token contract; that rejected the standard
symlink-backed `0440` projected secret. Finally, assembly had a startup timeout
but the supervisor's synchronous reconciliation did not receive it, so the
process could miss a stopped-upgrade termination signal before becoming ready.
The first fix overcorrected by letting that startup context own the published
supervisor lifecycle. Because the process releases its startup deadline after
readiness, the callback would immediately stop a freshly started worker pool;
startup cancellation and service lifetime are separate ownership facts.
Receipt-router completion had the same ownership-ordering problem in a smaller
form: readiness could be stored after the router had already published exit.

**Lesson:** process completion is state observed by several owners, so publish
the error once under a mutex and close a broadcast channel. Never wait while
holding the result mutex. Readiness must prove the authority required by the
first durable handoff, not merely that a socket connected. This is a stopped
upgrade and one exact phase-one consumer scope; no rolling compatibility,
dynamic multi-tenant registry, second queue, Gateway, or fallback is required.
Security checks should match the existing deployment contract: follow the
projected token symlink, reject world-readable targets, and keep canonical
single-token parsing. A serving startup deadline must reach the owning DB and
authority reads, but a successful startup must detach from that deadline and
remain owned by explicit `Stop`/`Shutdown`; wrapping a context-free start call
in a goroutine would only hide the blocked owner and is not cancellation.
Publish router liveness and process readiness under one critical section so an
exit and a ready transition cannot pass each other.

**Evidence:** race tests cover dynamic Runtime readiness, graceful drain,
idempotent close, occupied-listener cleanup, immediate/unexpected receipt-router
exit. A non-skipped TiDB v8.5.0 production-composition test creates the
canonical published schema, proves missing cutover authority fails closed, and
then starts the real supervisor/router/HTTP composition. Full backend tests,
vet, default/CRM distribution builds, release-pin verification, boundarygate,
and a dedicated Integration process dependency gate pass. Temporary TiDB and
app-packs test environment state was removed after verification.

**Next:** review and release this independent serving composition. Keep the
retained non-atomic workflow-action lifecycle and the final cross-process route
matrix in their separate M2 owner deliveries; D3 still owns exact images and
the isolated candidate, and D4 still owns the stopped production replacement.

### 2026-08-01 — M2 Integration authority: constrain the selecting read, not only the result

**Decision:** freeze one process-lifetime serving selection from the validated
`IntegrationBindingView`. Only inbound-route bindings with a Runtime command
grant webhook/worker ownership. Profile, mapping and push-contract bindings are
dependency facts, not permission to start another worker. Handler configs,
routes, worker packs, callout profiles, credential identities and ingress-
receipt claims all resolve through that same exact pack-installation closure.
The database remains Integration's value/projection store; Catalog remains the
only selector and revision authority.

**Mistake:** the first composition validated the consumer view and then threw it
away, allowing global database queries to choose the actual handler, route,
profile and worker set. The first correction replaced that with general host
authorization callbacks after global reads. That was still the wrong shape: it
observed unauthorized configuration and invented live callback, shutdown and
reauthorization problems that the stopped-upgrade rollout does not have. The
first exact pack query also accidentally dropped the existing converged-payload
and verifier/parser readiness predicates. A later exact lister silently omitted
a selected pack when it was missing or had the wrong digest, which was secure
against overreach but falsely left the process healthy without a required
worker. The first profile correction also weakened per-binding credential
authority into one process-wide union, allowing drift between two otherwise
authorized profiles. Finally, constraining the dispatcher was insufficient
because the receipt router had already claimed a durable row; claiming another
consumer's receipt is an ownership mutation even if dispatch later rejects it.
The first per-profile check covered only the profile's default credential and
missed the distinct credential that an optional batch spec can execute with.
It also treated a blank default credential as an identity and therefore rejected
a valid batch-only profile. The first receipt scope was present on selecting
queries but not repeated on the lease UPDATE, so the authorization and ownership
mutation were not one atomic predicate.
The Dispatcher still resolved a globally active pack and decrypted its webhook
secret before reaching the selected capability gate, so claim scoping alone did
not constrain the first confidential read. Replacing that resolver was still
insufficient because the generic receipt-mode Dispatcher cached pack snapshots
by namespace. A cache hit bypassed the new exact digest check and could combine
a cached pack row with current same-install configuration after drift. The Core
composition also called Controlplane's global projection-repair helper during
serving startup. That helper enumerates and mutates every active connector pack;
it belongs to publication/repair ownership, not an exact consumer read path.
The Core projection also lowercased opaque owner-pack identities and merged
profile credential grants into a process-wide set. Case folding changed Catalog
identity semantics, while the credential union discarded the profile context
needed to enforce the fact and duplicated the exact connectors loader.

**Lesson:** an authority boundary must constrain the read that selects work,
not filter after enumeration or after a lease mutation. Step out before adding
callbacks: if deployment replaces the process under a stopped upgrade, immutable
data is simpler and more correct than a watcher-shaped interface. Preserve all
existing execution-readiness gates when narrowing a query; exact identity is
necessary but not sufficient. A configured selection is a required identity:
missing or mismatched rows must fail startup rather than disappear. Preserve
credential authorization at the profile binding where Catalog authored it;
unioning facts across profiles discards authority even when every credential is
individually authorized. Derive that binding's complete credential set from the
actual execution model: today this includes the profile default and optional
batch credential when each is non-empty, not hypothetical future fields. A
claim scope belongs on the ownership UPDATE itself; a prior scoped SELECT is
only candidate discovery, not an atomic authorization fence. Keep that scope on
the final claimed-row read so work handed to the Worker cannot escape the same
selection. Distinguish bindings that
authorize a serving role from bindings that only complete its dependency
closure. Trace authorization from the first input-controlled read, not just the
first durable write: an unselected webhook namespace must be rejected before
pack, transient-state or secret lookup, while a selected namespace must resolve
the exact installation and digest rather than whichever generation is globally
active. Replacing a resolver does not replace every read path that fronts it:
audit inherited caches and singleflight layers too. Runtime serving therefore
revalidates exact pack authority on every webhook request before downstream
reads, while retaining exact-install secret/verifier caching after that check;
no watcher or rolling-reconfiguration mechanism is needed. Serving startup must
verify and consume already-published projections, never repair global metadata;
missing projection authority fails closed back to the Controlplane owner.
Treat Catalog identity strings as byte-exact unless the contract explicitly
defines normalization. Enforce credential authority only where the complete
profile binding is present: the Runtime serving selection carries each
profile's default/batch grant set, and the connectors loader rejects drift
before constructing the Worker. A context-free Core credential union is not
defense in depth; it is a weaker second policy and must not exist.

**Evidence:** connector tests prove exact namespace + installation lookup,
payload convergence, verifier/parser readiness, exact handler/profile reads,
missing/mismatched selected-generation rejection, default and batch per-profile
credential rejection, batch-only profile acceptance, empty-scope receipt
starvation, pre-claim installation filtering, and the same scope on single and
batch lease mutations and returned-work reads. Dispatcher tests prove an unselected namespace performs no pack or
transient-state database read and a selected namespace resolves only its exact
ready generation before secret lookup. A two-request regression proves that a
same-install digest change cannot hit the legacy namespace cache and is rejected
before downstream serving. Core tests prove mapping-only packs
cannot start workers or pass capability admission, unbound routes do not reach
the database resolver, route/definition drift fails closed, credentials remain
profile-bound, and mixed pack digests/installations are rejected.
Full backend tests/vet, boundary gates, focused race suites and a non-skipped
TiDB v8.5.0 production-composition test pass. The temporary TiDB container was
removed after verification.

**Next:** consume the reviewed connectors release in the Integration process,
rerun the same exact-head gates without a local workspace override, then merge
and publish #4205. Non-atomic workflow ownership, remaining sfcrm closure and
the final cross-process matrix remain separate M2 deliveries.

### 2026-08-01 — Serving verification must follow the activation pointer

**Mistake:** the first Core startup verifier joined `_System_PackGeneration`
directly to `_System_InstalledPack` by the Catalog-selected installation. That
proved that the named row existed and looked active, but it bypassed
`_System_PackActivation`, the sole pointer that decides which immutable pack
generation is actually active. The TiDB fixture repeated the same assumption by
seeding only one generation and no activation row, so it could not expose the
wrong read path.

**Lesson:** a consumer must verify an immutable identity through the owner's
authoritative selection path, not reconstruct an equivalent-looking predicate.
Reuse Controlplane's `VerifyIntegrationPackAuthority` inside one short startup
transaction so all selected packs are locked and verified against their exact
activation pointer together. This is stopped-upgrade startup verification, not
a watcher or rolling-upgrade mechanism.

**Evidence:** the production TiDB fixture now seeds the canonical activation
row. A second real-TiDB regression keeps the Catalog-selected generation valid
but moves the activation pointer to another valid generation; startup rejects
the stale selection. The existing exact-generation production composition still
starts successfully.

**Next:** rerun the complete #4205 exact-head gates, then seek review on the
same head. Do not absorb non-atomic command ownership or D3 composition into
this serving-process correction.

### 2026-08-01 — Readiness and command authority are closed lifecycle facts

**Mistake:** the Integration process closed a locally invented `routerStarted`
channel immediately before calling `ReceiptRouter.Run`. That signal proved only
that a goroutine had been scheduled, not that the router had completed any
operational step; an immediate return could race with publication of readiness.
The same composition also treated any non-nil Catalog `RuntimeCommand` as worker
authority, retaining only its definition name and discarding the neutral command
kind, payload version and exact definition version.

**Lesson:** do not infer a lifecycle fact from goroutine placement. The component
that owns startup must report readiness only after an operational probe, and a
return before that report is a deterministic startup failure. Likewise, a closed
Catalog binding must remain closed at the consumer boundary: support is the exact
neutral command profile, not merely the presence of a pointer. This is not a
reason to add a watcher, RPC, service or rolling-upgrade protocol.

**Evidence:** an immediate-exit router now fails `Start` before the HTTP listener
opens and before readiness can become true. The production adapter reports its
start only after a real receipt-router batch probe. Serving-authority tests prove
that the exact v2 Flow command binding, including definition version, survives
projection and that unsupported command kinds or payload versions fail startup
instead of admitting receipts that Runtime will later reject.

**Next:** run the focused lifecycle/authority tests, then the existing race,
backend, boundary and real-TiDB gates on one exact head. Keep the remaining
non-atomic lifecycle and image publication work in their own M2 deliveries.

### 2026-08-01 — Serving support is the end-to-end accepted command set

**Mistake:** the first command-authority correction treated both Flow command
v1 and v2 as supported because the Runtime Worker had executors for both. The
same exact source still admitted independent Integration consumers only for v1
at `AcceptCommand`, so a v2 Catalog binding could open Integration ingress and
then fail at its only L0 ownership handoff.

**Lesson:** startup may advertise only the intersection of Catalog authority,
consumer admission and Worker execution. A downstream executor is not proof
that the ingress path is enabled. Keep this serving composition v1-only; the
separate v2 consumer-wiring slice must update admission, Catalog/AppRelease
binding and Integration together before v2 enters the accepted set.

**Evidence:** serving-authority tests retain the complete exact v1 binding,
including definition version, and reject v2 as well as unknown kinds/versions
before any worker or receipt ownership starts.

### 2026-08-01 — Exact-head evidence must be derived, never completed by hand

**Mistake:** while refreshing the PR body after the v1 correction, the short
commit prefix was manually expanded into a guessed 40-character SHA. The body
was immediately checked against Git and corrected, but this repeated an earlier
clerical failure class and briefly made the evidence claim false.

**Lesson:** obtain the full reviewed head from `git rev-parse HEAD` (and confirm
it against the remote PR head) before editing evidence. Inject that derived
value mechanically; never type or infer the suffix.

### 2026-08-01 — Readiness publication and terminal observation need one owner

**Mistake:** the HTTP wait goroutine cleared semantic readiness before taking
the process lifecycle lock, while `Start` published readiness under that lock.
An immediate HTTP exit could therefore write `false` first and have startup
overwrite it with `true`, even though the ingress transport was already dead.
The earlier router fix did not automatically close this sibling lifecycle race.

**Lesson:** readiness is one conjunction owned by the process lifecycle, not a
collection of independent atomic flags. Publish it only while holding the same
lock used to record terminal component state, and prove the transport's own
readiness before committing `started=true`. This is required for a truthful
independent service; it does not imply health watchers, rolling upgrades or a
new serving abstraction.

**Evidence:** a controllable HTTP process that exits immediately now makes
`Start` fail closed and never advertises readiness. The focused lifecycle case
passes 100 consecutive runs, and the complete Integration package passes the
race detector.

### 2026-08-01 — Readiness must close the exact executable route identity

**Mistake:** the serving authority retained the Catalog command's exact Flow
version but verified only the connector route version and Flow name before
opening ingress. The connector worker obtains its handoff version from the
active cross-pack `FlowDefinition`, so Catalog and the database could disagree:
the process would report ready while every command handoff failed. Handoff also
re-read Catalog after startup, allowing one process to join two revisions even
though rollout is a stopped upgrade.

**Lesson:** retaining an identity is not the same as proving it. Before
readiness, close the existing owner chain: Catalog-selected route, exact
connector projection, cross-pack execution shape, and active Flow version must
all identify the same Runtime command. Then use that validated immutable
Catalog view for the process lifetime. A new revision is selected by restart;
no watcher, cache refresh or rolling-upgrade protocol is needed.

**Evidence:** real TiDB composition tests prove the selected generation starts,
an active Flow version mismatch fails before readiness, and a same-pack Flow
that would select the process-local legacy execution path also fails before
readiness. A unit regression proves later client-view changes and mutation of a
returned copy cannot alter the pinned view, while a newer minimum revision is
rejected. These exact tagged TiDB tests now run in the official source-backed
CI gate and fail if any named test is skipped or renamed.

**Next:** complete exact-head regression and review for #4205, merge the
independent serving composition, then publish its image foundation. Keep the
non-atomic lifecycle, remaining sfcrm journeys and final M2 cross-process matrix
outside this correction.

### 2026-08-01 — A published Worker payload version is not yet consumer serving authority

**Mistake:** v2 identity-dependency execution already existed in the Runtime
Worker and connectors handoff, which made the remaining gap look like a Worker
feature. Current-tree tracing showed two separate fail-closed gates still
excluded it: `AcceptCommand` decoded only v1, and the independent Integration
process rejected a v2 `IntegrationBindingView` before readiness.

**Lesson:** enable a payload version across the existing owner chain, not by
widening a shared selector. Runtime admission must decode the exact
Catalog-selected version; Integration readiness may expose only versions owned
by that same Runtime release; the immutable Runtime command view and current
Integration view must agree before persistence. This does not change the
synchronous `ExecuteFlow` profile or authorize v2 globally.

**Evidence:** unit tests prove exact v2 binding acceptance and v1/v2 mismatch
rejection before persistence. Real TiDB tests prove v2 crosses the production
gRPC boundary with exact bytes and that the independent Integration composition
starts for both published v1 and v2 routes. The test run also exposed a stale
fixture that omitted canonical `FlowDefinition.MasterLabel`; fixing that fixture
keeps the official TiDB gate honest instead of weakening schema strictness.

**Next:** add the separate L1 AppRelease v2 command registration. Controlplane
already projects arbitrary exact command versions, so no Core hardcode or new
contract is needed.
### 2026-08-01 — Image dependency closure starts at the Docker client

**Mistake:** the first Integration image copied only named repository
directories and produced an app-blind final binary, but it had no
Dockerfile-specific ignore. The Docker client could therefore send the whole CI
workspace—including a private L1 checkout—to the builder, and `COPY backend/`
also staged distribution and test sources that the Integration command did not
compile.

**Lesson:** `go list -deps` and the final image filesystem are necessary but not
sufficient release-boundary evidence. The Docker build context is an earlier
dependency boundary. Give each independent artifact a default-deny context and
allow only the exact local module/package closure used by its command.

**Evidence:** `integration.Dockerfile.dockerignore` now reduces the local build
context to 72.65 kB; all COPY stages complete with only the Integration command,
Integration composition, and four required app-blind L0 modules. The build then
reaches private-module resolution and fails only because the local host lacks
the four CI deploy keys. The existing process-boundary gate now requires the
allowlist and rejects Runtime, L1, tenant, frontend, and test-source entries.

**Next:** let exact-main CI perform the credentialed image build and smoke test;
do not add a local dependency fallback.
### 2026-08-01 — The independent Integration process needs its own release image boundary

**Decision:** package `integration-service` through its own
`infra/integration.Dockerfile`, exact image smoke and dormant trusted signer
foundation. Do not add it as another target in the Runtime Dockerfile: Runtime
API/Worker and Integration remain distinct release and deployment roles even
though their source currently composes in Core. The signer has no
`workflow_run` caller and its policy remains `pending` until a follow-up pins
the reviewed foundation merge SHA and activates the existing signing trust.

**Mistake:** the first packaging draft added Integration to
`infra/runtime.Dockerfile`. That made an Integration image build compile the
Runtime API and Worker binaries, blurred the artifact owner and coupled local
verification to unrelated build-cache state. Clearing a build cache can remove
environment residue, but it cannot make that release boundary correct.

**Evidence:** the Integration dependency/image boundary gate rejects Runtime,
Flow, record, L1 and distribution owners and now also rejects Runtime command
builds in the Integration Dockerfile. Dockerfile validation, YAML parsing, the
exact host binary build and fail-closed startup smoke, the boundary gate and
diff check pass. A clean isolated Docker builder reached the private-module
fetch and correctly failed because this host does not possess the repository
deploy keys; official exact-image CI remains the owner of that external secret
fact and must pass before merge. The isolated builder was removed.

**Next:** merge the serving composition, rebase this foundation onto its exact
merge SHA, run the official image job, then activate publication through a
small SHA-pin PR. Do not absorb non-atomic command execution or D3 deployment
composition into the image release.

### 2026-08-01 — Publication proof must exercise the digest it signs

**Mistake:** the first Integration image foundation ran the operational smoke
only in the exact-main go-ci build. The trusted publisher rebuilds and pushes a
separate candidate digest, but initially checked only its labels, entrypoint,
provenance and SBOM before signing it. Those checks could not prove that the
published binary actually starts and fails closed without serving authority.

**Lesson:** artifact identity checks and executable behavior are different
claims. Reuse the same narrow image smoke against the publisher's pulled exact
digest before signing; do not create a second release test or infer behavior
from labels.

**Evidence:** the trusted Integration publisher now runs
`integration-image-smoke.sh` against `$IMAGE_NAME@$IMAGE_DIGEST` and the exact
source SHA. The smoke verifies non-root identity and entrypoint, executes the
binary, and requires the missing-authority startup failure before publication.

### 2026-08-01 — Activate a reviewed image owner without reopening its release design

**Decision:** activate the Integration image publisher with one unprivileged
`workflow_run` caller pinned to the reviewed foundation merge
`86b8247f0da97eeddde73baf0ab0df920e9fc0ea`. The caller can only read the
successful exact-main go-ci receipt and delegate its exact SHA to that immutable
reusable workflow. Build, smoke, signature, provenance, tag and replay decisions
remain in the signer.

**Lesson:** the self-reference between a source-owned reusable workflow and its
exact trusted identity is a two-commit release fact, not a reason to add a
mutable branch call, manual dispatch or deployment compatibility path. Freeze
and review the signer first; activate only its merge SHA in a narrow follow-up.

**Evidence:** the Integration boundary gate now requires the caller's receipt
read, immutable signer pin, minimal permissions and source-SHA handoff; it
rejects manual/PR dispatch, direct attestation and unverified workflow-run head
selection. The signing policy names the same immutable workflow and remains
bound to main exact-source receipts.

**Next:** merge this activation only after exact-head review, then require the
resulting exact-main Integration image digest and signed provenance before the
final M2 cross-process candidate proof.

### 2026-08-02 — Approval read and execution must not share metadata authority

**Decision:** publish one neutral, immutable Approval definition through
Catalog, but project it through two explicit Runtime profiles. The execution
profile carries the bounded mutation and exact Flow dependency closure; the
read profile carries only record-read policy and the selected definitions.
Both remain scoped to one AppInstallation and one Catalog revision.

**Mistake:** the first draft used one Approval Runtime profile for both RPCs.
That made the read capability inherit mutation, write-policy and Flow authority
simply because both operations consume the same definition. Sharing immutable
content does not justify sharing execution authority.

**Evidence:** Catalog round-trip, cross-installation scoping, mixed-revision,
tamper, unresolved-action and missing-Flow tests pass. The read projection test
proves that no mutation document, write facts, executable Flow dependency,
OAuth scope or connector authority is present. Standalone metadata race tests,
Runtime process-boundary checks and the backend default/CRM build, vet and test
matrix pass against the exact official app-packs source revision.

**Lesson:** when a new private service exposes separate capabilities, derive
metadata authority per operation rather than per service. Also reproduce CI
with its exact source locks: testing a distribution against a newer L1 checkout
creates false dependency work and is a scope trap, not release evidence.

**Next:** let Controlplane publish the neutral Approval sidecar and invocation
schema from this substrate, then add the Runtime serving adapter and
Approval-specific same-transaction replay receipt. Do not add live metadata
fallback or reuse another engine's journal.

### 2026-08-02 — A green fixture can prove the wrong authority closure

**Mistake:** the first Approval execution test selected `NormalizeWorkspace`,
but that Flow was already reachable from an ordinary record trigger in the
shared mutation fixture. The test therefore stayed green even if the Approval
projection failed to select its own Flow dependency. Local tests were checking
that a Flow happened to be present, not that Approval was the reason it was
present.

**Decision:** validate Approval as one cross-representation authority chain:
the generic Definition lineage, specialized Approval sidecar, selected
projection objects and definitions, and exact mutation/Flow closure must agree
at compile, Catalog-open and Runtime-view-open boundaries. Use an
Approval-only Flow that no trigger or subflow can pull in, and reject both
missing dependencies and extra unselected authority.

**Evidence:** destructive tests now cover version and digest mismatch, exact
definition and object identity, object closure, missing Approval-only Flow,
extra generic lineage, extra unselected Flow authority, deterministic ordering
and isolation from ordinary mutation projections. The positive fixture proves
the Approval execution profile contains its own Flow while the read and
ordinary mutation profiles do not.

**Lesson:** when metadata is represented in several linked documents, do not
test each representation independently or rely on a shared fixture's incidental
closure. Start with the owner-level invariant matrix and make every positive
fixture uniquely attributable to the capability under test; otherwise review
becomes the missing conformance suite and produces an avoidable patch loop.

**Next:** run the complete metadata, race, vet and boundary gates against this
matrix before asking for another review. Keep Runtime serving, Controlplane
publication and upgrade behavior in their owning deliveries.

### 2026-08-03 — Approval serving must preserve one immutable and transactional authority

**Decision:** expose the existing Approval engine through the released neutral
Approval service on the one Runtime gRPC server. Resolve executable definition,
node, action and record-editability semantics from the request-bound Catalog
view; use live Approval rows only as durable instance state and stable storage
identity anchors. Bind idempotency identity, exact serving receipts, business
effects and terminal response in one Runtime-owned pessimistic TiDB transaction.

**Mistakes found before review:** the first complete lifecycle proof exposed
three owner-boundary defects. Reassign authored `LastModifiedDate` even though
the persistence owner stamps audit fields; work-item resolution collapsed an
underlying repository failure into NotFound; and the gRPC error mapper stamped
Approval failures with the Flow capability. A final receipt audit also found
that generic JSON decoding rounded adjacent `uint64` revisions above `2^53`.
Each was corrected at the owning boundary instead of adding compatibility or
fallback behavior.

**Mistake found by reopening the claim:** the implementation treated
`RunInPessimisticTransaction` as if ordinary Approval queries automatically
locked the rows they read. They do not. Two different idempotency keys could
therefore both consume one work item and create duplicate next-step state. The
same audit found legacy best-effort branches that swallowed metadata,
authorization and pending-node failures inside the new strict Runtime path.

**Correction:** every Runtime work-item action now current-reads and locks the
exact `ProcessInstance` first and selected `ProcessInstanceWorkitem` second,
then revalidates topology, pending state and current actor/submitter authority
before engine execution. Authority and state-gate query failures propagate;
Recall rolls back instead of committing after a pending-node query or update
failure. This is one execution-state fence in the existing engine, not a new
service, RPC, journal or rolling-upgrade mechanism.

**Mistakes found by the next full authority audit:** three more assumptions
were wrong at the same boundary. The Catalog adapter rejected the valid
process-level `StepApprovalActions` group instead of projecting its bounded
FieldUpdates; Recall carried `AllowRecall` into the synthesized definition but
never enforced it; and submit selected a definition and evaluated entry
criteria from a pre-lock snapshot. A concurrent target update could therefore
change the current eligibility before the submit acquired its target lock.

**Correction:** the adapter now projects process-level StepApproval
FieldUpdates through the same closed action mapper, Recall checks the immutable
Catalog policy, and Runtime submit current-reads the target only after the
complete canonical parent lock set is held. It then re-evaluates definition,
entry criteria, lock policy, submitter authority and initial routing; any plan
change aborts the outer transaction and its invocation acceptance. An earlier
draft attempted to lock the target before the full parent set, but that would
have inverted the global lock order. Moving revalidation after the existing
coordinated prelock preserved both freshness and lock ordering.

**Scope challenge:** review also claimed that Approval notification Flows could
run before the outer Runtime transaction committed. Nested transaction timing
made the premise look plausible, but the bounded Runtime Catalog profile only
synthesizes FieldUpdates, rejects Flow/Alert/external actions before execution,
and never reads live ProcessAction rows. The notification dispatcher skips
FieldUpdates. The claimed email effect is therefore unreachable in this
delivery; adding an outbox or post-commit owner would have expanded scope for a
future capability. The correct response was an evidence-backed rebuttal, not a
new mechanism.

**Lesson:** a focused happy-path test is not sufficient evidence for a serving
adapter over a mature engine. Exercise every retained operation and read shape
through the real production composition, restart between execution and replay,
and assert the durable rows and disclosure classification. Test fixtures must
match canonical bootstrap semantics, including hard versus soft deletion and
the recycle-bin owner; otherwise fixture shortcuts can hide or manufacture
product defects. Review is a challenge to already-complete evidence, not the
mechanism that discovers the operation matrix one comment at a time.

**Evidence:** the real TiDB test covers submit, approve, reject, reassign and
recall; restart replay after every operation; pending, latest and step-display
reads; exact invocation counts; no live ProcessAction authority; cancellation,
revision and permission fences. A before/after TiDB adversarial test reproduced
two successful concurrent Approves and duplicate next-step rows before the
fence; after the correction, same-work-item concurrency is exactly one success
and one closed rejection, while two different unanimous approvers serialize on
the instance and produce exactly one next step. Both concurrency cases pass
five repeated real-TiDB runs and are part of the official Runtime matrix. The
previous official Runtime/TiDB matrix,
full affected-module tests, relevant race tests, backend and exact-pinned CRM
distribution build/vet/test, boundarygate, Runtime process-boundary gate, and
Controlplane source-backed clean-install/recovery gate pass.

**Next:** merge and publish this Runtime Approval serving slice, then let sfcrm
replace its remaining process-local Approval calls through the private client.
Do not call M2 or D3 complete until the consumer and exact-image cross-process
route matrix are proven.

### 2026-08-03 — Approval wire parity must follow the reachable legacy shape, not a broad review summary

**Mistake:** an initial triage accepted the review claim that every terminal
Approve, Reject and Recall result preserved the authenticated actor. Reopening
the legacy adapter together with the production engine showed a narrower truth:
Approve and Reject explicitly return an empty `ActorIds` list, which the legacy
adapter preserves; Recall omits that list and therefore falls back to the
authenticated user. Applying the review statement literally would have created
a new Approve/Reject wire regression while fixing Recall.

**Decision:** add one optional, neutral acting-principal response fact to the
existing coarse Approval RPC. Runtime derives it only from trusted delegation
and stores it in the same durable result as the business effect and receipts.
sfcrm uses it only for the established Recall fallback and fails closed when
the exact launch tuple omits it. It remains response-only and never becomes a
caller actor selector or authorization input.

**Scope decision:** do not expand the closed `ActionFailure` classification
into target fields or raw engine errors. The bounded Runtime profile publishes
only FieldUpdates; FieldUpdate failures roll back the transaction, while the
legacy notification failures that remain representable do not populate those
target fields. Adding open diagnostics would broaden disclosure without closing
a reachable launch path.

**Lesson:** review feedback must be checked against the exact reachable producer
and consumer behavior, not merely against a nearby DTO that has more fields.
The right correction can be smaller than the reviewer requests while still
requiring a contract change when the trusted fact is genuinely unavailable at
the process boundary.

**Next:** release the semantic root first, then the independently pinned wire
module; only after both reviewed tags exist should Core and sfcrm pin the tuple.

### 2026-08-03 — M2 closure must prove the real process owner, not a hand-written peer

**Decision:** close the local M2 consumer boundary with two complementary
facts. The exact sfcrm source lock must run and name every frozen route owner
through its Platform client; the independent Integration composition must
start against a separately started real `runtimeapi.Assemble` process, and its
exact production handoff component must hand over and replay the durable
command across that process boundary. Runtime acceptance uses the existing
TiDB command repository. Exact signed images and BOM freeze remain D3 rather
than being pulled into this source-owner delivery.

**Mistake:** the first Integration child registered hand-written Catalog and
Command gRPC servers. That could prove the Integration client crossed a socket,
but it also reimplemented the wire mapping, admission and duplicate semantics
inside the test, so a green result did not prove the production Runtime serving
composition. A later test attempt also guessed that the second
`WorkloadAuthority` argument was the bearer token; the host contract actually
passes the RPC method and exposes the authenticated metadata through context.
Both mistakes came from modeling a neighboring owner instead of reopening its
actual interface.

**Correction:** the child now starts the existing Runtime process assembly with
the real policy, workload security admission, Catalog/command consumer
admission, Catalog and command servers, TLS transport, and durable TiDB command
repository. The test fixture supplies only immutable Catalog input and records
which production engines were reached. Restart proof requires two distinct
Runtime owner PIDs, one Catalog and Command endpoint per owner, byte-identical
command replay, one Integration receipt and one Runtime command row.

**Scope lesson:** a broad `go test ./...` is not a launch-route manifest, while
an exact-image deployment is not needed to establish a source boundary. Name
the frozen sfcrm route tests explicitly and fail when any disappears, retain
dependency gates that forbid process-local L0 imports, and leave container/BOM
composition to D3. This keeps the test strict without inventing rolling
compatibility, a new service, Gateway, metadata authority or fallback.

**Next:** pin the reviewed sfcrm merge SHA, run the exact source-backed and
real-TiDB matrices, merge the single Core closure PR, and declare M2 complete
only after exact-main artifacts and the route/owner audit agree.

### 2026-08-04 — An empty consumer definition set is absence, not a deployment error

**Mistake:** the first local D3 clean install treated an AppRelease capability
with zero Report or Approval definitions as an invalid Catalog projection. That
coupled process startup to tenant-authored L2 content and tempted two wrong
workarounds: omitting published capabilities in deployment or moving concrete
tenant definitions into the L1 release.

**Decision:** keep the existing capability bindings and immutable revision, but
allow an explicit empty Report/Approval definition selection. The Report view
contains no definition and therefore returns missing without consulting a
repository. The empty Approval execution view is projected with read-only
object facts, no mutation closure, and Runtime rejects it before opening an
invocation transaction. A non-empty Approval selection still requires the full
definition-bound mutation closure.

**Scope lesson:** this fixes clean-install absence semantics only. It does not
redesign L0/L1/L2 dependencies, publish PingCAP Report or Approval metadata, or
prove the D3 launch journeys. Those L2 definitions still need their owning
publication path before D3 can be declared complete.

**Next:** publish the Core change, pin it in the existing Controlplane release,
then rerun the same local clean install before changing any D3 BOM or test2
workflow.

### 2026-08-06 — A correct provider boundary needs a durable handoff, not provider execution in Runtime

**Mistake:** the first D3 outbound implementation put provider request
construction, claim/retry state and dispatch completion behind a Runtime RPC.
Although Integration made the HTTP call, Runtime still owned the provider
effect protocol and therefore depended on L1 connector semantics. Review then
kept finding local defects inside a fundamentally misplaced owner, producing
many patches without converging on the deployment goal.

**Decision:** Runtime now does only two transactional, provider-neutral jobs.
An exact record-triggered Flow writes an immutable outbound intent under its
request-bound Catalog revision, and provider success later permits only the
exact metadata-authorized local compatibility projection. Integration first
durably accepts that intent, then owns credential resolution, provider I/O,
lease/retry/uncertain-outcome state, remote-link reconciliation and result
writeback. The existing Integration process, connector callout engine and
NamedCredential mechanism are reused; no new service or network hop is added.

**Evidence correction:** `RecordAfterSave` does not execute in the original
record mutation transaction. It is delivered by the durable record-event
Worker. The important invariant is that the Worker invokes the subscriber and
marks the outbox event processed in one pessimistic transaction. The outbound
intent repository therefore requires that ambient transaction, rolls its row
back when subscriber execution fails, and never opens a second owner boundary.
Tests must prove this actual delivery path instead of relying on the plausible
but false assumption that an after-save Flow inherits the initial CRUD
transaction.

**Scope lesson:** do not move HubSpot, Salesforce or tenant vocabulary into L0,
and do not fix an impossible pure-validator review request by inventing durable
state in the contract package. Pure contract validators check immutable request
identity; Runtime persistence checks prior ACCEPTED command ownership under the
row lock. Rebut feedback that assigns an invariant to a layer that cannot
observe the required fact.

**Next:** publish the semantic contract and wire releases in dependency order,
pin and review api-host, Controlplane and connectors, replace the provider-aware
Core PR with this owner-correct composition, then rerun the local clean deploy
and Test2 inspection using one exact release tuple.

### 2026-08-06 — Close the crash window at the owner boundary and test the production shape

**Mistake:** focused tests initially proved only that Integration eventually
stored a remote identity after an association failure. They did not prove the
ordering across the real crash window: the main provider create could succeed,
the process could die during association I/O, and the command would still lack
a durable remote identity. A separate production-composition fixture also used
an incomplete zero-SecurityEpoch receipt, while one real-TiDB Catalog Action
test gave a four-second operation a one-second request and admission lease.
Those were test-shape errors, not reasons to weaken production validation.

**Decision:** the Integration Worker owns an explicit local checkpoint callback.
After the main provider mutation returns its exact remote identity, the Worker
commits that identity and the finalized RemoteLink while retaining the current
claim; association I/O cannot begin before that transaction succeeds. Lease
recovery resumes only idempotent association operations. The Integration
lifecycle verifies the actual Acceptor repository schema before its first
handoff probe. Catalog publication and Runtime execution both reject aliased
remote-ID and provenance fields, so malformed authority cannot survive until
post-provider writeback.

**Verification lesson:** run tests by the production entry point and confirm
that the named test actually ran. Use complete production-shaped receipts,
internally consistent test deadlines, real TiDB crash/reclaim evidence, and the
module boundary gate. Do not update a boundary baseline to admit new dialect
vocabulary: use the existing neutral `Record` carrier. Do not add rolling
generation fences to a stopped-upgrade candidate or duplicate schema checks in
both composition and lifecycle.

**Next:** merge the latest-main Core and connectors owners, publish exact
releases, pin them through Controlplane and the thin Core composition, then use
one immutable L0 release for the clean local deploy and Test2 inspection.

### 2026-08-06 — Validate failure semantics, not only schema names and happy values

**Mistake:** the Integration owner originally inspected a nullable query result
before checking the query error, verified the acceptance sequence only as a
`BIGINT` plus indexes, and accepted a valid first JSON value without proving the
provider response ended there. These checks looked strict but missed the
semantics the lifecycle actually relies on: transient authority failures must
remain retryable, acceptance ordering requires database-generated monotonic
values, and a remote identity must come from one complete response document.
Core also exposed result preparation and terminal completion as two independent
methods, so a caller could express the invalid state “completed without local
projection” even though the production caller used them in the intended order.

**Decision:** handle storage errors before inspecting scanned values, require
the `AcceptanceSequence` column to be `AUTO_INCREMENT` at lifecycle startup,
and require EOF after decoding the provider response. Test those failure modes
against the real TiDB schema as well as focused unit tests. An empty optional
remote-ID provenance field was already accepted and persisted as SQL `NULL`;
the correct response to that review claim was to add explicit real-TiDB proof,
not change a working production path. Core now exposes one result operation:
lock and validate ACCEPTED authority, invoke the Runtime-owned projection, then
privately write the terminal receipt in the same transaction. Removing the
separate completion method makes the invalid sequence unavailable by shape.

**Verification lesson:** reopen the exact reviewed head for every blocker.
Separate a demonstrated implementation defect from a misread line and from an
unrelated existing test-fixture drift. Full repository tests and race tests
still run, while the owner-specific real-TiDB suite is the release evidence for
this change. Do not absorb an unrelated workqueue fixture repair into the
outbound owner merely because enabling every optional database test exposed it.

**Next:** merge and release the reviewed owners, replace workspace dependencies
with exact published pins, then rerun the same Runtime API, Worker and
Integration production compositions before building the unified L0 release.

### 2026-08-06 — Durable recovery identity includes authority, phase, and the effect boundary

**Mistake:** the first Integration owner reused a finalized RemoteLink after
checking only its record/link identity, resolved association targets through
only the newest action slot, counted every claim as a provider attempt, and
returned normal retryable preparation outcomes as process errors. A transient
Catalog/profile failure after a provider-success checkpoint also overwrote the
association recovery marker. Each local path looked reasonable, but together
they could stop the Integration service, exhaust retries before external I/O,
reuse an old credential/path snapshot, or replay an already-successful main
provider mutation.

**Decision:** treat the durable execution identity as one closed set. Before
external I/O, revalidate the full RemoteLink authority snapshot against the
exact immutable IntegrationBindingView and active PackGeneration; allow only
an install-ID-only revalidation when profile, credential, endpoint and retry
policy are unchanged. Association lookup resolves its own target profile and
searches only the current or explicitly retained previous action slots.
AttemptCount advances at provider dispatch, not claim or local writeback, and
main delivery starts a separate bounded association lifecycle after its durable
success checkpoint. Expected business outcomes continue Service.Run only after
their durable state transition succeeds. Checkpointed preparation retries keep
the association phase, while update/association paths must contain the remote
identity placeholders they claim to address.

**Verification lesson:** run the full shared inventory tests when adding live
system tables; package-local engine tests do not prove the managed-table and
architecture-floor registries are synchronized. Conversely, do not implement
a review scenario that the compiler already makes unreachable. Record-mutation
views require the complete same-revision object closure, so multi-source routes
and their filtered finalization authorities remain aligned; a real multi-route
regression is stronger evidence than adding a second filter for an invalid
partial projection.

**Next:** merge and release Core and connectors, pin the reviewed releases in
Controlplane and the final composition, then rerun clean local deployment and
the same immutable release in Test2.

### 2026-08-06 — Runtime must consume Catalog facts, not reconstruct L1 provenance

**Mistake:** a review correctly found that the first Runtime result guard could
trust a stale `Updateable=true` flag, but its suggested fix was to call
`fieldcapabilities.ApplyIntrinsicBounds` in standalone Runtime. That helper
consults the process-global field-provenance classifier built from L1
declaration manifests. Independent app-blind L0 does not initialize that
classifier; using it would classify legitimate launch remote-ID fields as
discovered and would reintroduce an L1 authority dependency into Runtime.
Integration also checked only result identity and Catalog revision instead of
the complete neutral response receipt.

**Decision:** Runtime consumes the exact immutable mutation-view facts already
published by Catalog. A result field fails closed when the view marks its
source discovered or its write-protection policy non-updateable, in addition
to the existing formula, projection, system, compound, virtual, type, and
protected-name bounds. Integration uses the published
`ValidateCommitResultResponse` contract before terminal writeback, covering
protocol, capability, Catalog, schema, engine, and SecurityEpoch receipts.

**Verification lesson:** fix the failure class at the owning boundary, not the
literal review suggestion. Reopen current launch metadata before implementing
a compatibility claim: current D3 metadata has one direct Account association
and no junction association, while Core rejects junctions before producing a
neutral intent. A future junction launch path must be resolved and authorized
by Core before Integration receives a provider-neutral record identity; moving
the L1 junction query into Integration would be scope and authority drift.

**Next:** accept only fresh exact-head blockers that are reachable under the
published launch revision, then merge and release the two owners without
adding legacy query semantics to the D3 Integration process.

### 2026-08-09 — Candidate verification must initialize candidate runtime metadata

**Mistake:** the offline-candidate verifier assumed the setup process had
already registered candidate-owned runtime declarations. That happened during
a full ApplyAll, hiding the defect, but a fresh-process completed-marker retry
constructed the verification Runner directly. CRM formula bundles such as
`CASESAFEID` and `ADDMONTHS` were therefore absent even though their immutable
pack payloads were active.

**Decision:** candidate verification registers the exact candidate pack set's
runtime metadata before refreshing and checking metadata programs. It does not
read an L1 repository implicitly or relax formula validation; the immutable
candidate already supplied to Controlplane remains the only input.

**Verification lesson:** every idempotent deployment phase must work in a fresh
process. A full-apply pass is not proof that its restart/no-op path initializes
the same process-local accelerators. The setup regression gate now requires
candidate verification to use the registration-bearing path rather than the
prepared-runner shortcut.

**Next:** publish one unified L0 candidate from this exact Core revision, then
resume the same stopped Test2 deployment transaction without resetting the
database or replaying already-checkpointed downstream phases.

### 2026-08-09 — Scheduled cutover is an ownership boundary, not a data rewrite

**Mistake:** D3 initially treated every active legacy scheduled reservation as
if it had to be rewritten into a terminal Runtime row before the fenced Worker
could start. That coupled deployment correctness to repeated mutation of
historical prod-clone queue data and produced a misleading writer hunt when
four unchanged May/June rows remained visible.

**Decision:** bind scheduled cutover readiness to the exact durable
`_System_RuntimeWorkOwnership.LastModifiedDate`. The Worker ignores unscoped
rows created before that transfer because every claim/recovery query is already
restricted to its exact Org/AppInstallation/capability scope. It still fails
closed for any unscoped active row created at or after the transfer, and it
waits one complete legacy discovery lookback after the transfer before serving.

**Verification lesson:** migration checks should prove the new owner's readable
and writable domain, not require cosmetic normalization of data that the new
owner is structurally unable to consume. Preserve post-cutover intrusion
detection, but remove deployment-specific historical-row cleanup as an
authority prerequisite.

**Next:** publish the exact Core branch image, remove the Test2-only scheduled
reservation quarantine and temporary causality probes, then resume the same D3
checkpoint and prove serving plus inspection.

### 2026-08-21 — Related Count parent identity must come from Catalog before persistence

**Run:** `local-2026-08-21-related-count-keyprefix`, Core base `0177ba41d`,
macOS local workspace, browser-shaped staging incident workload for Account
`001BV000004O51LYAS`.

**Profile / finding:** the Related Count HTTP request was already one batch,
but its parent-read step received the `record-query/v1` projection, which does
not own `KeyPrefix`. Records therefore fell back to physical object discovery.
On TiDB, the Salesforce string ID was numerically coerced against a BIGINT
system-table ID, so the first physical hit could be the wrong object; even
without the collision, the fallback fan-out was unnecessary. The Catalog
serving set already publishes `KeyPrefix` once in the sibling
`record-access/v1` projection.

**Change / decision:** Related Count admission now requires and captures the
same-revision record-access sibling. Only parent-record authority temporarily
uses that view; child counts retain the primary record-query view. No
`KeyPrefix` field was duplicated into the query projection and no second
metadata source or network request was added.

**Evidence:** focused Runtime API, Runtime assembly, boundary, and Records tests
pass. A Catalog-backed Account regression resolves the incident ID and asserts
zero calls to physical object discovery. Another test proves parent access sees
the access view while child counts see the query view. Backend full testing
reached unrelated pre-existing integration failures: automation integration
timed out at 10 minutes and OAuth PKCE setup panicked in Controlplane; the
changed Runtime packages passed in that run.

**Next:** publish the branch candidate, verify CI, then deploy that exact branch
through the L2 Test2 runbook and compare Related Count output plus repository
SQL/probe evidence before merge.
