From 83f3ec29db091724b6d084385050bc7cb239d5a6 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 18:07:17 +0100 Subject: [PATCH 01/48] THOTH-ASYNC-01: propose shared async architecture --- ...ed-asynchronous-event-and-job-execution.md | 663 ++++++++++++++++++ 1 file changed, 663 insertions(+) create mode 100644 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md new file mode 100644 index 000000000..03c92a9d4 --- /dev/null +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -0,0 +1,663 @@ +# ADR-0012 - Shared asynchronous event and job execution architecture + +Status: PROPOSED +Date: 2026-09-29 +Decision owner: CTO +Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work +Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (domain executor/consumer); `thoth-pub/infrastructure` (worker runtime and IAM substrate) +Parent programme: [THOTH-ASYNC-01 #957](https://github.com/thoth-pub/thoth/issues/957) +Authoring task: [THOTH-ASYNC-01-ADR-01 #958](https://github.com/thoth-pub/thoth/issues/958) +Supersedes in part: ADR-0008 sections 3.3-3.5 and the corresponding programme consequences that prohibit a reusable cross-programme job/queue abstraction. ADR-0008's machine-role, least-privilege and SUPERUSER-separation decisions remain in force. + +Decision: Thoth establishes one shared PostgreSQL-backed asynchronous event and +job engine for cross-programme durable work. Events record durable facts and may +fan out idempotently into jobs; jobs record executable asynchronous work and may +also be created directly for scheduled, reconciliation or operator-requested +work. The engine provides one shared lifecycle, claim/lease/idempotency model and +attempt history. A headless `thoth-worker` ECS/Fargate service is the default +always-on executor for handlers owned by `thoth`. Domain-specific executors in +other repositories may consume the same shared job protocol without moving their +business logic into `thoth`. + +This ADR is architecture only. It authorizes no schema migration, source +implementation, worker deployment, IAM change, provider write, credential +provisioning, BE-04/DIS-02 activation, Hosting implementation or production +activation. + +Verification baselines at programme setup: + +```text +thoth-pub/thoth +develop @ 923545d5c9028bc04c40e38efeb7de674efed3fd + +thoth-pub/thoth-dissemination +main @ 0be4dfba0661816914a7fffa477fe33043a99793 + +thoth-pub/infrastructure +master @ 52c79cf19392df4c9989e5c68ad199c8bc2846a8 +``` + +These SHAs are architecture-authoring evidence only. Every implementation task +must bind its own fresh exact base. + +--- + +## 1. Context + +Thoth now has several workloads that are naturally asynchronous: + +- Hosting provisioning and reconciliation; +- Publisher Services dissemination such as Crossref deposits; +- validation of uploaded files; +- EPUB post-processing and reader deployment; +- CDN invalidation; +- website/cache revalidation; +- search-index updates such as Algolia; +- future external integrations and scheduled reconciliation. + +BE-04 introduced the first substantial durable-job implementation in `thoth` +using `distribution_job`, `distribution_job_target` and +`distribution_job_attempt`. ADR-0008 deliberately made those tables +Publisher-Services-local and required a later explicit cross-programme ADR before +a generic queue/job abstraction could exist. + +That later decision is now required. Deploying a Hosting-specific worker and +retaining a separate distribution-job engine would create parallel durable +execution architectures immediately before multiple further asynchronous +consumers are expected. + +### 1.1 Current BE-04 deployment fact + +Migration `thoth-api/migrations/20260814_v1.7.0` has been applied to +production and therefore the `distribution_job*` schema is part of production +migration history. The tables have not been operationally used. The dev +environment has not applied that BE-04 migration. + +Consequences: + +- the historical v1.7.0 migration is immutable and must not be rewritten; +- a later separately authorized migration must remove the unused + `distribution_job*` schema and introduce the approved shared async schema; +- no data-conversion requirement is currently known because the production + tables have not been used, but the implementation task must verify emptiness + immediately before any destructive migration is authorized; +- BE-04/DIS-02 deployment and activation remain on HOLD pending this + supersession. + +### 1.2 Existing dissemination boundary + +`thoth-dissemination` owns platform-specific dissemination behaviour. Its +current DIS-02A worker claims Thoth jobs and executes Crossref through Python +domain code and an isolated runner. That ownership does not move into +`thoth` merely because job durability becomes shared. + +The shared engine owns durable execution mechanics. The owning domain repository +continues to own provider-specific semantics. + +--- + +## 2. Decision drivers + +1. One robust queue/state-machine implementation should serve all future Thoth + asynchronous work rather than each programme rebuilding leases, retries and + audit independently. +2. Business writes and the events they cause must not suffer a dual-write gap. +3. External writes are not generally exactly-once; ambiguous outcomes require + reconciliation rather than blind replay. +4. Worker crashes, deploys and horizontal scaling must not lose or duplicate + authoritative job state. +5. One always-on Fargate worker should provide broad value rather than spend most + of its lifetime waiting for one Hosting-specific workload. +6. Shared execution mechanics must not erase domain/repository ownership. +7. A generic runtime must not become a generic all-powerful application role. +8. Operational complexity should remain proportionate: PostgreSQL is already the + canonical durable store and is sufficient for expected Thoth volumes. +9. Credentials and provider authority should remain simple to operate while + preventing unnecessary standing high-risk permissions. +10. Existing production migration history must remain truthful and immutable. + +--- + +## 3. Decision + +### 3.1 One shared durable engine + +`thoth` owns the canonical asynchronous persistence model. The implementation +will introduce shared durable concepts equivalent to: + +```text +async_event +async_job +async_job_attempt +``` + +The final table/column names are implementation-specification details, but the +semantic separation is architectural. + +PostgreSQL is the authoritative owner of: + +- event records; +- job state; +- routing/materialization state; +- leases and claim tokens; +- attempt history; +- retry availability; +- cancellation/terminal outcomes; +- reconciliation-required outcomes; +- correlation/causation metadata. + +No local file, worker memory, GitHub Actions run, S3 object, message-bus message +or external provider is the sole durable owner of that state. + +### 3.2 Events and jobs are different + +An **event** records a durable fact that has happened, for example +`WORK_UPDATED`, `FILE_UPLOADED` or `HOSTING_ENABLED`. + +A **job** records executable work, for example `ALGOLIA_INDEX_WORK`, +`VALIDATE_FILE`, `CROSSREF_SYNC` or `HOSTING_PROVISION`. + +One event may materialize zero, one or many jobs. Jobs may also be created +without an event for scheduled work, reconciliation, backfills or explicit +operator actions. + +Where an event is caused by a PostgreSQL business mutation, the business change +and event record are written in the same transaction. Either both commit or +neither commits. + +Event routing itself must be durable and idempotent. A crash after materializing +some downstream jobs must be able to resume without duplicating already-created +jobs. An event that has completed routing must not silently gain new historical +subscribers merely because a later software version adds a new handler. + +### 3.3 Versioned contracts + +Every event and job kind has: + +- a stable kind identifier; +- an explicit payload schema version; +- deterministic validation; +- an owning domain; +- a defined compatibility policy. + +Workers must reject or HOLD unsupported payload versions rather than guessing. +Changes that require old in-flight jobs to remain executable must preserve the +required compatibility window or provide an explicit migration/reconciliation +plan. + +### 3.4 Job lifecycle and claims + +The common job lifecycle must support at least the semantic states: + +```text +PENDING +RUNNING +WAITING / RETRY_SCHEDULED +SUCCEEDED +FAILED +CANCELLED +RECONCILIATION_REQUIRED +``` + +Exact enum spelling is implementation detail. + +Claims use database-enforced concurrency with leases and unforgeable claim +tokens. `FOR UPDATE SKIP LOCKED` is the default claiming primitive for the +shared queue because this workload explicitly requires multiple consumers to +claim independent ready jobs without blocking each other. + +A stale worker must not be able to complete or fail a job after its lease/claim +has been superseded. + +The engine must support horizontal scaling from one worker task to multiple +tasks without changing job semantics. + +### 3.5 Idempotency and concurrency + +Every externally meaningful job kind defines a deterministic idempotency key. +The database prevents duplicate live work for identities that the owning +handler declares equivalent. + +Handlers may additionally define a concurrency key, for example: + +```text +work::algolia +hosting: +crossref: +``` + +Jobs with unrelated concurrency keys may execute in parallel. Jobs that would +mutate the same protected external/domain resource must serialize where the +owning specification requires it. + +### 3.6 Delivery semantics and ambiguous writes + +The engine guarantees durable at-least-once execution opportunities. It does +**not** claim exactly-once external effects. + +A handler result distinguishes at least: + +- success; +- retryable failure before a relevant external write; +- deterministic permanent failure; +- wait/retry at or after a specified time; +- indeterminate/ambiguous external-write outcome requiring reconciliation. + +An indeterminate provider write is never blindly replayed merely because the +worker did not receive an acknowledgement. The owning handler must reconcile +provider state or require operator attention before another effectful attempt. + +### 3.7 Attempts and diagnostics + +Each execution attempt is append-only durable evidence containing enough +sanitized information to reconstruct: + +- job and attempt identity; +- claim identity; +- worker/executor identity; +- start/end times; +- result classification; +- bounded error code/detail; +- provider request/correlation identifiers where safe; +- retry/reconciliation disposition. + +Secrets, credentials and unbounded provider responses must not be written to +attempt records or ordinary logs. + +### 3.8 Default `thoth-worker` runtime + +The default always-on executor is a headless ECS/Fargate service using the +ordinary Thoth release image with a dedicated command, conceptually: + +```text +thoth start worker +``` + +Initial runtime shape: + +```text +same ECS cluster +same VPC/database +no ALB +no public hostname +DesiredCount = 1 initially +separate task definition/service +dedicated worker task role +``` + +The worker polls/claims durable PostgreSQL jobs. PostgreSQL +`LISTEN/NOTIFY` may be used as a wake-up optimization, but correctness must +never depend on receiving a notification; periodic durable polling remains the +fallback. + +The worker must shut down gracefully: stop claiming, finish or safely abandon +within the lease budget, and leave recoverable durable state. + +### 3.9 Handler registry and capabilities + +The worker has a typed registry of supported job kinds. Each handler declares: + +- job kind and supported payload version(s); +- owning domain; +- required runtime capability/configuration; +- timeout/lease requirements; +- retry/reconciliation policy; +- idempotency/concurrency rules. + +A worker only claims job kinds whose required capabilities are configured. +Capability declarations are runtime/configuration boundaries and future +scheduling primitives; they do not imply one IAM role per handler. + +### 3.10 AWS credentials and task role + +The initial `thoth-worker` uses one least-privilege ECS task role containing +the union of its **approved routine AWS capabilities**. + +Do not introduce one assumable IAM role per ordinary handler merely to mirror +code boundaries when the same process could assume all of them anyway. + +The task role may eventually include bounded capabilities such as: + +- approved Hosting tenant lifecycle; +- ACM operations needed by Hosting; +- writes to Thoth-controlled Hosting DNS; +- tenant/legacy invalidation where separately approved; +- bounded S3 object access required by file-processing handlers. + +The exact permission matrix remains an implementation/IAM task and must retain +negative tests and resource scoping. + +Standing permissions that materially increase blast radius and are not required +for routine operation remain excluded. + +### 3.11 No automated legacy-CDN migration + +The shared worker does not implement legacy CDN migration. + +An existing publisher moving to the new Hosting model is provisioned as a new +Hosting target using the ordinary greenfield Hosting lifecycle. Content +migration and any legacy domain/file cutover are operator procedures under their +own runbook and authorization. + +Therefore the worker architecture does not require: + +- standing `cloudfront:UpdateDistribution`; +- `cloudfront:UpdateDomainAssociation`; +- a special Hosting MigrationRole; +- automated migration rollback state. + +The R1C migration evidence remains useful operational evidence, but migration is +not a worker job family under this ADR. + +### 3.12 External-service credentials + +Non-AWS integration credentials such as Crossref or Algolia credentials are +provided to the relevant runtime through deployment environment variables. + +This ADR does not require Secrets Manager. + +Requirements: + +- credentials are never committed to source, infrastructure templates, task + specifications or GitHub records; +- application logs and durable job diagnostics must never dump the process + environment; +- handlers fail closed when required credentials are absent; +- providers use distinct credentials where operationally available; +- rotation is performed by updating deployment configuration and replacing the + affected task/runtime. + +Environment variables are an operational delivery mechanism, not a security +isolation boundary between handlers in the same process. + +### 3.13 Domain ownership and cross-repository executors + +The shared engine standardizes **durability and execution protocol**, not every +domain implementation. + +Handlers whose business logic is owned by `thoth` execute in +`thoth-worker`. + +Where another repository owns the domain implementation, that repository may +consume the same shared job protocol using a domain-specific executor identity. +In particular, `thoth-dissemination` continues to own dissemination/provider +logic and may claim only dissemination job kinds allowed by its own authorization +matrix. + +This does not require a second permanently running generic queue engine. +A downstream domain executor may be persistent, scheduled or on-demand as its +own approved implementation specifies. It must not create a second canonical job +database or lifecycle. + +The shared protocol must therefore allow a domain executor to: + +- claim only authorized job kinds; +- receive a versioned payload; +- report success/failure/indeterminate outcomes with the current claim token; +- leave all durable lifecycle state in `thoth`. + +### 3.14 Application authorization + +ADR-0008's machine-role decision remains binding. + +This ADR creates no generic ZITADEL role named `WORKER`, `SERVICE`, +`MACHINE` or equivalent. + +A domain executor that reaches protected Thoth API operations uses a +domain-specific, least-privilege authorization matrix. `DISSEMINATION_WORKER` +may be adapted to the generic job protocol for dissemination only; it does not +become authority for Hosting, Metrics, files, indexing or other job families. + +The in-process `thoth-worker` does not need to call its own public GraphQL API +to perform internal domain operations merely to impersonate a machine role; +internal authorization/execution boundaries must instead be specified explicitly +by the owning implementation. + +--- + +## 4. BE-04 supersession + +Once ADR-0012 is approved and repository-authoritative, the shared async engine +supersedes ADR-0008's decision that BE-04 durable job machinery is the only +permitted programme-local implementation and that no shared framework exists. + +It does **not** supersede ADR-0008's machine-role and least-privilege rules. + +BE-04/DIS-02 must be re-specified before deployment: + +- `distribution_job*` is not activated as a parallel production queue; +- the existing production tables are treated as unused legacy schema; +- a new migration drops those tables only after a fresh production preflight + proves they contain no operational data; +- shared async tables are introduced through a new migration; +- Publisher Services job semantics are mapped onto generic jobs; +- `thoth-dissemination` consumes only its authorized generic job kinds; +- existing BE-04/DIS-02 implementation remains historical evidence and may be + reused only where a new bounded specification explicitly adopts it. + +The already-applied v1.7.0 migration remains immutable. + +--- + +## 5. Initial job families + +These are architectural examples/initial consumers, not implementation +authorization and not a promise that all ship in the first implementation. + +```text +HOSTING_PROVISION +HOSTING_VERIFY_DOMAIN +HOSTING_RECONCILE + +DISTRIBUTION / CROSSREF_SYNC + +VALIDATE_FILE +DEPLOY_EPUB_READER +INVALIDATE_CDN + +ALGOLIA_INDEX_WORK +WEBSITE_REVALIDATE +``` + +The implementation should prove the framework first with a bounded set of +representative handlers rather than enabling every possible integration in one +release. + +--- + +## 6. Rejected alternatives + +### A. Hosting-specific worker and queue + +Rejected because it would create a second durable-job architecture immediately +before distribution, file-processing and indexing workloads need the same +primitives. + +### B. Reuse BE-04 `distribution_job*` as the generic schema unchanged + +Rejected because those tables and APIs were designed around Publisher Services +semantics. Renaming their interpretation by analogy would preserve programme +coupling and bypass the explicit ADR gate ADR-0008 established. + +### C. SQS/Kafka/EventBridge as canonical queue now + +Rejected for the initial architecture. PostgreSQL already provides transactional +co-location with Thoth business writes, durable state, row locking and sufficient +expected throughput. A later scale-driven ADR may add a delivery bus, but it +must not become the sole authoritative owner of job lifecycle state without a +new decision. + +### D. One IAM role per handler + +Rejected as the default because one process able to assume every handler role +does not materially isolate a compromise of that process and adds substantial +operational complexity. High-risk standing permissions may still justify +separate execution boundaries where evidence shows real blast-radius reduction. + +### E. One generic catch-all application machine role + +Rejected and still prohibited by ADR-0008. Shared queue mechanics do not imply +shared application authority. + +### F. Put dissemination business logic into `thoth` + +Rejected. Queue standardization is not repository-ownership transfer. +`thoth-dissemination` remains the owner of platform-specific dissemination +execution until a separate approved architecture decision says otherwise. + +--- + +## 7. Invariants + +1. PostgreSQL remains canonical for event/job lifecycle. +2. Business mutation plus caused event is atomic where both are PostgreSQL-owned. +3. Routing and job creation are idempotent. +4. No stale claim can finalize a superseded lease. +5. No job handler may claim exactly-once external effects. +6. Indeterminate writes require reconciliation before unsafe replay. +7. Domain ownership is explicit and preserved. +8. Generic execution does not create generic application authorization. +9. Historical migrations are never rewritten after deployment. +10. Credentials are not committed or emitted in logs/durable diagnostics. +11. A worker may execute only supported payload versions and configured + capabilities. +12. Merge, deployment, job activation and provider access remain separate gates. + +--- + +## 8. Implementation impact and decomposition + +Approval of this ADR should lead to separate bounded tasks, at minimum: + +1. **Shared schema/engine in `thoth`** + - replacement migration; + - event/job/attempt domain types; + - claim/lease/retry/reconciliation primitives; + - event routing/materialization; + - tests including real PostgreSQL concurrency. + +2. **Default `thoth-worker` runtime in `thoth`** + - worker command; + - handler registry; + - graceful shutdown; + - metrics/health/operational reporting. + +3. **Infrastructure** + - headless Fargate worker service; + - task role and networking; + - environment-variable configuration; + - no deployment/activation until separately authorized. + +4. **Publisher Services / `thoth-dissemination`** + - replace BE-04 job protocol with generic job-family consumption; + - preserve dissemination domain logic; + - independently review external-write/idempotency semantics. + +5. **Hosting** + - adopt generic jobs for greenfield provisioning/reconciliation; + - no automated legacy CDN migration. + +Each repository receives its own branch/PR and independent review. No downstream +repository guesses an unmerged upstream contract. + +--- + +## 9. Migration and rollout + +Architecture approval does not authorize migration execution. + +The future database rollout must be additive/safe until the exact replacement +migration is independently approved. Before any production DROP of +`distribution_job*`: + +1. verify the production tables exist from v1.7.0; +2. verify they contain no operational data; +3. verify no deployed process is reading/writing them; +4. verify the shared replacement schema has the required rollback/recovery plan; +5. receive explicit migration/deployment authorization. + +The development environment's unapplied v1.7.0 state must be handled explicitly +by the migration specification; it must not rely on dev matching production by +accident. + +Initial worker deployment is inert until job creation/activation is separately +authorized. Desired count starts at one unless deployment evidence justifies +otherwise. + +Rollout should prove: + +- schema and claim concurrency; +- crash/lease recovery; +- idempotent routing; +- one or more non-destructive representative handlers; +- one external-write handler with reconciliation evidence; +- observability and stuck-job procedures; + +before broad adoption. + +--- + +## 10. Rollback + +Architecture rollback before implementation is ordinary documentation revert. + +Once implemented, rollback must distinguish: + +- stopping new event/job creation; +- stopping worker claiming; +- preserving durable event/job/attempt records; +- rolling back individual handler activation; +- schema rollback, which may be impossible after live async data exists and + therefore requires its own migration plan. + +Do not treat disabling the worker as equivalent to deleting durable jobs. + +--- + +## 11. Validation requirements + +Before shared implementation can be approved, evidence must include: + +- real PostgreSQL concurrent claim tests with multiple workers; +- stale-token/expired-lease refusal; +- crash between claim and completion; +- crash during event fan-out/materialization; +- deterministic deduplication/idempotency tests; +- concurrency-key serialization tests; +- bounded retry/backoff tests; +- INDETERMINATE external-write reconciliation tests; +- unsupported payload-version refusal; +- graceful shutdown/restart recovery; +- worker capability filtering; +- authorization negative tests for every external/domain executor; +- proof that logs and durable diagnostics do not contain configured credentials; +- migration tests from a schema containing the production v1.7.0 + `distribution_job*` relations; +- deployment tests with more than one worker task even if production initially + runs one. + +Provider-specific handlers owe their own acceptance evidence in addition to the +shared-engine tests. + +--- + +## 12. Approval and authority + +This ADR is currently **PROPOSED**. + +The CTO has selected the architecture direction recorded in programme issue +#957, but that does not make this exact written ADR content approved. + +Before any implementation may rely on ADR-0012: + +1. this exact proposed content receives independent review; +2. required corrections, if any, are incorporated; +3. the CTO explicitly approves the exact decision content; +4. status/approval metadata are reconciled under the repository decision process; +5. the exact approved content receives required independent exact-head review; +6. it is merged into `develop`. + +No implementation task may treat the unmerged proposal as authoritative. + +BE-04/DIS-02 deployment/activation and Hosting implementation that depends on +the async architecture remain HOLD throughout this gate. From 30efbc63945c474d924fb233adf4ce4f4aaefb20 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 18:08:01 +0100 Subject: [PATCH 02/48] THOTH-ASYNC-01: register ADR-0012 proposal --- docs/engineering/decisions/decision-register.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 1d9b510d0..36a749676 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-09-09 +Last updated: 2026-09-29 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -18,6 +18,8 @@ Last updated: 2026-09-09 | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed cross-programme event/job engine, a default headless `thoth-worker` runtime, shared lease/idempotency/reconciliation semantics, domain-owned executors, one routine worker AWS task role, environment-variable external credentials, and no automated legacy-CDN migration. It would supersede ADR-0008 only where ADR-0008 prohibits a reusable cross-programme job framework; ADR-0008 machine-role/least-privilege rules remain. BE-04/DIS-02 deployment and Hosting implementation depending on this architecture remain HOLD. Requires independent review and exact CTO approval before implementation may rely on it. | + ## Merge and implementation rules These ADRs may be committed as part of the engineering-control foundation while `PROPOSED`. From fb8c1d4e9373153d367e2336b6a4dc349f165fc2 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 18:08:08 +0100 Subject: [PATCH 03/48] THOTH-ASYNC-01: document ADR-0012 proposal --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1eae7fda..e7cbb064d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal separates durable events from executable jobs, requires transactional event creation where coupled to PostgreSQL business writes, and standardizes versioned payloads, idempotent fan-out, leases/claim tokens, `FOR UPDATE SKIP LOCKED` claiming, attempt history, concurrency keys, bounded retries and explicit `RECONCILIATION_REQUIRED` handling for ambiguous external writes. It selects one headless `thoth-worker` Fargate service as the default always-on executor for Thoth-owned handlers, while preserving domain-owned executors such as `thoth-dissemination`; uses one least-privilege routine worker AWS task role rather than per-handler roles; keeps ADR-0008's prohibition on a generic catch-all ZITADEL machine role; uses deployment environment variables for external-service credentials; and excludes automated legacy-CDN migration. Because production has the unused v1.7.0 `distribution_job*` schema, a future separately authorized migration must remove it and introduce the shared async schema without rewriting historical v1.7.0. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** - `CTRL-BRANCH-RELEASE-01`: preserve each managed repository's established release branch name instead of normalizing every repository to `master` (issue [897](https://github.com/thoth-pub/thoth/issues/897), parent control programme [818](https://github.com/thoth-pub/thoth/issues/818)). Adds [`ADR-0011`](docs/engineering/decisions/ADR-0011-preserve-established-release-branch-names.md), which standardizes the **role** of the release branch, written ``, rather than whether it is spelled `main` or `master`. The shared conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `, with each repository's `` resolved from its own verified live state: `main` repositories remain on `main`, `master` repositories remain on `master`, and neither name is created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` release flow is unchanged, and [`ADR-0009`](docs/engineering/decisions/ADR-0009-programme-integration-branch-namespace.md) is unchanged in substance. Release-branch conversion is removed from `BR-APP-01`, `BR-DIS-01`, `BR-SPHINX-01`, `BR-DASH-01`, `BR-WIDGET-01` and `BR-LIC-01`, and Vercel production for `metrics-dashboard` is no longer moved from `main` for branch spelling; the independently justified development-branch, protection, CI, provider and publication readiness work in each of those tasks is retained, CG-03 and CG-04 remain **OPEN**, and no task's risk is lowered. Reconciles `branch-topology.md`, `control-gaps.md`, the affected repository-map entries and the Metrics task tracker. **Documentation/control only: no established release, default or development-topology branch is created, deleted, renamed or moved by `ADR-0011` in any managed repository; no repository setting, default branch, protection, ruleset, CI workflow, provider or deployment configuration is changed; and there is no runtime, schema, migration, GraphQL/API, auth, contract, generated client, publication or production effect.** - `ADM-ADR-01-CLOSEOUT-01`: reconcile [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md)'s durable repository approval metadata with the independent review, CTO approval and merge that already completed under `ADM-ADR-01` (issue [852](https://github.com/thoth-pub/thoth/issues/852), parent programme issue [846](https://github.com/thoth-pub/thoth/issues/846)). The ADR header and its section 17 now record `Status: APPROVED`, `Approved by: Javi, CTO` and `Approval date: 2026-08-28`, the proposal-state sentence becomes approved-state wording, and the ADR-0010 decision-register row records its approval blocker as satisfied. The approved architecture is unchanged: invariants, `ServiceOperation` semantics, ADR-0002 platform-domain separation, Distribution/Publisher Services boundaries, programme ordering and rollout semantics are untouched, as are the existing authority condition and the historical `ADM-ADR-01` verification base. **Documentation/control authority-state reconciliation only: no runtime, schema, migration, GraphQL/API, contract, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** - `ADM-ADR-01`: propose [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md), the Staff Operations Console architecture (issue [846](https://github.com/thoth-pub/thoth/issues/846)). The proposal reserves `/admin/*` for the superuser global control plane, keeps one publisher workspace entered through an explicit publisher operating context rather than identity impersonation, and makes Thoth the future canonical owner of staff-facing publisher-service operational records while separating desired state, execution, observed external state and attention/reconciliation. It defines `ServiceOperation` only as a common operational/audit seam — not a generic job/queue framework — preserves ADR-0002 platform-domain separation and ADR-0008 programme-local `distribution_job*`, and selects distribution as the first read-only operational integration before any replay or external-write activation. **Architecture/control proposal only: no runtime, schema, migration, GraphQL/API, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** From a69921682cd02e060110304f8f9868d0c39da65e Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 18:09:00 +0100 Subject: [PATCH 04/48] THOTH-ASYNC-01: add ADR-0012 authoring report --- ...H-ASYNC-01-ADR-01-implementation-report.md | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md new file mode 100644 index 000000000..d13272e8b --- /dev/null +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -0,0 +1,140 @@ +# THOTH-ASYNC-01-ADR-01 implementation report + +Status: AUTHORING COMPLETE - AWAITING INDEPENDENT REVIEW + +## Identity + +```text +Programme: THOTH-ASYNC-01 +Owning issue: thoth-pub/thoth#958 +Parent programme: thoth-pub/thoth#957 +Repository: thoth-pub/thoth +Task: THOTH-ASYNC-01-ADR-01 +Risk: CRITICAL +Workflow: STANDARD +Authorized base: develop @ 923545d5c9028bc04c40e38efeb7de674efed3fd +Task branch: feature/async/adr-0012 +PR target: develop +``` + +## Implemented scope + +Authored the proposed shared asynchronous event/job architecture and only its +required repository records. + +Content head before this report-only commit: + +```text +fb8c1d4e9373153d367e2336b6a4dc349f165fc2 +``` + +The final branch head including this report is recorded in the owning GitHub +issue and is the SHA to which independent review must bind. + +## Files changed before this report + +1. `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - new PROPOSED ADR. +2. `docs/engineering/decisions/decision-register.md` - registers ADR-0012 as PROPOSED and updates the register date. +3. `CHANGELOG.md` - records the architecture proposal under Unreleased/Added. + +This report is the fourth and final path in the authorized write budget. + +## Architecture recorded + +The ADR proposes: + +- one PostgreSQL-backed cross-programme async event/job engine; +- transactional event creation where coupled to PostgreSQL business writes; +- durable idempotent event fan-out; +- typed/versioned event and job payloads; +- shared claim/lease/claim-token/attempt/retry/reconciliation semantics; +- at-least-once execution opportunities, not exactly-once external effects; +- explicit reconciliation for indeterminate external writes; +- concurrency keys and deterministic idempotency; +- one default headless `thoth-worker` Fargate runtime for Thoth-owned handlers; +- domain-owned executors for cross-repository logic such as `thoth-dissemination`; +- one routine worker AWS task role rather than per-handler IAM roles; +- no generic catch-all ZITADEL machine role; +- external-service credentials supplied through deployment environment variables; +- no automated legacy-CDN migration; +- BE-04/DIS-02 pre-activation supersession onto the shared engine. + +## BE-04 migration/data effect + +No migration was created, modified or executed. + +The ADR records the CTO-provided environment fact that v1.7.0 has been applied +to production and its `distribution_job*` tables have not been operationally +used, while dev has not applied that migration. + +The proposal requires a future, separately authorized migration to remove the +unused legacy tables and introduce the shared async schema. Historical +`20260814_v1.7.0` remains immutable. + +## Authorization/security effect + +No authorization implementation changed. + +ADR-0008's domain-specific machine-role and least-privilege rules remain in +force. The proposal does not create a generic ZITADEL worker/service role. + +No IAM role, policy, credential, provider configuration or environment variable +was created or changed. + +## External/runtime effects + +```text +provider reads: 0 +provider writes: 0 +migration execution: 0 +CI dispatch/rerun: 0 +deployment: 0 +release/publication: 0 +production activation: 0 +``` + +Repository/GitHub mutations were limited to the authorized issues, branch and +four-path documentation write budget. + +## Validation + +Live preflight established: + +- `develop` exactly matched authorized base + `923545d5c9028bc04c40e38efeb7de674efed3fd`; +- no conflicting `feature/async*` ref existed; +- ADR-0012 did not already exist; +- ADR-0011 was already allocated, therefore ADR-0012 is the next decision number. + +Compare at content head `fb8c1d4e...` showed exactly three substantive paths: + +```text +CHANGELOG.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +No runtime tests were required or executed because this task changes +documentation/control only. + +## Deviations + +The GitHub connector omitted issue numbers from the normalized create response, +causing the initial #958 body to contain `#undefined` as its parent reference. +The returned URLs established the actual issue numbers (#957 and #958), and #958 +was corrected before any repository branch/source mutation. + +No scope deviation remains. + +## Remaining gates + +1. independent exact-head review of the final branch head; +2. CTO approval of the exact ADR content; +3. approval-state reconciliation as required by repository decision process; +4. pull-request creation/update only if separately authorized; +5. independent exact-head review and CTO merge authorization; +6. merge to `develop`; +7. separately specified/authorized implementation tasks. + +No implementation, migration, deployment or activation is authorized by this +report. From 4808d42364a621fc5a86e47cea65d86354df8af8 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 20:15:13 +0100 Subject: [PATCH 05/48] THOTH-ASYNC-01: address ADR-0012 review findings --- ...ed-asynchronous-event-and-job-execution.md | 596 ++++++++++++++---- 1 file changed, 480 insertions(+), 116 deletions(-) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 03c92a9d4..5528ea346 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -7,7 +7,10 @@ Programmes affected: Shared Backend Architecture (owning programme); Publisher S Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (domain executor/consumer); `thoth-pub/infrastructure` (worker runtime and IAM substrate) Parent programme: [THOTH-ASYNC-01 #957](https://github.com/thoth-pub/thoth/issues/957) Authoring task: [THOTH-ASYNC-01-ADR-01 #958](https://github.com/thoth-pub/thoth/issues/958) -Supersedes in part: ADR-0008 sections 3.3-3.5 and the corresponding programme consequences that prohibit a reusable cross-programme job/queue abstraction. ADR-0008's machine-role, least-privilege and SUPERUSER-separation decisions remain in force. +Superseded by: None +Supersedes in part: +- ADR-0008 only where it denies a reusable cross-programme job framework/API: the final shared-framework prohibition in section 3.3, section 3.4's programme-local-only ownership rule, section 5.1 item 6, section 5.2 item 6 and rejected alternative D. ADR-0008 section 3.5 is **satisfied by this ADR, not superseded**. Section 3.3's approved convention list, its `approved primitive != mandatory mechanism` rule, and all machine-role, least-privilege and `SUPERUSER` separation rules remain binding. +- ADR-0010 only where invariant 15 and section 7.2 assume `distribution_job*` remains the long-term Publisher Services execution source. ADR-0010's Staff Operations Console, `ServiceOperation` audit seam, desired/execution/observed-state separation, attention/reconciliation model and staff-command gates remain binding as specified in section 1.3 below. Decision: Thoth establishes one shared PostgreSQL-backed asynchronous event and job engine for cross-programme durable work. Events record durable facts and may @@ -94,6 +97,58 @@ domain code and an isolated runner. That ownership does not move into The shared engine owns durable execution mechanics. The owning domain repository continues to own provider-specific semantics. +### 1.3 Relationship to ADR-0008 and ADR-0010 + +ADR-0012 is the explicit later cross-programme decision that ADR-0008 section +3.5 required before a reusable generic job/queue abstraction could be introduced. + +ADR-0008's seven approved durable-work conventions remain valid engineering +primitives. This ADR selects a shared implementation that uses several of those +primitives because the cross-programme workload now justifies them; it does not +turn every ADR-0008 primitive into a mandatory mechanism for unrelated work. + +ADR-0010 remains the authority for the Staff Operations Console and the +cross-domain operational/audit seam. The relationship is binding: + +```text +async_job / async_job_attempt + canonical execution and attempt truth + +ServiceOperation + canonical staff operational/audit seam where ADR-0010 applies + +domain observed state + separately established external/service truth + +attention / reconciliation projection + whether human or reconciler action is required +``` + +`ServiceOperation` must project from or correlate to the canonical async +execution record; it must not create a second independently mutable attempt +history. Not every internal async job is automatically a Staff Operations +record, but any job important enough for staff to monitor, explain, retry, +replay, reconcile or report follows ADR-0010's canonical operational seam. + +ADR-0010 invariants 14, 16, 22 and 23 and sections 4.6 and 4.11-4.13 remain +binding. In particular, retry, replay and current-state redistribution remain +different actions; machine execution authority remains separate from staff +authority; initial reconciliation remains report-only until separately +activated; and staff external-write controls require their own production gate. + +A `RECONCILIATION_REQUIRED` async job may leave that state only through a +domain reconciler or protected staff action that can establish a safe next +state. The generic engine must never convert it into an ordinary retry merely +because time passed. Staff actions use ADR-0010's protected, audited command +seam with actor and reason. A reconciler may report evidence before write +activation, but may not silently repair external state unless its owning +specification and production activation authorize that action. + +When ADR-0012 reaches approval-state reconciliation, ADR-0008 and ADR-0010 +metadata/register entries must be updated to record these exact partial +supersessions. That is decision-record reconciliation, not runtime +implementation. + --- ## 2. Decision drivers @@ -149,7 +204,7 @@ PostgreSQL is the authoritative owner of: No local file, worker memory, GitHub Actions run, S3 object, message-bus message or external provider is the sole durable owner of that state. -### 3.2 Events and jobs are different +### 3.2 Events, routes and jobs are different An **event** records a durable fact that has happened, for example `WORK_UPDATED`, `FILE_UPLOADED` or `HOSTING_ENABLED`. @@ -165,10 +220,27 @@ Where an event is caused by a PostgreSQL business mutation, the business change and event record are written in the same transaction. Either both commit or neither commits. -Event routing itself must be durable and idempotent. A crash after materializing -some downstream jobs must be able to resume without duplicating already-created -jobs. An event that has completed routing must not silently gain new historical -subscribers merely because a later software version adds a new handler. +Every event has a unique durable `event_id` and a monotonically ordered durable +event sequence suitable for route activation boundaries. Event payload and +causation metadata are immutable after commit. + +Every event consumer has a stable `route_key`. Routing/materialization records +are unique on `(event_id, route_key)` regardless of whether the resulting job +is pending, running or terminal. A routing crash after some consumers have been +materialized therefore resumes from durable route records and cannot create a +second job for an already-materialized route merely because the first job later +completed. + +A route has a durable activation boundary. A route introduced by a later +software release receives only events at or after that boundary. Historical +events are processed by that new route only through an explicitly authorized +backfill/replay job; adding code must never silently subscribe the new route to +all historical events. + +The implementation may represent route completion per consumer or by an +equivalent snapshotted eligible-route set, but it must make partial fan-out +restartable and database-enforced. Event routing correctness must not depend on +in-memory knowledge of which handlers happened to run before a crash. ### 3.3 Versioned contracts @@ -185,40 +257,89 @@ Changes that require old in-flight jobs to remain executable must preserve the required compatibility window or provide an explicit migration/reconciliation plan. -### 3.4 Job lifecycle and claims +### 3.4 Job lifecycle, scheduling and claims The common job lifecycle must support at least the semantic states: ```text PENDING RUNNING -WAITING / RETRY_SCHEDULED +WAITING +RETRY_SCHEDULED SUCCEEDED FAILED CANCELLED RECONCILIATION_REQUIRED ``` -Exact enum spelling is implementation detail. +Exact enum spelling is implementation detail, but `WAITING` and +`RETRY_SCHEDULED` are semantically distinct: waiting means the job has +checkpointed and is waiting on an external/domain condition; retry-scheduled +means a failed attempt is eligible for another execution after `available_at`. -Claims use database-enforced concurrency with leases and unforgeable claim -tokens. `FOR UPDATE SKIP LOCKED` is the default claiming primitive for the -shared queue because this workload explicitly requires multiple consumers to -claim independent ready jobs without blocking each other. +Every job carries first-class scheduling attributes including `priority`, +`available_at`, attempt count/max attempts and the owning kind. Ready work is +ordered by priority and availability within the scheduler's fairness/concurrency +rules; priority must not permit one job kind to starve all other enabled kinds. -A stale worker must not be able to complete or fail a job after its lease/claim -has been superseded. +Each job kind declares a bounded maximum attempt count. Exhaustion is terminal +`FAILED` and creates an attention condition where ADR-0010 applies. The full +legal transition table, including which transitions require a current claim +token, must be fixed by the shared-engine implementation specification and +tested as a database/domain invariant. + +Claims use database-enforced concurrency with leases, unforgeable claim tokens +and `FOR UPDATE SKIP LOCKED`. The shared workload explicitly requires multiple +consumers to claim independent ready jobs without blocking each other. + +Leases are renewable by heartbeat using the **current** claim token. Renewal has +a bounded maximum lease/attempt runtime, may extend only an unexpired current +claim, and can never revive or extend a superseded claim. A stale worker cannot +complete, fail, checkpoint, cancel or renew a job after its claim has been +superseded. + +Every job kind declares whether execution is **read-only/non-effectful** or +**externally effectful**. Every attempt durably records an execution phase at +least equivalent to: + +```text +PRE_WRITE +EFFECT_STARTED +EFFECT_CONFIRMED +``` + +A lease expiry while an attempt is provably `PRE_WRITE` may return the job to +normal retry when its attempt budget permits. A lease expiry for an effectful +attempt at or after `EFFECT_STARTED` transitions to +`RECONCILIATION_REQUIRED`, never directly to `PENDING` or +`RETRY_SCHEDULED`, unless a domain-specific durable checkpoint proves that +resumption cannot repeat the external effect. + +Cancellation invalidates the current claim. Cancelling a pending/pre-write job +may produce terminal `CANCELLED`. Cancelling a running effectful job at or +after `EFFECT_STARTED` cannot assert that no external effect happened and +therefore produces `RECONCILIATION_REQUIRED` unless the provider/domain can +prove a stronger safe terminal outcome. The engine must support horizontal scaling from one worker task to multiple -tasks without changing job semantics. +tasks without changing these semantics. ### 3.5 Idempotency and concurrency -Every externally meaningful job kind defines a deterministic idempotency key. -The database prevents duplicate live work for identities that the owning -handler declares equivalent. +Every logical job has a deterministic idempotency key. Database uniqueness of +that key is **absolute across job states**, not merely "while live": a completed +job does not free its logical effect identity for accidental re-materialization. + +Retries create new attempts on the same job. An intentional replay/current-state +run creates a new logical job with a new idempotency key and an explicit +parent/replay/correlation relationship, following ADR-0010's distinction between +retry, replay and current-state redistribution. -Handlers may additionally define a concurrency key, for example: +Event-derived jobs additionally carry the unique `(event_id, route_key)` +materialization identity from section 3.2. Handler idempotency and route +idempotency solve different problems and neither substitutes for the other. + +Handlers may define a concurrency key, for example: ```text work::algolia @@ -227,10 +348,14 @@ crossref: ``` Jobs with unrelated concurrency keys may execute in parallel. Jobs that would -mutate the same protected external/domain resource must serialize where the -owning specification requires it. +mutate the same protected external/domain resource serialize where the owning +specification requires it. + +The shared scheduler supports per-kind concurrency limits. Provider-specific +rate limits remain owned by handlers, but a single failing or rate-limited job +kind must not consume all worker capacity indefinitely. -### 3.6 Delivery semantics and ambiguous writes +### 3.6 Delivery semantics, checkpoints and ambiguous writes The engine guarantees durable at-least-once execution opportunities. It does **not** claim exactly-once external effects. @@ -238,14 +363,29 @@ The engine guarantees durable at-least-once execution opportunities. It does A handler result distinguishes at least: - success; -- retryable failure before a relevant external write; +- retryable failure proven to occur before a relevant external write; - deterministic permanent failure; -- wait/retry at or after a specified time; +- waiting/checkpointed external work that must be revisited at or after a + specified time; - indeterminate/ambiguous external-write outcome requiring reconciliation. +Before an effectful handler crosses the boundary at which an external write may +have begun, it durably records `EFFECT_STARTED` under its current claim token. +Where the provider returns a submission/correlation identifier, the handler +persists that identifier before yielding `WAITING`. + +A resumed asynchronous-provider job with a durable provider identifier polls or +reconciles the existing provider operation. It does not submit a fresh write +unless the domain contract has established that no previous effect can exist. + An indeterminate provider write is never blindly replayed merely because the -worker did not receive an acknowledgement. The owning handler must reconcile -provider state or require operator attention before another effectful attempt. +worker did not receive an acknowledgement. Lease expiry, process crash or +shutdown after `EFFECT_STARTED` therefore fences the job into +`RECONCILIATION_REQUIRED` unless a durable domain checkpoint proves safe +resumption without duplicate effect. + +The owning handler/reconciler must establish provider state or require protected +operator attention before another effectful attempt. ### 3.7 Attempts and diagnostics @@ -285,13 +425,24 @@ separate task definition/service dedicated worker task role ``` +The existing generic infrastructure `service.yml` is web-service/ALB-oriented +and must not be reused unchanged for the headless worker. The infrastructure +implementation must either provide a bounded headless worker template or +explicitly generalize load-balancer attachment under its own reviewed task. + The worker polls/claims durable PostgreSQL jobs. PostgreSQL `LISTEN/NOTIFY` may be used as a wake-up optimization, but correctness must never depend on receiving a notification; periodic durable polling remains the fallback. -The worker must shut down gracefully: stop claiming, finish or safely abandon -within the lease budget, and leave recoverable durable state. +Graceful shutdown stops new claims first. A pre-write attempt may report a +retryable pre-write outcome or release/yield according to the shared transition +contract. An effectful attempt after `EFFECT_STARTED` must persist a safe +checkpoint and yield `WAITING`, or enter `RECONCILIATION_REQUIRED`; shutdown +must never intentionally abandon it to ordinary lease-expiry retry. + +The runtime must prove clean recovery after abrupt termination as well as +graceful shutdown. ### 3.9 Handler registry and capabilities @@ -310,8 +461,14 @@ scheduling primitives; they do not imply one IAM role per handler. ### 3.10 AWS credentials and task role -The initial `thoth-worker` uses one least-privilege ECS task role containing -the union of its **approved routine AWS capabilities**. +The initial `thoth-worker` uses one dedicated least-privilege ECS task role +containing the union of its **approved routine AWS capabilities**. + +AWS authority for the worker comes **exclusively** from that task role. The +worker must not receive static `AWS_ACCESS_KEY_ID`, +`AWS_SECRET_ACCESS_KEY`, session-token credentials or equivalent long-lived +AWS credentials through environment variables, committed configuration or any +other deployment mechanism. Do not introduce one assumable IAM role per ordinary handler merely to mirror code boundaries when the same process could assume all of them anyway. @@ -321,7 +478,7 @@ The task role may eventually include bounded capabilities such as: - approved Hosting tenant lifecycle; - ACM operations needed by Hosting; - writes to Thoth-controlled Hosting DNS; -- tenant/legacy invalidation where separately approved; +- approved CDN invalidation; - bounded S3 object access required by file-processing handlers. The exact permission matrix remains an implementation/IAM task and must retain @@ -330,6 +487,13 @@ negative tests and resource scoping. Standing permissions that materially increase blast radius and are not required for routine operation remain excluded. +The infrastructure implementation therefore intentionally departs from the +current shared `ECSTaskRole`/static-key pattern for existing Thoth web +services: it must create a dedicated worker task role and ensure no static AWS +keys are injected into the worker. Remediation/rotation of any pre-existing +static AWS credentials used by other services is separate security work and is +not authorized or performed by this ADR. + ### 3.11 No automated legacy-CDN migration The shared worker does not implement legacy CDN migration. @@ -358,8 +522,11 @@ This ADR does not require Secrets Manager. Requirements: -- credentials are never committed to source, infrastructure templates, task - specifications or GitHub records; +- credential **values** are never committed to source, infrastructure templates, + task specifications or GitHub records; +- deployment injects environment values from non-committed environment/provider + configuration or deploy-time parameters; the infrastructure task chooses the + concrete mechanism without changing the env-var application contract; - application logs and durable job diagnostics must never dump the process environment; - handlers fail closed when required credentials are absent; @@ -370,6 +537,9 @@ Requirements: Environment variables are an operational delivery mechanism, not a security isolation boundary between handlers in the same process. +This section does not apply to AWS credentials for `thoth-worker`; section +3.10 requires ECS task-role credentials exclusively. + ### 3.13 Domain ownership and cross-repository executors The shared engine standardizes **durability and execution protocol**, not every @@ -384,17 +554,27 @@ In particular, `thoth-dissemination` continues to own dissemination/provider logic and may claim only dissemination job kinds allowed by its own authorization matrix. -This does not require a second permanently running generic queue engine. -A downstream domain executor may be persistent, scheduled or on-demand as its -own approved implementation specifies. It must not create a second canonical job -database or lifecycle. +This does not create a second canonical queue. A downstream domain executor may +be persistent, scheduled or on-demand as its own approved implementation +specifies, but all authoritative job lifecycle state remains in `thoth`. + +For external/domain executors the shared claim/report protocol is a protected, +versioned, kind-scoped Thoth API contract. Its implementation may be GraphQL in +the current architecture, but downstream repositories must pin the exact merged +contract and must not guess an unmerged schema. + +The protocol must allow a domain executor to: -The shared protocol must therefore allow a domain executor to: +- claim only explicitly authorized job kinds; +- receive a versioned payload and claim/lease identity; +- heartbeat/checkpoint only the current claim where its kind permits it; +- report success/failure/waiting/reconciliation outcomes with the current claim + token; +- leave all durable lifecycle and attempt state in `thoth`. -- claim only authorized job kinds; -- receive a versioned payload; -- report success/failure/indeterminate outcomes with the current claim token; -- leave all durable lifecycle state in `thoth`. +`thoth-dissemination` may continue to use a scheduled executor/runtime if that +is operationally appropriate; scheduling frequency is not job durability and +does not make GitHub Actions or another runner the system of record. ### 3.14 Application authorization @@ -413,32 +593,151 @@ to perform internal domain operations merely to impersonate a machine role; internal authorization/execution boundaries must instead be specified explicitly by the owning implementation. ---- +### 3.15 Operability, fairness and operator controls -## 4. BE-04 supersession +The shared engine must expose durable/queryable operational signals at least by +job kind: -Once ADR-0012 is approved and repository-authoritative, the shared async engine -supersedes ADR-0008's decision that BE-04 durable job machinery is the only -permitted programme-local implementation and that no shared framework exists. +- ready queue depth; +- age of the oldest ready job; +- running jobs beyond their expected lease/runtime; +- retry-scheduled count; +- `RECONCILIATION_REQUIRED` count and age; +- terminal failure count/rate; +- execution latency/attempt distribution; +- worker liveness and last successful claim/heartbeat activity. + +Each job kind defines bounded attempts, backoff and concurrency limits. Backoff +must prevent tight retry loops; exact algorithms and jitter are implementation +details. The scheduler must provide bounded fairness so one enabled kind cannot +indefinitely starve another. Provider-specific rate limiting remains the owning +handler's responsibility on top of engine-level concurrency limits. + +A poison job terminates as `FAILED` or `RECONCILIATION_REQUIRED` according to +whether an external effect may be ambiguous; it is surfaced as attention rather +than retried forever. The architecture does not require a second dead-letter +queue. -It does **not** supersede ADR-0008's machine-role and least-privilege rules. +Operator controls such as cancel, safe manual retry, replay and mark/reconcile +outcome are protected, audited operations through ADR-0010's staff operational +seam where staff control is applicable. They record authenticated actor, reason +and correlation to the underlying async job. Direct production SQL is not the +normal operator interface. -BE-04/DIS-02 must be re-specified before deployment: +Manual retry is allowed only from a state whose domain contract proves replay is +safe. `RECONCILIATION_REQUIRED` cannot be converted to ordinary retry by a +generic "retry" button. -- `distribution_job*` is not activated as a parallel production queue; -- the existing production tables are treated as unused legacy schema; -- a new migration drops those tables only after a fresh production preflight - proves they contain no operational data; -- shared async tables are introduced through a new migration; -- Publisher Services job semantics are mapped onto generic jobs; -- `thoth-dissemination` consumes only its authorized generic job kinds; -- existing BE-04/DIS-02 implementation remains historical evidence and may be - reused only where a new bounded specification explicitly adopts it. +Job and attempt history is immutable while retained. Retention/archival/deletion +policy requires a separately reviewed specification; the initial engine must not +silently delete historical attempts. + +Coalescing/debouncing may be defined by individual job kinds, but it must be +durable and auditable: it may reduce executable jobs only under an explicit +domain rule and must retain enough correlation to explain which events or +requests were represented by the surviving job. -The already-applied v1.7.0 migration remains immutable. --- +## 4. BE-04 supersession and compatibility + +Once ADR-0012 is approved and repository-authoritative, the shared async engine +supersedes the ADR-0008/ADR-0010 assumptions identified in the header and +section 1.3. It does **not** supersede ADR-0008's machine-role/least-privilege +rules or ADR-0010's staff operational/audit architecture. + +BE-04 is already part of released Thoth code. The replacement therefore +distinguishes the **storage/execution model** from the **released API +compatibility surface**. + +### 4.1 Storage and domain replacement + +The next separately authorized shared-engine implementation must: + +- introduce the generic async schema through a new forward migration; +- remove the unused `distribution_job`, `distribution_job_target` and + `distribution_job_attempt` tables and their dedicated indexes; +- remove the four BE-04-only enum types + `distribution_job_kind`, `distribution_job_status`, + `distribution_job_attempt_result` and + `distribution_job_cancellation_reason`; +- atomically reconcile `thoth-api/src/schema.rs`, persistence/domain models, + CRUD/query code and tests as required by ADR-0003 and repository doctrine; +- **retain** `distribution_platform`, which remains shared Publisher Services + domain state and is not a queue-only type. + +The already-applied `20260814_v1.7.0` migration remains immutable. + +### 4.2 Released BE-04 GraphQL compatibility window + +The released BE-04 GraphQL lifecycle contract is not removed at the same moment +as the legacy tables. + +During a bounded compatibility window, the existing released +claim/complete/fail/cancel job operations and their GraphQL types remain +available as a **deprecated compatibility facade** backed by the generic async +engine. Any existing BE-04 automatic job-creation path that remains exposed must +either create the equivalent generic job under the same default-OFF activation +semantics or be explicitly disabled through its separately approved deprecation +path; it must never continue writing removed legacy tables. + +`thoth-dissemination` then migrates to the new protected generic, +kind-scoped claim/report contract under its own repository-local task. + +Only after all downstream consumers are verified on the generic contract may a +later separately approved API-deprecation task remove the legacy BE-04 GraphQL +fields/types/toggle and update generated SDL/client surfaces. That removal must +follow the repository's approved deprecation-path rule; downstream repositories +must not be forced to guess a contract that has not merged. + +### 4.3 Fresh preflight before removing legacy storage + +The future migration/deployment task must verify immediately before production +storage removal: + +1. all three legacy tables exist as expected; +2. all three contain zero rows and no operational history; +3. the deployed API's BE-04 creation toggle is OFF or absent; +4. the DIS-02 worker activation variable is not ON and no executor is actively + claiming the legacy contract; +5. there is no current `DISSEMINATION_WORKER` legacy-job activity; +6. the compatibility facade has been changed to the generic storage before any + request can reach dropped relations. + +If any of those checks fail, the DROP is HOLD and data/consumer migration must +be designed explicitly. + +### 4.4 Preserve proven BE-04 safety work + +Superseding BE-04 storage does not discard its safety evidence. The generic +engine and dissemination adapter must preserve or improve the already-proven +principles: + +- database claim fencing and stale-token refusal; +- absolute logical deduplication; +- bounded attempts; +- conservative classification of post-write ambiguity; +- no automatic replay of an abandoned predecessor when a provider write may + already have happened; +- bounded/sanitized durable diagnostics. + +Existing BE-04/DIS-02 source remains historical implementation evidence and may +be reused only where the new bounded specifications explicitly adopt and retest +it against the generic contract. + +### 4.5 Development and fresh-database migration path + +Because repository migrations execute in order, an environment currently before +v1.7.0, including dev as recorded at this decision, will apply v1.7.0 and then +the later replacement migration when brought fully current. A fresh database +does the same. + +The replacement migration and test plan must therefore prove both the +production-like upgrade path from a schema where v1.7.0 is already present and +the full empty-database migration chain. It must not rely on dev and production +having identical starting versions by accident. + ## 5. Initial job families These are architectural examples/initial consumers, not implementation @@ -511,91 +810,140 @@ execution until a separate approved architecture decision says otherwise. 1. PostgreSQL remains canonical for event/job lifecycle. 2. Business mutation plus caused event is atomic where both are PostgreSQL-owned. -3. Routing and job creation are idempotent. -4. No stale claim can finalize a superseded lease. -5. No job handler may claim exactly-once external effects. -6. Indeterminate writes require reconciliation before unsafe replay. -7. Domain ownership is explicit and preserved. -8. Generic execution does not create generic application authorization. -9. Historical migrations are never rewritten after deployment. -10. Credentials are not committed or emitted in logs/durable diagnostics. -11. A worker may execute only supported payload versions and configured +3. Every event has durable identity and ordering; every `(event_id, route_key)` + materialization is database-unique irrespective of job terminal state. +4. A newly registered route does not consume historical events without an + explicit backfill/replay. +5. Job idempotency identity is absolute across states; retries are attempts on + the same logical job. +6. No stale/superseded claim can finalize, checkpoint, cancel or renew a job. +7. Effectful attempts durably fence `EFFECT_STARTED` before crossing the + external-write boundary. +8. Lease expiry/shutdown after an ambiguous effect cannot return the job to + ordinary retry; it requires reconciliation unless a durable checkpoint proves + safe resume. +9. No job handler may claim exactly-once external effects. +10. Indeterminate writes require reconciliation before unsafe replay. +11. Priority, delayed availability, bounded attempts, per-kind concurrency and + fairness are first-class shared-engine concerns. +12. Cancellation never asserts that an already-started external effect did not + happen. +13. `async_job`/`async_job_attempt` are canonical execution truth; + `ServiceOperation` is the ADR-0010 staff operational/audit seam where + applicable, not a second attempt ledger. +14. Domain ownership is explicit and preserved. +15. Generic execution does not create generic application authorization. +16. Historical migrations are never rewritten after deployment. +17. Worker AWS authority comes exclusively from its dedicated ECS task role; no + static AWS access keys are injected into the worker. +18. Non-AWS credential values are not committed or emitted in logs/durable + diagnostics. +19. A worker may execute only supported payload versions and configured capabilities. -12. Merge, deployment, job activation and provider access remain separate gates. - ---- +20. Protected operator retry/reconcile controls are audited and may not bypass + reconciliation safety. +21. Merge, migration, deployment, handler activation, provider access and + production activation remain separate gates. ## 8. Implementation impact and decomposition Approval of this ADR should lead to separate bounded tasks, at minimum: -1. **Shared schema/engine in `thoth`** - - replacement migration; - - event/job/attempt domain types; - - claim/lease/retry/reconciliation primitives; +1. **Shared schema/engine and BE-04 compatibility facade in `thoth`** + - forward replacement migration; + - event/route/job/attempt domain types; + - claim/renewal/lease/retry/reconciliation primitives; - event routing/materialization; + - execution-phase checkpoints; + - deprecated BE-04 GraphQL facade backed by generic jobs; + - atomic Diesel/model/query/generated-contract reconciliation; - tests including real PostgreSQL concurrency. 2. **Default `thoth-worker` runtime in `thoth`** - worker command; - handler registry; - - graceful shutdown; - - metrics/health/operational reporting. + - graceful shutdown/checkpointing; + - metrics/health/operational reporting; + - capability/configuration filtering. 3. **Infrastructure** - - headless Fargate worker service; - - task role and networking; - - environment-variable configuration; + - headless Fargate worker service not bound to the ALB; + - dedicated worker task role; + - explicit prohibition of static AWS credentials for the worker; + - non-committed deploy-time injection of external env-var credential values; + - environment/network configuration; - no deployment/activation until separately authorized. 4. **Publisher Services / `thoth-dissemination`** - - replace BE-04 job protocol with generic job-family consumption; + - migrate from the deprecated BE-04 facade to generic kind-scoped job + consumption; - preserve dissemination domain logic; - - independently review external-write/idempotency semantics. + - independently review external-write/idempotency/reconciliation semantics. + +5. **BE-04 public-contract retirement** + - after downstream migration, remove deprecated legacy GraphQL lifecycle + fields/types/toggle under a separately approved deprecation task; + - regenerate/update affected client SDL/types and verify all downstream + consumers. -5. **Hosting** +6. **Hosting** - adopt generic jobs for greenfield provisioning/reconciliation; - no automated legacy CDN migration. +7. **Existing AWS static-credential security debt** + - inspect and remediate any existing service static AWS credential pattern + under a separate security task and explicit provider/write authorization; + - this is not silently folded into worker deployment. + +Before ADR-0012 becomes repository-authoritative, its approval-state +reconciliation must also update ADR-0008 and ADR-0010 decision metadata/register +entries to record the exact partial supersessions selected here. That is +documentation/control reconciliation, not implementation authorization. + Each repository receives its own branch/PR and independent review. No downstream repository guesses an unmerged upstream contract. ---- - ## 9. Migration and rollout Architecture approval does not authorize migration execution. -The future database rollout must be additive/safe until the exact replacement -migration is independently approved. Before any production DROP of -`distribution_job*`: +The future database rollout must remain safe across both the production upgrade +path and the full migration chain. Before any production DROP of +`distribution_job*`, section 4.3's fresh preflight is mandatory. + +The production replacement change must ensure no deployed code path can access +legacy relations after they are dropped. The deprecated GraphQL compatibility +facade must already target generic storage in the same bounded source/migration +release, and generated/client compatibility must be proven. -1. verify the production tables exist from v1.7.0; -2. verify they contain no operational data; -3. verify no deployed process is reading/writing them; -4. verify the shared replacement schema has the required rollback/recovery plan; -5. receive explicit migration/deployment authorization. +The development environment's currently unapplied v1.7.0 state is handled by +ordinary ordered migrations: v1.7.0 applies first, followed by the replacement. +The test matrix must include: -The development environment's unapplied v1.7.0 state must be handled explicitly -by the migration specification; it must not rely on dev matching production by -accident. +- production-like schema with v1.7.0 already applied; +- pre-v1.7 upgrade applying v1.7.0 plus replacement; +- full empty-database migration chain; +- forward repair/rollback strategy once generic async data exists. Initial worker deployment is inert until job creation/activation is separately authorized. Desired count starts at one unless deployment evidence justifies otherwise. -Rollout should prove: - -- schema and claim concurrency; -- crash/lease recovery; -- idempotent routing; -- one or more non-destructive representative handlers; -- one external-write handler with reconciliation evidence; -- observability and stuck-job procedures; +Rollout should prove in order: -before broad adoption. +1. schema/event-route/job concurrency without external writes; +2. crash/lease renewal/expiry recovery; +3. idempotent partial fan-out recovery; +4. priority/fairness/per-kind concurrency and bounded retry behaviour; +5. one or more non-destructive representative handlers; +6. one effectful handler with durable `EFFECT_STARTED`, provider checkpoint and + reconciliation evidence; +7. protected operator controls and operational signals; +8. only then broader handler activation. ---- +Handler activation is independent: enabling Hosting must not automatically +enable Crossref, Algolia, file processing or another family merely because they +share the worker. ## 10. Rollback @@ -619,28 +967,44 @@ Do not treat disabling the worker as equivalent to deleting durable jobs. Before shared implementation can be approved, evidence must include: - real PostgreSQL concurrent claim tests with multiple workers; -- stale-token/expired-lease refusal; -- crash between claim and completion; -- crash during event fan-out/materialization; -- deterministic deduplication/idempotency tests; +- current-token lease renewal and refusal to renew superseded/expired claims; +- stale-token refusal for completion, failure, cancellation and checkpointing; +- crash immediately after claim; +- crash during event fan-out/materialization with exact resume and no duplicate + `(event_id, route_key)` job; +- proof that newly registered routes do not consume historical events absent an + explicit backfill; +- crash before and after `EFFECT_STARTED`; +- lease expiry after `EFFECT_STARTED` producing reconciliation rather than + ordinary retry; +- provider-correlation checkpoint followed by WAITING/resume that polls rather + than resubmits; +- deterministic absolute job idempotency tests; - concurrency-key serialization tests; -- bounded retry/backoff tests; -- INDETERMINATE external-write reconciliation tests; +- priority, `available_at`, bounded attempt and scheduler fairness tests; +- retry/backoff and poison-job exhaustion tests; +- cancellation before write and cancellation after effect-start tests; +- `INDETERMINATE` external-write reconciliation tests; - unsupported payload-version refusal; -- graceful shutdown/restart recovery; +- graceful shutdown and abrupt restart recovery; - worker capability filtering; - authorization negative tests for every external/domain executor; +- protected/audited operator cancel/retry/reconcile tests; +- queryable queue-depth, oldest-ready-age, expired-lease, + `RECONCILIATION_REQUIRED`, failure and worker-liveness signals; - proof that logs and durable diagnostics do not contain configured credentials; +- proof that the worker receives no static AWS access-key credentials; - migration tests from a schema containing the production v1.7.0 `distribution_job*` relations; +- full empty-database migration-chain tests; +- compatibility tests showing released BE-04 GraphQL operations no longer + depend on legacy tables during the compatibility window; - deployment tests with more than one worker task even if production initially runs one. Provider-specific handlers owe their own acceptance evidence in addition to the shared-engine tests. ---- - ## 12. Approval and authority This ADR is currently **PROPOSED**. From aa08cd38531f17ce98c1a44fdcfe0b1b258872e1 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 20:15:47 +0100 Subject: [PATCH 06/48] THOTH-ASYNC-01: reconcile ADR-0012 proposal record --- docs/engineering/decisions/decision-register.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 36a749676..3c73b9fc0 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,8 +17,7 @@ Last updated: 2026-09-29 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | - -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed cross-programme event/job engine, a default headless `thoth-worker` runtime, shared lease/idempotency/reconciliation semantics, domain-owned executors, one routine worker AWS task role, environment-variable external credentials, and no automated legacy-CDN migration. It would supersede ADR-0008 only where ADR-0008 prohibits a reusable cross-programme job framework; ADR-0008 machine-role/least-privilege rules remain. BE-04/DIS-02 deployment and Hosting implementation depending on this architecture remain HOLD. Requires independent review and exact CTO approval before implementation may rely on it. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed event/route/job engine, crash-safe route materialization, claim-token-fenced renewable leases, durable effect checkpoints/reconciliation, a default headless `thoth-worker`, domain-owned external executors, one dedicated routine worker AWS task role, deployment env vars for non-AWS credentials, and no automated legacy-CDN migration. It satisfies ADR-0008's explicit gate for a later shared abstraction and would supersede only ADR-0008's no-shared-framework/programme-local-only statements while retaining its durable-work conventions and machine-role rules. It would also partially supersede ADR-0010 only where that decision assumes `distribution_job*` remains the long-term distribution execution source; `ServiceOperation`, desired/execution/observed-state separation, attention/reconciliation and staff-command gates remain binding. Released BE-04 GraphQL operations receive a deprecated compatibility facade over generic jobs before later removal through a separate deprecation task. BE-04/DIS-02 activation and Hosting implementation depending on this architecture remain HOLD. Requires fresh independent exact-head review and exact CTO approval before implementation may rely on it. | ## Merge and implementation rules From f06ba7ffdf351e8f0829a6eb0e55dd8256bccc2b Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 20:15:53 +0100 Subject: [PATCH 07/48] THOTH-ASYNC-01: update ADR-0012 proposal summary --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7cbb064d..9bf7b056b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal separates durable events from executable jobs, requires transactional event creation where coupled to PostgreSQL business writes, and standardizes versioned payloads, idempotent fan-out, leases/claim tokens, `FOR UPDATE SKIP LOCKED` claiming, attempt history, concurrency keys, bounded retries and explicit `RECONCILIATION_REQUIRED` handling for ambiguous external writes. It selects one headless `thoth-worker` Fargate service as the default always-on executor for Thoth-owned handlers, while preserving domain-owned executors such as `thoth-dissemination`; uses one least-privilege routine worker AWS task role rather than per-handler roles; keeps ADR-0008's prohibition on a generic catch-all ZITADEL machine role; uses deployment environment variables for external-service credentials; and excludes automated legacy-CDN migration. Because production has the unused v1.7.0 `distribution_job*` schema, a future separately authorized migration must remove it and introduce the shared async schema without rewriting historical v1.7.0. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal separates durable events from executable jobs; makes event-route materialization database-unique and restartable; standardizes versioned payloads, absolute job idempotency, priority/availability, bounded attempts, concurrency/fairness, claim-token-fenced renewable leases and durable effect-phase checkpoints; and requires ambiguous post-write lease loss/cancellation to enter reconciliation rather than ordinary retry. It selects one headless `thoth-worker` Fargate service for Thoth-owned handlers while preserving domain-owned executors such as `thoth-dissemination`; gives the worker one dedicated routine AWS task role with no static AWS keys; uses deployment environment variables for non-AWS credentials with values injected from non-committed deployment configuration; and excludes automated legacy-CDN migration. It explicitly reconciles ADR-0008 and ADR-0010 boundaries, keeps `async_job`/`async_job_attempt` as canonical execution truth while `ServiceOperation` remains the staff audit/command seam, and gives the released BE-04 GraphQL contract a deprecated compatibility facade over generic jobs before a later separate removal. A future forward migration must remove the unused production `distribution_job*` storage only after a fresh zero-use preflight, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** - `CTRL-BRANCH-RELEASE-01`: preserve each managed repository's established release branch name instead of normalizing every repository to `master` (issue [897](https://github.com/thoth-pub/thoth/issues/897), parent control programme [818](https://github.com/thoth-pub/thoth/issues/818)). Adds [`ADR-0011`](docs/engineering/decisions/ADR-0011-preserve-established-release-branch-names.md), which standardizes the **role** of the release branch, written ``, rather than whether it is spelled `main` or `master`. The shared conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `, with each repository's `` resolved from its own verified live state: `main` repositories remain on `main`, `master` repositories remain on `master`, and neither name is created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` release flow is unchanged, and [`ADR-0009`](docs/engineering/decisions/ADR-0009-programme-integration-branch-namespace.md) is unchanged in substance. Release-branch conversion is removed from `BR-APP-01`, `BR-DIS-01`, `BR-SPHINX-01`, `BR-DASH-01`, `BR-WIDGET-01` and `BR-LIC-01`, and Vercel production for `metrics-dashboard` is no longer moved from `main` for branch spelling; the independently justified development-branch, protection, CI, provider and publication readiness work in each of those tasks is retained, CG-03 and CG-04 remain **OPEN**, and no task's risk is lowered. Reconciles `branch-topology.md`, `control-gaps.md`, the affected repository-map entries and the Metrics task tracker. **Documentation/control only: no established release, default or development-topology branch is created, deleted, renamed or moved by `ADR-0011` in any managed repository; no repository setting, default branch, protection, ruleset, CI workflow, provider or deployment configuration is changed; and there is no runtime, schema, migration, GraphQL/API, auth, contract, generated client, publication or production effect.** - `ADM-ADR-01-CLOSEOUT-01`: reconcile [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md)'s durable repository approval metadata with the independent review, CTO approval and merge that already completed under `ADM-ADR-01` (issue [852](https://github.com/thoth-pub/thoth/issues/852), parent programme issue [846](https://github.com/thoth-pub/thoth/issues/846)). The ADR header and its section 17 now record `Status: APPROVED`, `Approved by: Javi, CTO` and `Approval date: 2026-08-28`, the proposal-state sentence becomes approved-state wording, and the ADR-0010 decision-register row records its approval blocker as satisfied. The approved architecture is unchanged: invariants, `ServiceOperation` semantics, ADR-0002 platform-domain separation, Distribution/Publisher Services boundaries, programme ordering and rollout semantics are untouched, as are the existing authority condition and the historical `ADM-ADR-01` verification base. **Documentation/control authority-state reconciliation only: no runtime, schema, migration, GraphQL/API, contract, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** - `ADM-ADR-01`: propose [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md), the Staff Operations Console architecture (issue [846](https://github.com/thoth-pub/thoth/issues/846)). The proposal reserves `/admin/*` for the superuser global control plane, keeps one publisher workspace entered through an explicit publisher operating context rather than identity impersonation, and makes Thoth the future canonical owner of staff-facing publisher-service operational records while separating desired state, execution, observed external state and attention/reconciliation. It defines `ServiceOperation` only as a common operational/audit seam — not a generic job/queue framework — preserves ADR-0002 platform-domain separation and ADR-0008 programme-local `distribution_job*`, and selects distribution as the first read-only operational integration before any replay or external-write activation. **Architecture/control proposal only: no runtime, schema, migration, GraphQL/API, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** From 9d329e063b2731b691c62a412555b470f2a9fe39 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 29 Sep 2026 20:16:49 +0100 Subject: [PATCH 08/48] THOTH-ASYNC-01: record ADR-0012 correction round --- ...H-ASYNC-01-ADR-01-implementation-report.md | 271 +++++++++++++----- 1 file changed, 205 insertions(+), 66 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index d13272e8b..baae37de6 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -1,6 +1,9 @@ # THOTH-ASYNC-01-ADR-01 implementation report -Status: AUTHORING COMPLETE - AWAITING INDEPENDENT REVIEW +This report records the bounded ADR-0012 authoring task and its first +independent-review correction round. Review/approval state itself remains in +GitHub under issues #957 and #958 rather than being copied as transient +repository status. ## Identity @@ -17,69 +20,203 @@ Task branch: feature/async/adr-0012 PR target: develop ``` -## Implemented scope +## Authorized write footprint -Authored the proposed shared asynchronous event/job architecture and only its -required repository records. +Exactly four repository paths are used: -Content head before this report-only commit: +1. `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md`; +2. `docs/engineering/decisions/decision-register.md`; +3. `CHANGELOG.md`; +4. this implementation report. + +No other source, migration, GraphQL, workflow or infrastructure path is changed. + +## Initial candidate + +The first complete proposal was handed to independent review at: + +```text +a69921682cd02e060110304f8f9868d0c39da65e +``` + +It was based directly on: + +```text +develop @ 923545d5c9028bc04c40e38efeb7de674efed3fd +``` + +and contained exactly the four authorized paths. + +## Independent review and correction round + +The independent CRITICAL review of +`a69921682cd02e060110304f8f9868d0c39da65e` returned +`CHANGES REQUIRED`. Durable control receipt is issue #958 comment +`5896883454`. + +All nine blocking findings were accepted and corrected within the existing +four-path documentation budget: + +### F-01 - ADR-0008 supersession + +The ADR now identifies the exact ADR-0008 statements/items displaced by the new +shared framework, states that ADR-0008 section 3.5 is satisfied rather than +superseded, and preserves the approved durable-work convention list, +`approved != mandatory` rule and machine-role/least-privilege boundaries. + +Approval-state reconciliation of ADR-0008 metadata/register is explicitly a +later decision-record action, not runtime implementation. + +### F-02 - ADR-0010 relationship + +The ADR now explicitly partially supersedes only ADR-0010's assumption that +`distribution_job*` remains the long-term execution source. + +It defines: ```text -fb8c1d4e9373153d367e2336b6a4dc349f165fc2 +async_job / async_job_attempt + canonical execution truth + +ServiceOperation + staff operational/audit seam where ADR-0010 applies + +observed state + separate domain truth + +attention / reconciliation + separate operational projection ``` -The final branch head including this report is recorded in the owning GitHub -issue and is the SHA to which independent review must bind. +`ServiceOperation` is not a second mutable attempt ledger. ADR-0010 staff +command, reconciliation and activation gates remain binding. + +### F-03 - event route identity + +The corrected proposal gives every event durable identity/order and every +consumer a stable `route_key`. + +Materialization is database-unique on `(event_id, route_key)` regardless of +job terminal state. Route activation has a durable boundary so later code does +not silently subscribe to historical events; historical processing requires an +explicit backfill/replay. -## Files changed before this report +### F-04 - effectful lease expiry -1. `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - new PROPOSED ADR. -2. `docs/engineering/decisions/decision-register.md` - registers ADR-0012 as PROPOSED and updates the register date. -3. `CHANGELOG.md` - records the architecture proposal under Unreleased/Added. +The proposal now distinguishes effectful jobs and requires durable attempt phases +equivalent to `PRE_WRITE`, `EFFECT_STARTED` and `EFFECT_CONFIRMED`. -This report is the fourth and final path in the authorized write budget. +Leases are renewable using the current claim token only; renewal cannot revive a +superseded claim. -## Architecture recorded +Lease expiry, crash, cancellation or shutdown at/after `EFFECT_STARTED` enters +`RECONCILIATION_REQUIRED` unless a durable domain checkpoint proves safe +resume. Provider identifiers must be persisted before yielding asynchronous +work, and resumed work polls/reconciles rather than blindly resubmitting. -The ADR proposes: +### F-05 - scheduling, bounded attempts and cancellation -- one PostgreSQL-backed cross-programme async event/job engine; +Priority, `available_at`, bounded attempts, distinct WAITING versus +RETRY_SCHEDULED semantics, per-kind concurrency/fairness and cancellation +semantics are now first-class architecture. + +Cancellation after a possible external effect does not assert that the effect +did not happen. + +### F-06 - released BE-04 contract + +The proposal now separates BE-04 storage replacement from released API +compatibility. + +The future forward migration removes the three unused `distribution_job*` +tables, dedicated indexes and four queue-only enum types while retaining +`distribution_platform` and reconciling Diesel/domain code. + +The released BE-04 GraphQL lifecycle API receives a bounded deprecated +compatibility facade backed by generic jobs. `thoth-dissemination` migrates to +the generic contract first; later API removal is a separate approved deprecation +task. + +Fresh production preflight must prove zero rows, disabled legacy creation, +inactive legacy executor and no live legacy contract activity before storage +DROP. + +### F-07 - worker AWS credential model + +The worker's AWS authority is exclusively its dedicated ECS task role. + +Static AWS access keys are prohibited for the worker regardless of deployment +mechanism. The infrastructure task must create a headless service with a +dedicated task role rather than silently inheriting the existing shared-role, +web/ALB-oriented service pattern. + +Non-AWS credential values remain deployment environment variables but must be +injected from non-committed deployment configuration. + +Existing service static-AWS-credential remediation is identified as separate +security work and is not performed by this task. + +### F-08 - operability + +The ADR now requires queryable queue/worker signals, per-kind bounded +attempts/concurrency/backoff, starvation protection, poison-job attention, +protected audited operator controls, immutable retained attempt history and +separately governed retention. + +Generic retry cannot move `RECONCILIATION_REQUIRED` back to an effectful +attempt without domain proof of safety. + +### F-09 - durable documentation hygiene + +The ADR-0012 decision-register row is inside the Markdown decision table. + +This report no longer carries a transient `Status: ... awaiting review` line. + +## Architecture currently recorded + +The corrected proposal includes: + +- one PostgreSQL-backed cross-programme event/route/job engine; - transactional event creation where coupled to PostgreSQL business writes; -- durable idempotent event fan-out; -- typed/versioned event and job payloads; -- shared claim/lease/claim-token/attempt/retry/reconciliation semantics; -- at-least-once execution opportunities, not exactly-once external effects; -- explicit reconciliation for indeterminate external writes; -- concurrency keys and deterministic idempotency; +- durable crash-safe route materialization; +- typed/versioned event and job contracts; +- absolute job idempotency plus separate route idempotency; +- priority, delayed availability, bounded attempts and concurrency/fairness; +- claim-token-fenced renewable leases; +- durable effect checkpoints and reconciliation-required handling; +- append-only sanitized attempt evidence; - one default headless `thoth-worker` Fargate runtime for Thoth-owned handlers; -- domain-owned executors for cross-repository logic such as `thoth-dissemination`; -- one routine worker AWS task role rather than per-handler IAM roles; -- no generic catch-all ZITADEL machine role; -- external-service credentials supplied through deployment environment variables; +- domain-owned external executors such as `thoth-dissemination`; +- one dedicated routine worker AWS task role, with no static AWS worker keys; +- non-AWS credentials supplied as environment variables from non-committed + deployment configuration; - no automated legacy-CDN migration; -- BE-04/DIS-02 pre-activation supersession onto the shared engine. +- explicit ADR-0008 and ADR-0010 partial-supersession boundaries; +- a BE-04 GraphQL compatibility window over generic storage; +- ADR-level observability and protected operator-control requirements. -## BE-04 migration/data effect +## Migration/data effect No migration was created, modified or executed. -The ADR records the CTO-provided environment fact that v1.7.0 has been applied -to production and its `distribution_job*` tables have not been operationally -used, while dev has not applied that migration. +The proposal records the CTO-provided environment fact that v1.7.0 has been +applied to production and its `distribution_job*` tables have not been +operationally used, while dev has not applied that migration. + +Historical `20260814_v1.7.0` remains immutable. -The proposal requires a future, separately authorized migration to remove the -unused legacy tables and introduce the shared async schema. Historical -`20260814_v1.7.0` remains immutable. +Any future storage removal requires a separately authorized forward migration +and fresh zero-use production preflight. ## Authorization/security effect No authorization implementation changed. -ADR-0008's domain-specific machine-role and least-privilege rules remain in -force. The proposal does not create a generic ZITADEL worker/service role. +ADR-0008's domain-specific machine-role, least-privilege and `SUPERUSER` +separation rules remain in force. No IAM role, policy, credential, provider configuration or environment variable -was created or changed. +was created, rotated or changed. ## External/runtime effects @@ -93,48 +230,50 @@ release/publication: 0 production activation: 0 ``` -Repository/GitHub mutations were limited to the authorized issues, branch and -four-path documentation write budget. +Repository/GitHub mutations remain bounded to the task ledger, existing task +branch and authorized documentation footprint. -## Validation +## Validation and evidence -Live preflight established: +Initial branch preflight established: - `develop` exactly matched authorized base `923545d5c9028bc04c40e38efeb7de674efed3fd`; - no conflicting `feature/async*` ref existed; - ADR-0012 did not already exist; -- ADR-0011 was already allocated, therefore ADR-0012 is the next decision number. +- ADR-0011 was already allocated. -Compare at content head `fb8c1d4e...` showed exactly three substantive paths: +The first independent review independently verified the original candidate's +exact four-path footprint and exact head before returning `CHANGES REQUIRED`. -```text -CHANGELOG.md -docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md -docs/engineering/decisions/decision-register.md -``` +The corrected final branch head is recorded in issue #958 after this report +commit and is the SHA to which the next independent review must bind. + +No runtime tests are claimed by this documentation-only task. + +## Deviation history -No runtime tests were required or executed because this task changes -documentation/control only. +During initial issue creation, the GitHub connector omitted issue numbers from +the normalized create response, causing #958 initially to contain +`#undefined` as its parent reference. The returned issue URLs established +#957/#958 and the ledger was corrected before branch/source mutation. -## Deviations +No source-scope deviation remains. -The GitHub connector omitted issue numbers from the normalized create response, -causing the initial #958 body to contain `#undefined` as its parent reference. -The returned URLs established the actual issue numbers (#957 and #958), and #958 -was corrected before any repository branch/source mutation. +## Remaining lifecycle gates -No scope deviation remains. +The repository files themselves do not assert completion of these gates. -## Remaining gates +The task still requires, in order: -1. independent exact-head review of the final branch head; -2. CTO approval of the exact ADR content; -3. approval-state reconciliation as required by repository decision process; -4. pull-request creation/update only if separately authorized; -5. independent exact-head review and CTO merge authorization; -6. merge to `develop`; -7. separately specified/authorized implementation tasks. +1. fresh independent CRITICAL exact-head review of the corrected final head; +2. exact-content CTO architecture approval if that review returns APPROVED; +3. approval-state decision-record reconciliation, including ADR-0008/ADR-0010 + supersession metadata; +4. PR creation only under separate authorization; +5. required CI and independent exact-head source/document review; +6. explicit CTO merge authorization for the CRITICAL task; +7. merge into `develop`; +8. separately specified and authorized implementation/migration/deployment tasks. -No implementation, migration, deployment or activation is authorized by this -report. +This report authorizes none of those later actions. From 28658c6672181a41c03e7b0d974ec4c0ebe41ac0 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:10:45 +0100 Subject: [PATCH 09/48] THOTH-ASYNC-01: address round-2 architecture findings --- ...ed-asynchronous-event-and-job-execution.md | 868 ++++++++++++------ 1 file changed, 587 insertions(+), 281 deletions(-) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 5528ea346..d58124463 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -4,7 +4,7 @@ Status: PROPOSED Date: 2026-09-29 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work -Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (domain executor/consumer); `thoth-pub/infrastructure` (worker runtime and IAM substrate) +Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) Parent programme: [THOTH-ASYNC-01 #957](https://github.com/thoth-pub/thoth/issues/957) Authoring task: [THOTH-ASYNC-01-ADR-01 #958](https://github.com/thoth-pub/thoth/issues/958) Superseded by: None @@ -18,9 +18,12 @@ fan out idempotently into jobs; jobs record executable asynchronous work and may also be created directly for scheduled, reconciliation or operator-requested work. The engine provides one shared lifecycle, claim/lease/idempotency model and attempt history. A headless `thoth-worker` ECS/Fargate service is the default -always-on executor for handlers owned by `thoth`. Domain-specific executors in -other repositories may consume the same shared job protocol without moving their -business logic into `thoth`. +always-on executor for trusted handlers owned by `thoth`. The shared engine may +use additional worker pools only where a materially different trust/authority +boundary requires process/task isolation; this ADR specifically requires such an +isolation boundary for publisher-controlled untrusted-content parsing. Domain- +specific executors in other repositories may consume the same shared job +protocol without moving their business logic into `thoth`. This ADR is architecture only. It authorizes no schema migration, source implementation, worker deployment, IAM change, provider write, credential @@ -220,29 +223,43 @@ Where an event is caused by a PostgreSQL business mutation, the business change and event record are written in the same transaction. Either both commit or neither commits. -Every event has a unique durable `event_id` and a monotonically ordered durable -event sequence suitable for route activation boundaries. Event payload and -causation metadata are immutable after commit. +Every event has a unique durable `event_id`. Route eligibility and routing +completion are determined from durable database state, not from worker-local +handler registration and not from a bare PostgreSQL sequence high-water mark. +A sequence may be used as an identifier/order aid, but the implementation must +not assume sequence allocation order equals commit order. -Every event consumer has a stable `route_key`. Routing/materialization records -are unique on `(event_id, route_key)` regardless of whether the resulting job -is pending, running or terminal. A routing crash after some consumers have been -materialized therefore resumes from durable route records and cannot create a -second job for an already-materialized route merely because the first job later -completed. +Every event consumer has a durable route registration with: -A route has a durable activation boundary. A route introduced by a later -software release receives only events at or after that boundary. Historical -events are processed by that new route only through an explicitly authorized -backfill/replay job; adding code must never silently subscribe the new route to -all historical events. +- a stable `route_key`; +- an explicit activation boundary recorded in PostgreSQL; +- the supported event kind/version contract; +- enough durable state to decide whether an event is eligible for that route. -The implementation may represent route completion per consumer or by an -equivalent snapshotted eligible-route set, but it must make partial fan-out -restartable and database-enforced. Event routing correctness must not depend on -in-memory knowledge of which handlers happened to run before a crash. +For every committed event, the engine must eventually establish a durable +routing/materialization record for every route whose durable activation boundary +the event meets. Those route records are unique on `(event_id, route_key)` +regardless of whether the resulting job is pending, running, waiting or terminal. -### 3.3 Versioned contracts +A crash after some consumers are materialized therefore resumes from durable +route records. A worker must never mark an event fully routed merely because it +processed all routes known to its own binary. During a mixed-version deployment, +if a worker encounters an active durable route whose kind/version it cannot +materialize, it leaves that route incomplete for a compatible worker rather than +silently completing the event. + +A route introduced by a later release receives only events at or after its +durable activation boundary. Historical events are processed by that route only +through an explicitly authorized backfill/replay. Backfill uses the same +`(event_id, route_key)` materialization identity, so it cannot bypass routing +deduplication. + +The implementation may optimize discovery/scanning, but routing completeness +must be proven from durable event/route/materialization records. Any cursor or +high-water optimization must prove that no lower/in-flight event can commit +later and be skipped; absent such proof, it is not authoritative. + +### 3.3 Versioned contracts and payload minimization Every event and job kind has: @@ -257,6 +274,27 @@ Changes that require old in-flight jobs to remain executable must preserve the required compatibility window or provide an explicit migration/reconciliation plan. +Event/job payloads default to **canonical references**, not copied domain +snapshots. A payload should normally contain only: + +- canonical entity/resource identifiers; +- the source revision/version/fingerprint needed to bind the requested effect; +- minimal routing/command parameters; +- correlation/causation identifiers. + +Credentials, tokens and secrets are forbidden in event/job payloads. + +Personal data or immutable domain snapshots may be embedded only when a +kind-specific approved specification proves that references are insufficient, +bounds the data, and defines retention/erasure behaviour. The generic engine's +immutability/retention rules must not make a data-erasure obligation impossible. + +A current-state job reads canonical state when it executes and records which +revision/fingerprint it acted on. Historical replay is permitted only when the +referenced historical revision is actually retained and authorized for replay; +a stale embedded snapshot must never silently substitute for canonical +current-state execution. + ### 3.4 Job lifecycle, scheduling and claims The common job lifecycle must support at least the semantic states: @@ -272,31 +310,45 @@ CANCELLED RECONCILIATION_REQUIRED ``` -Exact enum spelling is implementation detail, but `WAITING` and -`RETRY_SCHEDULED` are semantically distinct: waiting means the job has -checkpointed and is waiting on an external/domain condition; retry-scheduled -means a failed attempt is eligible for another execution after `available_at`. +Exact enum spelling is implementation detail. + +`WAITING` and `RETRY_SCHEDULED` are semantically distinct: + +- `WAITING` means an effectful or asynchronous operation has durably + checkpointed and is waiting on an external/domain condition; +- `RETRY_SCHEDULED` means a failed **pre-write** attempt is eligible for + another execution after `available_at`. + +Transitioning to `WAITING` atomically persists the durable checkpoint and +releases the current claim and lease. A later resume is a new claim and a new +attempt that reads the checkpoint. Waiting/polling attempts are not silently +treated as fresh external submissions. Every job carries first-class scheduling attributes including `priority`, -`available_at`, attempt count/max attempts and the owning kind. Ready work is -ordered by priority and availability within the scheduler's fairness/concurrency -rules; priority must not permit one job kind to starve all other enabled kinds. +`available_at`, the owning kind and counters/deadlines appropriate to its +lifecycle. Ready work is ordered by priority and availability within the +scheduler's fairness/concurrency rules; priority must not permit one job kind to +starve all other enabled kinds. -Each job kind declares a bounded maximum attempt count. Exhaustion is terminal -`FAILED` and creates an attention condition where ADR-0010 applies. The full -legal transition table, including which transitions require a current claim -token, must be fixed by the shared-engine implementation specification and -tested as a database/domain invariant. +Every job kind declares separately: -Claims use database-enforced concurrency with leases, unforgeable claim tokens -and `FOR UPDATE SKIP LOCKED`. The shared workload explicitly requires multiple -consumers to claim independent ready jobs without blocking each other. +- a bounded **pre-write retry budget**; +- for asynchronous/post-write work, a bounded waiting/reconciliation deadline + or equivalent domain-specific completion horizon; +- expected effect window/runner deadline; +- maximum claim/attempt runtime. + +Exhausting the pre-write retry budget is terminal `FAILED`. Exhausting a +waiting/post-write deadline after `EFFECT_STARTED` is never `FAILED` merely +because time elapsed; it becomes `RECONCILIATION_REQUIRED` unless durable +provider/domain evidence proves a stronger terminal outcome. -Leases are renewable by heartbeat using the **current** claim token. Renewal has -a bounded maximum lease/attempt runtime, may extend only an unexpired current -claim, and can never revive or extend a superseded claim. A stale worker cannot -complete, fail, checkpoint, cancel or renew a job after its claim has been -superseded. +Claims use database-enforced concurrency with leases, unforgeable claim tokens +and `FOR UPDATE SKIP LOCKED`. Leases are renewable by heartbeat using the +**current** claim token only. Renewal may extend only an unexpired current claim, +has a bounded maximum runtime, and can never revive or extend a superseded +claim. A stale worker cannot complete, fail, checkpoint, cancel, renew or +reconcile a job after its claim is superseded. Every job kind declares whether execution is **read-only/non-effectful** or **externally effectful**. Every attempt durably records an execution phase at @@ -308,38 +360,82 @@ EFFECT_STARTED EFFECT_CONFIRMED ``` -A lease expiry while an attempt is provably `PRE_WRITE` may return the job to -normal retry when its attempt budget permits. A lease expiry for an effectful -attempt at or after `EFFECT_STARTED` transitions to +An effectful attempt must not cross `EFFECT_STARTED` unless the remaining +lease/runtime budget covers the kind's declared effect window. Long operations +must checkpoint/yield before their maximum claim runtime rather than rely on an +expired lease. + +A lease expiry while an attempt is provably `PRE_WRITE` may schedule another +pre-write attempt when its retry budget permits. Lease expiry/crash/shutdown for +an effectful attempt at or after `EFFECT_STARTED` transitions to `RECONCILIATION_REQUIRED`, never directly to `PENDING` or `RETRY_SCHEDULED`, unless a domain-specific durable checkpoint proves that resumption cannot repeat the external effect. -Cancellation invalidates the current claim. Cancelling a pending/pre-write job -may produce terminal `CANCELLED`. Cancelling a running effectful job at or -after `EFFECT_STARTED` cannot assert that no external effect happened and -therefore produces `RECONCILIATION_REQUIRED` unless the provider/domain can -prove a stronger safe terminal outcome. +`WAITING` and `RECONCILIATION_REQUIRED` retain exclusive ownership of their +concurrency key by default. A kind may release it only under an explicit, +reviewed rule proving that overlapping work cannot create conflicting external +effects. + +Cancellation invalidates the current claim. Cancelling pending/pre-write work +may produce terminal `CANCELLED`. Cancelling an effectful job at or after +`EFFECT_STARTED` cannot assert that no external effect happened and therefore +produces `RECONCILIATION_REQUIRED` unless provider/domain evidence proves a +stronger safe terminal outcome. + +Resolving `RECONCILIATION_REQUIRED` is itself a claimed, claim-token-fenced, +kind-authorized action recorded as a durable attempt. A domain reconciler or +protected staff command may establish `SUCCEEDED`, `FAILED`, `CANCELLED`, +a safe `WAITING` checkpoint, or a new explicitly authorized effectful attempt +only from evidence permitted by that kind's reconciliation contract. + +The full legal transition table, including claim-token requirements and attempt +budget/deadline effects, is fixed by the shared-engine implementation +specification and tested as a database/domain invariant. The engine must support horizontal scaling from one worker task to multiple tasks without changing these semantics. -### 3.5 Idempotency and concurrency +### 3.5 Idempotency, coalescing and concurrency Every logical job has a deterministic idempotency key. Database uniqueness of -that key is **absolute across job states**, not merely "while live": a completed -job does not free its logical effect identity for accidental re-materialization. +that key is absolute across job states, but the key identifies a **specific +intended effect**, not merely a resource. + +A kind's key therefore incorporates the dimension that makes a later effect +meaningfully new, for example: + +- `(event_id, route_key)` or an event-derived source revision; +- a source revision/fingerprint; +- a schedule slot; +- an explicit command/request id; +- an approved coalescing/debounce window. + +A completed Crossref sync, search-index update, invalidation or reconciliation +must not suppress a later effect for a newer revision merely because both target +the same DOI/work/hosting target. -Retries create new attempts on the same job. An intentional replay/current-state -run creates a new logical job with a new idempotency key and an explicit -parent/replay/correlation relationship, following ADR-0010's distinction between -retry, replay and current-state redistribution. +Retries create new attempts on the same logical job. Intentional replay, +current-state execution and backfill create new logical jobs with new effect +identity and explicit parent/replay/correlation metadata, following ADR-0010's +distinction between retry, replay and current-state redistribution. -Event-derived jobs additionally carry the unique `(event_id, route_key)` -materialization identity from section 3.2. Handler idempotency and route -idempotency solve different problems and neither substitutes for the other. +Event routing identity and job idempotency are related but distinct. Every +`(event_id, route_key)` route record points to exactly one materialized job, +while several route records may point to one **not-yet-started** job only when +the kind defines an explicit durable coalescing rule. Coalescing therefore forms +a many-route-to-one-job relationship; it never erases the per-event route +records needed for audit/completeness. -Handlers may define a concurrency key, for example: +An idempotency conflict never silently means "route satisfied". It either: + +1. attaches the route to an existing not-yet-started job when the kind's + coalescing rule explicitly permits it; or +2. produces a deterministic routing/materialization error surfaced for + attention/recovery. + +Handlers additionally define a concurrency key where overlapping effects must +serialize, for example: ```text work::algolia @@ -372,17 +468,24 @@ A handler result distinguishes at least: Before an effectful handler crosses the boundary at which an external write may have begun, it durably records `EFFECT_STARTED` under its current claim token. Where the provider returns a submission/correlation identifier, the handler -persists that identifier before yielding `WAITING`. +persists that identifier before atomically yielding `WAITING` and releasing +its claim/lease. -A resumed asynchronous-provider job with a durable provider identifier polls or -reconciles the existing provider operation. It does not submit a fresh write -unless the domain contract has established that no previous effect can exist. +A later WAITING resume is a new claimed attempt. With a durable provider +identifier it polls or reconciles the existing provider operation; it does not +submit a fresh write unless the domain contract has established that no previous +effect can exist. + +If durable `EFFECT_CONFIRMED` evidence exists before a crash, a later claimed +recovery may complete `SUCCEEDED` without repeating the effect, where the +kind's contract defines that evidence as sufficient. An indeterminate provider write is never blindly replayed merely because the -worker did not receive an acknowledgement. Lease expiry, process crash or -shutdown after `EFFECT_STARTED` therefore fences the job into -`RECONCILIATION_REQUIRED` unless a durable domain checkpoint proves safe -resumption without duplicate effect. +worker did not receive an acknowledgement. Lease expiry, process crash, +shutdown, waiting-deadline exhaustion or cancellation after +`EFFECT_STARTED` therefore fences the job into +`RECONCILIATION_REQUIRED` unless a durable domain checkpoint proves a safe +non-duplicating continuation. The owning handler/reconciler must establish provider state or require protected operator attention before another effectful attempt. @@ -404,7 +507,7 @@ sanitized information to reconstruct: Secrets, credentials and unbounded provider responses must not be written to attempt records or ordinary logs. -### 3.8 Default `thoth-worker` runtime +### 3.8 Worker runtime and trust-separated pools The default always-on executor is a headless ECS/Fargate service using the ordinary Thoth release image with a dedicated command, conceptually: @@ -413,7 +516,8 @@ ordinary Thoth release image with a dedicated command, conceptually: thoth start worker ``` -Initial runtime shape: +The default **trusted** worker pool handles Thoth-owned jobs whose inputs do not +cross a materially different trust boundary. Initial runtime shape: ```text same ECS cluster @@ -422,7 +526,7 @@ no ALB no public hostname DesiredCount = 1 initially separate task definition/service -dedicated worker task role +dedicated trusted-worker task role ``` The existing generic infrastructure `service.yml` is web-service/ALB-oriented @@ -435,6 +539,22 @@ The worker polls/claims durable PostgreSQL jobs. PostgreSQL never depend on receiving a notification; periodic durable polling remains the fallback. +### Untrusted-content pool + +A handler that parses or executes against publisher-controlled/untrusted file +content is a separate **UNTRUSTED_CONTENT** risk class. It must not run in a task +that also holds Hosting DNS, ACM or CloudFront tenant-management authority. + +Such handlers execute in a separate task/service or equivalent process-isolated +worker pool with a minimal dedicated task role. It may use the same canonical +async database/protocol, but its AWS/provider capabilities are limited to those +needed for the untrusted-content job family. + +A handler may cross that boundary only through explicit CTO risk acceptance +that identifies the concrete parser/runtime, standing authorities and blast +radius. This is a trust-boundary exception to the single routine role, not a +return to one IAM role per ordinary handler. + Graceful shutdown stops new claims first. A pre-write attempt may report a retryable pre-write outcome or release/yield according to the shared transition contract. An effectful attempt after `EFFECT_STARTED` must persist a safe @@ -444,55 +564,68 @@ must never intentionally abandon it to ordinary lease-expiry retry. The runtime must prove clean recovery after abrupt termination as well as graceful shutdown. -### 3.9 Handler registry and capabilities +### 3.9 Handler registry, capabilities and risk class -The worker has a typed registry of supported job kinds. Each handler declares: +Each worker pool has a typed registry of supported job kinds. Each handler +declares: - job kind and supported payload version(s); - owning domain; - required runtime capability/configuration; -- timeout/lease requirements; +- trust/risk class, including whether it handles untrusted content; +- effect classification and expected effect window; +- timeout/lease/waiting-deadline requirements; - retry/reconciliation policy; -- idempotency/concurrency rules. +- idempotency/coalescing/concurrency rules. + +A worker only claims job kinds whose required capabilities and risk class are +configured for that pool. Capability declarations are runtime/configuration +boundaries and scheduling primitives; they do not imply one IAM role per +ordinary handler. -A worker only claims job kinds whose required capabilities are configured. -Capability declarations are runtime/configuration boundaries and future -scheduling primitives; they do not imply one IAM role per handler. +External/domain executors use the same kind scoping: their authorization permits +only the job kinds assigned to that domain executor. A dissemination executor +cannot claim/checkpoint/complete/reconcile a Hosting, Metrics, file-processing +or indexing job merely because those jobs share the same database. -### 3.10 AWS credentials and task role +### 3.10 AWS credentials and task roles -The initial `thoth-worker` uses one dedicated least-privilege ECS task role -containing the union of its **approved routine AWS capabilities**. +The default trusted `thoth-worker` pool uses one dedicated least-privilege ECS +task role containing the union of its **approved routine trusted capabilities**. -AWS authority for the worker comes **exclusively** from that task role. The -worker must not receive static `AWS_ACCESS_KEY_ID`, +AWS authority for each worker pool comes **exclusively** from that pool's ECS +task role. Worker tasks must not receive static `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, session-token credentials or equivalent long-lived AWS credentials through environment variables, committed configuration or any other deployment mechanism. Do not introduce one assumable IAM role per ordinary handler merely to mirror -code boundaries when the same process could assume all of them anyway. +code boundaries when the same trusted process could assume all of them anyway. -The task role may eventually include bounded capabilities such as: +The trusted-worker role may eventually include bounded capabilities such as: - approved Hosting tenant lifecycle; - ACM operations needed by Hosting; - writes to Thoth-controlled Hosting DNS; - approved CDN invalidation; -- bounded S3 object access required by file-processing handlers. +- bounded S3 object access required by trusted handlers. -The exact permission matrix remains an implementation/IAM task and must retain -negative tests and resource scoping. +Handlers in the `UNTRUSTED_CONTENT` risk class use a **separate** worker +pool/task role that excludes Hosting DNS, ACM and CloudFront tenant-management +authority unless explicitly risk-accepted by the CTO. This separation exists +because compromise of an untrusted parser is a materially different blast-radius +case, not because handler boundaries should mechanically map to IAM roles. -Standing permissions that materially increase blast radius and are not required -for routine operation remain excluded. +The exact permission matrices remain implementation/IAM tasks and must retain +negative tests and resource scoping. Standing permissions that materially +increase blast radius and are not required for that worker pool remain excluded. The infrastructure implementation therefore intentionally departs from the current shared `ECSTaskRole`/static-key pattern for existing Thoth web -services: it must create a dedicated worker task role and ensure no static AWS -keys are injected into the worker. Remediation/rotation of any pre-existing -static AWS credentials used by other services is separate security work and is -not authorized or performed by this ADR. +services: it must create dedicated worker-pool task roles and ensure no static +AWS keys are injected into worker tasks. Remediation/rotation of any +pre-existing static AWS credentials used by other services is separate security +work and is not authorized or performed by this ADR. ### 3.11 No automated legacy-CDN migration @@ -640,103 +773,164 @@ requests were represented by the surviving job. --- -## 4. BE-04 supersession and compatibility +## 4. BE-04 supersession and expand-migrate-contract transition Once ADR-0012 is approved and repository-authoritative, the shared async engine supersedes the ADR-0008/ADR-0010 assumptions identified in the header and section 1.3. It does **not** supersede ADR-0008's machine-role/least-privilege rules or ADR-0010's staff operational/audit architecture. -BE-04 is already part of released Thoth code. The replacement therefore -distinguishes the **storage/execution model** from the **released API -compatibility surface**. +BE-04 is already part of released Thoth code and is consumed by more than the +dissemination worker. The transition therefore uses **expand -> migrate +consumers -> retire legacy API -> contract storage**. It does not translate the +new lifecycle through the old five-state GraphQL enum. + +### 4.1 Release N - expand without contracting legacy BE-04 + +A separately authorized Release N introduces: + +- the generic async schema/domain/API; +- event/route/job/attempt primitives; +- the new kind-scoped claim/checkpoint/complete/reconcile contract; +- generic job creation for newly specified consumers. + +Release N **retains unchanged legacy BE-04 storage and released GraphQL +contract**. It does not drop: -### 4.1 Storage and domain replacement +- `distribution_job`; +- `distribution_job_target`; +- `distribution_job_attempt`; +- the four BE-04-only queue enum types; +- BE-04 lifecycle/query GraphQL fields/types. -The next separately authorized shared-engine implementation must: +The existing BE-04 automatic job-creation path/toggle remains OFF/inactive while +legacy consumers are migrated. No state-translating compatibility facade is +introduced. -- introduce the generic async schema through a new forward migration; -- remove the unused `distribution_job`, `distribution_job_target` and - `distribution_job_attempt` tables and their dedicated indexes; -- remove the four BE-04-only enum types - `distribution_job_kind`, `distribution_job_status`, - `distribution_job_attempt_result` and - `distribution_job_cancellation_reason`; -- atomically reconcile `thoth-api/src/schema.rs`, persistence/domain models, - CRUD/query code and tests as required by ADR-0003 and repository doctrine; -- **retain** `distribution_platform`, which remains shared Publisher Services - domain state and is not a queue-only type. +Historical `20260814_v1.7.0` remains immutable. -The already-applied `20260814_v1.7.0` migration remains immutable. +### 4.2 Known released consumers that must migrate -### 4.2 Released BE-04 GraphQL compatibility window +At ADR authoring time, the known released consumers include at least: -The released BE-04 GraphQL lifecycle contract is not removed at the same moment -as the legacy tables. +1. **`thoth-pub/thoth-dissemination`** + - BE-04 claim/complete/fail lifecycle operations; + - current DIS-02 semantics and worker authorization. -During a bounded compatibility window, the existing released -claim/complete/fail/cancel job operations and their GraphQL types remain -available as a **deprecated compatibility facade** backed by the generic async -engine. Any existing BE-04 automatic job-creation path that remains exposed must -either create the equivalent generic job under the same default-OFF activation -semantics or be explicitly disabled through its separately approved deprecation -path; it must never continue writing removed legacy tables. +2. **`thoth-pub/thoth-app`** + - `PublisherServiceConfigurationSummary.latestBackCatalogueJob`; + - service-configuration list/count filters including `jobStatuses`; + - `withoutBackCatalogueJob`; + - generated `DistributionJobStatus` and related GraphQL client contract. -`thoth-dissemination` then migrates to the new protected generic, -kind-scoped claim/report contract under its own repository-local task. +The implementation programme must inspect all repository-authoritative contract +consumers again immediately before retirement; this list is not permission to +assume no additional consumer exists. -Only after all downstream consumers are verified on the generic contract may a -later separately approved API-deprecation task remove the legacy BE-04 GraphQL -fields/types/toggle and update generated SDL/client surfaces. That removal must -follow the repository's approved deprecation-path rule; downstream repositories -must not be forced to guess a contract that has not merged. +`thoth-app` and `thoth-dissemination` each receive their own repository-local +bounded migration task/branch/PR and must consume an exact merged generic +contract. No downstream repository guesses the new schema. -### 4.3 Fresh preflight before removing legacy storage +### 4.3 Consumer migration -The future migration/deployment task must verify immediately before production -storage removal: +After Release N is the deployed/rollback-safe Thoth version: -1. all three legacy tables exist as expected; -2. all three contain zero rows and no operational history; -3. the deployed API's BE-04 creation toggle is OFF or absent; -4. the DIS-02 worker activation variable is not ON and no executor is actively - claiming the legacy contract; -5. there is no current `DISSEMINATION_WORKER` legacy-job activity; -6. the compatibility facade has been changed to the generic storage before any - request can reach dropped relations. +- `thoth-dissemination` migrates to the generic kind-scoped + claim/checkpoint/complete/reconcile contract; +- `thoth-app` migrates its back-catalogue job reads/filters/status presentation + to the new operational contract selected by its approved task; +- generated SDL/client surfaces are refreshed from the merged Thoth contract; +- each downstream deployment is independently verified. -If any of those checks fail, the DROP is HOLD and data/consumer migration must -be designed explicitly. +Until both known consumers (and any newly discovered consumers) are verified on +the generic contract, legacy BE-04 API/storage remains present and unchanged. -### 4.4 Preserve proven BE-04 safety work +### 4.4 Legacy GraphQL contract retirement + +Only after all consumers are verified migrated may a later separately approved +Thoth release remove the legacy BE-04 GraphQL lifecycle/read/filter +fields/types/toggle. + +That removal follows the repository's approved deprecation-path rule and must +include generated SDL/client impact. It must not collapse +`WAITING`/`RECONCILIATION_REQUIRED` into the legacy five-value +`DistributionJobStatus`; consumers migrate to the new lifecycle before the +legacy enum disappears. + +The legacy creation path must be removed/disabled before any subsequent storage +DROP can make it invalid. + +### 4.5 Later storage contraction + +Legacy storage is removed only in a **later destructive migration** after: + +1. the generic schema/API release is the sole running production version; +2. the legacy GraphQL contract has been retired from deployed Thoth; +3. all known downstream consumers are on the generic contract; +4. the previous deployment rollback target no longer requires the legacy + relations; +5. no executor is claiming/writing the legacy contract; +6. the destructive migration itself verifies all three legacy tables exist as + expected and contain zero rows. + +The emptiness check is part of the same migration/transactional gate that +performs the DROP. If any legacy row exists, the migration aborts and production +deployment is HOLD; an earlier read-only preflight is useful evidence but is not +sufficient by itself. + +The contraction removes: + +- `distribution_job`; +- `distribution_job_target`; +- `distribution_job_attempt`; +- their dedicated indexes; +- `distribution_job_kind`; +- `distribution_job_status`; +- `distribution_job_attempt_result`; +- `distribution_job_cancellation_reason`. + +It **retains** `distribution_platform`, which remains Publisher Services domain +state. + +The same bounded source/migration task atomically reconciles +`thoth-api/src/schema.rs`, persistence/domain models, CRUD/query code and tests +as required by ADR-0003 and repository doctrine. + +### 4.6 Preserve proven BE-04 safety work Superseding BE-04 storage does not discard its safety evidence. The generic -engine and dissemination adapter must preserve or improve the already-proven -principles: +engine and dissemination adapter must preserve or improve: - database claim fencing and stale-token refusal; -- absolute logical deduplication; -- bounded attempts; +- effect-scoped deterministic deduplication; +- bounded pre-write retries; +- runner/effect-window checks before starting an external write; - conservative classification of post-write ambiguity; - no automatic replay of an abandoned predecessor when a provider write may already have happened; - bounded/sanitized durable diagnostics. Existing BE-04/DIS-02 source remains historical implementation evidence and may -be reused only where the new bounded specifications explicitly adopt and retest -it against the generic contract. +be reused only where new bounded specifications explicitly adopt and retest it +against the generic contract. + +### 4.7 Development and fresh-database migration path -### 4.5 Development and fresh-database migration path +Repository migrations still execute in order. An environment currently before +v1.7.0, including dev as recorded at this decision, first applies v1.7.0, then +Release N's additive generic schema migration, and only later the separately +authorized contraction migration after the consumer/API retirement gates are +satisfied. -Because repository migrations execute in order, an environment currently before -v1.7.0, including dev as recorded at this decision, will apply v1.7.0 and then -the later replacement migration when brought fully current. A fresh database -does the same. +A fresh database follows the same ordered history. Tests must therefore cover: -The replacement migration and test plan must therefore prove both the -production-like upgrade path from a schema where v1.7.0 is already present and -the full empty-database migration chain. It must not rely on dev and production -having identical starting versions by accident. +- production-like upgrade from v1.7.0 with legacy relations present; +- pre-v1.7 upgrade through v1.7.0 and the additive generic migration; +- full empty-database migration chain; +- the later contraction migration with zero legacy rows; +- fail-closed contraction when any legacy row exists. + +No environment is allowed to skip or rewrite the historical v1.7.0 migration. ## 5. Initial job families @@ -788,10 +982,14 @@ new decision. ### D. One IAM role per handler -Rejected as the default because one process able to assume every handler role -does not materially isolate a compromise of that process and adds substantial -operational complexity. High-risk standing permissions may still justify -separate execution boundaries where evidence shows real blast-radius reduction. +Rejected as the default because one trusted process able to assume every +ordinary handler role does not materially isolate compromise of that process and +adds substantial operational complexity. + +This rejection does **not** apply across materially different trust boundaries. +Publisher-controlled untrusted-content parsing is explicitly separated into a +different worker pool/task role because a parser exploit gaining Hosting +DNS/ACM/tenant authority is a concrete blast-radius increase. ### E. One generic catch-all application machine role @@ -810,95 +1008,131 @@ execution until a separate approved architecture decision says otherwise. 1. PostgreSQL remains canonical for event/job lifecycle. 2. Business mutation plus caused event is atomic where both are PostgreSQL-owned. -3. Every event has durable identity and ordering; every `(event_id, route_key)` - materialization is database-unique irrespective of job terminal state. +3. Route registrations/activation boundaries are durable database state; routing + completeness is proven per eligible `(event_id, route_key)`, not by + worker-local route knowledge or an unsafe sequence high-water mark. 4. A newly registered route does not consume historical events without an - explicit backfill/replay. -5. Job idempotency identity is absolute across states; retries are attempts on - the same logical job. -6. No stale/superseded claim can finalize, checkpoint, cancel or renew a job. -7. Effectful attempts durably fence `EFFECT_STARTED` before crossing the - external-write boundary. -8. Lease expiry/shutdown after an ambiguous effect cannot return the job to - ordinary retry; it requires reconciliation unless a durable checkpoint proves - safe resume. -9. No job handler may claim exactly-once external effects. -10. Indeterminate writes require reconciliation before unsafe replay. -11. Priority, delayed availability, bounded attempts, per-kind concurrency and - fairness are first-class shared-engine concerns. -12. Cancellation never asserts that an already-started external effect did not - happen. -13. `async_job`/`async_job_attempt` are canonical execution truth; + explicit backfill/replay through the same route materialization records. +5. Job idempotency identifies a specific intended effect; a resource-only key + must not suppress later revisions/schedule slots/commands. +6. Every event-route record maps to exactly one job; many route records may map + to one job only under an explicit not-yet-started coalescing rule. +7. No stale/superseded claim can finalize, checkpoint, cancel, renew or + reconcile a job. +8. WAITING atomically checkpoints and releases claim/lease; resume is a new + claimed attempt. +9. Effectful attempts durably fence `EFFECT_STARTED` before crossing the + external-write boundary and start only when the remaining effect window fits + within the current lease/runtime. +10. Lease expiry/shutdown/cancellation/deadline exhaustion after an ambiguous + effect cannot return the job to ordinary retry or terminal FAILED; it + requires reconciliation unless durable evidence proves a stronger outcome. +11. WAITING and RECONCILIATION_REQUIRED retain concurrency-key exclusion by + default. +12. Reconciliation is itself kind-authorized, claim-token-fenced and recorded as + an attempt. +13. No job handler may claim exactly-once external effects. +14. Priority, delayed availability, bounded pre-write attempts, + post-write/waiting deadlines, per-kind concurrency and fairness are + first-class shared-engine concerns. +15. `async_job`/`async_job_attempt` are canonical execution truth; `ServiceOperation` is the ADR-0010 staff operational/audit seam where applicable, not a second attempt ledger. -14. Domain ownership is explicit and preserved. -15. Generic execution does not create generic application authorization. -16. Historical migrations are never rewritten after deployment. -17. Worker AWS authority comes exclusively from its dedicated ECS task role; no - static AWS access keys are injected into the worker. -18. Non-AWS credential values are not committed or emitted in logs/durable +16. Event/job payloads default to canonical references plus revision/fingerprint + and minimal parameters; credentials are forbidden and personal/snapshot data + requires explicit retention/erasure justification. +17. Domain ownership is explicit and preserved. +18. Generic execution does not create generic application authorization. +19. Historical migrations are never rewritten after deployment. +20. Trusted-worker AWS authority comes exclusively from its dedicated ECS task + role; untrusted-content parsing runs in a separate minimal-authority worker + pool unless explicitly risk-accepted by the CTO. +21. No worker receives static AWS access keys. +22. Non-AWS credential values are not committed or emitted in logs/durable diagnostics. -19. A worker may execute only supported payload versions and configured - capabilities. -20. Protected operator retry/reconcile controls are audited and may not bypass +23. A worker/executor may claim only supported job versions/kinds and configured + capabilities/risk classes. +24. Protected operator retry/reconcile controls are audited and may not bypass reconciliation safety. -21. Merge, migration, deployment, handler activation, provider access and +25. BE-04 retirement follows expand -> migrate consumers -> retire API -> + contract storage; legacy storage is never dropped while old code remains a + production rollback target. +26. Merge, migration, deployment, handler activation, provider access and production activation remain separate gates. ## 8. Implementation impact and decomposition Approval of this ADR should lead to separate bounded tasks, at minimum: -1. **Shared schema/engine and BE-04 compatibility facade in `thoth`** - - forward replacement migration; - - event/route/job/attempt domain types; - - claim/renewal/lease/retry/reconciliation primitives; - - event routing/materialization; - - execution-phase checkpoints; - - deprecated BE-04 GraphQL facade backed by generic jobs; - - atomic Diesel/model/query/generated-contract reconciliation; - - tests including real PostgreSQL concurrency. - -2. **Default `thoth-worker` runtime in `thoth`** +1. **Release N shared schema/engine/API in `thoth`** + - additive generic async migration; + - durable event-route registration/materialization model; + - job/attempt/checkpoint lifecycle; + - claim/renewal/wait/reconciliation primitives; + - effect-scoped idempotency and explicit coalescing; + - new kind-scoped executor API; + - legacy BE-04 schema/API left intact and inactive; + - real PostgreSQL concurrency/mixed-version/out-of-order-commit tests. + +2. **Trusted default `thoth-worker` runtime in `thoth`** - worker command; - handler registry; - graceful shutdown/checkpointing; - metrics/health/operational reporting; - - capability/configuration filtering. - -3. **Infrastructure** - - headless Fargate worker service not bound to the ALB; - - dedicated worker task role; - - explicit prohibition of static AWS credentials for the worker; - - non-committed deploy-time injection of external env-var credential values; - - environment/network configuration; - - no deployment/activation until separately authorized. - -4. **Publisher Services / `thoth-dissemination`** - - migrate from the deprecated BE-04 facade to generic kind-scoped job - consumption; - - preserve dissemination domain logic; - - independently review external-write/idempotency/reconciliation semantics. - -5. **BE-04 public-contract retirement** - - after downstream migration, remove deprecated legacy GraphQL lifecycle - fields/types/toggle under a separately approved deprecation task; - - regenerate/update affected client SDL/types and verify all downstream - consumers. - -6. **Hosting** + - capability/kind filtering. + +3. **Infrastructure - trusted worker** + - headless Fargate service not bound to the ALB; + - dedicated trusted-worker task role; + - no static AWS credentials; + - non-committed deploy-time injection of non-AWS env-var credential values; + - environment/network configuration. + +4. **Infrastructure/runtime - untrusted-content pool** + - separate process/task/service boundary for publisher-controlled parsers; + - minimal task role with no Hosting DNS/ACM/CloudFront tenant authority; + - explicit job-kind/risk-class filtering; + - deployment only when an approved untrusted-content handler is ready. + +5. **`thoth-dissemination` consumer migration** + - move from legacy BE-04 operations to generic kind-scoped + claim/checkpoint/complete/reconcile; + - preserve dissemination domain logic and external-write safety; + - independently review retry/reconciliation behaviour. + +6. **`thoth-app` consumer migration** + - migrate `latestBackCatalogueJob`, `jobStatuses`, + `withoutBackCatalogueJob` and related presentation/filter semantics to + the merged generic operational contract; + - regenerate/update GraphQL client surfaces; + - independently verify staff UX semantics. + +7. **Legacy BE-04 GraphQL retirement in `thoth`** + - only after all consumers are deployed on generic contracts; + - remove legacy lifecycle/read/filter fields/types/toggle under approved + deprecation controls; + - update generated SDL/client compatibility evidence. + +8. **Later BE-04 storage contraction in `thoth`** + - only after legacy API retirement and rollback-window closure; + - migration-local zero-row assertion; + - remove the three legacy tables/indexes/four queue-only enums; + - retain `distribution_platform`; + - atomically reconcile Diesel/model/query code. + +9. **Hosting** - adopt generic jobs for greenfield provisioning/reconciliation; - no automated legacy CDN migration. -7. **Existing AWS static-credential security debt** - - inspect and remediate any existing service static AWS credential pattern - under a separate security task and explicit provider/write authorization; +10. **Existing AWS static-credential security debt** + - inspect/remediate any existing service static AWS credential pattern under a + separate security task and explicit provider/write authorization; - this is not silently folded into worker deployment. -Before ADR-0012 becomes repository-authoritative, its approval-state -reconciliation must also update ADR-0008 and ADR-0010 decision metadata/register -entries to record the exact partial supersessions selected here. That is -documentation/control reconciliation, not implementation authorization. +11. **Approval-state ADR reconciliation** + - update ADR-0008/ADR-0010 metadata/register under a separate authorized write + budget after exact-content CTO approval; + - this documentation/control step is not implementation authorization. Each repository receives its own branch/PR and independent review. No downstream repository guesses an unmerged upstream contract. @@ -907,43 +1141,81 @@ repository guesses an unmerged upstream contract. Architecture approval does not authorize migration execution. -The future database rollout must remain safe across both the production upgrade -path and the full migration chain. Before any production DROP of -`distribution_job*`, section 4.3's fresh preflight is mandatory. +The production transition is deliberately non-atomic across releases/repos: + +### Phase A - expand -The production replacement change must ensure no deployed code path can access -legacy relations after they are dropped. The deprecated GraphQL compatibility -facade must already target generic storage in the same bounded source/migration -release, and generated/client compatibility must be proven. +Release N adds the generic async schema/API and can deploy the inert trusted +worker runtime. Legacy BE-04 schema/API remains present and unchanged, with +legacy automatic job creation/activation OFF. + +Release N must be fully deployed and become a valid rollback target before any +downstream consumer migration starts. + +### Phase B - migrate consumers + +`thoth-dissemination` and `thoth-app` migrate independently to the exact +merged generic contracts. Any newly discovered consumer is added to the same +gate. + +No legacy GraphQL field/type is removed until every consumer is verified +deployed on the generic contract. + +### Phase C - retire legacy API + +A later Thoth release removes the legacy BE-04 GraphQL +lifecycle/read/filter/toggle contract under approved deprecation controls. That +release is deployed and observed until the previous rollback target no longer +needs legacy API/storage. + +### Phase D - contract legacy storage + +Only a still-later destructive migration removes `distribution_job*` storage +and queue-only enum types. The migration itself checks legacy table emptiness and +aborts if any row exists. The legacy tables are not dropped while any old Thoth +image that references them remains a supported production rollback target. + +Because the current production GraphQL image runs the default `thoth init` +path, which performs migrations before serving, this separation is mandatory: +the first task in a rolling deploy must never drop relations still needed by old +tasks or by automatic rollback. The development environment's currently unapplied v1.7.0 state is handled by -ordinary ordered migrations: v1.7.0 applies first, followed by the replacement. -The test matrix must include: +ordered migrations: v1.7.0 applies first, followed by additive generic schema, +and only later the separately authorized contraction migration after the same +consumer/API retirement gates. + +The migration test matrix must include: - production-like schema with v1.7.0 already applied; -- pre-v1.7 upgrade applying v1.7.0 plus replacement; +- pre-v1.7 upgrade applying v1.7.0 plus additive generic schema; - full empty-database migration chain; -- forward repair/rollback strategy once generic async data exists. +- rolling deploy with mixed old/new application versions during Phase A; +- later API-retirement rollback window; +- contraction with zero legacy rows; +- contraction abort with any legacy row present. Initial worker deployment is inert until job creation/activation is separately -authorized. Desired count starts at one unless deployment evidence justifies -otherwise. +authorized. Effectful handler activation is prohibited until its +`RECONCILIATION_REQUIRED` exit path (reconciler and/or protected staff +operation) is itself available and approved. Rollout should prove in order: 1. schema/event-route/job concurrency without external writes; -2. crash/lease renewal/expiry recovery; -3. idempotent partial fan-out recovery; -4. priority/fairness/per-kind concurrency and bounded retry behaviour; -5. one or more non-destructive representative handlers; -6. one effectful handler with durable `EFFECT_STARTED`, provider checkpoint and +2. mixed-version route completeness and out-of-order transaction commits; +3. crash/lease renewal/WAITING/reconciliation recovery; +4. effect-scoped idempotency and coalescing; +5. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; +6. one or more non-destructive representative handlers; +7. protected operator controls and reconciliation exit path; +8. one effectful handler with durable `EFFECT_STARTED`, provider checkpoint and reconciliation evidence; -7. protected operator controls and operational signals; -8. only then broader handler activation. +9. only then broader handler activation. Handler activation is independent: enabling Hosting must not automatically enable Crossref, Algolia, file processing or another family merely because they -share the worker. +share the engine. ## 10. Rollback @@ -953,54 +1225,88 @@ Once implemented, rollback must distinguish: - stopping new event/job creation; - stopping worker claiming; -- preserving durable event/job/attempt records; +- preserving durable event/route/job/attempt/checkpoint records; - rolling back individual handler activation; +- rolling back application binaries while preserving schema compatibility; - schema rollback, which may be impossible after live async data exists and - therefore requires its own migration plan. + therefore requires its own forward-repair/migration plan. -Do not treat disabling the worker as equivalent to deleting durable jobs. +During the expand/migrate phases, a rollback to the previous Thoth image is +permitted only while all schema/API that image needs remains present. The legacy +BE-04 storage contraction cannot occur until that old image is no longer a +supported rollback target. ---- +After generic async data exists, rolling the runtime back does not authorize +dropping/rewriting that data. A rollback plan must either run a binary that +understands the generic schema or stop job production/claiming and preserve the +durable queue for forward recovery. + +Do not treat disabling a worker as equivalent to deleting durable jobs. ## 11. Validation requirements Before shared implementation can be approved, evidence must include: - real PostgreSQL concurrent claim tests with multiple workers; +- durable route-registration/activation tests; +- mixed worker-version routing where an older worker cannot silently complete an + unknown active route; +- out-of-order transaction commit tests proving no event can be skipped by a + sequence/cursor optimization; +- crash during partial fan-out with exact resume and no duplicate route record; +- explicit historical backfill through the same `(event_id, route_key)` + materialization identity; +- effect-scoped idempotency tests across newer source revisions/schedule slots; +- allowed many-route-to-one-job coalescing tests and deterministic conflict + attention when coalescing is not permitted; - current-token lease renewal and refusal to renew superseded/expired claims; -- stale-token refusal for completion, failure, cancellation and checkpointing; -- crash immediately after claim; -- crash during event fan-out/materialization with exact resume and no duplicate - `(event_id, route_key)` job; -- proof that newly registered routes do not consume historical events absent an - explicit backfill; +- stale-token refusal for completion, failure, cancellation, checkpointing and + reconciliation; +- WAITING transition atomically persisting checkpoint and releasing claim/lease; +- resume from WAITING as a new claim/attempt that polls/reconciles rather than + resubmits; +- pre-write retry-budget exhaustion producing FAILED; +- post-write waiting/deadline exhaustion producing + RECONCILIATION_REQUIRED rather than FAILED; +- concurrency-key retention across WAITING/RECONCILIATION_REQUIRED unless an + explicit safe-release rule exists; +- effect-window test proving an effectful runner is not started without enough + remaining lease/runtime; - crash before and after `EFFECT_STARTED`; -- lease expiry after `EFFECT_STARTED` producing reconciliation rather than - ordinary retry; -- provider-correlation checkpoint followed by WAITING/resume that polls rather - than resubmits; -- deterministic absolute job idempotency tests; -- concurrency-key serialization tests; -- priority, `available_at`, bounded attempt and scheduler fairness tests; -- retry/backoff and poison-job exhaustion tests; +- durable `EFFECT_CONFIRMED` recovery where supported by the kind; - cancellation before write and cancellation after effect-start tests; - `INDETERMINATE` external-write reconciliation tests; +- claimed/token-fenced/kind-authorized reconciliation race tests; +- priority, `available_at`, bounded attempt and scheduler fairness tests; +- retry/backoff and poison-job exhaustion tests; - unsupported payload-version refusal; +- payload tests proving credentials are rejected and personal/snapshot data is + absent unless explicitly allowed by the kind contract; +- current-state execution reading canonical state/revision rather than stale + embedded snapshots; - graceful shutdown and abrupt restart recovery; -- worker capability filtering; -- authorization negative tests for every external/domain executor; +- worker capability/kind/risk-class filtering; +- authorization negative tests proving domain executors cannot mutate another + domain's jobs; - protected/audited operator cancel/retry/reconcile tests; - queryable queue-depth, oldest-ready-age, expired-lease, `RECONCILIATION_REQUIRED`, failure and worker-liveness signals; - proof that logs and durable diagnostics do not contain configured credentials; -- proof that the worker receives no static AWS access-key credentials; -- migration tests from a schema containing the production v1.7.0 - `distribution_job*` relations; +- proof that trusted/untrusted worker tasks receive no static AWS access keys; +- proof that untrusted-content worker roles exclude Hosting DNS/ACM/CloudFront + tenant authority; +- Phase-A migration tests retaining legacy BE-04 storage/API; +- `thoth-dissemination` generic-contract compatibility tests; +- `thoth-app` migration tests for latest job/status/filter semantics; +- generated SDL/client contract checks; +- rolling deployment/rollback tests proving old tasks remain valid during Phase + A/B; +- later legacy-API retirement tests; +- contraction migration tests from production v1.7.0 state; +- contraction abort when any legacy row exists; - full empty-database migration-chain tests; -- compatibility tests showing released BE-04 GraphQL operations no longer - depend on legacy tables during the compatibility window; -- deployment tests with more than one worker task even if production initially - runs one. +- deployment tests with more than one compatible worker task even if production + initially runs one. Provider-specific handlers owe their own acceptance evidence in addition to the shared-engine tests. From 5e9af9fee5a8f1b499ee74ba78091ed9f398fb8e Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:11:30 +0100 Subject: [PATCH 10/48] THOTH-ASYNC-01: update ADR-0012 round-3 proposal record --- docs/engineering/decisions/decision-register.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 3c73b9fc0..446dbac41 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-09-29 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed event/route/job engine, crash-safe route materialization, claim-token-fenced renewable leases, durable effect checkpoints/reconciliation, a default headless `thoth-worker`, domain-owned external executors, one dedicated routine worker AWS task role, deployment env vars for non-AWS credentials, and no automated legacy-CDN migration. It satisfies ADR-0008's explicit gate for a later shared abstraction and would supersede only ADR-0008's no-shared-framework/programme-local-only statements while retaining its durable-work conventions and machine-role rules. It would also partially supersede ADR-0010 only where that decision assumes `distribution_job*` remains the long-term distribution execution source; `ServiceOperation`, desired/execution/observed-state separation, attention/reconciliation and staff-command gates remain binding. Released BE-04 GraphQL operations receive a deprecated compatibility facade over generic jobs before later removal through a separate deprecation task. BE-04/DIS-02 activation and Hosting implementation depending on this architecture remain HOLD. Requires fresh independent exact-head review and exact CTO approval before implementation may rely on it. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed event/route/job engine with durable route registration/completeness, effect-scoped idempotency/coalescing, claim-token-fenced renewable leases, explicit WAITING/reconciliation semantics and minimal reference/revision payloads. Uses a default trusted headless `thoth-worker` with one routine task role, plus a separate minimal-authority `UNTRUSTED_CONTENT` worker pool where publisher-controlled parsers create a material trust boundary; no worker receives static AWS keys. Keeps domain-owned executors such as `thoth-dissemination`, preserves ADR-0008 machine-role rules and ADR-0010 `ServiceOperation` staff audit/command semantics, and excludes automated legacy-CDN migration. BE-04 transition is expand -> migrate both known consumers (`thoth-dissemination` and `thoth-app`) -> retire legacy GraphQL API -> later contract legacy storage with a migration-local zero-row assertion; v1.7.0 remains immutable and `distribution_platform` remains. BE-04/DIS-02 activation and Hosting implementation depending on this architecture remain HOLD. Requires fresh independent exact-head review and exact CTO approval before implementation may rely on it. | ## Merge and implementation rules From 3402308e8ed75953bf262dd436788cefecc94633 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:11:39 +0100 Subject: [PATCH 11/48] THOTH-ASYNC-01: update round-3 proposal summary --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf7b056b..aa5c66b63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal separates durable events from executable jobs; makes event-route materialization database-unique and restartable; standardizes versioned payloads, absolute job idempotency, priority/availability, bounded attempts, concurrency/fairness, claim-token-fenced renewable leases and durable effect-phase checkpoints; and requires ambiguous post-write lease loss/cancellation to enter reconciliation rather than ordinary retry. It selects one headless `thoth-worker` Fargate service for Thoth-owned handlers while preserving domain-owned executors such as `thoth-dissemination`; gives the worker one dedicated routine AWS task role with no static AWS keys; uses deployment environment variables for non-AWS credentials with values injected from non-committed deployment configuration; and excludes automated legacy-CDN migration. It explicitly reconciles ADR-0008 and ADR-0010 boundaries, keeps `async_job`/`async_job_attempt` as canonical execution truth while `ServiceOperation` remains the staff audit/command seam, and gives the released BE-04 GraphQL contract a deprecated compatibility facade over generic jobs before a later separate removal. A future forward migration must remove the unused production `distribution_job*` storage only after a fresh zero-use preflight, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal distinguishes durable events/routes/jobs, requires database-owned route registration/completeness rather than unsafe sequence high-water semantics, uses effect-scoped idempotency with explicit coalescing, and standardizes priority/availability, bounded pre-write retries, WAITING checkpoints, post-write deadlines, claim-token-fenced renewable leases and reconciliation-required handling. Event/job payloads default to canonical IDs plus source revision/fingerprint and minimal parameters rather than copied snapshots. The default trusted headless `thoth-worker` uses one dedicated routine task role, while publisher-controlled file parsing is isolated in a separate minimal-authority `UNTRUSTED_CONTENT` worker pool; no worker receives static AWS keys. Non-AWS credentials remain deployment environment variables supplied from non-committed deployment configuration. The proposal preserves domain-owned executors such as `thoth-dissemination`, ADR-0008 application-role boundaries and ADR-0010 `ServiceOperation` staff audit/command semantics, and excludes automated legacy-CDN migration. Released BE-04 is retired through expand -> migrate `thoth-dissemination` and `thoth-app` -> retire legacy GraphQL contract -> later contract `distribution_job*` storage with an in-migration zero-row assertion, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** - `CTRL-BRANCH-RELEASE-01`: preserve each managed repository's established release branch name instead of normalizing every repository to `master` (issue [897](https://github.com/thoth-pub/thoth/issues/897), parent control programme [818](https://github.com/thoth-pub/thoth/issues/818)). Adds [`ADR-0011`](docs/engineering/decisions/ADR-0011-preserve-established-release-branch-names.md), which standardizes the **role** of the release branch, written ``, rather than whether it is spelled `main` or `master`. The shared conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `, with each repository's `` resolved from its own verified live state: `main` repositories remain on `main`, `master` repositories remain on `master`, and neither name is created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` release flow is unchanged, and [`ADR-0009`](docs/engineering/decisions/ADR-0009-programme-integration-branch-namespace.md) is unchanged in substance. Release-branch conversion is removed from `BR-APP-01`, `BR-DIS-01`, `BR-SPHINX-01`, `BR-DASH-01`, `BR-WIDGET-01` and `BR-LIC-01`, and Vercel production for `metrics-dashboard` is no longer moved from `main` for branch spelling; the independently justified development-branch, protection, CI, provider and publication readiness work in each of those tasks is retained, CG-03 and CG-04 remain **OPEN**, and no task's risk is lowered. Reconciles `branch-topology.md`, `control-gaps.md`, the affected repository-map entries and the Metrics task tracker. **Documentation/control only: no established release, default or development-topology branch is created, deleted, renamed or moved by `ADR-0011` in any managed repository; no repository setting, default branch, protection, ruleset, CI workflow, provider or deployment configuration is changed; and there is no runtime, schema, migration, GraphQL/API, auth, contract, generated client, publication or production effect.** - `ADM-ADR-01-CLOSEOUT-01`: reconcile [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md)'s durable repository approval metadata with the independent review, CTO approval and merge that already completed under `ADM-ADR-01` (issue [852](https://github.com/thoth-pub/thoth/issues/852), parent programme issue [846](https://github.com/thoth-pub/thoth/issues/846)). The ADR header and its section 17 now record `Status: APPROVED`, `Approved by: Javi, CTO` and `Approval date: 2026-08-28`, the proposal-state sentence becomes approved-state wording, and the ADR-0010 decision-register row records its approval blocker as satisfied. The approved architecture is unchanged: invariants, `ServiceOperation` semantics, ADR-0002 platform-domain separation, Distribution/Publisher Services boundaries, programme ordering and rollout semantics are untouched, as are the existing authority condition and the historical `ADM-ADR-01` verification base. **Documentation/control authority-state reconciliation only: no runtime, schema, migration, GraphQL/API, contract, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** - `ADM-ADR-01`: propose [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md), the Staff Operations Console architecture (issue [846](https://github.com/thoth-pub/thoth/issues/846)). The proposal reserves `/admin/*` for the superuser global control plane, keeps one publisher workspace entered through an explicit publisher operating context rather than identity impersonation, and makes Thoth the future canonical owner of staff-facing publisher-service operational records while separating desired state, execution, observed external state and attention/reconciliation. It defines `ServiceOperation` only as a common operational/audit seam — not a generic job/queue framework — preserves ADR-0002 platform-domain separation and ADR-0008 programme-local `distribution_job*`, and selects distribution as the first read-only operational integration before any replay or external-write activation. **Architecture/control proposal only: no runtime, schema, migration, GraphQL/API, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** From 907ea34f8dbcf88ea1c822492fe28aae3b5de993 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:12:30 +0100 Subject: [PATCH 12/48] THOTH-ASYNC-01: record round-3 architecture corrections --- ...H-ASYNC-01-ADR-01-implementation-report.md | 316 ++++++++---------- 1 file changed, 147 insertions(+), 169 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index baae37de6..1e8f2ca41 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -1,9 +1,8 @@ # THOTH-ASYNC-01-ADR-01 implementation report -This report records the bounded ADR-0012 authoring task and its first -independent-review correction round. Review/approval state itself remains in -GitHub under issues #957 and #958 rather than being copied as transient -repository status. +This report records the bounded ADR-0012 architecture-authoring task and its +independent-review correction rounds. Fast-changing review/approval state remains +in GitHub issues #957/#958 rather than being duplicated as repository status. ## Identity @@ -29,195 +28,183 @@ Exactly four repository paths are used: 3. `CHANGELOG.md`; 4. this implementation report. -No other source, migration, GraphQL, workflow or infrastructure path is changed. +No runtime source, migration, GraphQL implementation, workflow or +infrastructure path is changed. -## Initial candidate +## Initial candidate and review round 1 -The first complete proposal was handed to independent review at: +Initial candidate: ```text a69921682cd02e060110304f8f9868d0c39da65e ``` -It was based directly on: +The independent CRITICAL review returned `CHANGES REQUIRED`; durable receipt: +#958 comment `5896883454`. + +F-01 through F-09 were accepted. Round-2 candidate: ```text -develop @ 923545d5c9028bc04c40e38efeb7de674efed3fd +9d329e063b2731b691c62a412555b470f2a9fe39 ``` -and contained exactly the four authorized paths. +The round-1 corrections established precise ADR-0008/ADR-0010 boundaries, +crash-safe route identity, effect-phase checkpoints, bounded scheduling, +released-contract handling, worker task-role-only AWS authority, operability and +documentation hygiene. -## Independent review and correction round +## Independent review round 2 The independent CRITICAL review of -`a69921682cd02e060110304f8f9868d0c39da65e` returned -`CHANGES REQUIRED`. Durable control receipt is issue #958 comment -`5896883454`. - -All nine blocking findings were accepted and corrected within the existing -four-path documentation budget: - -### F-01 - ADR-0008 supersession - -The ADR now identifies the exact ADR-0008 statements/items displaced by the new -shared framework, states that ADR-0008 section 3.5 is satisfied rather than -superseded, and preserves the approved durable-work convention list, -`approved != mandatory` rule and machine-role/least-privilege boundaries. - -Approval-state reconciliation of ADR-0008 metadata/register is explicitly a -later decision-record action, not runtime implementation. - -### F-02 - ADR-0010 relationship +`9d329e063b2731b691c62a412555b470f2a9fe39` again returned +`CHANGES REQUIRED`; durable receipt: #958 comment `5909933000`. -The ADR now explicitly partially supersedes only ADR-0010's assumption that -`distribution_job*` remains the long-term execution source. +Seven blockers were accepted: -It defines: - -```text -async_job / async_job_attempt - canonical execution truth - -ServiceOperation - staff operational/audit seam where ADR-0010 applies - -observed state - separate domain truth - -attention / reconciliation - separate operational projection -``` +- R2-01 - incomplete WAITING/resume/attempt/concurrency/reconciliation lifecycle; +- R2-02 - resource-ambiguous idempotency keys and coalescing contradiction; +- R2-03 - event-routing completeness vulnerable to commit-order and mixed-version + worker assumptions; +- R2-04 - incomplete BE-04 consumer/API coverage, including `thoth-app`; +- R2-05 - unsafe same-release BE-04 table DROP under the actual rolling + migrate-on-start deployment model; +- R2-06 - untrusted publisher-file parsing sharing high Hosting authority; +- R2-07 - unbounded immutable payload snapshots/personal-data duplication. -`ServiceOperation` is not a second mutable attempt ledger. ADR-0010 staff -command, reconciliation and activation gates remain binding. +## Round-3 corrections -### F-03 - event route identity +### Job lifecycle -The corrected proposal gives every event durable identity/order and every -consumer a stable `route_key`. +The ADR now requires: -Materialization is database-unique on `(event_id, route_key)` regardless of -job terminal state. Route activation has a durable boundary so later code does -not silently subscribe to historical events; historical processing requires an -explicit backfill/replay. +- WAITING to atomically persist checkpoint state and release claim/lease; +- resume from WAITING as a new claim/attempt; +- separate bounded pre-write retry budget and post-write waiting/deadline; +- post-write deadline exhaustion to become `RECONCILIATION_REQUIRED`, not + `FAILED`; +- WAITING and RECONCILIATION_REQUIRED to retain their concurrency key by + default; +- effectful work to start only when its remaining lease/runtime can cover the + declared effect window; +- reconciliation itself to be a claimed, claim-token-fenced, kind-authorized + attempt; +- durable `EFFECT_CONFIRMED` evidence to permit non-duplicating recovery where + a kind defines it. -### F-04 - effectful lease expiry +### Effect-scoped idempotency and coalescing -The proposal now distinguishes effectful jobs and requires durable attempt phases -equivalent to `PRE_WRITE`, `EFFECT_STARTED` and `EFFECT_CONFIRMED`. +Idempotency now identifies a specific intended effect, not merely a resource. -Leases are renewable using the current claim token only; renewal cannot revive a -superseded claim. +Keys are derived from the event/route, source revision/fingerprint, schedule +slot, command/request id or explicit coalescing window as appropriate. -Lease expiry, crash, cancellation or shutdown at/after `EFFECT_STARTED` enters -`RECONCILIATION_REQUIRED` unless a durable domain checkpoint proves safe -resume. Provider identifiers must be persisted before yielding asynchronous -work, and resumed work polls/reconciles rather than blindly resubmitting. +Each `(event_id, route_key)` has its own durable route record. Several route +records may target one not-yet-started job only under an explicit coalescing +rule. Idempotency conflicts never silently mark a route complete. -### F-05 - scheduling, bounded attempts and cancellation +### Routing completeness -Priority, `available_at`, bounded attempts, distinct WAITING versus -RETRY_SCHEDULED semantics, per-kind concurrency/fairness and cancellation -semantics are now first-class architecture. +Route registrations and activation boundaries are durable PostgreSQL state. -Cancellation after a possible external effect does not assert that the effect -did not happen. +Routing completeness is determined per eligible route, not from worker-local +handler knowledge or an unproved sequence high-water mark. Mixed worker +versions cannot silently complete an unknown active route, and backfill uses the +same route materialization records. -### F-06 - released BE-04 contract +### Payload minimization -The proposal now separates BE-04 storage replacement from released API -compatibility. +Event/job payloads default to canonical IDs, source revision/fingerprint and +minimal command/routing parameters. -The future forward migration removes the three unused `distribution_job*` -tables, dedicated indexes and four queue-only enum types while retaining -`distribution_platform` and reconciling Diesel/domain code. +Credentials are prohibited. Personal data or immutable domain snapshots require +kind-specific justification and retention/erasure handling. Current-state work +reads canonical state at execution time. -The released BE-04 GraphQL lifecycle API receives a bounded deprecated -compatibility facade backed by generic jobs. `thoth-dissemination` migrates to -the generic contract first; later API removal is a separate approved deprecation -task. +### BE-04 expand-migrate-contract transition -Fresh production preflight must prove zero rows, disabled legacy creation, -inactive legacy executor and no live legacy contract activity before storage -DROP. +The previous state-translating compatibility-facade design has been removed. -### F-07 - worker AWS credential model +The architecture now requires: -The worker's AWS authority is exclusively its dedicated ECS task role. +1. **Expand** - Release N adds the generic schema/API while retaining legacy + BE-04 schema/API and keeping legacy automatic job creation inactive. +2. **Migrate consumers** - independently migrate both known released consumers: + `thoth-dissemination` and `thoth-app`. +3. **Retire legacy API** - later remove legacy lifecycle/read/filter/toggle + GraphQL contract after all consumers are verified migrated. +4. **Contract storage** - only in a still-later migration, after old code is no + longer a supported rollback target; the migration itself asserts all three + legacy tables contain zero rows and aborts otherwise. -Static AWS access keys are prohibited for the worker regardless of deployment -mechanism. The infrastructure task must create a headless service with a -dedicated task role rather than silently inheriting the existing shared-role, -web/ALB-oriented service pattern. +Known `thoth-app` dependencies are explicitly recorded: +`latestBackCatalogueJob`, `jobStatuses`, +`withoutBackCatalogueJob` and generated `DistributionJobStatus` contract. -Non-AWS credential values remain deployment environment variables but must be -injected from non-committed deployment configuration. +Historical `20260814_v1.7.0` remains immutable. +`distribution_platform` remains. -Existing service static-AWS-credential remediation is identified as separate -security work and is not performed by this task. +### Deployment and rollback -### F-08 - operability +The ADR now incorporates the authoritative current deployment property that the +GraphQL image defaults to `thoth init`, which runs migrations before serving, +while production uses multiple rolling ECS tasks. -The ADR now requires queryable queue/worker signals, per-kind bounded -attempts/concurrency/backoff, starvation protection, poison-job attention, -protected audited operator controls, immutable retained attempt history and -separately governed retention. +Therefore destructive contraction cannot occur in the same release that moves +the API. The previous binary/schema remains rollback-compatible throughout +expand/migrate/API-retirement phases; after generic data exists, runtime rollback +must preserve that data and use forward recovery rather than deleting/reverting +it. -Generic retry cannot move `RECONCILIATION_REQUIRED` back to an effectful -attempt without domain proof of safety. +### Trust-separated worker pools -### F-09 - durable documentation hygiene +The default trusted worker still uses one routine task role containing the union +of approved routine trusted capabilities. -The ADR-0012 decision-register row is inside the Markdown decision table. +Handlers that parse publisher-controlled/untrusted content are a distinct +`UNTRUSTED_CONTENT` risk class and run in a separate task/service/process +boundary with minimal authority and no Hosting DNS/ACM/CloudFront +tenant-management permissions unless explicit CTO risk acceptance records the +blast radius. -This report no longer carries a transient `Status: ... awaiting review` line. +This is a real trust-boundary exception, not one IAM role per ordinary handler. -## Architecture currently recorded +### Validation additions -The corrected proposal includes: +The ADR now requires tests for: -- one PostgreSQL-backed cross-programme event/route/job engine; -- transactional event creation where coupled to PostgreSQL business writes; -- durable crash-safe route materialization; -- typed/versioned event and job contracts; -- absolute job idempotency plus separate route idempotency; -- priority, delayed availability, bounded attempts and concurrency/fairness; -- claim-token-fenced renewable leases; -- durable effect checkpoints and reconciliation-required handling; -- append-only sanitized attempt evidence; -- one default headless `thoth-worker` Fargate runtime for Thoth-owned handlers; -- domain-owned external executors such as `thoth-dissemination`; -- one dedicated routine worker AWS task role, with no static AWS worker keys; -- non-AWS credentials supplied as environment variables from non-committed - deployment configuration; -- no automated legacy-CDN migration; -- explicit ADR-0008 and ADR-0010 partial-supersession boundaries; -- a BE-04 GraphQL compatibility window over generic storage; -- ADR-level observability and protected operator-control requirements. +- mixed worker versions; +- out-of-order commits; +- route completeness/backfill; +- effect-scoped idempotency and coalescing; +- WAITING claim release/resume; +- pre-write versus post-write exhaustion; +- concurrency-key retention; +- claimed reconciliation races; +- payload minimization; +- untrusted-worker IAM isolation; +- both downstream consumer migrations; +- rolling deploy/rollback safety; +- contraction abort on any legacy row. ## Migration/data effect No migration was created, modified or executed. -The proposal records the CTO-provided environment fact that v1.7.0 has been -applied to production and its `distribution_job*` tables have not been -operationally used, while dev has not applied that migration. - -Historical `20260814_v1.7.0` remains immutable. - -Any future storage removal requires a separately authorized forward migration -and fresh zero-use production preflight. +Production's v1.7.0 legacy schema fact remains an architecture input. The ADR no +longer proposes immediate replacement/drop: generic schema expansion precedes +consumer migration/API retirement, and legacy storage contraction is a later +separately authorized migration. ## Authorization/security effect No authorization implementation changed. -ADR-0008's domain-specific machine-role, least-privilege and `SUPERUSER` -separation rules remain in force. - -No IAM role, policy, credential, provider configuration or environment variable +No IAM role/policy, credential, provider configuration or environment variable was created, rotated or changed. +ADR-0008 domain-specific application authorization remains binding. Worker-pool +IAM authority and ZITADEL/application authorization remain separate concepts. + ## External/runtime effects ```text @@ -230,50 +217,41 @@ release/publication: 0 production activation: 0 ``` -Repository/GitHub mutations remain bounded to the task ledger, existing task -branch and authorized documentation footprint. +## Validation performed for this documentation task -## Validation and evidence +- exact authorized base was preserved; +- all candidate commits remain on `feature/async/adr-0012`; +- cumulative diff is verified against the authorized base after each correction + round; +- ADR remains `PROPOSED`; +- decision-register row remains inside the Markdown table; +- no PR has been created; +- no runtime test result is claimed by this documentation-only task. -Initial branch preflight established: - -- `develop` exactly matched authorized base - `923545d5c9028bc04c40e38efeb7de674efed3fd`; -- no conflicting `feature/async*` ref existed; -- ADR-0012 did not already exist; -- ADR-0011 was already allocated. - -The first independent review independently verified the original candidate's -exact four-path footprint and exact head before returning `CHANGES REQUIRED`. - -The corrected final branch head is recorded in issue #958 after this report -commit and is the SHA to which the next independent review must bind. - -No runtime tests are claimed by this documentation-only task. +The final round-3 head is recorded in #958 after this report commit. Any further +independent review is valid only against that exact SHA. ## Deviation history -During initial issue creation, the GitHub connector omitted issue numbers from -the normalized create response, causing #958 initially to contain -`#undefined` as its parent reference. The returned issue URLs established -#957/#958 and the ledger was corrected before branch/source mutation. +During initial issue creation, the GitHub connector omitted the issue number in +its normalized create response, briefly producing a `#undefined` parent +reference in #958. The returned URLs established #957/#958 and the issue was +corrected before branch/source mutation. No source-scope deviation remains. ## Remaining lifecycle gates -The repository files themselves do not assert completion of these gates. - -The task still requires, in order: - -1. fresh independent CRITICAL exact-head review of the corrected final head; -2. exact-content CTO architecture approval if that review returns APPROVED; -3. approval-state decision-record reconciliation, including ADR-0008/ADR-0010 - supersession metadata; +1. fresh independent CRITICAL exact-head review of the round-3 candidate; +2. exact-content CTO architecture approval only if that review returns + `APPROVED`; +3. separate approval-state decision-record reconciliation, including ADR-0008 + and ADR-0010 metadata, under its own authorized write budget; 4. PR creation only under separate authorization; -5. required CI and independent exact-head source/document review; -6. explicit CTO merge authorization for the CRITICAL task; -7. merge into `develop`; -8. separately specified and authorized implementation/migration/deployment tasks. +5. required CI/document checks and independent exact-head review; +6. explicit CTO merge authorization; +7. merge to `develop`; +8. separately specified and authorized implementation, migration, downstream + consumer, infrastructure, deployment and activation tasks. This report authorizes none of those later actions. From 482e2df9b719a230488936079edaf334df02a3ee Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:14:22 +0100 Subject: [PATCH 13/48] THOTH-ASYNC-01: tighten round-3 trust and ADR boundaries --- ...ed-asynchronous-event-and-job-execution.md | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index d58124463..d67625afb 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -10,7 +10,7 @@ Authoring task: [THOTH-ASYNC-01-ADR-01 #958](https://github.com/thoth-pub/thoth/ Superseded by: None Supersedes in part: - ADR-0008 only where it denies a reusable cross-programme job framework/API: the final shared-framework prohibition in section 3.3, section 3.4's programme-local-only ownership rule, section 5.1 item 6, section 5.2 item 6 and rejected alternative D. ADR-0008 section 3.5 is **satisfied by this ADR, not superseded**. Section 3.3's approved convention list, its `approved primitive != mandatory mechanism` rule, and all machine-role, least-privilege and `SUPERUSER` separation rules remain binding. -- ADR-0010 only where invariant 15 and section 7.2 assume `distribution_job*` remains the long-term Publisher Services execution source. ADR-0010's Staff Operations Console, `ServiceOperation` audit seam, desired/execution/observed-state separation, attention/reconciliation model and staff-command gates remain binding as specified in section 1.3 below. +- ADR-0010 only where invariant 15 and section 7.2 assume `distribution_job*` remains the long-term Publisher Services execution source, and where section 4.4's final sentence says ADR-0008 remains *fully* binding without the later shared-framework exception introduced here. Section 4.4's substantive rule that `ServiceOperation` is not itself a queue/framework/executor API remains binding. ADR-0010's Staff Operations Console, `ServiceOperation` audit seam, desired/execution/observed-state separation, attention/reconciliation model and staff-command gates remain binding as specified in section 1.3 below. Decision: Thoth establishes one shared PostgreSQL-backed asynchronous event and job engine for cross-programme durable work. Events record durable facts and may @@ -111,7 +111,10 @@ primitives because the cross-programme workload now justifies them; it does not turn every ADR-0008 primitive into a mandatory mechanism for unrelated work. ADR-0010 remains the authority for the Staff Operations Console and the -cross-domain operational/audit seam. The relationship is binding: +cross-domain operational/audit seam. Its section 4.4 statement that ADR-0008 +remains fully binding is qualified only by the exact ADR-0008 partial +supersession recorded by this later ADR; the `ServiceOperation` seam itself does +not become the generic queue or executor API. The relationship is binding: ```text async_job / async_job_attempt @@ -321,8 +324,10 @@ Exact enum spelling is implementation detail. Transitioning to `WAITING` atomically persists the durable checkpoint and releases the current claim and lease. A later resume is a new claim and a new -attempt that reads the checkpoint. Waiting/polling attempts are not silently -treated as fresh external submissions. +attempt that reads the checkpoint. Waiting/polling attempts do **not** consume +the pre-write retry budget; they are bounded by the kind's waiting deadline, +poll/backoff policy and any separately defined reconciliation budget. They are +not silently treated as fresh external submissions. Every job carries first-class scheduling attributes including `priority`, `available_at`, the owning kind and counters/deadlines appropriate to its @@ -550,10 +555,20 @@ worker pool with a minimal dedicated task role. It may use the same canonical async database/protocol, but its AWS/provider capabilities are limited to those needed for the untrusted-content job family. +The untrusted-content pool must also avoid inheriting the primary application +database credential merely because it uses the same canonical async engine. Its +implementation must use either: + +- the protected kind-scoped executor API; or +- a separately reviewed database principal whose permissions are limited to the + exact async/file-processing operations required by that pool. + +It must not receive broad canonical-domain mutation authority by default. + A handler may cross that boundary only through explicit CTO risk acceptance -that identifies the concrete parser/runtime, standing authorities and blast -radius. This is a trust-boundary exception to the single routine role, not a -return to one IAM role per ordinary handler. +that identifies the concrete parser/runtime, AWS/provider authority, database +authority and blast radius. This is a trust-boundary exception to the single +routine role, not a return to one IAM role per ordinary handler. Graceful shutdown stops new claims first. A pre-write attempt may report a retryable pre-write outcome or release/yield according to the shared transition From 6af54e45478f3ecb9422c395a4f7c070700de240 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:14:54 +0100 Subject: [PATCH 14/48] THOTH-ASYNC-01: make route activation concurrency-safe --- ...DR-0012-shared-asynchronous-event-and-job-execution.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index d67625afb..1553834d4 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -239,6 +239,14 @@ Every event consumer has a durable route registration with: - the supported event kind/version contract; - enough durable state to decide whether an event is eligible for that route. +Route activation boundaries must themselves be safe under concurrent +transactions. The implementation must use a durable routing generation/epoch +(or an equivalently strong serialized database mechanism) that an event captures +inside its creating transaction and a route records when it becomes active. +Eligibility is evaluated from those durable values. A route boundary must +therefore partition events deterministically even if transactions commit out of +sequence; sequence allocation or commit observation order alone is insufficient. + For every committed event, the engine must eventually establish a durable routing/materialization record for every route whose durable activation boundary the event meets. Those route records are unique on `(event_id, route_key)` From 2d5eb1ae1e25bc222c4ca94a4f6dc5e81ecceba8 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 12:15:17 +0100 Subject: [PATCH 15/48] THOTH-ASYNC-01: finalize round-3 correction report --- .../THOTH-ASYNC-01-ADR-01-implementation-report.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 1e8f2ca41..408fa241d 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -73,11 +73,18 @@ Seven blockers were accepted: ## Round-3 corrections +The final tightening also explicitly qualifies ADR-0010 section 4.4's statement +that ADR-0008 remains fully binding: `ServiceOperation` remains non-queue audit +infrastructure, while ADR-0012 supplies the later explicit shared-framework +exception ADR-0008 anticipated. + ### Job lifecycle The ADR now requires: - WAITING to atomically persist checkpoint state and release claim/lease; +- WAITING/poll attempts not to consume the pre-write retry budget, instead being + bounded by the waiting deadline/poll policy; - resume from WAITING as a new claim/attempt; - separate bounded pre-write retry budget and post-write waiting/deadline; - post-write deadline exhaustion to become `RECONCILIATION_REQUIRED`, not @@ -105,6 +112,9 @@ rule. Idempotency conflicts never silently mark a route complete. ### Routing completeness Route registrations and activation boundaries are durable PostgreSQL state. +Activation uses a durable routing generation/epoch or equivalently strong +serialized database mechanism captured transactionally by events/routes, so +out-of-order commits cannot create an ambiguous boundary. Routing completeness is determined per eligible route, not from worker-local handler knowledge or an unproved sequence high-water mark. Mixed worker @@ -164,7 +174,9 @@ Handlers that parse publisher-controlled/untrusted content are a distinct `UNTRUSTED_CONTENT` risk class and run in a separate task/service/process boundary with minimal authority and no Hosting DNS/ACM/CloudFront tenant-management permissions unless explicit CTO risk acceptance records the -blast radius. +blast radius. That pool also does not inherit the primary application database +credential: it must use the kind-scoped executor API or a separately reviewed +minimal database principal. This is a real trust-boundary exception, not one IAM role per ordinary handler. From 359c3b7c33cc1a6794dca48b46818da039c1d627 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 13:59:51 +0100 Subject: [PATCH 16/48] THOTH-ASYNC-01: address round-3 review findings --- ...ed-asynchronous-event-and-job-execution.md | 558 +++++++++++++----- 1 file changed, 397 insertions(+), 161 deletions(-) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 1553834d4..584ce245a 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -235,42 +235,74 @@ not assume sequence allocation order equals commit order. Every event consumer has a durable route registration with: - a stable `route_key`; -- an explicit activation boundary recorded in PostgreSQL; +- an explicit activation boundary; +- an optional explicit deactivation boundary; - the supported event kind/version contract; - enough durable state to decide whether an event is eligible for that route. -Route activation boundaries must themselves be safe under concurrent -transactions. The implementation must use a durable routing generation/epoch -(or an equivalently strong serialized database mechanism) that an event captures -inside its creating transaction and a route records when it becomes active. -Eligibility is evaluated from those durable values. A route boundary must -therefore partition events deterministically even if transactions commit out of -sequence; sequence allocation or commit observation order alone is insufficient. - -For every committed event, the engine must eventually establish a durable -routing/materialization record for every route whose durable activation boundary -the event meets. Those route records are unique on `(event_id, route_key)` -regardless of whether the resulting job is pending, running, waiting or terminal. +Activation and deactivation are durable, separately authorized and audited +operations. They are not side effects of merely deploying or deleting handler +code. + +Route boundaries use a durable routing generation/epoch, or an equivalently +strong serialized database mechanism, that an event captures inside its +creating transaction and a route records when it is activated or deactivated. +An event is eligible exactly when its captured routing generation is at or after +the route's activation generation and, when a deactivation generation exists, +strictly before that deactivation generation. + +That interval must partition events deterministically even when transactions +commit out of sequence. Sequence allocation order and commit observation order +alone are insufficient. + +For every committed event, the engine must eventually establish a durable route +disposition for every route for which that event is eligible. The normal +disposition materializes a job and is unique on `(event_id, route_key)` +regardless of whether the resulting job is pending, running, waiting or +terminal. + +A route may not be "fixed" by deleting its registration. Deactivation affects +only events at or after the deactivation boundary. Events already eligible +before that boundary remain owed after retirement. If an eligible route can no +longer be materialized safely, each affected event requires an explicit, +authorized, audited terminal route disposition describing why it was not +materialized and what reconciliation/backfill, if any, covers it. A crash after some consumers are materialized therefore resumes from durable route records. A worker must never mark an event fully routed merely because it processed all routes known to its own binary. During a mixed-version deployment, -if a worker encounters an active durable route whose kind/version it cannot +if a worker encounters an eligible route whose kind/version it cannot materialize, it leaves that route incomplete for a compatible worker rather than silently completing the event. -A route introduced by a later release receives only events at or after its -durable activation boundary. Historical events are processed by that route only -through an explicitly authorized backfill/replay. Backfill uses the same -`(event_id, route_key)` materialization identity, so it cannot bypass routing -deduplication. +A route introduced by a later release receives only events in its active +routing-generation interval. Historical events are processed only through an +explicitly authorized backfill/replay using the same `(event_id, route_key)` +identity; backfill cannot bypass route deduplication. + +### Event-emission deployment floor + +A route consuming event kind/version `K` may be activated only after deployment +evidence proves that **every running binary that can perform the relevant +business mutation, and every binary still retained as a supported production +rollback target, emits K atomically with that mutation**. + +The event-emission floor is therefore established before route activation. +Activating a route while some serving/rollback binary can still perform the +business mutation without emitting K is prohibited. + +If production must roll back below that emission-capable floor after activation, +the route is deactivated before the older binary can perform relevant writes, or +the rollback is accompanied by an explicitly authorized current-state +reconciliation/backfill that covers the precise emission gap. Silent loss of +business changes is not an acceptable rollback behaviour. The implementation may optimize discovery/scanning, but routing completeness -must be proven from durable event/route/materialization records. Any cursor or +must be proven from durable event/route/disposition records. Any cursor or high-water optimization must prove that no lower/in-flight event can commit later and be skipped; absent such proof, it is not authoritative. -### 3.3 Versioned contracts and payload minimization +### 3.3 Versioned contracts, payload minimization and revision semantics Every event and job kind has: @@ -300,11 +332,36 @@ kind-specific approved specification proves that references are insufficient, bounds the data, and defines retention/erasure behaviour. The generic engine's immutability/retention rules must not make a data-erasure obligation impossible. -A current-state job reads canonical state when it executes and records which -revision/fingerprint it acted on. Historical replay is permitted only when the -referenced historical revision is actually retained and authorized for replay; -a stale embedded snapshot must never silently substitute for canonical -current-state execution. +Every job kind declares its **revision semantics**. The initial architectural +modes are: + +```text +CURRENT_STATE +REVISION_BOUND +``` + +A `CURRENT_STATE` job means "make the external/derived target reflect the +latest canonical state when this job executes". It reads canonical state at +execution time, records the revision/fingerprint actually acted on, may coalesce +older triggers under its explicit kind rule, and may complete as a deterministic +no-op when the required current revision is already applied. + +A `REVISION_BOUND` job means "process exactly this retained source +revision/fingerprint". Before any external write, the handler must determine +whether a newer revision for the same effect/concurrency target has already been +applied. If so, the older job must not overwrite/regress that newer state: it +records a deterministic superseded/no-effect disposition unless it is an +explicitly authorized historical replay/rollback operation. + +Each REVISION_BOUND kind therefore defines the revision ordering/evidence by +which "newer already applied" is established. Merely serializing execution by a +concurrency key is not sufficient because an older retry may run after a newer +job has completed. + +Historical replay is permitted only when the referenced historical revision is +actually retained and the replay/rollback is explicitly authorized. A stale +embedded snapshot must never silently substitute for either current-state +execution or an authorized historical replay. ### 3.4 Job lifecycle, scheduling and claims @@ -491,7 +548,9 @@ effect can exist. If durable `EFFECT_CONFIRMED` evidence exists before a crash, a later claimed recovery may complete `SUCCEEDED` without repeating the effect, where the -kind's contract defines that evidence as sufficient. +kind's contract defines that evidence as sufficient. `EFFECT_CONFIRMED` is +execution evidence that rules out replay of that effect; it is **not** by itself +ADR-0010 observed publication, acceptance or current external-state truth. An indeterminate provider write is never blindly replayed merely because the worker did not receive an acknowledgement. Lease expiry, process crash, @@ -503,7 +562,7 @@ non-duplicating continuation. The owning handler/reconciler must establish provider state or require protected operator attention before another effectful attempt. -### 3.7 Attempts and diagnostics +### 3.7 Attempts, diagnostics and evidence retention Each execution attempt is append-only durable evidence containing enough sanitized information to reconstruct: @@ -520,6 +579,14 @@ sanitized information to reconstruct: Secrets, credentials and unbounded provider responses must not be written to attempt records or ordinary logs. +Payload minimization and data-protection rules apply equally to attempt +diagnostics and provider evidence. Personal data or provider response excerpts +may be retained only where the kind-specific specification proves they are +required, bounds/sanitizes them and defines retention/erasure behaviour. +Append-only auditability does not override an applicable erasure obligation; +where evidence must be redacted/tombstoned, the system retains the non-sensitive +fact that evidence existed and why it was altered. + ### 3.8 Worker runtime and trust-separated pools The default always-on executor is a headless ECS/Fargate service using the @@ -558,36 +625,64 @@ A handler that parses or executes against publisher-controlled/untrusted file content is a separate **UNTRUSTED_CONTENT** risk class. It must not run in a task that also holds Hosting DNS, ACM or CloudFront tenant-management authority. -Such handlers execute in a separate task/service or equivalent process-isolated -worker pool with a minimal dedicated task role. It may use the same canonical -async database/protocol, but its AWS/provider capabilities are limited to those -needed for the untrusted-content job family. +Such handlers **must use the same canonical async engine/protocol** through a +separate task/service or equivalent process-isolated worker pool with a minimal +dedicated task role. This is a trust-separated executor pool, not a second queue +architecture. + +The untrusted-content pool: + +- may read only the input objects required by its authorized jobs; +- writes transformed/generated output only to an isolated quarantine/staging + location, never directly to publisher-served or Hosting production paths; +- cannot choose or write the final publisher domain, served bucket, served key + or Hosting target; +- cannot create arbitrary downstream events/jobs; it may report only the + declared bounded completion/evidence facts for its authorized job kinds; +- receives no broad canonical-domain mutation authority. -The untrusted-content pool must also avoid inheriting the primary application -database credential merely because it uses the same canonical async engine. Its -implementation must use either: +Untrusted completion payloads/results are themselves **untrusted data**. +A trusted promotion/deployment job validates the staged artifact, verifies its +hash/size/type/evidence, and resolves publisher identity, destination domain, +bucket, key and Hosting/CDN target from canonical Thoth state. Trusted handlers +must never take authority-bearing destination values from an untrusted result. + +The untrusted-content pool must not inherit the primary application database +credential. It uses either: - the protected kind-scoped executor API; or -- a separately reviewed database principal whose permissions are limited to the - exact async/file-processing operations required by that pool. +- a separately reviewed database principal whose database enforcement limits it + to the exact async/file-processing rows and operations required by that pool. + +Table-level grants on shared async tables are not sufficient kind isolation. +Direct database access therefore requires row/kind enforcement such as RLS, +security-definer functions/procedures or an equivalently reviewed database +boundary. -It must not receive broad canonical-domain mutation authority by default. +Its AWS/provider role likewise excludes Hosting DNS, ACM and CloudFront +tenant-management authority and direct write access to publisher-served +locations unless an explicit CTO risk acceptance identifies the concrete need +and blast radius. A handler may cross that boundary only through explicit CTO risk acceptance that identifies the concrete parser/runtime, AWS/provider authority, database -authority and blast radius. This is a trust-boundary exception to the single -routine role, not a return to one IAM role per ordinary handler. +authority, served-content authority and blast radius. This is a trust-boundary +exception to the single routine role, not a return to one IAM role per ordinary +handler. -Graceful shutdown stops new claims first. A pre-write attempt may report a -retryable pre-write outcome or release/yield according to the shared transition -contract. An effectful attempt after `EFFECT_STARTED` must persist a safe -checkpoint and yield `WAITING`, or enter `RECONCILIATION_REQUIRED`; shutdown -must never intentionally abandon it to ordinary lease-expiry retry. +### Shutdown/recovery for every pool -The runtime must prove clean recovery after abrupt termination as well as +Graceful shutdown rules apply to every worker pool. A pool stops new claims +first. A pre-write attempt may report a retryable pre-write outcome or +release/yield according to the shared transition contract. An effectful attempt +after `EFFECT_STARTED` must persist a safe checkpoint and yield `WAITING`, or +enter `RECONCILIATION_REQUIRED`; shutdown must never intentionally abandon it +to ordinary lease-expiry retry. + +Every pool must prove clean recovery after abrupt termination as well as graceful shutdown. -### 3.9 Handler registry, capabilities and risk class +### 3.9 Handler registry, capabilities, risk class and revision mode Each worker pool has a typed registry of supported job kinds. Each handler declares: @@ -596,15 +691,18 @@ declares: - owning domain; - required runtime capability/configuration; - trust/risk class, including whether it handles untrusted content; +- revision semantics: `CURRENT_STATE` or `REVISION_BOUND`; - effect classification and expected effect window; - timeout/lease/waiting-deadline requirements; - retry/reconciliation policy; -- idempotency/coalescing/concurrency rules. +- idempotency/coalescing/concurrency rules; +- for high-volume families, retention/coalescing/admission/backpressure rules + required before activation. -A worker only claims job kinds whose required capabilities and risk class are -configured for that pool. Capability declarations are runtime/configuration -boundaries and scheduling primitives; they do not imply one IAM role per -ordinary handler. +A worker only claims job kinds whose required capabilities, revision mode and +risk class are configured for that pool. Capability declarations are +runtime/configuration boundaries and scheduling primitives; they do not imply +one IAM role per ordinary handler. External/domain executors use the same kind scoping: their authorization permits only the job kinds assigned to that domain executor. A dissemination executor @@ -868,37 +966,74 @@ After Release N is the deployed/rollback-safe Thoth version: Until both known consumers (and any newly discovered consumers) are verified on the generic contract, legacy BE-04 API/storage remains present and unchanged. -### 4.4 Legacy GraphQL contract retirement +### 4.4 Legacy API and runtime retirement + +Only after all downstream consumers are verified migrated may a later +separately approved Thoth release retire the legacy BE-04 contract. -Only after all consumers are verified migrated may a later separately approved -Thoth release remove the legacy BE-04 GraphQL lifecycle/read/filter -fields/types/toggle. +Retirement covers **every runtime path that can read or write +`distribution_job*`**, not merely the named GraphQL job fields/types/filters. +Before this release is approved, a repository-wide source audit must identify +and disposition all runtime legacy-table access. -That removal follows the repository's approved deprecation-path rule and must -include generated SDL/client impact. It must not collapse -`WAITING`/`RECONCILIATION_REQUIRED` into the legacy five-value -`DistributionJobStatus`; consumers migrate to the new lifecycle before the -legacy enum disappears. +At the current architecture baseline this includes at least: -The legacy creation path must be removed/disabled before any subsequent storage -DROP can make it invalid. +- legacy BE-04 claim/complete/fail/cancel/query/filter GraphQL surfaces; +- `PublisherServiceConfigurationSummary` legacy job reads; +- `replacePublisherServiceConfiguration` automatic back-catalogue creation; +- its fail-closed `AUTOMATIC_PUSH` activation guard; +- assignment-disable cancellation through + `cancel_pending_jobs_for_disabled_group_on`. + +The Publisher Services desired-state mutation retains its atomic/fail-closed +invariant throughout retirement: + +- if an activation requires asynchronous onboarding work and the approved + generic creation path is active, the desired-state change and caused generic + event/job intent commit atomically; +- if that generic path is not active/available, the activation remains rejected + rather than committing without its required work; +- assignment disable/cancellation/supersession is redirected to the generic + lifecycle or remains conservatively blocked; it does not query a legacy table + that a later release may remove. + +The legacy creation toggle/guard is therefore replaced by the generic rule or +retained as a fail-closed gate until replacement is available; it is never +simply deleted while allowing jobless activations. + +This retirement release follows the repository's approved deprecation path and +updates generated SDL/client evidence. It does not collapse +`WAITING`/`RECONCILIATION_REQUIRED` into the old five-value +`DistributionJobStatus`. + +A retirement gate must prove, by repository-wide runtime-reference search and +tests, that the deployed/rollback-safe Phase-C binary performs **zero runtime +queries or writes against the legacy job tables**. Compile-time/inert schema or +type declarations may remain temporarily for the later contraction task, but no +request, background path or feature toggle may touch those relations. + +Only after that zero-runtime-access release is deployed and its rollback window +is closed may storage contraction proceed. ### 4.5 Later storage contraction Legacy storage is removed only in a **later destructive migration** after: 1. the generic schema/API release is the sole running production version; -2. the legacy GraphQL contract has been retired from deployed Thoth; +2. the legacy API/runtime retirement release is deployed and repository evidence + proves zero runtime legacy-table access; 3. all known downstream consumers are on the generic contract; -4. the previous deployment rollback target no longer requires the legacy +4. the previous deployment rollback target is the zero-runtime-access Phase-C + binary and therefore no supported rollback binary requires the legacy relations; -5. no executor is claiming/writing the legacy contract; -6. the destructive migration itself verifies all three legacy tables exist as - expected and contain zero rows. - -The emptiness check is part of the same migration/transactional gate that -performs the DROP. If any legacy row exists, the migration aborts and production -deployment is HOLD; an earlier read-only preflight is useful evidence but is not +5. no executor can claim/write the legacy contract; +6. the destructive migration obtains locks that prevent a concurrent legacy + write, verifies all three legacy tables exist as expected and contain zero + rows, and performs the DROP under the same fail-closed migration transaction. + +If any legacy row exists, required relation is unexpected, or the final locked +emptiness assertion cannot be established, the migration aborts and production +deployment is HOLD. An earlier read-only preflight is useful evidence but is not sufficient by itself. The contraction removes: @@ -915,9 +1050,12 @@ The contraction removes: It **retains** `distribution_platform`, which remains Publisher Services domain state. -The same bounded source/migration task atomically reconciles -`thoth-api/src/schema.rs`, persistence/domain models, CRUD/query code and tests -as required by ADR-0003 and repository doctrine. +Because all runtime legacy access was already removed in Phase C, the Phase-D +source/migration change removes only remaining inert legacy declarations such as +Diesel `schema.rs` table/type definitions and obsolete model/type declarations, +atomically with the DROP as required by ADR-0003 and repository doctrine. An +older Phase-C task may coexist during the rolling Phase-D deployment because it +contains no runtime access to the relations being removed. ### 4.6 Preserve proven BE-04 safety work @@ -1031,56 +1169,74 @@ execution until a separate approved architecture decision says otherwise. 1. PostgreSQL remains canonical for event/job lifecycle. 2. Business mutation plus caused event is atomic where both are PostgreSQL-owned. -3. Route registrations/activation boundaries are durable database state; routing - completeness is proven per eligible `(event_id, route_key)`, not by - worker-local route knowledge or an unsafe sequence high-water mark. -4. A newly registered route does not consume historical events without an +3. Route registration, activation and deactivation boundaries are durable + database state; eligibility is a durable routing-generation interval. +4. Route activation occurs only after all serving and supported rollback binaries + that can perform the relevant business mutation emit the consumed event + kind/version atomically. +5. Deactivation never erases previously eligible work: pre-deactivation events + remain owed until materialized or given an explicit authorized/audited route + disposition. +6. A newly activated route does not consume historical events without an explicit backfill/replay through the same route materialization records. -5. Job idempotency identifies a specific intended effect; a resource-only key +7. Job idempotency identifies a specific intended effect; a resource-only key must not suppress later revisions/schedule slots/commands. -6. Every event-route record maps to exactly one job; many route records may map +8. Every event-route record maps to exactly one job; many route records may map to one job only under an explicit not-yet-started coalescing rule. -7. No stale/superseded claim can finalize, checkpoint, cancel, renew or - reconcile a job. -8. WAITING atomically checkpoints and releases claim/lease; resume is a new - claimed attempt. -9. Effectful attempts durably fence `EFFECT_STARTED` before crossing the - external-write boundary and start only when the remaining effect window fits - within the current lease/runtime. -10. Lease expiry/shutdown/cancellation/deadline exhaustion after an ambiguous +9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. + A REVISION_BOUND effect must not regress a newer already-applied revision + except through explicit historical replay/rollback authorization. +10. No stale/superseded claim can finalize, checkpoint, cancel, renew or + reconcile a job. +11. WAITING atomically checkpoints and releases claim/lease; resume is a new + claimed attempt. +12. Effectful attempts durably fence `EFFECT_STARTED` before crossing the + external-write boundary and start only when the remaining effect window fits + within the current lease/runtime. +13. Lease expiry/shutdown/cancellation/deadline exhaustion after an ambiguous effect cannot return the job to ordinary retry or terminal FAILED; it requires reconciliation unless durable evidence proves a stronger outcome. -11. WAITING and RECONCILIATION_REQUIRED retain concurrency-key exclusion by - default. -12. Reconciliation is itself kind-authorized, claim-token-fenced and recorded as +14. `EFFECT_CONFIRMED` prevents duplicate replay where defined; it is not by + itself ADR-0010 observed-state/publication/acceptance truth. +15. WAITING and RECONCILIATION_REQUIRED retain concurrency-key exclusion by + default, and blocked successors are operationally visible. +16. Reconciliation is itself kind-authorized, claim-token-fenced and recorded as an attempt. -13. No job handler may claim exactly-once external effects. -14. Priority, delayed availability, bounded pre-write attempts, +17. No job handler may claim exactly-once external effects. +18. Priority, delayed availability, bounded pre-write attempts, post-write/waiting deadlines, per-kind concurrency and fairness are first-class shared-engine concerns. -15. `async_job`/`async_job_attempt` are canonical execution truth; +19. `async_job`/`async_job_attempt` are canonical execution truth; `ServiceOperation` is the ADR-0010 staff operational/audit seam where applicable, not a second attempt ledger. -16. Event/job payloads default to canonical references plus revision/fingerprint +20. Event/job payloads default to canonical references plus revision/fingerprint and minimal parameters; credentials are forbidden and personal/snapshot data - requires explicit retention/erasure justification. -17. Domain ownership is explicit and preserved. -18. Generic execution does not create generic application authorization. -19. Historical migrations are never rewritten after deployment. -20. Trusted-worker AWS authority comes exclusively from its dedicated ECS task - role; untrusted-content parsing runs in a separate minimal-authority worker - pool unless explicitly risk-accepted by the CTO. -21. No worker receives static AWS access keys. -22. Non-AWS credential values are not committed or emitted in logs/durable + requires explicit retention/erasure justification. The same rule applies to + attempt diagnostics/provider evidence. +21. Domain ownership is explicit and preserved. +22. Generic execution does not create generic application authorization. +23. Historical migrations are never rewritten after deployment. +24. Trusted-worker AWS authority comes exclusively from its dedicated ECS task + role. +25. UNTRUSTED_CONTENT jobs use the same canonical engine but a separate + minimal-authority execution boundary; they write only quarantine/staging + output and cannot choose or write served/Hosting destinations. +26. Trusted promotion resolves authority-bearing destinations from canonical + state and treats untrusted results as untrusted data. +27. No worker receives static AWS access keys. +28. Non-AWS credential values are not committed or emitted in logs/durable diagnostics. -23. A worker/executor may claim only supported job versions/kinds and configured +29. A worker/executor may claim only supported job versions/kinds and configured capabilities/risk classes. -24. Protected operator retry/reconcile controls are audited and may not bypass +30. High-volume job kinds define retention/coalescing/admission/backpressure + rules before activation. +31. Protected operator retry/reconcile controls are audited and may not bypass reconciliation safety. -25. BE-04 retirement follows expand -> migrate consumers -> retire API -> - contract storage; legacy storage is never dropped while old code remains a - production rollback target. -26. Merge, migration, deployment, handler activation, provider access and +32. BE-04 retirement follows expand -> migrate consumers -> remove every runtime + legacy-table dependency -> close rollback window -> contract storage. +33. Legacy storage is never dropped while a supported running/rollback binary + can query or write it. +34. Merge, migration, deployment, handler activation, provider access and production activation remain separate gates. ## 8. Implementation impact and decomposition @@ -1089,11 +1245,13 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: 1. **Release N shared schema/engine/API in `thoth`** - additive generic async migration; - - durable event-route registration/materialization model; + - durable route registration/activation/deactivation/materialization model; - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - effect-scoped idempotency and explicit coalescing; + - revision-semantics contract; - new kind-scoped executor API; + - generic operational/read contract required by downstream consumers; - legacy BE-04 schema/API left intact and inactive; - real PostgreSQL concurrency/mixed-version/out-of-order-commit tests. @@ -1102,7 +1260,7 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - handler registry; - graceful shutdown/checkpointing; - metrics/health/operational reporting; - - capability/kind filtering. + - capability/kind/revision-mode filtering. 3. **Infrastructure - trusted worker** - headless Fargate service not bound to the ALB; @@ -1114,34 +1272,43 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: 4. **Infrastructure/runtime - untrusted-content pool** - separate process/task/service boundary for publisher-controlled parsers; - minimal task role with no Hosting DNS/ACM/CloudFront tenant authority; + - quarantine/staging-only output permissions; + - protected executor API or row/kind-enforced minimal database principal; - explicit job-kind/risk-class filtering; + - trusted promotion path from staged artifact to served content; - deployment only when an approved untrusted-content handler is ready. 5. **`thoth-dissemination` consumer migration** - move from legacy BE-04 operations to generic kind-scoped claim/checkpoint/complete/reconcile; + - consume the exact merged upstream generic Publisher Services job-creation + and executor contracts; - preserve dissemination domain logic and external-write safety; - - independently review retry/reconciliation behaviour. + - independently review retry/reconciliation/revision behaviour. 6. **`thoth-app` consumer migration** - migrate `latestBackCatalogueJob`, `jobStatuses`, `withoutBackCatalogueJob` and related presentation/filter semantics to - the merged generic operational contract; + the exact merged generic operational/read contract; - regenerate/update GraphQL client surfaces; - independently verify staff UX semantics. -7. **Legacy BE-04 GraphQL retirement in `thoth`** +7. **Legacy BE-04 API/runtime retirement in `thoth`** - only after all consumers are deployed on generic contracts; - - remove legacy lifecycle/read/filter fields/types/toggle under approved - deprecation controls; + - remove legacy lifecycle/read/filter fields/types/toggle; + - redirect/remove Publisher Services runtime creation/cancellation/guard paths + so no code accesses `distribution_job*`; + - preserve fail-closed AUTOMATIC_PUSH activation through the generic rule; + - repository-wide source audit proving zero runtime legacy-table access; - update generated SDL/client compatibility evidence. 8. **Later BE-04 storage contraction in `thoth`** - - only after legacy API retirement and rollback-window closure; - - migration-local zero-row assertion; - - remove the three legacy tables/indexes/four queue-only enums; + - only after the zero-runtime-access retirement release is deployed and its + older rollback window is closed; + - migration-local lock + zero-row assertion; + - remove three legacy tables/indexes/four queue-only enums; - retain `distribution_platform`; - - atomically reconcile Diesel/model/query code. + - remove only remaining inert schema/model declarations atomically with DROP. 9. **Hosting** - adopt generic jobs for greenfield provisioning/reconciliation; @@ -1164,14 +1331,19 @@ repository guesses an unmerged upstream contract. Architecture approval does not authorize migration execution. -The production transition is deliberately non-atomic across releases/repos: +The production transition is deliberately non-atomic across releases/repos. -### Phase A - expand +### Phase A - expand and establish event-emission floor -Release N adds the generic async schema/API and can deploy the inert trusted +Release N adds the generic async schema/API and may deploy the inert trusted worker runtime. Legacy BE-04 schema/API remains present and unchanged, with legacy automatic job creation/activation OFF. +Any new event route remains INACTIVE until every serving binary and supported +rollback target capable of the relevant business mutation emits the event +kind/version atomically. Route activation is a separate authorized operation +after deployment evidence establishes that emission floor. + Release N must be fully deployed and become a valid rollback target before any downstream consumer migration starts. @@ -1184,19 +1356,30 @@ gate. No legacy GraphQL field/type is removed until every consumer is verified deployed on the generic contract. -### Phase C - retire legacy API +### Phase C - retire legacy API and all runtime table access -A later Thoth release removes the legacy BE-04 GraphQL -lifecycle/read/filter/toggle contract under approved deprecation controls. That -release is deployed and observed until the previous rollback target no longer -needs legacy API/storage. +A later Thoth release removes the legacy BE-04 +lifecycle/read/filter/toggle contract **and every other runtime dependency on +`distribution_job*`**, including Publisher Services creation/cancellation and +activation-guard paths. + +Publisher-service activation either atomically creates its required generic +event/job intent under the separately activated generic rule or remains +fail-closed. It must not become a jobless activation merely because the legacy +toggle disappears. + +A repository-wide source audit and runtime tests must prove the Phase-C binary +does not query/write legacy job tables. + +That release is deployed and observed until all supported production rollback +targets are also zero-runtime-access binaries. ### Phase D - contract legacy storage Only a still-later destructive migration removes `distribution_job*` storage -and queue-only enum types. The migration itself checks legacy table emptiness and -aborts if any row exists. The legacy tables are not dropped while any old Thoth -image that references them remains a supported production rollback target. +and queue-only enum types. The migration obtains locks preventing concurrent +legacy writes, performs the final zero-row assertion and DROP under the same +fail-closed transaction, and aborts on any unexpected row/state. Because the current production GraphQL image runs the default `thoth init` path, which performs migrations before serving, this separation is mandatory: @@ -1206,7 +1389,7 @@ tasks or by automatic rollback. The development environment's currently unapplied v1.7.0 state is handled by ordered migrations: v1.7.0 applies first, followed by additive generic schema, and only later the separately authorized contraction migration after the same -consumer/API retirement gates. +consumer/runtime retirement gates. The migration test matrix must include: @@ -1214,27 +1397,36 @@ The migration test matrix must include: - pre-v1.7 upgrade applying v1.7.0 plus additive generic schema; - full empty-database migration chain; - rolling deploy with mixed old/new application versions during Phase A; -- later API-retirement rollback window; -- contraction with zero legacy rows; +- event-route activation only after emission-capable deployment/rollback floor; +- route deactivation before rollback below an event-emission floor; +- current-state reconciliation/backfill covering an authorized emission gap; +- later API/runtime-retirement rollback window; +- proof Phase-C binaries have zero runtime legacy-table access; +- contraction with final locks and zero legacy rows; - contraction abort with any legacy row present. -Initial worker deployment is inert until job creation/activation is separately -authorized. Effectful handler activation is prohibited until its -`RECONCILIATION_REQUIRED` exit path (reconciler and/or protected staff -operation) is itself available and approved. +Initial worker deployment is inert until job creation/route/handler activation is +separately authorized. Effectful handler activation is prohibited until its +`RECONCILIATION_REQUIRED` exit path is itself available and approved. + +High-volume handlers such as indexing/revalidation additionally require +kind-specific retention, coalescing and admission/backpressure limits before +activation. Rollout should prove in order: 1. schema/event-route/job concurrency without external writes; 2. mixed-version route completeness and out-of-order transaction commits; -3. crash/lease renewal/WAITING/reconciliation recovery; -4. effect-scoped idempotency and coalescing; -5. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; -6. one or more non-destructive representative handlers; -7. protected operator controls and reconciliation exit path; -8. one effectful handler with durable `EFFECT_STARTED`, provider checkpoint and +3. activation/deactivation and event-emission-floor behaviour; +4. crash/lease renewal/WAITING/reconciliation recovery; +5. effect-scoped idempotency, revision ordering and coalescing; +6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; +7. one or more non-destructive representative handlers; +8. protected operator controls and reconciliation exit path; +9. one effectful handler with durable `EFFECT_STARTED`, provider checkpoint and reconciliation evidence; -9. only then broader handler activation. +10. quarantine-to-trusted-promotion flow for one untrusted-content handler; +11. only then broader handler activation. Handler activation is independent: enabling Hosting must not automatically enable Crossref, Algolia, file processing or another family merely because they @@ -1247,6 +1439,7 @@ Architecture rollback before implementation is ordinary documentation revert. Once implemented, rollback must distinguish: - stopping new event/job creation; +- deactivating routes before rolling below their event-emission floor; - stopping worker claiming; - preserving durable event/route/job/attempt/checkpoint records; - rolling back individual handler activation; @@ -1254,32 +1447,55 @@ Once implemented, rollback must distinguish: - schema rollback, which may be impossible after live async data exists and therefore requires its own forward-repair/migration plan. -During the expand/migrate phases, a rollback to the previous Thoth image is -permitted only while all schema/API that image needs remains present. The legacy -BE-04 storage contraction cannot occur until that old image is no longer a -supported rollback target. +During the expand/migrate phases, rollback to a previous Thoth image is permitted +only while all schema/API that image needs remains present **and** route +activation remains compatible with that image's event-emission behaviour. + +If rollback below an already-active route's emission floor is unavoidable, the +route is deactivated before old writers resume or a specifically authorized +current-state reconciliation/backfill covers the exact gap. Existing +pre-deactivation eligible route obligations remain durable; deactivation does +not delete them. + +Legacy BE-04 storage contraction cannot occur until every supported rollback +binary has zero runtime legacy-table access. After generic async data exists, rolling the runtime back does not authorize dropping/rewriting that data. A rollback plan must either run a binary that understands the generic schema or stop job production/claiming and preserve the durable queue for forward recovery. -Do not treat disabling a worker as equivalent to deleting durable jobs. +Do not treat disabling a worker or deactivating a route as equivalent to deleting +durable jobs/events/route obligations. ## 11. Validation requirements Before shared implementation can be approved, evidence must include: - real PostgreSQL concurrent claim tests with multiple workers; -- durable route-registration/activation tests; +- durable route registration/activation/deactivation tests; +- concurrent event creation versus route activation/deactivation under the + routing generation/epoch mechanism; +- proof that pre-deactivation eligible events remain owed after route retirement; +- explicit authorized terminal route-disposition tests when an eligible route + can no longer be materialized; +- event-emission-floor tests proving a route cannot activate while any serving + or supported rollback binary can perform the mutation without emitting its + event kind/version; +- rollback-below-emission-floor tests requiring prior deactivation or explicit + current-state reconciliation/backfill; - mixed worker-version routing where an older worker cannot silently complete an - unknown active route; + unknown eligible route; - out-of-order transaction commit tests proving no event can be skipped by a sequence/cursor optimization; - crash during partial fan-out with exact resume and no duplicate route record; - explicit historical backfill through the same `(event_id, route_key)` materialization identity; - effect-scoped idempotency tests across newer source revisions/schedule slots; +- CURRENT_STATE tests that read canonical latest state and safely no-op/coalesce + when already applied; +- REVISION_BOUND tests proving an older retried revision cannot overwrite a newer + already-applied revision without explicit historical replay authorization; - allowed many-route-to-one-job coalescing tests and deterministic conflict attention when coalescing is not permitted; - current-token lease renewal and refusal to renew superseded/expired claims; @@ -1293,10 +1509,12 @@ Before shared implementation can be approved, evidence must include: RECONCILIATION_REQUIRED rather than FAILED; - concurrency-key retention across WAITING/RECONCILIATION_REQUIRED unless an explicit safe-release rule exists; +- operational signal for jobs blocked behind a retained concurrency key; - effect-window test proving an effectful runner is not started without enough remaining lease/runtime; - crash before and after `EFFECT_STARTED`; -- durable `EFFECT_CONFIRMED` recovery where supported by the kind; +- durable `EFFECT_CONFIRMED` recovery where supported by the kind, plus proof + that this evidence is not conflated with ADR-0010 observed-state truth; - cancellation before write and cancellation after effect-start tests; - `INDETERMINATE` external-write reconciliation tests; - claimed/token-fenced/kind-authorized reconciliation race tests; @@ -1305,29 +1523,47 @@ Before shared implementation can be approved, evidence must include: - unsupported payload-version refusal; - payload tests proving credentials are rejected and personal/snapshot data is absent unless explicitly allowed by the kind contract; +- diagnostics/provider-evidence retention and erasure tests; - current-state execution reading canonical state/revision rather than stale embedded snapshots; -- graceful shutdown and abrupt restart recovery; -- worker capability/kind/risk-class filtering; +- graceful shutdown and abrupt restart recovery for every worker pool; +- worker capability/kind/risk-class/revision-mode filtering; - authorization negative tests proving domain executors cannot mutate another domain's jobs; - protected/audited operator cancel/retry/reconcile tests; - queryable queue-depth, oldest-ready-age, expired-lease, - `RECONCILIATION_REQUIRED`, failure and worker-liveness signals; + `RECONCILIATION_REQUIRED`, failure, blocked-successor and worker-liveness + signals; - proof that logs and durable diagnostics do not contain configured credentials; - proof that trusted/untrusted worker tasks receive no static AWS access keys; - proof that untrusted-content worker roles exclude Hosting DNS/ACM/CloudFront - tenant authority; + tenant authority and served-content write paths; +- quarantine/staging tests proving untrusted output cannot directly become + publisher-served content; +- trusted-promotion tests proving publisher/domain/bucket/key/target are resolved + from canonical state rather than untrusted result fields; +- negative tests proving an untrusted worker cannot create arbitrary follow-on + jobs/events; +- if direct DB access is used by an untrusted pool, proof of row/kind enforcement + through RLS/security-definer functions or an equivalent database boundary; - Phase-A migration tests retaining legacy BE-04 storage/API; - `thoth-dissemination` generic-contract compatibility tests; - `thoth-app` migration tests for latest job/status/filter semantics; - generated SDL/client contract checks; +- Phase-C repository-wide source/runtime tests proving zero + `distribution_job*` runtime access, including service-configuration + activation/disable paths; +- proof AUTOMATIC_PUSH remains fail closed unless generic job/event creation is + atomically available; - rolling deployment/rollback tests proving old tasks remain valid during Phase - A/B; -- later legacy-API retirement tests; + A/B and Phase-C rollback targets need no legacy relations; - contraction migration tests from production v1.7.0 state; +- contraction lock + zero-row assertion in the same fail-closed migration + transaction; - contraction abort when any legacy row exists; - full empty-database migration-chain tests; +- per-kind retention/coalescing/admission tests before high-volume family + activation; - deployment tests with more than one compatible worker task even if production initially runs one. From 88fbe6f88a864fceb713b7055acf77860c2d6e43 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 14:00:59 +0100 Subject: [PATCH 17/48] THOTH-ASYNC-01: update round-4 ADR proposal record --- docs/engineering/decisions/decision-register.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 446dbac41..fef7d64d3 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-09-29 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed event/route/job engine with durable route registration/completeness, effect-scoped idempotency/coalescing, claim-token-fenced renewable leases, explicit WAITING/reconciliation semantics and minimal reference/revision payloads. Uses a default trusted headless `thoth-worker` with one routine task role, plus a separate minimal-authority `UNTRUSTED_CONTENT` worker pool where publisher-controlled parsers create a material trust boundary; no worker receives static AWS keys. Keeps domain-owned executors such as `thoth-dissemination`, preserves ADR-0008 machine-role rules and ADR-0010 `ServiceOperation` staff audit/command semantics, and excludes automated legacy-CDN migration. BE-04 transition is expand -> migrate both known consumers (`thoth-dissemination` and `thoth-app`) -> retire legacy GraphQL API -> later contract legacy storage with a migration-local zero-row assertion; v1.7.0 remains immutable and `distribution_platform` remains. BE-04/DIS-02 activation and Hosting implementation depending on this architecture remain HOLD. Requires fresh independent exact-head review and exact CTO approval before implementation may rely on it. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed event/route/job engine with durable route activation/deactivation boundaries, event-emission deployment floors, effect-scoped idempotency/coalescing, explicit CURRENT_STATE vs REVISION_BOUND semantics, claim-token-fenced WAITING/reconciliation, and minimal reference/revision payloads. Uses a default trusted headless `thoth-worker` with one routine task role plus a separate minimal-authority `UNTRUSTED_CONTENT` pool that writes only quarantine/staging output; trusted promotion resolves served targets from canonical state. Keeps domain-owned executors such as `thoth-dissemination`, preserves ADR-0008 machine-role rules and ADR-0010 `ServiceOperation` staff audit/command semantics, and excludes automated legacy-CDN migration. BE-04 transition is expand -> migrate `thoth-dissemination` and `thoth-app` -> remove every runtime `distribution_job*` dependency while preserving fail-closed Publisher Services activation -> close the old rollback window -> later contract legacy storage with a locked migration-local zero-row assertion; v1.7.0 remains immutable and `distribution_platform` remains. BE-04/DIS-02 activation and Hosting implementation depending on this architecture remain HOLD. Requires fresh independent exact-head review and exact CTO approval before implementation may rely on it. | ## Merge and implementation rules From ca31d792aa3f5212a9715278464e9d5ed928dd0c Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 14:01:07 +0100 Subject: [PATCH 18/48] THOTH-ASYNC-01: update round-4 proposal summary --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa5c66b63..712c6badf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal distinguishes durable events/routes/jobs, requires database-owned route registration/completeness rather than unsafe sequence high-water semantics, uses effect-scoped idempotency with explicit coalescing, and standardizes priority/availability, bounded pre-write retries, WAITING checkpoints, post-write deadlines, claim-token-fenced renewable leases and reconciliation-required handling. Event/job payloads default to canonical IDs plus source revision/fingerprint and minimal parameters rather than copied snapshots. The default trusted headless `thoth-worker` uses one dedicated routine task role, while publisher-controlled file parsing is isolated in a separate minimal-authority `UNTRUSTED_CONTENT` worker pool; no worker receives static AWS keys. Non-AWS credentials remain deployment environment variables supplied from non-committed deployment configuration. The proposal preserves domain-owned executors such as `thoth-dissemination`, ADR-0008 application-role boundaries and ADR-0010 `ServiceOperation` staff audit/command semantics, and excludes automated legacy-CDN migration. Released BE-04 is retired through expand -> migrate `thoth-dissemination` and `thoth-app` -> retire legacy GraphQL contract -> later contract `distribution_job*` storage with an in-migration zero-row assertion, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal distinguishes durable events/routes/jobs; requires database-owned route registration with separately authorized activation/deactivation boundaries and an event-emission deployment floor; uses effect-scoped idempotency/coalescing; requires every kind to declare CURRENT_STATE or REVISION_BOUND semantics; and standardizes priority/availability, bounded pre-write retries, WAITING checkpoints, post-write deadlines, claim-token-fenced renewable leases and reconciliation-required handling. Event/job payloads default to canonical IDs plus source revision/fingerprint and minimal parameters rather than copied snapshots. The default trusted headless `thoth-worker` uses one dedicated routine task role, while publisher-controlled parsing is isolated in a separate minimal-authority `UNTRUSTED_CONTENT` pool that writes only quarantine/staging output; a trusted promotion job validates artifacts and resolves publisher/domain/bucket/key/Hosting targets from canonical state. No worker receives static AWS keys. Released BE-04 is retired through expand -> migrate `thoth-dissemination` and `thoth-app` -> remove legacy GraphQL and every remaining runtime `distribution_job*` dependency while preserving fail-closed AUTOMATIC_PUSH activation -> close the old rollback window -> later contract legacy storage with a locked in-migration zero-row assertion, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** - `CTRL-BRANCH-RELEASE-01`: preserve each managed repository's established release branch name instead of normalizing every repository to `master` (issue [897](https://github.com/thoth-pub/thoth/issues/897), parent control programme [818](https://github.com/thoth-pub/thoth/issues/818)). Adds [`ADR-0011`](docs/engineering/decisions/ADR-0011-preserve-established-release-branch-names.md), which standardizes the **role** of the release branch, written ``, rather than whether it is spelled `main` or `master`. The shared conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `, with each repository's `` resolved from its own verified live state: `main` repositories remain on `main`, `master` repositories remain on `master`, and neither name is created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` release flow is unchanged, and [`ADR-0009`](docs/engineering/decisions/ADR-0009-programme-integration-branch-namespace.md) is unchanged in substance. Release-branch conversion is removed from `BR-APP-01`, `BR-DIS-01`, `BR-SPHINX-01`, `BR-DASH-01`, `BR-WIDGET-01` and `BR-LIC-01`, and Vercel production for `metrics-dashboard` is no longer moved from `main` for branch spelling; the independently justified development-branch, protection, CI, provider and publication readiness work in each of those tasks is retained, CG-03 and CG-04 remain **OPEN**, and no task's risk is lowered. Reconciles `branch-topology.md`, `control-gaps.md`, the affected repository-map entries and the Metrics task tracker. **Documentation/control only: no established release, default or development-topology branch is created, deleted, renamed or moved by `ADR-0011` in any managed repository; no repository setting, default branch, protection, ruleset, CI workflow, provider or deployment configuration is changed; and there is no runtime, schema, migration, GraphQL/API, auth, contract, generated client, publication or production effect.** - `ADM-ADR-01-CLOSEOUT-01`: reconcile [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md)'s durable repository approval metadata with the independent review, CTO approval and merge that already completed under `ADM-ADR-01` (issue [852](https://github.com/thoth-pub/thoth/issues/852), parent programme issue [846](https://github.com/thoth-pub/thoth/issues/846)). The ADR header and its section 17 now record `Status: APPROVED`, `Approved by: Javi, CTO` and `Approval date: 2026-08-28`, the proposal-state sentence becomes approved-state wording, and the ADR-0010 decision-register row records its approval blocker as satisfied. The approved architecture is unchanged: invariants, `ServiceOperation` semantics, ADR-0002 platform-domain separation, Distribution/Publisher Services boundaries, programme ordering and rollout semantics are untouched, as are the existing authority condition and the historical `ADM-ADR-01` verification base. **Documentation/control authority-state reconciliation only: no runtime, schema, migration, GraphQL/API, contract, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** - `ADM-ADR-01`: propose [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md), the Staff Operations Console architecture (issue [846](https://github.com/thoth-pub/thoth/issues/846)). The proposal reserves `/admin/*` for the superuser global control plane, keeps one publisher workspace entered through an explicit publisher operating context rather than identity impersonation, and makes Thoth the future canonical owner of staff-facing publisher-service operational records while separating desired state, execution, observed external state and attention/reconciliation. It defines `ServiceOperation` only as a common operational/audit seam — not a generic job/queue framework — preserves ADR-0002 platform-domain separation and ADR-0008 programme-local `distribution_job*`, and selects distribution as the first read-only operational integration before any replay or external-write activation. **Architecture/control proposal only: no runtime, schema, migration, GraphQL/API, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** From ced4cb19224d7acc1824863a85d83b2387617449 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 14:01:54 +0100 Subject: [PATCH 19/48] THOTH-ASYNC-01: record round-4 architecture corrections --- ...H-ASYNC-01-ADR-01-implementation-report.md | 118 +++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 408fa241d..de039993e 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -198,6 +198,120 @@ The ADR now requires tests for: - rolling deploy/rollback safety; - contraction abort on any legacy row. +## Independent review round 3 + +The independent CRITICAL review of +`2d5eb1ae1e25bc222c4ca94a4f6dc5e81ecceba8` returned +`CHANGES REQUIRED`; durable receipt: #958 comment `5911717880`. + +Four blockers were accepted: + +- R3-01 HIGH - route activation had no durable deactivation/retirement boundary + and was not gated on every serving/rollback binary emitting the consumed event + kind; +- R3-02 MEDIUM - Publisher Services still accessed `distribution_job*` outside + the named GraphQL surfaces, making later DROP unsafe during rolling deploy and + rollback; +- R3-03 MEDIUM - untrusted-content isolation still allowed served-object writes + or trusted-handler steering through untrusted destination data; +- R3-04 MEDIUM - job kinds did not declare current-state versus revision-bound + semantics, permitting an older retried revision to overwrite newer applied + state. + +## Round-4 corrections + +### Route activation, retirement and emission floor + +Routes now have durable activation and optional deactivation boundaries under the +same routing-generation mechanism. + +Activation/deactivation is separately authorized and audited rather than a side +effect of deploying handler code. Events eligible before deactivation remain +owed after route retirement; route registrations are never deleted to erase +outstanding obligations. An eligible route that cannot be materialized requires +an explicit authorized/audited terminal route disposition. + +A route for event kind/version K may activate only after every serving binary +and supported rollback binary capable of the relevant business mutation emits K +atomically. Rollback below that emission floor requires prior deactivation or an +explicitly authorized current-state reconciliation/backfill covering the exact +gap. + +### Revision semantics + +Every job kind now declares either: + +```text +CURRENT_STATE +REVISION_BOUND +``` + +CURRENT_STATE jobs read current canonical state when they execute and may safely +coalesce/no-op under their declared rule. + +REVISION_BOUND jobs bind to a retained revision/fingerprint and must not apply an +older revision after a newer revision of the same effect target has already been +applied unless the operation is an explicitly authorized historical +replay/rollback. + +### Full BE-04 runtime retirement before contraction + +The Phase-C retirement release now covers every runtime access to +`distribution_job*`, not only named legacy GraphQL fields. + +The ADR explicitly includes the current +`replacePublisherServiceConfiguration` runtime dependencies: + +- automatic back-catalogue creation; +- the fail-closed AUTOMATIC_PUSH activation guard; +- assignment-disable legacy-job cancellation. + +Those paths must be redirected to the generic lifecycle or remain conservatively +fail-closed before the legacy tables can be dropped. + +A repository-wide source/runtime audit must prove the deployed and supported +rollback Phase-C binaries have zero runtime legacy-table access. + +Phase D then performs only the storage DROP plus removal of remaining inert +schema/model declarations. Its migration takes locks preventing a concurrent +legacy write, performs the final zero-row assertion and DROP in the same +fail-closed migration transaction, and retains `distribution_platform`. + +### Untrusted-content quarantine and trusted promotion + +UNTRUSTED_CONTENT remains part of the same canonical async engine but executes +in a separate minimal-authority pool. + +It may read authorized input and write only quarantine/staging output. It cannot +write served publisher/Hosting locations or choose final domain/bucket/key/ +Hosting targets. + +Untrusted completion results are explicitly untrusted data. A trusted promotion +job validates the artifact and resolves all authority-bearing destination values +from canonical Thoth state before publishing. + +The untrusted worker may report only its declared completion/evidence facts and +cannot create arbitrary downstream work. + +If direct database access is chosen instead of the protected kind-scoped +executor API, ordinary table grants are insufficient: the separately reviewed +database boundary must enforce row/kind scope through RLS, security-definer +functions/procedures or an equivalent mechanism. + +### Additional safety clarifications + +Round 4 also records that: + +- `EFFECT_CONFIRMED` is anti-replay execution evidence and is not by itself + ADR-0010 observed publication/acceptance/current-state truth; +- attempt diagnostics/provider evidence are covered by data + minimization/retention/erasure rules; +- jobs blocked behind WAITING/RECONCILIATION_REQUIRED concurrency keys must be + operationally visible; +- high-volume job families require retention/coalescing/admission/backpressure + policy before activation; +- graceful-shutdown rules apply to every worker pool. + ## Migration/data effect No migration was created, modified or executed. @@ -240,7 +354,7 @@ production activation: 0 - no PR has been created; - no runtime test result is claimed by this documentation-only task. -The final round-3 head is recorded in #958 after this report commit. Any further +The final round-4 head is recorded in #958 after this report commit. Any further independent review is valid only against that exact SHA. ## Deviation history @@ -254,7 +368,7 @@ No source-scope deviation remains. ## Remaining lifecycle gates -1. fresh independent CRITICAL exact-head review of the round-3 candidate; +1. fresh independent CRITICAL exact-head review of the round-4 candidate; 2. exact-content CTO architecture approval only if that review returns `APPROVED`; 3. separate approval-state decision-record reconciliation, including ADR-0008 From d25bb1ecd1665cc4e635b16dbd5fd09582a5889b Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 14:48:08 +0100 Subject: [PATCH 20/48] THOTH-ASYNC-01: address round-4 architecture review --- ...H-ASYNC-01-ADR-01-implementation-report.md | 78 ++++++- ...ed-asynchronous-event-and-job-execution.md | 202 +++++++++++++++--- 2 files changed, 249 insertions(+), 31 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index de039993e..6a605d7c9 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -1,8 +1,9 @@ # THOTH-ASYNC-01-ADR-01 implementation report This report records the bounded ADR-0012 architecture-authoring task and its -independent-review correction rounds. Fast-changing review/approval state remains -in GitHub issues #957/#958 rather than being duplicated as repository status. +independent-review correction rounds. GitHub issues #957/#958 remain the live +authority for lifecycle state; this committed report records durable authoring +and correction evidence without asserting the current live gate. ## Identity @@ -312,6 +313,72 @@ Round 4 also records that: policy before activation; - graceful-shutdown rules apply to every worker pool. +## Independent review round 4 + +The independent CRITICAL review of +`ced4cb19224d7acc1824863a85d83b2387617449` returned `CHANGES REQUIRED`. + +Three MEDIUM blockers were accepted: + +- R4-01 - durable route obligations lacked route-level backlog/materializer + visibility and could be terminally dispositioned without explicit divergence + attention; +- R4-02 - UNTRUSTED_CONTENT IAM/database separation did not prevent cross-tenant + persistence or network access to shared private services; +- R4-03 - stale-write protection and concurrency were still kind-scoped rather + than shared by every job kind mutating the same external effect target. + +The accepted non-blocking clarifications also covered the BE-04 migration-backfill +runtime path, queue-specific error-contract retirement, canonical writes outside +normal event-emitting binaries, safe job-kind retirement, explicit upstream +Publisher Services generic creation ownership and contraction-revert semantics. + +## Round-5 corrections + +### Route obligations and materializer health + +The ADR now requires per-route count/oldest-age signals for eligible events with +no disposition, explicit visibility when no compatible materializer is deployed, +and route activation only after both the event-emission floor and a compatible +materializer exist. A terminal route disposition without covering +reconciliation/backfill is an accepted divergence requiring durable attention; +bulk actions still create one audited disposition per event/route. + +### Canonical external effect targets + +Effectful kinds now resolve domain-owned canonical external effect-target +identities independent of job kind. Every writer of one target shares the same +target-scoped concurrency namespace and durable applied-revision/fingerprint +evidence. Batch jobs decompose by target by default; retained multi-target jobs +must acquire complete target serialization deterministically and maintain +per-target evidence. + +### Stronger untrusted-content isolation + +UNTRUSTED_CONTENT execution now requires cross-publisher process/state isolation, +job-scoped staging authority, independently verified trusted promotion and a +deny-by-default network boundary. The pool cannot inherit private-subnet access to +Redis, EFS, unrelated RDS/database endpoints or other internal services merely +because IAM/database credentials are restricted. + +### Retirement and rollback clarifications + +The ADR now names the current migration-backfill runtime path and released +`DISTRIBUTION_JOB_CREATION_DISABLED` compatibility surface in Phase C, requires +non-emitting migration/repair writes to emit transactionally or receive exact-scope +reconciliation/backfill, prevents job-kind retirement from orphaning non-terminal +work, assigns the Publisher Services generic creation/cancellation/activation +contract upstream to `thoth`, and makes Phase-D contraction a forward-repair +boundary rather than an automatic restoration of BE-04. + +### Validation additions + +Round 5 adds explicit validation for route backlog/materializer health, accepted +divergence, cross-kind target serialization/evidence, provider-without-revision +ordering, cross-publisher untrusted isolation, per-job staging, private-network +denial, trusted staging provenance, job-kind retirement, non-binary domain writes, +migration-backfill/error-contract retirement and contraction-revert behaviour. + ## Migration/data effect No migration was created, modified or executed. @@ -354,8 +421,9 @@ production activation: 0 - no PR has been created; - no runtime test result is claimed by this documentation-only task. -The final round-4 head is recorded in #958 after this report commit. Any further -independent review is valid only against that exact SHA. +Independent review is valid only against the exact candidate head it names. +Live candidate/gate evidence belongs in GitHub #957/#958 rather than in a +self-invalidating committed status line. ## Deviation history @@ -368,7 +436,7 @@ No source-scope deviation remains. ## Remaining lifecycle gates -1. fresh independent CRITICAL exact-head review of the round-4 candidate; +1. fresh independent CRITICAL exact-head review of the corrected candidate; 2. exact-content CTO architecture approval only if that review returns `APPROVED`; 3. separate approval-state decision-record reconciliation, including ADR-0008 diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 584ce245a..3426a9bca 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -266,7 +266,24 @@ only events at or after the deactivation boundary. Events already eligible before that boundary remain owed after retirement. If an eligible route can no longer be materialized safely, each affected event requires an explicit, authorized, audited terminal route disposition describing why it was not -materialized and what reconciliation/backfill, if any, covers it. +materialized and what reconciliation/backfill, if any, covers it. Each such +disposition remains one durable `(event_id, route_key)` record even when an +operator authorizes a bulk action, and records the actor/authority and reason. + +A terminal disposition with no covering reconciliation/backfill is not normal +successful routing. It records an explicit accepted divergence for the affected +domain/effect target and raises durable operator attention; an operator cannot +make required work disappear merely by supplying a reason. + +The engine exposes route-level backlog independently of job-level queue metrics. +For every route key it must make queryable at least the count of eligible events +without a disposition, the age of the oldest such event, and whether a compatible +materializer for the route kind/version is deployed and eligible to perform the +materialization. A route may not activate until deployment evidence establishes +both the event-emission floor below and at least one compatible materializer in +the intended environment. If compatible materialization later becomes +unavailable, outstanding obligations remain owed and the unavailable-materializer +and route-backlog signals raise attention. A crash after some consumers are materialized therefore resumes from durable route records. A worker must never mark an event fully routed merely because it @@ -297,6 +314,14 @@ the rollback is accompanied by an explicitly authorized current-state reconciliation/backfill that covers the precise emission gap. Silent loss of business changes is not an acceptable rollback behaviour. +The binary emission floor does not exempt canonical writes performed outside +those binaries. An authorized data migration, administrative repair or exceptional +operator write that changes state consumed by an active route must either emit the +same required event transactionally through an approved database/application +mechanism or be followed by an explicitly authorized current-state +reconciliation/backfill covering the exact affected write set. Direct production +SQL is not an event-emission mechanism by implication. + The implementation may optimize discovery/scanning, but routing completeness must be proven from durable event/route/disposition records. Any cursor or high-water optimization must prove that no lower/in-flight event can commit @@ -353,11 +378,32 @@ applied. If so, the older job must not overwrite/regress that newer state: it records a deterministic superseded/no-effect disposition unless it is an explicitly authorized historical replay/rollback operation. -Each REVISION_BOUND kind therefore defines the revision ordering/evidence by -which "newer already applied" is established. Merely serializing execution by a +Every effectful job kind also declares how it resolves one or more **canonical +external effect target identities** owned by the relevant domain. The identity is +defined at the granularity at which two mutations can conflict, is independent of +job kind, and is the common ordering boundary for every kind that can mutate that +same target. + +All job kinds that can mutate one canonical effect target use the same +target-scoped concurrency namespace and consult the same durable target-scoped +applied-revision/fingerprint evidence in Thoth. Kind-local history is not +sufficient evidence that a newer effect has or has not been applied. Where the +provider exposes no revision identifier, the owning domain still records durable +local applied-revision/fingerprint and provider-correlation/reconciliation +evidence sufficient to prevent an older effect from regressing a newer one; an +ambiguous provider state requires reconciliation rather than an ordering guess. + +Each REVISION_BOUND kind defines its source revision ordering and how that revision +maps to the canonical effect target, but "newer already applied" is established +from the shared target-scoped evidence. Merely serializing execution by a concurrency key is not sufficient because an older retry may run after a newer job has completed. +A batch or multi-target job normally decomposes effectful work to target-scoped +jobs/effects. If a reviewed kind keeps one job across several effect targets, it +must acquire all required target serialization in a deterministic order and +record/check applied-revision evidence independently for each target. + Historical replay is permitted only when the referenced historical revision is actually retained and the replay/rollback is explicitly authorized. A stale embedded snapshot must never silently substitute for either current-state @@ -504,8 +550,9 @@ An idempotency conflict never silently means "route satisfied". It either: 2. produces a deterministic routing/materialization error surfaced for attention/recovery. -Handlers additionally define a concurrency key where overlapping effects must -serialize, for example: +The owning domain defines the canonical effect-target identity and its +target-scoped concurrency namespace. Handlers resolve their intended effects to +that identity, for example: ```text work::algolia @@ -513,9 +560,11 @@ hosting: crossref: ``` -Jobs with unrelated concurrency keys may execute in parallel. Jobs that would -mutate the same protected external/domain resource serialize where the owning -specification requires it. +Jobs with unrelated target identities may execute in parallel. Every job across +every job kind that can mutate the same canonical effect target must serialize on +the same target-scoped key; a kind-specific key prefix must not partition two +writers that can conflict. Multi-target jobs either decompose by target or acquire +their complete target-key set in a deterministic order as defined above. The shared scheduler supports per-kind concurrency limits. Provider-specific rate limits remain owned by handlers, but a single failing or rate-limited job @@ -630,11 +679,20 @@ separate task/service or equivalent process-isolated worker pool with a minimal dedicated task role. This is a trust-separated executor pool, not a second queue architecture. +An untrusted job executes in an isolation context that is not reused across jobs +belonging to different publishers. Disposable single-job execution is the default +model; an alternative may be approved only when its own review proves equivalent +cross-tenant process/state isolation. No writable local process/filesystem state +from one publisher's job may be carried into another publisher's execution. + The untrusted-content pool: - may read only the input objects required by its authorized jobs; -- writes transformed/generated output only to an isolated quarantine/staging - location, never directly to publisher-served or Hosting production paths; +- writes transformed/generated output only to a job-scoped isolated + quarantine/staging location whose authority is issued/resolved by the trusted + side; it cannot read or overwrite another publisher/job's staging output unless + an explicit owning-domain relation requires and authorizes that access; +- never writes directly to publisher-served or Hosting production paths; - cannot choose or write the final publisher domain, served bucket, served key or Hosting target; - cannot create arbitrary downstream events/jobs; it may report only the @@ -642,8 +700,12 @@ The untrusted-content pool: - receives no broad canonical-domain mutation authority. Untrusted completion payloads/results are themselves **untrusted data**. -A trusted promotion/deployment job validates the staged artifact, verifies its -hash/size/type/evidence, and resolves publisher identity, destination domain, +A trusted promotion/deployment job resolves the expected staging object/location +from canonical job/domain state or a capability previously issued by the trusted +side, reads the staged artifact through trusted authority, and independently +validates its hash/size/type/evidence before promotion. An untrusted-reported +object path, hash, size or media type is a claim to verify, not trusted integrity +evidence. The trusted job also resolves publisher identity, destination domain, bucket, key and Hosting/CDN target from canonical Thoth state. Trusted handlers must never take authority-bearing destination values from an untrusted result. @@ -659,6 +721,16 @@ Direct database access therefore requires row/kind enforcement such as RLS, security-definer functions/procedures or an equivalently reviewed database boundary. +Network reachability is part of the untrusted security boundary, not an +assumption inherited from placement in a private subnet. The untrusted pool uses +a dedicated security-group/network-policy boundary with deny-by-default access to +private services. It may reach only the selected executor control path (or the +separately reviewed scoped database endpoint when direct DB access is selected), +the job-scoped object-storage path and explicitly approved external dependencies. +It must have no network path to shared Redis, EFS, unrelated RDS/database +endpoints or other internal services merely because those services accept traffic +from the application's private CIDRs. + Its AWS/provider role likewise excludes Hosting DNS, ACM and CloudFront tenant-management authority and direct write access to publisher-served locations unless an explicit CTO risk acceptance identifies the concrete need @@ -692,10 +764,14 @@ declares: - required runtime capability/configuration; - trust/risk class, including whether it handles untrusted content; - revision semantics: `CURRENT_STATE` or `REVISION_BOUND`; +- canonical external effect-target resolution and the shared target-scoped + concurrency/applied-revision evidence namespace for effectful kinds; - effect classification and expected effect window; - timeout/lease/waiting-deadline requirements; - retry/reconciliation policy; - idempotency/coalescing/concurrency rules; +- kind-retirement compatibility for outstanding jobs, including WAITING and + RECONCILIATION_REQUIRED work; - for high-volume families, retention/coalescing/admission/backpressure rules required before activation. @@ -704,6 +780,14 @@ risk class are configured for that pool. Capability declarations are runtime/configuration boundaries and scheduling primitives; they do not imply one IAM role per ordinary handler. +A job kind/version cannot be removed from every compatible executor while +non-terminal durable jobs of that kind/version remain. Retirement first stops new +admission, then drains, migrates, reconciles or explicitly dispositions existing +PENDING/RUNNING/WAITING/RETRY_SCHEDULED/RECONCILIATION_REQUIRED work under its +approved domain rules. A compatible executor remains available until that +obligation is closed; retirement must not orphan a retained concurrency key or +make a durable job uninterpretable. + External/domain executors use the same kind scoping: their authorization permits only the job kinds assigned to that domain executor. A dissemination executor cannot claim/checkpoint/complete/reconcile a Hosting, Metrics, file-processing @@ -861,6 +945,14 @@ job kind: - execution latency/attempt distribution; - worker liveness and last successful claim/heartbeat activity. +It also exposes route-materialization health at least by route key: + +- count of eligible events with no durable route disposition; +- age of the oldest eligible event with no route disposition; +- active routes with no compatible deployed/eligible materializer; +- terminal route dispositions that represent accepted divergence rather than + covered reconciliation/backfill. + Each job kind defines bounded attempts, backoff and concurrency limits. Backoff must prevent tight retry loops; exact algorithms and jitter are implementation details. The scheduler must provide bounded fairness so one enabled kind cannot @@ -1185,7 +1277,10 @@ execution until a separate approved architecture decision says otherwise. to one job only under an explicit not-yet-started coalescing rule. 9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. A REVISION_BOUND effect must not regress a newer already-applied revision - except through explicit historical replay/rollback authorization. + except through explicit historical replay/rollback authorization. All job + kinds capable of mutating the same canonical external effect target share the + domain-owned target identity, concurrency namespace and durable + applied-revision/fingerprint evidence. 10. No stale/superseded claim can finalize, checkpoint, cancel, renew or reconcile a job. 11. WAITING atomically checkpoints and releases claim/lease; resume is a new @@ -1219,8 +1314,10 @@ execution until a separate approved architecture decision says otherwise. 24. Trusted-worker AWS authority comes exclusively from its dedicated ECS task role. 25. UNTRUSTED_CONTENT jobs use the same canonical engine but a separate - minimal-authority execution boundary; they write only quarantine/staging - output and cannot choose or write served/Hosting destinations. + minimal-authority, cross-tenant-isolated execution and network boundary; + they write only their job-scoped quarantine/staging output, cannot carry + writable execution state across publishers, cannot reach unrelated private + services, and cannot choose or write served/Hosting destinations. 26. Trusted promotion resolves authority-bearing destinations from canonical state and treats untrusted results as untrusted data. 27. No worker receives static AWS access keys. @@ -1249,9 +1346,13 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - effect-scoped idempotency and explicit coalescing; - - revision-semantics contract; + - revision semantics plus domain-owned canonical effect-target identity and + target-scoped ordering/evidence contract; - new kind-scoped executor API; - generic operational/read contract required by downstream consumers; + - upstream-owned Publisher Services generic creation/cancellation/ + activation-guard contract in `thoth`, preserving atomic fail-closed desired + state before any BE-04 retirement; - legacy BE-04 schema/API left intact and inactive; - real PostgreSQL concurrency/mixed-version/out-of-order-commit tests. @@ -1270,12 +1371,17 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - environment/network configuration. 4. **Infrastructure/runtime - untrusted-content pool** - - separate process/task/service boundary for publisher-controlled parsers; + - separate process/task/service boundary for publisher-controlled parsers and + cross-publisher execution/state isolation; - minimal task role with no Hosting DNS/ACM/CloudFront tenant authority; - - quarantine/staging-only output permissions; + - per-job quarantine/staging-only output permissions; - protected executor API or row/kind-enforced minimal database principal; + - deny-by-default network boundary excluding Redis, EFS and unrelated private + database/internal-service reachability; - explicit job-kind/risk-class filtering; - - trusted promotion path from staged artifact to served content; + - trusted promotion path that independently resolves/verifies staged artifacts + before served-content publication; + - dedicated negative IAM, staging-scope, cross-job and network tests; - deployment only when an approved untrusted-content handler is ready. 5. **`thoth-dissemination` consumer migration** @@ -1361,7 +1467,15 @@ deployed on the generic contract. A later Thoth release removes the legacy BE-04 lifecycle/read/filter/toggle contract **and every other runtime dependency on `distribution_job*`**, including Publisher Services creation/cancellation and -activation-guard paths. +activation-guard paths. The zero-runtime-access inventory explicitly includes +`thoth-api/src/model/publisher_service_configuration/migration_backfill.rs` +preflight/runtime reads and every command/startup toggle or administrative path +that can still query those tables. + +Released queue-specific GraphQL error semantics are part of the compatibility +surface too. They require an explicit generic mapping/deprecation/removal +decision, including `DISTRIBUTION_JOB_CREATION_DISABLED`; deleting the legacy +tables or toggle must not silently change a released error contract. Publisher-service activation either atomically creates its required generic event/job intent under the separately activated generic rule or remains @@ -1386,6 +1500,14 @@ path, which performs migrations before serving, this separation is mandatory: the first task in a rolling deploy must never drop relations still needed by old tasks or by automatic rollback. +The contraction migration is a one-way operational boundary, not an automatic +return to BE-04 execution. Its separately reviewed revert strategy must fail +closed or explicitly document any schema-only recreation as insufficient to +restore legacy lifecycle state. Recreating empty legacy tables must never be +presented as a safe application rollback. Restoring operational BE-04 storage or +data after contraction requires a separately authorized forward-repair/data +recovery migration and compatible binary. + The development environment's currently unapplied v1.7.0 state is handled by ordered migrations: v1.7.0 applies first, followed by additive generic schema, and only later the separately authorized contraction migration after the same @@ -1458,7 +1580,9 @@ pre-deactivation eligible route obligations remain durable; deactivation does not delete them. Legacy BE-04 storage contraction cannot occur until every supported rollback -binary has zero runtime legacy-table access. +binary has zero runtime legacy-table access. After that contraction, an older +binary requiring the deleted relations is no longer a supported rollback target; +schema-only recreation of empty legacy tables does not make it one. After generic async data exists, rolling the runtime back does not authorize dropping/rewriting that data. A rollback plan must either run a binary that @@ -1478,12 +1602,19 @@ Before shared implementation can be approved, evidence must include: routing generation/epoch mechanism; - proof that pre-deactivation eligible events remain owed after route retirement; - explicit authorized terminal route-disposition tests when an eligible route - can no longer be materialized; + can no longer be materialized, including per-event actor/authority/reason; +- accepted-divergence attention when a terminal disposition has no covering + reconciliation/backfill; +- queryable per-route undisposed-event count/oldest-age and unavailable + compatible-materializer signals; +- activation refusal until at least one compatible materializer is deployed; - event-emission-floor tests proving a route cannot activate while any serving or supported rollback binary can perform the mutation without emitting its event kind/version; - rollback-below-emission-floor tests requiring prior deactivation or explicit current-state reconciliation/backfill; +- migration/administrative-write tests proving non-emitting canonical writes are + paired with transactional emission or exact-scope reconciliation/backfill; - mixed worker-version routing where an older worker cannot silently complete an unknown eligible route; - out-of-order transaction commit tests proving no event can be skipped by a @@ -1496,6 +1627,12 @@ Before shared implementation can be approved, evidence must include: when already applied; - REVISION_BOUND tests proving an older retried revision cannot overwrite a newer already-applied revision without explicit historical replay authorization; +- cross-kind tests proving every writer of one canonical external effect target + shares the same target-scoped serialization and applied-revision evidence; +- provider-without-revision-id tests proving local target evidence prevents stale + regression or forces reconciliation when state is ambiguous; +- multi-target tests proving decomposition or deterministic complete target-lock + acquisition plus per-target revision evidence; - allowed many-route-to-one-job coalescing tests and deterministic conflict attention when coalescing is not permitted; - current-token lease renewal and refusal to renew superseded/expired claims; @@ -1528,6 +1665,8 @@ Before shared implementation can be approved, evidence must include: embedded snapshots; - graceful shutdown and abrupt restart recovery for every worker pool; - worker capability/kind/risk-class/revision-mode filtering; +- job-kind/version retirement tests proving non-terminal WAITING/retry/ + reconciliation work remains interpretable and retains a compatible executor; - authorization negative tests proving domain executors cannot mutate another domain's jobs; - protected/audited operator cancel/retry/reconcile tests; @@ -1538,10 +1677,16 @@ Before shared implementation can be approved, evidence must include: - proof that trusted/untrusted worker tasks receive no static AWS access keys; - proof that untrusted-content worker roles exclude Hosting DNS/ACM/CloudFront tenant authority and served-content write paths; -- quarantine/staging tests proving untrusted output cannot directly become +- cross-publisher execution-isolation tests proving writable process/filesystem + state from one untrusted job cannot affect another publisher's job; +- job-scoped quarantine/staging tests proving an untrusted job cannot read or + overwrite another publisher/job's staging output and cannot directly create publisher-served content; -- trusted-promotion tests proving publisher/domain/bucket/key/target are resolved - from canonical state rather than untrusted result fields; +- network-denial tests proving the untrusted pool cannot reach Redis, EFS, + unrelated RDS/database endpoints or other unapproved private services; +- trusted-promotion tests proving staging identity and integrity are independently + resolved/verified by the trusted side and publisher/domain/bucket/key/target are + resolved from canonical state rather than untrusted result fields; - negative tests proving an untrusted worker cannot create arbitrary follow-on jobs/events; - if direct DB access is used by an untrusted pool, proof of row/kind enforcement @@ -1552,7 +1697,9 @@ Before shared implementation can be approved, evidence must include: - generated SDL/client contract checks; - Phase-C repository-wide source/runtime tests proving zero `distribution_job*` runtime access, including service-configuration - activation/disable paths; + activation/disable paths and the migration-backfill preflight/runtime path; +- released queue-specific GraphQL error-contract compatibility tests, including + `DISTRIBUTION_JOB_CREATION_DISABLED` disposition; - proof AUTOMATIC_PUSH remains fail closed unless generic job/event creation is atomically available; - rolling deployment/rollback tests proving old tasks remain valid during Phase @@ -1561,6 +1708,9 @@ Before shared implementation can be approved, evidence must include: - contraction lock + zero-row assertion in the same fail-closed migration transaction; - contraction abort when any legacy row exists; +- contraction-revert tests proving schema-only recreation cannot be mistaken for + operational BE-04 rollback and any data/state restoration is separately + authorized forward repair; - full empty-database migration-chain tests; - per-kind retention/coalescing/admission tests before high-volume family activation; From 631e28d1f05495f24ce88369d4557f1033f970b3 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 15:09:52 +0100 Subject: [PATCH 21/48] THOTH-ASYNC-01: bind trusted promotion to validated bytes --- ...H-ASYNC-01-ADR-01-implementation-report.md | 56 ++++++ ...ed-asynchronous-event-and-job-execution.md | 181 +++++++++++++----- 2 files changed, 184 insertions(+), 53 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 6a605d7c9..e63e4dafe 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -379,6 +379,62 @@ ordering, cross-publisher untrusted isolation, per-job staging, private-network denial, trusted staging provenance, job-kind retirement, non-binary domain writes, migration-backfill/error-contract retirement and contraction-revert behaviour. +## Independent review round 5 + +The independent CRITICAL review of +`d25bb1ecd1665cc4e635b16dbd5fd09582a5889b` returned `CHANGES REQUIRED`. +R4-01 through R4-03 were verified closed with no regression identified in the +earlier F/R2/R3 findings. + +One MEDIUM blocker was accepted: + +- R5-01 - trusted promotion validated a staged object but did not bind + publication to the exact immutable bytes/version validated or require the + untrusted writer's authority to end first, leaving a validate-then-swap TOCTOU + path. + +The missing round-4/round-5 lifecycle chain was reconciled in GitHub under #958 +comment `5912886635`; the programme gate was reconciled under #957 comment +`5912887392`. + +## Round-6 corrections + +### Trusted promotion integrity + +Untrusted staging-write authority is now current-claim-scoped and bounded in +lifetime. Trusted validation cannot begin until write authority over the selected +artifact has ended (or an unrevocable capability has expired / an immutable +provider-version boundary is established). The trusted side seals and resolves +the artifact identity, independently validates it, and may publish only the exact +immutable bytes/version that passed validation. A mutable key cannot bridge the +validation/publication boundary; any replacement requires fresh validation. + +### Effect-target fencing and identity + +Target evidence is now current-claim-fenced, updated only while holding the +target serialization boundary and committed atomically with the durable local +effect/job outcome. It is monotonic except under authorized historical replay. +CURRENT_STATE fingerprints cover every canonical input determining the effect. +Exactly one domain owns a shared effect-target identity; identity changes have +explicit old/new-target disposition rules; multi-target locking is all-or-nothing +rather than waiting while holding a subset. + +### Untrusted execution and egress + +The isolation rule is job-to-job, including jobs for the same publisher. Staging +capabilities are claim-bound and excluded from durable payload/attempt records. +The deny-by-default network boundary explicitly covers outbound traffic, limits +object storage to approved Thoth input/staging locations and requires explicit +allowlisting of other dependencies. + +### Operator and validation tightening + +Staff-created terminal route dispositions use ADR-0010's protected audited command +seam. Validation now covers complete CURRENT_STATE fingerprints, target-evidence +claim fencing and monotonicity, target-identity transitions, all-or-nothing +multi-target locking, same-publisher job isolation, staging-capability expiry, +egress restrictions and staged-object replacement after validation. + ## Migration/data effect No migration was created, modified or executed. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 3426a9bca..9eb21363a 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -269,6 +269,9 @@ authorized, audited terminal route disposition describing why it was not materialized and what reconciliation/backfill, if any, covers it. Each such disposition remains one durable `(event_id, route_key)` record even when an operator authorizes a bulk action, and records the actor/authority and reason. +Where a staff/operator action creates that disposition, it uses ADR-0010's +protected, audited staff-command seam; direct production SQL is not the normal +route-disposition interface. A terminal disposition with no covering reconciliation/backfill is not normal successful routing. It records an explicit accepted divergence for the affected @@ -367,9 +370,13 @@ REVISION_BOUND A `CURRENT_STATE` job means "make the external/derived target reflect the latest canonical state when this job executes". It reads canonical state at -execution time, records the revision/fingerprint actually acted on, may coalesce -older triggers under its explicit kind rule, and may complete as a deterministic -no-op when the required current revision is already applied. +execution time and records the revision/fingerprint actually acted on. That +fingerprint covers every canonical input that determines the intended external +effect -- including artifact/content identity and relevant configuration where +those affect the result -- rather than only the primary entity's metadata +revision. The kind may coalesce older triggers under its explicit rule and may +complete as a deterministic no-op only when target-scoped evidence proves that +the same complete effect fingerprint is already applied. A `REVISION_BOUND` job means "process exactly this retained source revision/fingerprint". Before any external write, the handler must determine @@ -382,7 +389,10 @@ Every effectful job kind also declares how it resolves one or more **canonical external effect target identities** owned by the relevant domain. The identity is defined at the granularity at which two mutations can conflict, is independent of job kind, and is the common ordering boundary for every kind that can mutate that -same target. +same target. Exactly one owning domain defines that target-identity and ordering +contract even when job kinds owned by several domains can mutate the same provider +object; other domains consume that contract and must not invent a parallel target +identity for the same effect. All job kinds that can mutate one canonical effect target use the same target-scoped concurrency namespace and consult the same durable target-scoped @@ -393,16 +403,34 @@ local applied-revision/fingerprint and provider-correlation/reconciliation evidence sufficient to prevent an older effect from regressing a newer one; an ambiguous provider state requires reconciliation rather than an ordering guess. +Applied-revision/fingerprint evidence for a target may be advanced only while the +worker/reconciler holds that target's serialization boundary under the current +valid claim token. The local evidence update is claim-token-fenced and committed +atomically with the corresponding durable job/effect outcome. It is monotonic and +must not move backwards except through an explicitly authorized historical +replay/rollback operation. Reconciliation acquires the same target boundary and +uses the same fencing before it can change target-scoped evidence. + Each REVISION_BOUND kind defines its source revision ordering and how that revision maps to the canonical effect target, but "newer already applied" is established from the shared target-scoped evidence. Merely serializing execution by a concurrency key is not sufficient because an older retry may run after a newer job has completed. +If canonical state changes the external target identity itself, the owning kind +specification defines the transition explicitly. Pending work bound to the old +identity must not silently retarget to the new one; the specification decides +whether old-target work is superseded, cancelled, reconciled or requires an +explicit withdrawal/cleanup effect. Creation/update/deletion/withdrawal operations +that can conflict on the same external object share its target identity or an +equivalently strong conflict-linked serialization contract. + A batch or multi-target job normally decomposes effectful work to target-scoped jobs/effects. If a reviewed kind keeps one job across several effect targets, it -must acquire all required target serialization in a deterministic order and -record/check applied-revision evidence independently for each target. +must acquire the complete target set using a deterministic, deadlock-free +all-or-nothing protocol: it may not wait while holding only a strict subset of +required target keys. It records/checks applied-revision evidence independently +for each target. Historical replay is permitted only when the referenced historical revision is actually retained and the replay/rollback is explicitly authorized. A stale @@ -679,19 +707,24 @@ separate task/service or equivalent process-isolated worker pool with a minimal dedicated task role. This is a trust-separated executor pool, not a second queue architecture. -An untrusted job executes in an isolation context that is not reused across jobs -belonging to different publishers. Disposable single-job execution is the default +An untrusted job executes in an isolation context that is not reused across +untrusted jobs by default, including two jobs for the same publisher or different +uploaders within one publisher. Disposable single-job execution is the default model; an alternative may be approved only when its own review proves equivalent -cross-tenant process/state isolation. No writable local process/filesystem state -from one publisher's job may be carried into another publisher's execution. +job-to-job process/state isolation and reset semantics. No writable local +process/filesystem/container/cache/sidecar state from one untrusted job may be +carried into another untrusted job's execution. The untrusted-content pool: - may read only the input objects required by its authorized jobs; - writes transformed/generated output only to a job-scoped isolated quarantine/staging location whose authority is issued/resolved by the trusted - side; it cannot read or overwrite another publisher/job's staging output unless - an explicit owning-domain relation requires and authorizes that access; + side; any object-store write capability is minted for the current job/claim, + bound to the current claim token, bounded in lifetime, and never serialized into + the durable event/job payload or attempt diagnostics. It cannot read or overwrite + another publisher/job's staging output unless an explicit owning-domain relation + requires and authorizes that access; - never writes directly to publisher-served or Hosting production paths; - cannot choose or write the final publisher domain, served bucket, served key or Hosting target; @@ -700,12 +733,26 @@ The untrusted-content pool: - receives no broad canonical-domain mutation authority. Untrusted completion payloads/results are themselves **untrusted data**. -A trusted promotion/deployment job resolves the expected staging object/location -from canonical job/domain state or a capability previously issued by the trusted -side, reads the staged artifact through trusted authority, and independently -validates its hash/size/type/evidence before promotion. An untrusted-reported -object path, hash, size or media type is a claim to verify, not trusted integrity -evidence. The trusted job also resolves publisher identity, destination domain, +Before trusted validation starts, the untrusted job's write authority over the +artifact selected for promotion must have ended. Claim completion/supersession +revokes that capability where revocation exists; where a capability cannot be +revoked, trusted validation waits until it has expired or uses an immutable +provider/version boundary that the former writer cannot change. + +The untrusted stage hands off a **sealed artifact identity** in canonical trusted +state. A trusted promotion/deployment job resolves that identity from canonical +job/domain state, reads the staged artifact through trusted authority, and +independently validates its bytes, cryptographic digest, size, media/type and other +required evidence. An untrusted-reported object path, hash, size or media type is a +claim to verify, not trusted integrity evidence. + +Promotion must publish the **exact bytes/version that passed trusted validation**. +A mutable object key is not sufficient identity across validation and publication. +The implementation may use an immutable provider version plus digest, a +content-addressed immutable object, or have the trusted promoter publish the bytes +it validated directly. If the source bytes/version change after validation, +promotion refuses them and must revalidate the new immutable identity before any +publication. The trusted job also resolves publisher identity, destination domain, bucket, key and Hosting/CDN target from canonical Thoth state. Trusted handlers must never take authority-bearing destination values from an untrusted result. @@ -723,13 +770,17 @@ boundary. Network reachability is part of the untrusted security boundary, not an assumption inherited from placement in a private subnet. The untrusted pool uses -a dedicated security-group/network-policy boundary with deny-by-default access to -private services. It may reach only the selected executor control path (or the -separately reviewed scoped database endpoint when direct DB access is selected), -the job-scoped object-storage path and explicitly approved external dependencies. -It must have no network path to shared Redis, EFS, unrelated RDS/database -endpoints or other internal services merely because those services accept traffic -from the application's private CIDRs. +a dedicated security-group/network-policy boundary that enforces deny-by-default +**egress as well as ingress**. It may reach only the selected executor control +path (or the separately reviewed scoped database endpoint when direct DB access is +selected), the job-scoped object-storage path and explicitly approved external +dependencies. Object-storage access is constrained by VPC endpoint/resource policy +or an equivalently strong control to the approved Thoth input/staging +buckets/prefixes rather than arbitrary provider storage. Other external egress is +explicitly allowlisted; DNS resolution alone never authorizes reachability to a +private service. The pool must have no network path to shared Redis, EFS, unrelated +RDS/database endpoints or other internal services merely because those services +accept traffic from the application's private CIDRs. Its AWS/provider role likewise excludes Hosting DNS, ACM and CloudFront tenant-management authority and direct write access to publisher-served @@ -764,8 +815,9 @@ declares: - required runtime capability/configuration; - trust/risk class, including whether it handles untrusted content; - revision semantics: `CURRENT_STATE` or `REVISION_BOUND`; -- canonical external effect-target resolution and the shared target-scoped - concurrency/applied-revision evidence namespace for effectful kinds; +- canonical external effect-target resolution, its owning domain, target-identity + transition rules and the shared target-scoped concurrency/applied-revision + evidence namespace for effectful kinds; - effect classification and expected effect window; - timeout/lease/waiting-deadline requirements; - retry/reconciliation policy; @@ -1276,11 +1328,12 @@ execution until a separate approved architecture decision says otherwise. 8. Every event-route record maps to exactly one job; many route records may map to one job only under an explicit not-yet-started coalescing rule. 9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. + A CURRENT_STATE fingerprint covers all canonical inputs determining its effect. A REVISION_BOUND effect must not regress a newer already-applied revision except through explicit historical replay/rollback authorization. All job - kinds capable of mutating the same canonical external effect target share the - domain-owned target identity, concurrency namespace and durable - applied-revision/fingerprint evidence. + kinds capable of mutating the same canonical external effect target share one + owning-domain target identity, concurrency namespace and durable + claim-token-fenced, monotonic applied-revision/fingerprint evidence. 10. No stale/superseded claim can finalize, checkpoint, cancel, renew or reconcile a job. 11. WAITING atomically checkpoints and releases claim/lease; resume is a new @@ -1314,12 +1367,16 @@ execution until a separate approved architecture decision says otherwise. 24. Trusted-worker AWS authority comes exclusively from its dedicated ECS task role. 25. UNTRUSTED_CONTENT jobs use the same canonical engine but a separate - minimal-authority, cross-tenant-isolated execution and network boundary; - they write only their job-scoped quarantine/staging output, cannot carry - writable execution state across publishers, cannot reach unrelated private - services, and cannot choose or write served/Hosting destinations. -26. Trusted promotion resolves authority-bearing destinations from canonical - state and treats untrusted results as untrusted data. + minimal-authority, job-to-job-isolated execution and deny-by-default egress/ + ingress boundary; they write only through current-claim-scoped authority to + their job-scoped quarantine/staging output, cannot carry writable execution + state across jobs, cannot reach unrelated private/provider services, and + cannot choose or write served/Hosting destinations. +26. Trusted promotion starts only after untrusted write authority over the sealed + artifact identity has ended, independently validates that immutable identity, + publishes only the exact validated bytes/version, resolves authority-bearing + destinations from canonical state and treats untrusted results as untrusted + data. 27. No worker receives static AWS access keys. 28. Non-AWS credential values are not committed or emitted in logs/durable diagnostics. @@ -1372,16 +1429,17 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: 4. **Infrastructure/runtime - untrusted-content pool** - separate process/task/service boundary for publisher-controlled parsers and - cross-publisher execution/state isolation; + job-to-job execution/state isolation, including jobs of the same publisher; - minimal task role with no Hosting DNS/ACM/CloudFront tenant authority; - - per-job quarantine/staging-only output permissions; + - current-claim-scoped, bounded-lifetime per-job quarantine/staging writes; - protected executor API or row/kind-enforced minimal database principal; - - deny-by-default network boundary excluding Redis, EFS and unrelated private - database/internal-service reachability; + - deny-by-default ingress/egress boundary excluding Redis, EFS and unrelated + private database/internal-service reachability, with object-storage access + constrained to approved Thoth input/staging buckets/prefixes; - explicit job-kind/risk-class filtering; - - trusted promotion path that independently resolves/verifies staged artifacts - before served-content publication; - - dedicated negative IAM, staging-scope, cross-job and network tests; + - trusted promotion path that starts only after staging-write authority closes + and independently pins, validates and publishes the same immutable bytes; + - dedicated negative IAM, staging-scope, cross-job, TOCTOU and network tests; - deployment only when an approved untrusted-content handler is ready. 5. **`thoth-dissemination` consumer migration** @@ -1602,7 +1660,8 @@ Before shared implementation can be approved, evidence must include: routing generation/epoch mechanism; - proof that pre-deactivation eligible events remain owed after route retirement; - explicit authorized terminal route-disposition tests when an eligible route - can no longer be materialized, including per-event actor/authority/reason; + can no longer be materialized, including per-event actor/authority/reason and + ADR-0010 protected staff-command authorization/audit where staff acts; - accepted-divergence attention when a terminal disposition has no covering reconciliation/backfill; - queryable per-route undisposed-event count/oldest-age and unavailable @@ -1623,16 +1682,24 @@ Before shared implementation can be approved, evidence must include: - explicit historical backfill through the same `(event_id, route_key)` materialization identity; - effect-scoped idempotency tests across newer source revisions/schedule slots; -- CURRENT_STATE tests that read canonical latest state and safely no-op/coalesce - when already applied; +- CURRENT_STATE tests that read canonical latest state and prove the effect + fingerprint covers every canonical input affecting the external result before + any no-op/coalescing decision; - REVISION_BOUND tests proving an older retried revision cannot overwrite a newer already-applied revision without explicit historical replay authorization; - cross-kind tests proving every writer of one canonical external effect target - shares the same target-scoped serialization and applied-revision evidence; + shares the same owning-domain identity, target-scoped serialization and + applied-revision evidence; +- stale-claim and normal-vs-reconciler races proving target evidence advances only + under the current claim while holding the target boundary and never moves + backwards without authorized historical replay; - provider-without-revision-id tests proving local target evidence prevents stale regression or forces reconciliation when state is ambiguous; -- multi-target tests proving decomposition or deterministic complete target-lock - acquisition plus per-target revision evidence; +- target-identity-change tests proving pending old-identity work is explicitly + superseded/cancelled/reconciled/cleaned up rather than silently retargeted; +- multi-target tests proving decomposition or deterministic all-or-nothing complete + target-lock acquisition with no wait while holding a strict subset, plus + per-target revision evidence; - allowed many-route-to-one-job coalescing tests and deterministic conflict attention when coalescing is not permitted; - current-token lease renewal and refusal to renew superseded/expired claims; @@ -1677,16 +1744,24 @@ Before shared implementation can be approved, evidence must include: - proof that trusted/untrusted worker tasks receive no static AWS access keys; - proof that untrusted-content worker roles exclude Hosting DNS/ACM/CloudFront tenant authority and served-content write paths; -- cross-publisher execution-isolation tests proving writable process/filesystem - state from one untrusted job cannot affect another publisher's job; +- job-to-job execution-isolation tests proving writable process/filesystem/ + container/cache/sidecar state from one untrusted job cannot affect any later + untrusted job, including another job for the same publisher; - job-scoped quarantine/staging tests proving an untrusted job cannot read or overwrite another publisher/job's staging output and cannot directly create publisher-served content; -- network-denial tests proving the untrusted pool cannot reach Redis, EFS, - unrelated RDS/database endpoints or other unapproved private services; +- staging-capability tests proving write authority is current-claim-scoped, bounded + in lifetime, absent from durable payload/attempt records and ended before trusted + validation of the artifact selected for promotion; +- network-denial tests proving outbound as well as inbound controls prevent access + to Redis, EFS, unrelated RDS/database endpoints, arbitrary provider object + storage or other unapproved private/external services; - trusted-promotion tests proving staging identity and integrity are independently resolved/verified by the trusted side and publisher/domain/bucket/key/target are resolved from canonical state rather than untrusted result fields; +- adversarial TOCTOU tests that replace/mutate the staged key after validation and + prove promotion refuses it, plus positive proof that only the exact immutable + bytes/version that passed trusted validation can be published; - negative tests proving an untrusted worker cannot create arbitrary follow-on jobs/events; - if direct DB access is used by an untrusted pool, proof of row/kind enforcement From 42d79d4617a73696efd328943c1cc547e807d39d Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 15:42:50 +0100 Subject: [PATCH 22/48] THOTH-ASYNC-01: reconcile ADR-0012 approval state --- CHANGELOG.md | 1 + ...achine-roles-and-durable-job-primitives.md | 3 +- .../ADR-0010-staff-operations-console.md | 3 +- ...ed-asynchronous-event-and-job-execution.md | 38 ++++++++++++------- .../decisions/decision-register.md | 4 +- 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 712c6badf..5242baf56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + - `THOTH-ASYNC-01-ADR-01-APPROVAL`: reconcile the durable decision state for [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) after CTO architecture approval on 2026-09-30. ADR-0012 now records `Status: APPROVED`, the decision owner approval metadata and a durable authority condition under which the approved record becomes repository-authoritative only after independent exact-head review and merge to `develop`. ADR-0008 and ADR-0010 now record only the exact partial supersession boundaries already defined by approved ADR-0012, leaving all unaffected machine-role, least-privilege, `SUPERUSER`-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules binding. The decision register reflects the approved decision state. The approved ADR-0012 architecture and invariants are otherwise unchanged, and the historical proposal entry below is preserved unchanged. **Documentation/control authority-state reconciliation only: no runtime implementation, schema/data migration, GraphQL/API contract, IAM/provider change, credential action, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal distinguishes durable events/routes/jobs; requires database-owned route registration with separately authorized activation/deactivation boundaries and an event-emission deployment floor; uses effect-scoped idempotency/coalescing; requires every kind to declare CURRENT_STATE or REVISION_BOUND semantics; and standardizes priority/availability, bounded pre-write retries, WAITING checkpoints, post-write deadlines, claim-token-fenced renewable leases and reconciliation-required handling. Event/job payloads default to canonical IDs plus source revision/fingerprint and minimal parameters rather than copied snapshots. The default trusted headless `thoth-worker` uses one dedicated routine task role, while publisher-controlled parsing is isolated in a separate minimal-authority `UNTRUSTED_CONTENT` pool that writes only quarantine/staging output; a trusted promotion job validates artifacts and resolves publisher/domain/bucket/key/Hosting targets from canonical state. No worker receives static AWS keys. Released BE-04 is retired through expand -> migrate `thoth-dissemination` and `thoth-app` -> remove legacy GraphQL and every remaining runtime `distribution_job*` dependency while preserving fail-closed AUTOMATIC_PUSH activation -> close the old rollback window -> later contract legacy storage with a locked in-migration zero-row assertion, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** - `CTRL-BRANCH-RELEASE-01`: preserve each managed repository's established release branch name instead of normalizing every repository to `master` (issue [897](https://github.com/thoth-pub/thoth/issues/897), parent control programme [818](https://github.com/thoth-pub/thoth/issues/818)). Adds [`ADR-0011`](docs/engineering/decisions/ADR-0011-preserve-established-release-branch-names.md), which standardizes the **role** of the release branch, written ``, rather than whether it is spelled `main` or `master`. The shared conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `, with each repository's `` resolved from its own verified live state: `main` repositories remain on `main`, `master` repositories remain on `master`, and neither name is created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` release flow is unchanged, and [`ADR-0009`](docs/engineering/decisions/ADR-0009-programme-integration-branch-namespace.md) is unchanged in substance. Release-branch conversion is removed from `BR-APP-01`, `BR-DIS-01`, `BR-SPHINX-01`, `BR-DASH-01`, `BR-WIDGET-01` and `BR-LIC-01`, and Vercel production for `metrics-dashboard` is no longer moved from `main` for branch spelling; the independently justified development-branch, protection, CI, provider and publication readiness work in each of those tasks is retained, CG-03 and CG-04 remain **OPEN**, and no task's risk is lowered. Reconciles `branch-topology.md`, `control-gaps.md`, the affected repository-map entries and the Metrics task tracker. **Documentation/control only: no established release, default or development-topology branch is created, deleted, renamed or moved by `ADR-0011` in any managed repository; no repository setting, default branch, protection, ruleset, CI workflow, provider or deployment configuration is changed; and there is no runtime, schema, migration, GraphQL/API, auth, contract, generated client, publication or production effect.** - `ADM-ADR-01-CLOSEOUT-01`: reconcile [`ADR-0010`](docs/engineering/decisions/ADR-0010-staff-operations-console.md)'s durable repository approval metadata with the independent review, CTO approval and merge that already completed under `ADM-ADR-01` (issue [852](https://github.com/thoth-pub/thoth/issues/852), parent programme issue [846](https://github.com/thoth-pub/thoth/issues/846)). The ADR header and its section 17 now record `Status: APPROVED`, `Approved by: Javi, CTO` and `Approval date: 2026-08-28`, the proposal-state sentence becomes approved-state wording, and the ADR-0010 decision-register row records its approval blocker as satisfied. The approved architecture is unchanged: invariants, `ServiceOperation` semantics, ADR-0002 platform-domain separation, Distribution/Publisher Services boundaries, programme ordering and rollout semantics are untouched, as are the existing authority condition and the historical `ADM-ADR-01` verification base. **Documentation/control authority-state reconciliation only: no runtime, schema, migration, GraphQL/API, contract, generated client, workflow, provider, deployment, external-write, replay or production effect; no implementation is authorized.** diff --git a/docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md b/docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md index 19273fe60..622060ad9 100644 --- a/docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +++ b/docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md @@ -8,7 +8,8 @@ Decision owner: CTO Programmes affected: Shared Engineering Control / Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Metrics; future `thoth-api` programmes requiring machine/service identities or durable-job primitives Repositories affected: `thoth-pub/thoth` Supersedes: None -Superseded by: None +Superseded by: None in full +Superseded in part by: [ADR-0012](ADR-0012-shared-asynchronous-event-and-job-execution.md) under ADR-0012's authority condition, only where ADR-0008 denies a reusable cross-programme job framework/API: the final shared-framework prohibition in section 3.3, section 3.4's programme-local-only ownership rule, section 5.1 item 6, section 5.2 item 6 and rejected alternative D. Section 3.5 is satisfied by ADR-0012, not superseded. Section 3.3's approved convention list, its `approved primitive != mandatory mechanism` rule, and all machine-role, least-privilege and `SUPERUSER` separation rules remain binding. Decision: machine and service authorization in `thoth` uses dedicated, least-privilege, **domain-specific** project roles, and no generic catch-all diff --git a/docs/engineering/decisions/ADR-0010-staff-operations-console.md b/docs/engineering/decisions/ADR-0010-staff-operations-console.md index 0e3b04743..aca2142a3 100644 --- a/docs/engineering/decisions/ADR-0010-staff-operations-console.md +++ b/docs/engineering/decisions/ADR-0010-staff-operations-console.md @@ -11,7 +11,8 @@ Owning programme issue: [#846](https://github.com/thoth-pub/thoth/issues/846) Architecture task: `ADM-ADR-01` Verification base: `develop @ 91dd607d674fcb9a75c8df31891eea0355c9ca84` Supersedes: None -Superseded by: None +Superseded by: None in full +Superseded in part by: [ADR-0012](ADR-0012-shared-asynchronous-event-and-job-execution.md) under ADR-0012's authority condition, only where ADR-0010 invariant 15 and section 7.2 assume `distribution_job*` remains the long-term Publisher Services execution source, and where section 4.4's final sentence says ADR-0008 remains fully binding without ADR-0012's later shared-framework exception. Section 4.4's substantive rule that `ServiceOperation` is not itself a queue/framework/executor API remains binding. The Staff Operations Console, `ServiceOperation` audit seam, desired/execution/observed-state separation, attention/reconciliation model and staff-command gates remain binding. Decision: establish an admin-first Staff Operations Console with a deliberately entered publisher operating context; make `thoth` the canonical durable owner of diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 9eb21363a..46ed34782 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-09-30 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -1797,21 +1799,31 @@ shared-engine tests. ## 12. Approval and authority -This ADR is currently **PROPOSED**. +This ADR is **APPROVED**. -The CTO has selected the architecture direction recorded in programme issue -#957, but that does not make this exact written ADR content approved. +Approved by: Javi, CTO +Approval date: 2026-09-30 -Before any implementation may rely on ADR-0012: +The approval adopts the architecture decision and invariants recorded in this +ADR. It remains architecture approval only: it does not authorize implementation, +schema or data migration, worker deployment, IAM/provider changes, credentials, +external writes, BE-04/DIS-02 activation, Hosting implementation, release or +production activation. -1. this exact proposed content receives independent review; -2. required corrections, if any, are incorporated; -3. the CTO explicitly approves the exact decision content; -4. status/approval metadata are reconciled under the repository decision process; -5. the exact approved content receives required independent exact-head review; -6. it is merged into `develop`. +Authority condition: this approved record becomes repository-authoritative only +when this exact approval-state content has received independent exact-head review +and is reachable from the repository's authoritative integration branch +(`develop`). A branch carrying `APPROVED` is not repository-authoritative +before merge. Live review, CI, merge-authorization and merge evidence belongs in +GitHub and is not duplicated into this ADR as transient lifecycle metadata. -No implementation task may treat the unmerged proposal as authoritative. +The partial supersession of ADR-0008 and ADR-0010 is limited exactly to the +clauses identified in this ADR's header. All unaffected architecture in those +records remains binding. After this decision becomes repository-authoritative, +material architectural changes require a new ADR or other approved superseding +decision under the normal repository controls; factual clarifications remain +subject to normal review. BE-04/DIS-02 deployment/activation and Hosting implementation that depends on -the async architecture remain HOLD throughout this gate. +the async architecture remain separately controlled and are not authorized by +this approval. diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index fef7d64d3..d55082718 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-09-29 +Last updated: 2026-09-30 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-09-29 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Proposed under programme issue [#957](https://github.com/thoth-pub/thoth/issues/957) and bounded authoring task [#958](https://github.com/thoth-pub/thoth/issues/958). Establishes one PostgreSQL-backed event/route/job engine with durable route activation/deactivation boundaries, event-emission deployment floors, effect-scoped idempotency/coalescing, explicit CURRENT_STATE vs REVISION_BOUND semantics, claim-token-fenced WAITING/reconciliation, and minimal reference/revision payloads. Uses a default trusted headless `thoth-worker` with one routine task role plus a separate minimal-authority `UNTRUSTED_CONTENT` pool that writes only quarantine/staging output; trusted promotion resolves served targets from canonical state. Keeps domain-owned executors such as `thoth-dissemination`, preserves ADR-0008 machine-role rules and ADR-0010 `ServiceOperation` staff audit/command semantics, and excludes automated legacy-CDN migration. BE-04 transition is expand -> migrate `thoth-dissemination` and `thoth-app` -> remove every runtime `distribution_job*` dependency while preserving fail-closed Publisher Services activation -> close the old rollback window -> later contract legacy storage with a locked migration-local zero-row assertion; v1.7.0 remains immutable and `distribution_platform` remains. BE-04/DIS-02 activation and Hosting implementation depending on this architecture remain HOLD. Requires fresh independent exact-head review and exact CTO approval before implementation may rely on it. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - the CTO approved the exact architecture decision on 2026-09-30. ADR-0012 partially supersedes ADR-0008 and ADR-0010 only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: this approved decision is repository-authoritative only when this exact approval-state content has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | ## Merge and implementation rules From c43aa750ea4d971fe4f803cf761868876f0783bd Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 15:44:10 +0100 Subject: [PATCH 23/48] THOTH-ASYNC-01: clean approval metadata formatting --- .../ADR-0012-shared-asynchronous-event-and-job-execution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 46ed34782..fa40dda16 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1801,7 +1801,7 @@ shared-engine tests. This ADR is **APPROVED**. -Approved by: Javi, CTO +Approved by: Javi, CTO Approval date: 2026-09-30 The approval adopts the architecture decision and invariants recorded in this From bd5223deee44b465dd860ff96b75d81ad41cbf2d Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 30 Sep 2026 16:06:31 +0100 Subject: [PATCH 24/48] THOTH-ASYNC-01: align approved ADR amendment rule --- ...DR-0012-shared-asynchronous-event-and-job-execution.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index fa40dda16..cffee08b0 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1819,10 +1819,10 @@ GitHub and is not duplicated into this ADR as transient lifecycle metadata. The partial supersession of ADR-0008 and ADR-0010 is limited exactly to the clauses identified in this ADR's header. All unaffected architecture in those -records remains binding. After this decision becomes repository-authoritative, -material architectural changes require a new ADR or other approved superseding -decision under the normal repository controls; factual clarifications remain -subject to normal review. +records remains binding. After approval, material architectural changes require a new ADR that +supersedes this one. Factual clarifications may update this ADR only when they +do not alter the decision. Every update follows normal review and changelog +requirements. BE-04/DIS-02 deployment/activation and Hosting implementation that depends on the async architecture remain separately controlled and are not authorized by From 227057cae8a4c10f7f7795e3afe68271e56681b9 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 1 Oct 2026 15:57:09 +0100 Subject: [PATCH 25/48] THOTH-ASYNC-01-ADR-01: clarify route dispositions --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 258 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 28 +- 3 files changed, 225 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 951d10c28..66cfbb5b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - `THOTH-ASYNC-01-CTRL-01`: record the CTO-approved [`ADR-0013`](docs/engineering/decisions/ADR-0013-programme-integration-decision-reliance.md), a shared engineering-control decision that preserves `develop` as the repository-authority boundary while defining a narrower, explicitly opt-in programme-integration reliance mechanism for one exact `APPROVED` and independently reviewed ADR version merged into a designated `feature/` integration branch. The decision requires durable programme opt-in, exact-version pinning, separately authorized and reviewed dependent slices on the same integration line, fail-closed handling of decision drift, and explicit pinned preview/dependency evidence for cross-repository consumers; unrelated programmes and normal `develop` work do not inherit programme-local reliance, existing programmes do not adopt it implicitly or retroactively, and final programme integration remains subject to the normal integrated review and CTO merge gates. ADR-0013 itself must be `APPROVED`, independently reviewed and repository-authoritative before the mechanism can be used. **Control/documentation decision only: no programme branch is created, no existing ADR is modified, no runtime/schema/API/migration contract changes, and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-CLARIFICATION`: clarify [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) so its route-disposition/job-mapping invariant matches the already-approved terminal non-materialization semantics in section 3.2: each `(event_id, route_key)` has exactly one durable disposition; a materialized disposition points to one job (with explicit durable coalescing permitted only for not-yet-started jobs), while an authorized/audited terminal non-materialized disposition points to no job and records actor, authority, reason and any covering reconciliation/backfill under the same uniqueness boundary. This is a factual clarification only and changes no selected architecture. **It authorizes no implementation, migration, provider/IAM/runtime action, deployment, release or production activation.** - `THOTH-ASYNC-01-ADR-01-APPROVAL`: reconcile the durable decision state for [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) after CTO architecture approval on 2026-09-30. ADR-0012 now records `Status: APPROVED`, the decision owner approval metadata and a durable authority condition under which the approved record becomes repository-authoritative only after independent exact-head review and merge to `develop`. ADR-0008 and ADR-0010 now record only the exact partial supersession boundaries already defined by approved ADR-0012, leaving all unaffected machine-role, least-privilege, `SUPERUSER`-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules binding. The decision register reflects the approved decision state. The approved ADR-0012 architecture and invariants are otherwise unchanged, and the historical proposal entry below is preserved unchanged. **Documentation/control authority-state reconciliation only: no runtime implementation, schema/data migration, GraphQL/API contract, IAM/provider change, credential action, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01`: propose [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md), a shared PostgreSQL-backed asynchronous event/route/job architecture for Thoth (programme [#957](https://github.com/thoth-pub/thoth/issues/957), authoring task [#958](https://github.com/thoth-pub/thoth/issues/958)). The proposal distinguishes durable events/routes/jobs; requires database-owned route registration with separately authorized activation/deactivation boundaries and an event-emission deployment floor; uses effect-scoped idempotency/coalescing; requires every kind to declare CURRENT_STATE or REVISION_BOUND semantics; and standardizes priority/availability, bounded pre-write retries, WAITING checkpoints, post-write deadlines, claim-token-fenced renewable leases and reconciliation-required handling. Event/job payloads default to canonical IDs plus source revision/fingerprint and minimal parameters rather than copied snapshots. The default trusted headless `thoth-worker` uses one dedicated routine task role, while publisher-controlled parsing is isolated in a separate minimal-authority `UNTRUSTED_CONTENT` pool that writes only quarantine/staging output; a trusted promotion job validates artifacts and resolves publisher/domain/bucket/key/Hosting targets from canonical state. No worker receives static AWS keys. Released BE-04 is retired through expand -> migrate `thoth-dissemination` and `thoth-app` -> remove legacy GraphQL and every remaining runtime `distribution_job*` dependency while preserving fail-closed AUTOMATIC_PUSH activation -> close the old rollback window -> later contract legacy storage with a locked in-migration zero-row assertion, while historical v1.7.0 remains immutable and `distribution_platform` remains. **Architecture/control proposal only: no runtime implementation, migration, IAM/provider change, credential remediation, worker deployment, BE-04/DIS-02 activation, Hosting implementation, external write or production activation is authorized.** - `CTRL-BRANCH-RELEASE-01`: preserve each managed repository's established release branch name instead of normalizing every repository to `master` (issue [897](https://github.com/thoth-pub/thoth/issues/897), parent control programme [818](https://github.com/thoth-pub/thoth/issues/818)). Adds [`ADR-0011`](docs/engineering/decisions/ADR-0011-preserve-established-release-branch-names.md), which standardizes the **role** of the release branch, written ``, rather than whether it is spelled `main` or `master`. The shared conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `, with each repository's `` resolved from its own verified live state: `main` repositories remain on `main`, `master` repositories remain on `master`, and neither name is created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` release flow is unchanged, and [`ADR-0009`](docs/engineering/decisions/ADR-0009-programme-integration-branch-namespace.md) is unchanged in substance. Release-branch conversion is removed from `BR-APP-01`, `BR-DIS-01`, `BR-SPHINX-01`, `BR-DASH-01`, `BR-WIDGET-01` and `BR-LIC-01`, and Vercel production for `metrics-dashboard` is no longer moved from `main` for branch spelling; the independently justified development-branch, protection, CI, provider and publication readiness work in each of those tasks is retained, CG-03 and CG-04 remain **OPEN**, and no task's risk is lowered. Reconciles `branch-topology.md`, `control-gaps.md`, the affected repository-map entries and the Metrics task tracker. **Documentation/control only: no established release, default or development-topology branch is created, deleted, renamed or moved by `ADR-0011` in any managed repository; no repository setting, default branch, protection, ruleset, CI workflow, provider or deployment configuration is changed; and there is no runtime, schema, migration, GraphQL/API, auth, contract, generated client, publication or production effect.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index e74a4edfe..104df8c02 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -461,6 +461,72 @@ was created, rotated or changed. ADR-0008 domain-specific application authorization remains binding. Worker-pool IAM authority and ZITADEL/application authorization remain separate concepts. +## Approval-state and review history + +The architecture content at +`631e28d1f05495f24ce88369d4557f1033f970b3` received CTO exact-content +approval before the durable approval-state reconciliation. + +That approval-state reconciliation made the following durable control changes +without altering the selected architecture: + +- ADR-0012 records `Status: APPROVED`, `Approved by: Javi, CTO`, + `Approval date: 2026-09-30` and the approval/authority rule in section 12; +- ADR-0008's header records only the explicit partial-supersession boundary + selected by ADR-0012, leaving its unaffected machine-role and least-privilege + controls binding; +- ADR-0010's header records only the explicit partial-supersession boundary + selected by ADR-0012, leaving its unaffected Staff Operations, + `ServiceOperation`, desired/execution/observed-state, attention, + reconciliation and staff-command controls binding; +- the engineering decision register records ADR-0012 as `APPROVED` with its + repository-authority condition and preserves the distinction between + architecture approval and implementation authorization; +- the Unreleased changelog contains the durable ADR-0012 approval entry while + preserving the earlier proposal entry. + +The five-path approval-state source authorization existed before that mutation. +Its omitted GitHub authorization receipt was reconciled as historical provenance +in #958 comment `5914033493`; the reconciliation explicitly does not portray +the later comment as retroactive authorization. + +The approval-state candidate reached +`c43aa750ea4d971fe4f803cf761868876f0783bd` and received +`CHANGES REQUIRED` for AS-01. The direct-child AS-01 correction +`bd5223deee44b465dd860ff96b75d81ad41cbf2d` restored the repository ADR +amendment rule. Fresh independent CRITICAL review then returned `APPROVED` +for that exact head in #958 comment `5914564111`. + +The later programme-integration compatibility reconciliation produced +`7f7e62824eb40308688b46351ffea182e9e52942` against +`feature/worker @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462`. Fresh independent +CRITICAL review of that exact integration candidate returned `CHANGES REQUIRED` +in #958 comment `5919509096`. The accepted blockers were missing exact +validation evidence, transient/incomplete durable report history and the +contradiction between section 3.2's terminal route dispositions and the +section 3.5/invariant-8 every-route-record-maps-to-a-job wording. The latter is +a factual clarification of the already-selected section 3.2 semantics, not a +new architecture decision. + +## Verified BE-04 consumer impact + +The independent consumer-impact check for the retiring BE-04 identifiers is +durably recorded as follows: + +- `thoth-pub/thoth-app`: downstream migration required; +- `thoth-pub/thoth-dissemination`: downstream migration required; +- `thoth-pub/thoth-pyramid`: unaffected by the retiring BE-04 identifiers; +- standalone `thoth-pub/thoth-client`: unaffected; +- internal Rust `thoth-client` / `thoth-export-server`: unaffected; +- `thoth-pub/metrics-dashboard`: unaffected; +- `thoth-pub/metrics-widget`: unaffected; +- `thoth-pub/baboon`: unaffected; +- `thoth-pub/thoth-sphinx`: not a verified active consumer; +- `thoth-pub/infrastructure`: runtime/IAM substrate, not a GraphQL consumer. + +This record is compatibility evidence for the BE-04 retirement plan. It does +not authorize either downstream migration or removal of the legacy contract. + ## Programme-integration compatibility reconciliation After ADR-0013 became repository-authoritative and THOTH-ASYNC-01 explicitly @@ -485,29 +551,21 @@ The histories had diverged. The only content changed on both sides since the historical common base was `CHANGELOG.md` and `docs/engineering/decisions/decision-register.md`. -The reconciliation preserves the reviewed ADR-0012, ADR-0008 and ADR-0010 -decision blobs byte-for-byte. It carries the repository-authoritative ADR-0013, -branching workflow and operating-model doctrine from `feature/worker` -unchanged. Manual text reconciliation is limited to: - -- `CHANGELOG.md`: retain the ADR-0013 control entry and both ADR-0012 - proposal/approval entries; -- `docs/engineering/decisions/decision-register.md`: retain ADR-0013 and its - programme-integration reliance rule while restoring the approved ADR-0012 - row; -- this implementation report: reconcile durable task topology and approval - state without copying transient live review or merge status into source. - -ADR-0012 remains not repository-authoritative until its own authority condition -is satisfied on `develop`. Under ADR-0013, programme-local reliance is also -not effective merely because this reconciliation exists: the exact reconciled -ADR-0012 version must first merge into `feature/worker`, and the programme -ledger must record the merge/reachability evidence required by ADR-0013 -eligibility conditions 7 and 8. - -Any source head created by this reconciliation requires fresh independent -CRITICAL exact-head review against the exact `feature/worker` base before -merge authorization. +The compatibility reconciliation preserved the then-reviewed ADR-0012, +ADR-0008 and ADR-0010 decision blobs byte-for-byte, while carrying the +repository-authoritative ADR-0013, branching workflow and operating-model +doctrine from `feature/worker` unchanged. The subsequent factual ADR-0012 +clarification changes the exact ADR-0012 version but does not alter ADR-0013 or +those shared control documents. + +ADR-0012's normal repository-authority condition remains reachability from +`develop`. ADR-0013's narrower programme-local reliance mechanism applies only +to an exact approved and independently reviewed ADR version that is merged into +the designated `feature/worker` line and durably pinned by the programme. A +change to ADR-0012 makes any earlier exact-version programme pin stale until the +clarified version has received fresh independent CRITICAL review, explicit CTO +approval and separate programme-pin reconciliation. This report does not assert +that programme-local reliance is currently effective. ## External/runtime effects @@ -521,22 +579,100 @@ release/publication: 0 production activation: 0 ``` -## Validation performed for this documentation task +## Final proposed candidate validation + +The final documentation candidate was validated before commit in a controlled +workspace for the exact six-path PR slice, with the relevant base/candidate +bytes bound to the GitHub blob identities recorded below. + +Cumulative base: + +```text +feature/worker @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462 +``` + +Literal unrestricted whitespace validation: + +```text +git diff --check HEAD + +exit: 0 +stdout: "" +stderr: "" +``` + +Literal cumulative path validation: + +```text +git diff --name-only HEAD + +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +The unrestricted correction-boundary comparison against prior PR head +`7f7e62824eb40308688b46351ffea182e9e52942` contains exactly: + +```text +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +``` + +No fourth correction path is present. + +Exact blob verification for the final proposed candidate/control inputs: + +```text +ADR-0008: +622060ad90eec41c792f110c373efffbb11a4b56 + +ADR-0010: +aca2142a3387785e80db908b3a5c1b0afd82ab51 + +ADR-0012 final clarified bytes: +dec6665353804e42f22474954bf878308092b717 + +ADR-0013: +d928f957bdf775d03e99fc73888ec8e2dec5f80b + +branching-and-release-workflow: +6edce35dd29307b0554bcd122ed5518038f57df5 -- the original authorized base remains recorded as historical authoring evidence; -- ADR-0012 is `APPROVED`, with Javi as approver and approval date 2026-09-30; -- PR #960 exists and its programme target is `feature/worker`; -- the programme integration base for this reconciliation is - `345a7a04131e7c0539f7518c6ea457fa5cb6a462`; -- ADR-0012, ADR-0008 and ADR-0010 decision bytes are preserved from the - previously reviewed ADR source head; -- the final candidate against `feature/worker` is required to remain exactly - the cumulative six-path ADR task footprint; -- no runtime test result is claimed by this documentation/control-only task; -- live candidate head, CI, review and merge evidence belongs in GitHub #957/#958 - rather than in a self-invalidating committed status line. +operating-model: +fc8b9c90a7d0ae98a44645d2e316a0573c13ce2a +``` + +Composition and doctrine checks: + +- changelog: one `[Unreleased]` structure; ADR-0013, ADR-0012 proposal, + ADR-0012 approval and this factual clarification are all retained; +- decision register: unchanged candidate blob + `fc64dddbf6beddd602d31d694d9dae7c40c417c2`; ADR-0012 remains + `APPROVED` and ADR-0013's programme-integration rule remains present; +- internal links/repository-relative paths: checked for the modified ADR, + report and changelog references; referenced repository paths exist; +- terminology/repository names: changed text uses canonical repository names, + including `thoth-sphinx`; no obsolete `thoth-sphynx` spelling is + introduced; +- stale/conflicting doctrine: section 3.2's selected terminal-disposition + behaviour is unchanged, while section 3.5 and invariant 8 now use the same + materialized-versus-terminal disposition model; ADR-0013, branching workflow + and operating-model doctrine are byte-preserved. + +Test applicability: -Independent review is valid only against the exact candidate head it names. +```text +runtime/unit tests: NOT APPLICABLE +database/migration tests: NOT APPLICABLE +provider/runtime tests: NOT APPLICABLE +manual CI dispatch/rerun: NO +final-head automatic CI: GitHub-owned post-push evidence +``` ## Deviation history @@ -547,22 +683,36 @@ corrected before branch/source mutation. No source-scope deviation remains. -## Remaining lifecycle gates - -1. fresh independent CRITICAL exact-head review of the reconciled PR #960 - candidate against exact programme base `feature/worker @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462`; -2. explicit CTO merge authorization only if that review returns `APPROVED`; -3. merge PR #960 into `feature/worker`; -4. record the exact ADR-0012 merge/reachability evidence required by ADR-0013 - eligibility conditions 7 and 8; -5. only after programme-local reliance is effective, separately specify, - authorize, implement and independently review each repository-local async - slice and any pinned cross-repository preview dependency; -6. retain separate migration execution, provider/IAM/runtime, deployment, - release and production-activation gates. - -Merging ADR-0012 into `feature/worker` does not make ADR-0012 -repository-authoritative on `develop`, does not itself authorize -implementation and does not authorize any external effect. +## Durable control conditions + +These conditions remain true regardless of the current live PR/review/merge +stage: + +1. ADR-0012 is an architecture decision only. Approval or merge does not + authorize runtime implementation, schema/data migration, provider/IAM/runtime + mutation, external writes, deployment, release or production activation. +2. Any exact ADR-0012 version used for merge-readiness or programme-local + reliance requires the review/approval evidence required by repository + doctrine for that exact version; a later source change invalidates + exact-head approval for the earlier version. +3. ADR-0012 becomes repository-authoritative only under its section-12 authority + condition, including reachability from `develop`. +4. ADR-0013 programme-local reliance, when used, is exact-version-bound: the + relied-upon ADR version must be approved, independently reviewed, merged into + the designated `feature/worker` line and durably pinned. Any later ADR-0012 + change makes the earlier programme pin stale until separately reconciled. +5. Every async implementation slice remains separately specified, authorized, + independently reviewed and integrated. Downstream repositories may not guess + an unmerged upstream contract and must use an explicitly authorized pinned + version/preview where applicable. +6. Merge authorization, merge, migration execution, provider/IAM/runtime + mutation, deployment, release, production activation and observation remain + separate lifecycle gates. +7. Historical migration `20260814_v1.7.0` remains immutable; BE-04 retirement + continues to require the approved expand -> consumer migration -> runtime/API + retirement -> rollback-floor closure -> later storage-contraction sequence. +8. The terminal route-disposition clarification does not permit silent work + loss: terminal non-materialization remains explicit, authorized, audited and + subject to the section 3.2 reconciliation/backfill and attention semantics. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index cffee08b0..dec666535 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -566,12 +566,18 @@ current-state execution and backfill create new logical jobs with new effect identity and explicit parent/replay/correlation metadata, following ADR-0010's distinction between retry, replay and current-state redistribution. -Event routing identity and job idempotency are related but distinct. Every -`(event_id, route_key)` route record points to exactly one materialized job, -while several route records may point to one **not-yet-started** job only when -the kind defines an explicit durable coalescing rule. Coalescing therefore forms -a many-route-to-one-job relationship; it never erases the per-event route -records needed for audit/completeness. +Event routing identity and job idempotency are related but distinct. Exactly one +durable disposition exists for each `(event_id, route_key)`. A successfully +materialized disposition points to exactly one materialized job. Several +materialized route dispositions may point to one **not-yet-started** job only +when the kind defines an explicit durable coalescing rule. An explicit, +authorized and audited terminal non-materialized disposition points to no job +and records the actor, authority, reason and any covering +reconciliation/backfill. Materialized and terminal non-materialized +dispositions share the same `(event_id, route_key)` uniqueness boundary and +cannot coexist for the same route/event pair. Coalescing therefore forms a +many-route-to-one-job relationship only among materialized dispositions; it +never erases the per-event route records needed for audit/completeness. An idempotency conflict never silently means "route satisfied". It either: @@ -1327,8 +1333,14 @@ execution until a separate approved architecture decision says otherwise. explicit backfill/replay through the same route materialization records. 7. Job idempotency identifies a specific intended effect; a resource-only key must not suppress later revisions/schedule slots/commands. -8. Every event-route record maps to exactly one job; many route records may map - to one job only under an explicit not-yet-started coalescing rule. +8. Exactly one durable disposition exists for each `(event_id, route_key)`. + A successfully materialized disposition maps to exactly one job; many + materialized dispositions may map to one not-yet-started job only under an + explicit durable coalescing rule. An explicit authorized/audited terminal + non-materialized disposition maps to no job, records actor, authority, + reason and any covering reconciliation/backfill, and shares the same + uniqueness boundary so it cannot coexist with a materialized disposition for + the same event/route pair. 9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. A CURRENT_STATE fingerprint covers all canonical inputs determining its effect. A REVISION_BOUND effect must not regress a newer already-applied revision From b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 1 Oct 2026 16:49:01 +0100 Subject: [PATCH 26/48] THOTH-ASYNC-01-ADR-01: correct validation provenance --- ...H-ASYNC-01-ADR-01-implementation-report.md | 201 ++++++++++++++---- 1 file changed, 157 insertions(+), 44 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 104df8c02..01c23ffec 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -579,33 +579,46 @@ release/publication: 0 production activation: 0 ``` -## Final proposed candidate validation +## Validation evidence and provenance -The final documentation candidate was validated before commit in a controlled -workspace for the exact six-path PR slice, with the relevant base/candidate -bytes bound to the GitHub blob identities recorded below. +### Correction at `227057cae8a4c10f7f7795e3afe68271e56681b9` -Cumulative base: +Before the route-disposition clarification commit, the proposed source bytes +were frozen, the three modified-file blob identities were computed, and the +created GitHub blobs matched those expected identities exactly. The cumulative +six-path footprint, the three-path correction boundary, the unchanged control +blobs and full-file whitespace/composition checks were also verified. -```text -feature/worker @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462 -``` - -Literal unrestricted whitespace validation: +The literal commands recorded by the previous version of this report: ```text git diff --check HEAD - -exit: 0 -stdout: "" -stderr: "" +git diff --name-only HEAD ``` -Literal cumulative path validation: +were run in a controlled six-path **projection** workspace. That workspace did +not contain all six exact GitHub file bytes. Those command results are therefore +historical projection evidence only and are not represented here as exact-byte +Git validation of the final candidate. This provenance defect was recorded +durably in #958 comment `5934574460` and the corresponding programme HOLD in +#957 comment `5934575236`. + +After push, GitHub verification of exact head +`227057cae8a4c10f7f7795e3afe68271e56681b9` established: ```text -git diff --name-only HEAD +tree: +1645d0ebdca5df231d3fa72ede229cccab76771e + +parent: +7f7e62824eb40308688b46351ffea182e9e52942 + +correction paths: +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +cumulative PR paths against feature/worker: CHANGELOG.md docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md @@ -614,29 +627,27 @@ docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution. docs/engineering/decisions/decision-register.md ``` -The unrestricted correction-boundary comparison against prior PR head -`7f7e62824eb40308688b46351ffea182e9e52942` contains exactly: +Exact final blobs at that head were: ```text -CHANGELOG.md -docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md -docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md -``` +CHANGELOG.md: +66cfbb5b56dfe7a9382614e274a872c68df26dcb -No fourth correction path is present. +implementation report: +104df8c02d8aa8077e720a63bb3c16415afe54c3 -Exact blob verification for the final proposed candidate/control inputs: - -```text ADR-0008: 622060ad90eec41c792f110c373efffbb11a4b56 ADR-0010: aca2142a3387785e80db908b3a5c1b0afd82ab51 -ADR-0012 final clarified bytes: +ADR-0012: dec6665353804e42f22474954bf878308092b717 +decision register: +fc64dddbf6beddd602d31d694d9dae7c40c417c2 + ADR-0013: d928f957bdf775d03e99fc73888ec8e2dec5f80b @@ -647,22 +658,121 @@ operating-model: fc8b9c90a7d0ae98a44645d2e316a0573c13ce2a ``` -Composition and doctrine checks: - -- changelog: one `[Unreleased]` structure; ADR-0013, ADR-0012 proposal, - ADR-0012 approval and this factual clarification are all retained; -- decision register: unchanged candidate blob - `fc64dddbf6beddd602d31d694d9dae7c40c417c2`; ADR-0012 remains - `APPROVED` and ADR-0013's programme-integration rule remains present; -- internal links/repository-relative paths: checked for the modified ADR, - report and changelog references; referenced repository paths exist; -- terminology/repository names: changed text uses canonical repository names, - including `thoth-sphinx`; no obsolete `thoth-sphynx` spelling is - introduced; -- stale/conflicting doctrine: section 3.2's selected terminal-disposition - behaviour is unchanged, while section 3.5 and invariant 8 now use the same - materialized-versus-terminal disposition model; ADR-0013, branching workflow - and operating-model doctrine are byte-preserved. +Automatic PR CI for that head completed with four successful jobs and six +classification-skipped jobs, with no failure or in-progress job and no manual +dispatch/rerun. + +### This report-only provenance correction candidate + +The correction authorized by #958 comment `5934661557` changes only this +implementation report from parent +`227057cae8a4c10f7f7795e3afe68271e56681b9`. + +Before commit, the complete candidate bytes for all six cumulative PR paths were +read from the exact GitHub parent, with only this report replaced by the +proposed corrected bytes. Full-file byte-level whitespace checks found no +trailing whitespace on any candidate file. + +The exact cumulative-base blobs are: + +```text +CHANGELOG.md: +cf662733b61354f08f7d6b26ead3b8079f10d8ba + +implementation report: +ABSENT + +ADR-0008: +19273fe60520b8a907c8b5a28e76d4fe741bb832 + +ADR-0010: +0e3b047435dbdcd2fdd15cbfc9ac4c1fc2e601ce + +ADR-0012: +ABSENT + +decision register: +ed03bcc6766f05fb7f026468689727b3c42f4c94 +``` + +The five unchanged candidate paths retain their exact parent blob identities: + +```text +CHANGELOG.md: +66cfbb5b56dfe7a9382614e274a872c68df26dcb + +ADR-0008: +622060ad90eec41c792f110c373efffbb11a4b56 + +ADR-0010: +aca2142a3387785e80db908b3a5c1b0afd82ab51 + +ADR-0012: +dec6665353804e42f22474954bf878308092b717 + +decision register: +fc64dddbf6beddd602d31d694d9dae7c40c417c2 +``` + +The proposed report bytes were Git-blob hashed before commit; the exact proposed +blob is recorded in the owning #958 completion handoff rather than self-pinned +inside the bytes whose hash it would change. + +GitHub's exact base-to-parent comparison contains exactly the six cumulative PR +paths listed above. Because this proposed correction changes only the report, +the cumulative path set remains those same six paths and the direct correction +delta is exactly: + +```text +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +``` + +The execution shell available for this control step cannot resolve GitHub and +cannot materialize an exact repository worktree. Consequently this report does +**not** claim that literal local Git commands were executed against a full +exact-byte worktree. + +The required literal commands were nevertheless re-run without restrictive +pathspecs in a six-path path/projection workspace after the exact GitHub-byte +checks: + +```text +git diff --check HEAD + +exit: 0 +stdout: "" +stderr: "" + +git diff --name-only HEAD + +exit: 0 +stdout: +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +stderr: "" +``` + +Those literal command results are projection evidence only. Exact-byte evidence +is instead the complete GitHub file reads, Git blob identities, full-file +whitespace checks and exact GitHub compare path sets above. This limitation +remains visible for the fresh independent reviewer to adjudicate; it is not +converted into stronger evidence by assertion. + +Composition and doctrine checks remain: + +- changelog has one `[Unreleased]` structure and retains ADR-0013, ADR-0012 + proposal, approval and factual-clarification entries; +- decision register remains byte-identical and records ADR-0012 as `APPROVED` + while retaining ADR-0013's programme-integration rule; +- section 3.2's terminal-disposition semantics remain unchanged and section 3.5 + plus invariant 8 use the same materialized-versus-terminal disposition model; +- ADR-0013, branching workflow and operating-model doctrine remain + byte-preserved; +- repository names use canonical spellings, including `thoth-sphinx`. Test applicability: @@ -681,7 +791,10 @@ its normalized create response, briefly producing a `#undefined` parent reference in #958. The returned URLs established #957/#958 and the issue was corrected before branch/source mutation. -No source-scope deviation remains. +The source write budget remains compliant. The validation-evidence provenance +deviation at head `227057cae8a4c10f7f7795e3afe68271e56681b9` is recorded in +#958 comment `5934574460`; this report correction removes the unsupported +exact-worktree claim rather than retroactively curing it. ## Durable control conditions From c046e6bf6510f24ee1231f86293a3d499bceed7e Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Mon, 5 Oct 2026 18:38:30 +0100 Subject: [PATCH 27/48] THOTH-ASYNC-01-ADR-01: record ADR-0012 approval state Record the CTO content approval of 2026-10-05 for the exact Route-B candidate (head 95769537, ADR-0012 blob a88aed0a) as durable decision state, as the single approval-state commit Route B permits (#958). - ADR-0012: Status PROPOSED -> APPROVED, Approved by: Javi, CTO, Approval date: 2026-10-05, and the section 12 approval-state sentence; no architecture text changes. - decision register: ADR-0012 row approval state only. - CHANGELOG: correction entry approval-state wording only. - implementation report: approval-state and validation evidence (Amendment 3 C4/C5). ADR-0008, ADR-0010, ADR-0013 and inherited control files unchanged. Architecture approval only: no runtime, migration, provider, deployment, release or activation change. --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 460 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 14 +- .../decisions/decision-register.md | 2 +- 4 files changed, 371 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83882c489..ff7089a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-CORRECTION`: propose a material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `CTRL-ADR-PR-FIRST-01`: add a PR-first staged approval route for a material pre-authority ADR correction to the engineering-decision amendment process in [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md) (issue [966](https://github.com/thoth-pub/thoth/issues/966)). The existing pre-commit exact-final-blob approval route (now Route A) is retained unchanged. Route B lets the implementing agent push the corrected candidate to a real task branch and draft PR as `PROPOSED`, without presenting the earlier version's approver or approval date as approval of the corrected bytes; the CTO then records architecture-content approval against the exact candidate blob/head from the PR; only afterwards may exactly one bounded approval-state commit record `PROPOSED -> APPROVED` with the fresh approver/date and authorized companion metadata; the CTO records exact-final-blob approval of the final ADR blob before merge; fresh independent exact-head review binds to the final PR head; and HIGH/CRITICAL merge authorization and the guarded merge remain separate gates. The doctrine records why that approval-state commit is durable decision state rather than the lifecycle transcription `ADR-0005` prohibits. Material-correction eligibility, the stale-control list, `ADR-0013` exact-version reliance/drift rules and the repository-authority condition are unchanged. **Documentation/control only: no ADR is changed by this task, and no runtime, schema, migration, API, authorization, workflow, provider/IAM, deployment, release or production behaviour is authorized or affected.** - `CTRL-ADR-AMEND-01`: correct the engineering-decision authority and amendment process so `APPROVED` records exact CTO/decision-owner approval while repository authority additionally requires independent exact-head review and reachability from `develop`; permit a material in-place correction only for an approved ADR number that has never been repository-authoritative, under explicit CTO classification/authorization, exact-final-blob approval before commit, stale exact-version control invalidation, and fresh independent exact-head review. Factual clarifications remain separately classified and do not alter the selected decision; `ADR-0013` programme-local reliance remains exact-version-bound and fail-closed on decision drift. **Documentation/control only: no ADR is changed by this task, no runtime/schema/API/migration/provider/IAM/deployment/release/production behaviour is authorized or modified.** - [932](https://github.com/thoth-pub/thoth/pull/932) - `BR-DASH-01C-CONTROL-RECONCILIATION`: reconcile the shared engineering-control and Thoth Metrics records with the CTO-approved permanent `metrics-dashboard` topology, `feature/* -> dev -> main` (issue [931](https://github.com/thoth-pub/thoth/issues/931), parent programme [766](https://github.com/thoth-pub/thoth/issues/766)). `dev` is recorded as that repository's permanent development/integration branch and `main` as its preserved release/default branch, and no active control now requires a dashboard `dev -> develop` normalization. The cross-repository Metrics flow resolves through each repository's own verified, approved `` (`develop` in `thoth`, `dev` in `metrics-dashboard`), so dashboard `feature/metrics` is later created from verified `dev` and never from the stale legacy `develop` ref, which remains undeleted and separately controlled. Dashboard protection, CI/lint/test and Vercel-verification gaps remain open and `BR-DASH-01` stays HIGH; `thoth`'s own `develop -> master` flow, `ADR-0009` and `ADR-0011` are unchanged. Documentation/control only: no API, schema, runtime, authorization, provider, deployment or production change, and no default-branch, protection or ruleset change. diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 594b2c914..cd97eb441 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -29,17 +29,20 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the candidate commit that contains this report; it is not -self-pinned here (section 3). Its publication as the PR #960 head is a -post-commit, GitHub-owned lifecycle fact under `ADR-0005`. +Head commit: the approval-state commit that contains this report, a direct +child of the content-approved candidate +`95769537cdc9871e42f9f3e5443604a96beb74da`; it is not self-pinned here +(section 3). Its publication as the PR #960 head is a post-commit, +GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting `feature/worker`; its live state, head and checks are GitHub-owned lifecycle evidence. Expected branch deletion after merge: YES Final programme PR required: YES - `feature/worker -> develop` is a separate programme-level gate. -Implementing model: Route-B candidate stage - Claude (Opus 5.5), bounded -implementing agent. Earlier rounds are recorded in Appendix A. +Implementing model: Route-B candidate and approval-state stages - Claude +(Opus 5.5), bounded implementing agent. Earlier rounds are recorded in +Appendix A. Reasoning level: standard Historical and current control identities: @@ -82,6 +85,11 @@ b44303c214498baf76c9d9a0a7cab374377f9cbe unmodified Route-B merge-tree baseline: e15607878e66d2ff12b819dcea13293a46674f13 + +content-approved Route-B candidate / approval-state parent: +95769537cdc9871e42f9f3e5443604a96beb74da +tree 5084c6951a1b57b449f6045b139aacfbc488e534 +ADR-0012 blob a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c ``` Both `feature/worker` refreshes were separately authorized control actions @@ -101,16 +109,28 @@ Approved specification: - fresh independent CRITICAL specification approval receipt: #958 comment `5992836778`; - candidate-stage implementation authorization: #958 comment `5993634460`; -- replacement-candidate authorization: #958 comment `5998119577` (section 5). - -Implemented objective: the Route-B **candidate stage** of a material -architectural correction of ADR-0012 before repository authority. The candidate -implements the CTO-selected append-only activation-epoch architecture (#958 -comment `5935475916`) that resolves review finding `R-b467-01` - a single -activation/deactivation pair cannot represent repeated route activation without -rewriting historical eligibility or changing the route identity - and carries -`Status: PROPOSED` together with a `PROPOSED` decision-register row, one -`PROPOSED` changelog correction entry and this reconciled report. +- replacement-candidate authorization: #958 comment `5998119577` (section 5); +- approval-state authorization: #958 comment `5999579357`. + +Implemented objective: the Route-B material architectural correction of +ADR-0012 before repository authority, in two source stages. + +- **Candidate stage.** The candidate implements the CTO-selected append-only + activation-epoch architecture (#958 comment `5935475916`) that resolves + review finding `R-b467-01` - a single activation/deactivation pair cannot + represent repeated route activation without rewriting historical eligibility + or changing the route identity. It carried `Status: PROPOSED` together with a + `PROPOSED` decision-register row, one `PROPOSED` changelog correction entry + and the candidate-stage report. +- **Approval-state stage (this commit).** The CTO approved the exact candidate + content - head `95769537cdc9871e42f9f3e5443604a96beb74da`, ADR-0012 blob + `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`, inspected against prior blob + `dec6665353804e42f22474954bf878308092b717` - on 2026-10-05 in a durable record + on #958. This commit records that approval as durable decision state: + ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and + `Approval date: 2026-10-05`, the ADR-0012 register row and the correction + changelog entry moved to the approved state, and this report's approval-state + evidence. No architecture content changes. Out-of-scope changes made: NONE @@ -180,28 +200,41 @@ the corrected version: `APPROVED`. Those records remain historical evidence for their exact versions. They are -not approval of the corrected version, which requires fresh CTO content -approval, a single approval-state commit, CTO exact-final-blob approval, fresh -independent exact-head review and separate merge authorization under Route B. +not approval of the corrected version. Under Route B the corrected version +received fresh CTO content approval on 2026-10-05, recorded as `APPROVED` by +this approval-state commit; CTO exact-final-blob approval, fresh independent +exact-head review and separate merge authorization remain separate later gates. ## 3. Commits -Route-B candidate commit (this stage): +Route-B approval-state commit (the commit that contains this report): + +- exactly one ordinary single-parent commit on `feature/async/adr-0012` - + `THOTH-ASYNC-01-ADR-01: record ADR-0012 approval state` + - direct parent: the content-approved candidate + `95769537cdc9871e42f9f3e5443604a96beb74da` + - changes only the four authorized paths, and only their approval state + (section 4) -- exactly one genuine two-parent merge commit on `feature/async/adr-0012` - +Its SHA and tree are not embedded here because this report is part of that +tree. They are recorded in the post-publication handoff; once the commit is +published, the PR #960 head and head tree are GitHub-owned lifecycle evidence +under `ADR-0005`. The authorized topology is a fast-forward of the task branch +from the candidate to this commit; no merge commit, rebase, squash, amend, +force update or further source commit is authorized. + +Route-B candidate commit (content-approved): + +- `95769537cdc9871e42f9f3e5443604a96beb74da` - `THOTH-ASYNC-01-ADR-01: propose Route-B activation-epoch correction` - - parent 1: `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` - - parent 2: `b44303c214498baf76c9d9a0a7cab374377f9cbe` - - tree: the unmodified merge-tree + - genuine two-parent merge commit; parent 1 + `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20`, parent 2 + `b44303c214498baf76c9d9a0a7cab374377f9cbe` + - tree `5084c6951a1b57b449f6045b139aacfbc488e534`: the unmodified merge-tree `e15607878e66d2ff12b819dcea13293a46674f13` overlaid only by the four correction paths in section 4 - -The candidate commit SHA and its tree are not embedded here because this report -is part of that tree. They are recorded in the post-publication handoff; once -the candidate is published, the PR #960 head and head tree are GitHub-owned -lifecycle evidence under `ADR-0005`. The authorized topology is a fast-forward -of the task branch from parent 1 to the candidate; no rebase, squash, amend, -force update or third parent is authorized. + - published by a non-force fast-forward of `feature/async/adr-0012` from + parent 1 before this approval-state commit was created Earlier task-branch history from `923545d5c9028bc04c40e38efeb7de674efed3fd`, first-parent order, newest first: @@ -255,8 +288,9 @@ Authorized cumulative PR footprint against refreshed `feature/worker`: Authorized new-file paths: NONE for this stage; this report already existed. -Actual files changed by the candidate relative to the unmodified merge-tree -baseline `e15607878e66d2ff12b819dcea13293a46674f13`: +Candidate-stage changes (content-approved candidate +`95769537cdc9871e42f9f3e5443604a96beb74da`) relative to the unmodified +merge-tree baseline `e15607878e66d2ff12b819dcea13293a46674f13`: - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement the CTO-selected append-only activation-epoch @@ -307,6 +341,48 @@ baseline `e15607878e66d2ff12b819dcea13293a46674f13`: only the tense or framing changes needed to keep it truthful as history. - within authorized write budget: YES +Approval-state changes in this commit relative to the content-approved +candidate `95769537cdc9871e42f9f3e5443604a96beb74da` (the same four paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO content approval of 2026-10-05. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-05`; section 12's candidate-state text ("Current + decision state: **PROPOSED**." and the statement that the corrected content + had not been approved) is replaced by `This ADR is **APPROVED**.`, the same + approver and date, and a statement that the CTO approved this exact + corrected content on 2026-10-05. No architecture text changes; the + repository-authority condition, the ADR-0013 reliance distinction and the + architecture-versus-implementation separation are unchanged. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row only - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, and "its corrected content awaits CTO + approval" becomes "the CTO approved its exact corrected content on + 2026-10-05". All other row text, every other row and the header are + unchanged. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the new correction entry. + - behavioural effect: the `THOTH-ASYNC-01-ADR-01-CORRECTION` entry only - + "propose" becomes "record a CTO-approved", "`PROPOSED`: the corrected + content is not approved" becomes "`APPROVED`: the CTO approved the exact + corrected content on 2026-10-05", and "proposal only" becomes "decision + only". Its statement that the earlier approval entries apply only to the + historical pre-correction versions is unchanged, and no other entry + changes. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: Amendment 3 C4/C5 approval-state and validation evidence. + - behavioural effect: documentation only; records the content approval, the + candidate as approval-state parent, the approval-state validation + (section 9) and the stage-specific action matrix, and preserves the + candidate-stage and historical evidence. + - within authorized write budget: YES + Inherited unchanged from refreshed `develop` through parent 2 (the closed inherited set between the task head and the merge-tree baseline): @@ -336,51 +412,64 @@ PASS The candidate differs from the unmodified merge-tree baseline in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly -the six cumulative PR paths (section 9). No ADR-0008, ADR-0010, ADR-0013, -engineering-control doctrine, workflow, `AGENTS.md`, runtime, migration or -schema path is edited. +the six cumulative PR paths (section 9). The approval-state commit differs +from the content-approved candidate in exactly the same four paths and from +refreshed `feature/worker` in exactly the six cumulative PR paths. No ADR-0008, +ADR-0010, ADR-0013, engineering-control doctrine, workflow, `AGENTS.md`, +runtime, migration or schema path is edited. ## 4.2 Authorized actions actually used -For the Route-B candidate stage authorized by #958 comments `5993634460` and -`5998119577`. This matrix states only what had been done when the commit that -contains this report was created. +For the Route-B candidate stage (#958 comments `5993634460` and `5998119577`) +and the approval-state stage (#958 comment `5999579357`). This matrix states +only what had been done when the commit that contains this report was created. -Done up to and including the creation of this candidate commit: +Candidate stage - completed before this commit: - repository inspection: USED (Git history, refs, doctrine and the #957/#958/PR #960 records, read-only) - source edit: USED (the four authorized correction paths only) - new file creation: NOT USED - file deletion/move/rename: NOT USED (not authorized) -- branch creation: NOT USED (no branch is created; a detached local worktree is - used for construction, and the existing task branch is the authorized - fast-forward target) -- commit: USED (the two-parent replacement candidate merge commit that contains - this report; see section 5 for the earlier unpublished local candidate) -- PR creation/update: USED for one PR #960 state change only - Ready -> Draft, - performed before any candidate was constructed and independently verified by - control. Draft -> Ready is not authorized at this stage. - -Authorized post-commit actions - GitHub-owned lifecycle evidence under -`ADR-0005`, not self-recorded here: +- branch creation: NOT USED (detached local worktrees were used for + construction; the existing task branch was the fast-forward target) +- commit: USED (the two-parent candidate merge commit + `95769537cdc9871e42f9f3e5443604a96beb74da`; see section 5 for the earlier + unpublished local candidate) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` to the candidate) +- PR creation/update: USED (PR #960 Ready -> Draft before any candidate was + constructed, independently verified by control, and the PR description + updated for the published candidate); PR #960 remained a draft + +Approval-state stage - done up to and including the creation of this commit: + +- repository inspection: USED (read-only re-verification of refs, PR #960 + state and the #958 approval records) +- source edit: USED (the same four paths, approval state only) +- commit: USED (the single-parent approval-state commit that contains this + report) + +Authorized post-commit actions for this commit - GitHub-owned lifecycle +evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` to this candidate; -- post-push verification of the remote head, tree and ordered parents; -- the truthful PR #960 description update for the published candidate; + `95769537cdc9871e42f9f3e5443604a96beb74da` to this commit; +- post-push verification of the remote head, tree, direct parent and ADR-0012 + blob; +- the truthful PR #960 description update for the published approval-state + head; - the automatic PR CI and any automatic review activity those actions cause. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the -GitHub ledger rather than written into the candidate source tree as completed. +GitHub ledger rather than written into the source tree as completed. -Not authorized at this stage, and separate later gates: +Not authorized at either stage, and separate later gates: -- candidate content approval, the approval-state commit, Draft -> Ready, - final-head Codex/app review and the final independent review: later Route-B - gates outside this stage; the approval-state commit and Draft -> Ready are not - authorized here +- CTO exact-final-blob approval, Draft -> Ready, final-head Codex/app review + and the final independent review: later Route-B gates; Draft -> Ready is not + authorized for the implementing agent at either stage - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -404,7 +493,7 @@ Automatic CI/provider effects observed (for example a workflow triggered by opening the PR, and whether it performed any external write such as a container-registry push): -Pushing the candidate to the PR head triggers the repository's +Pushing a new head to the PR triggers the repository's `pull_request` workflows (`build-test-and-check`, `check-changelog`, `publish-to-dockerhub`, `run-migrations`). Every path in the change set is `CHANGELOG.md` or under `docs/`, which `.github/scripts/classify_ci_changes.py` @@ -415,9 +504,9 @@ and no migration execution is expected. Under Amendment 3 C2/C11, configured Codex/app review is not expected from a push to a draft PR; final-head Codex/app review is expected at the separately authorized Draft -> Ready transition after the approval-state commit. The actual job outcomes and any -automatic review for this candidate exist only after publication; they are -GitHub-owned lifecycle evidence recorded in the pull request and the -post-publication handoff, not in this file. +automatic review for this approval-state commit exist only after its +publication; they are GitHub-owned lifecycle evidence recorded in the pull +request and the post-publication handoff, not in this file. Historical automatic CI for earlier PR heads followed the same documentation-only classification, with no manual dispatch or rerun: @@ -426,6 +515,7 @@ documentation-only classification, with no manual dispatch or rerun: 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped 227057cae8a4c10f7f7795e3afe68271e56681b9: 4 success, 6 skipped b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20: 4 success (classify x3, check-changelog), 6 skipped +95769537cdc9871e42f9f3e5443604a96beb74da: 4 success (classify x3, check-changelog), 6 skipped (content-approved candidate) ``` Manually initiated external actions (anything the implementing agent @@ -464,8 +554,9 @@ List decisions made within the approved design: logical route with its own epochs and dispositions and receives earlier events only through explicitly authorized backfill/replay; the ADR defines no successor mechanism. -8. The header drops the current `Approved by` / `Approval date` fields. - Section 12 binds the 2026-09-30 approval to the historical pre-correction +8. At the candidate stage the header dropped the earlier `Approved by` / + `Approval date` fields; the approval-state commit adds fresh values + (decision 12). Section 12 binds the 2026-09-30 approval to the historical pre-correction versions without embedding their identities; those identities live in this report and the owning issue. 9. Section 12's approval, authority and supersession text is written to remain @@ -477,6 +568,13 @@ List decisions made within the approved design: 11. The report is restructured to the template rather than appended to, so its durable current state is readable without implying that historical approvals cover the corrected version. +12. The approval-state header uses the same field order as the earlier approved + ADR-0012 versions (`Status`, `Date`, `Approved by`, `Approval date`, + `Decision owner`), and section 12 repeats the approver and date, so the + approval metadata appears in both places as before. +13. The approval date carried by the ADR is the date of the durable CTO + content-approval record, 2026-10-05, as Route B requires; it is neither + back-dated nor anticipated. List any deviation from the specification requiring authorization: @@ -490,6 +588,15 @@ List any deviation from the specification requiring authorization: from the same two authorized parents with the same ADR-0012, decision-register and changelog bytes; only this report differs, to correct that lifecycle wording. +- The approval-state authorization asked this report to record the identifier + of the CTO content-approval comment. The repository-authoritative Route-B rule + in `docs/engineering/decisions/README.md` says the approval-state commit must + not copy content-approval, exact-final-blob approval, review or merge + identifiers into repository files, and that repository records reference the + owning issue and pull request instead. Control selected the + README-conforming resolution before this commit: the report identifies the + content approval by its owning issue (#958), its date and the exact candidate + head and ADR blob it approved, without restating its comment identifier. ## 6. Database and migration effects @@ -522,6 +629,12 @@ Security limitations: NONE Record exact commands and outcomes. +The subsections "Formatting" through "Content checks" record the +candidate-stage validation of candidate +`95769537cdc9871e42f9f3e5443604a96beb74da`, preserved as candidate-stage +evidence. "Approval-state validation" at the end of this section records the +validation of the commit that contains this report. + Validation environment: a full local Git worktree of `thoth-pub/thoth`, detached at `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20`, with every object fetched from `origin`. Immediately before construction, `git ls-remote origin` @@ -532,11 +645,9 @@ returned `develop` and `feature/worker` at The candidate index was built by merging parent 2 into parent 1 without committing, recording the unmodified merge tree, then overlaying only the four correction paths. The checks below ran against that exact staged index before -the candidate commit was created from it. Verification of the created commit's -parents and tree, and of the remote head after push, follows the creation of the -commit that contains this report and is recorded in the post-publication -handoff. Commands that compare with a fixed commit or tree use the full -identities shown. +the candidate commit was created from it. The created candidate commit's +parents and tree were then verified before push (results below). Commands that +compare with a fixed commit or tree use the full identities shown. ### Formatting @@ -689,23 +800,23 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c ``` -Candidate ADR-0012 blob (`PROPOSED`): `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`. This report's own blob -and the candidate tree depend on this report's bytes, so they are not -self-pinned here; they are recorded in the post-publication handoff and, once -published, are GitHub-owned lifecycle evidence. +Candidate ADR-0012 blob (`PROPOSED`): `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`. The +candidate-stage report blob was `594b2c914a123b4cb1b41260d66855e2e377a823`. -Commit topology checks, run after the commit that contains this report is -created and before any push; their results are recorded in the -post-publication handoff: +Candidate commit topology, verified after the candidate commit was created and +before it was pushed: ```text git rev-list --parents -n 1 HEAD -required: b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20 b44303c214498baf76c9d9a0a7cab374377f9cbe +95769537cdc9871e42f9f3e5443604a96beb74da b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20 b44303c214498baf76c9d9a0a7cab374377f9cbe git rev-parse HEAD^{tree} -required: equal to the git write-tree output of the validated index +5084c6951a1b57b449f6045b139aacfbc488e534 (equal to the git write-tree output of the validated index) ``` +After publication, the remote task-branch head, tree and ordered parents +matched these values. + Content checks: - the ADR heading outline is unchanged; the header carries `Status: PROPOSED` @@ -716,6 +827,145 @@ Content checks: approved; - internal links in the changed files resolve to existing repository files. +### Approval-state validation + +Environment: the same full local Git worktree, detached at the content-approved +candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, after `git ls-remote +origin` and the PR #960 record showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`95769537cdc9871e42f9f3e5443604a96beb74da` and PR #960 open, draft and unmerged +with that head. The four paths were edited and staged, and the checks below ran +against that exact staged index before the approval-state commit was created +from it. Verification of the created commit's parent and tree, and of the +remote head after push, follows the creation of the commit that contains this +report and is recorded in the post-publication handoff. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 95769537cdc9871e42f9f3e5443604a96beb74da +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths): + +```text +git diff --cached --name-only 95769537cdc9871e42f9f3e5443604a96beb74da +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Size of the approval-state delta in the three decision files: + +```text +git diff --cached --numstat 95769537cdc9871e42f9f3e5443604a96beb74da -- CHANGELOG.md docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md docs/engineering/decisions/decision-register.md +1 1 CHANGELOG.md +9 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +ADR-0012 candidate -> approval state (every changed line, diff header lines +omitted; no architecture text changes): + +```text +git diff --cached -U0 95769537cdc9871e42f9f3e5443604a96beb74da -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +@@ -3 +3 @@ +-Status: PROPOSED ++Status: APPROVED +@@ -4,0 +5,2 @@ Date: 2026-09-29 ++Approved by: Javi, CTO ++Approval date: 2026-10-05 +@@ -1970 +1972 @@ shared-engine tests. +-Current decision state: **PROPOSED**. ++This ADR is **APPROVED**. +@@ -1972,3 +1974,5 @@ Current decision state: **PROPOSED**. +-The corrected content of this version has not been approved. It may carry +-`APPROVED`, an approver and an approval date only after the CTO approves this +-exact corrected content under the repository decision process. ++Approved by: Javi, CTO ++Approval date: 2026-10-05 ++ ++The CTO approved this exact corrected content on 2026-10-05 under the ++repository decision process. +``` + +Decision-register candidate -> approval state (one line, the ADR-0012 row; +word-level changes, condensed to one pair per line): + +```text +git diff --cached --word-diff=plain -U0 95769537cdc9871e42f9f3e5443604a96beb74da -- docs/engineering/decisions/decision-register.md +[-PROPOSED-] {+APPROVED+} +[-Pending-] {+Satisfied+} +{+the CTO approved+} {+exact+} +[-awaits CTO approval-] {+on 2026-10-05+} +``` + +Changelog candidate -> approval state (one line, the +`THOTH-ASYNC-01-ADR-01-CORRECTION` entry; word-level changes, condensed to one +pair per line): + +```text +git diff --cached --word-diff=plain -U0 95769537cdc9871e42f9f3e5443604a96beb74da -- CHANGELOG.md +[-propose-] {+record+} {+CTO-approved+} +[-`PROPOSED`:-] {+`APPROVED`:+} +{+CTO approved the exact+} +[-is not approved.**-] {+on 2026-10-05.**+} +[-proposal-] {+decision+} +``` + +Approval-state blob identities (condensed `git ls-files -s` output and the +ADR-0012 `git hash-object --no-filters` result): + +```text +ADR-0012 (APPROVED) 4848470a252076268a9fac9da5c40f0b4ebff474 +decision-register.md b19b1404dafa62a7aaebcd33708841cf44ce1831 +CHANGELOG.md ff7089a4a93e6a8c2c27c75a0ebf56ce43405c27 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +``` + +This report's own blob and the approval-state tree depend on this report's +bytes, so they are not self-pinned here; they are recorded in the +post-publication handoff and, once published, are GitHub-owned lifecycle +evidence. + +Required topology of the approval-state commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 95769537cdc9871e42f9f3e5443604a96beb74da + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index +``` + ## 10. Manual verification Environment: the local worktree described in section 9. @@ -728,10 +978,14 @@ Observed result: all twelve A2 invariants are expressed in section 3.2 and invariant 3 and exercised by section 11 validation requirements; C7's write-once close wording is used; text unrelated to route activation, the disposition model and approval state is byte-identical to the prior blob. +Approval-state stage: compared the staged ADR-0012 with candidate blob +`a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`, and the staged register and +changelog with the candidate blobs; the observed changes are exactly the +approval-state lines listed in section 4 and in "Approval-state validation". Evidence link/screenshot/log reference: the local validation in section 9. -PR #960 diffs of earlier heads are historical, pre-candidate evidence only. The -live PR #960 diff of this candidate against `feature/worker` and against -`b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` exists only after publication and is +Pull-request diffs of heads before this commit, including the published +candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, are historical evidence; +the live PR #960 diff of this commit exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -745,12 +999,14 @@ failure, or any migration execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback -Initial state after merge: not applicable to this candidate. Under Route B the -`PROPOSED` candidate is not a merge candidate. Merge requires CTO content -approval of this exact candidate, one approval-state commit, CTO -exact-final-blob approval, Draft -> Ready, final-head CI and Codex/app review, -fresh independent CRITICAL exact-head review and separate CTO merge -authorization, followed by a guarded merge into `feature/worker`. +Initial state after merge: after a guarded merge into `feature/worker`, the +approved ADR-0012 version is reachable from the programme integration branch +only. It is not repository-authoritative until its authority condition, +including reachability from `develop`, is met, and ADR-0013 programme-local +reliance additionally requires the programme-pin conditions on #957. Merge +itself still requires CTO exact-final-blob approval, Draft -> Ready, final-head +CI and Codex/app review, fresh independent CRITICAL exact-head review and +separate CTO merge authorization. Activation required: NONE. Merging the eventual approved version would make no runtime, schema, provider or deployment change. Feature flag/configuration: NONE @@ -762,8 +1018,9 @@ Monitoring required: NONE ## 13. Known limitations and deferred work -- This candidate stage does not include the approval-state commit, the - exact-final-blob approval, Draft -> Ready, the final-head review or merge. +- The candidate and approval-state stages do not include the exact-final-blob + approval, Draft -> Ready, the final-head Codex/app review, the final + independent review or merge. - Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The stale #957 pin must be superseded using the corrected exact ADR-0012 blob and the fresh independently reviewed PR #960 head; dependent `feature/worker` @@ -779,17 +1036,18 @@ Monitoring required: NONE - The projection-evidence limitation of the historical heads `227057cae8a4c10f7f7795e3afe68271e56681b9` and `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; - section 9 is exact-worktree evidence for this candidate. + section 9 is exact-worktree evidence for the candidate and the + approval-state commit. ## 14. Unresolved issues - The PR #960 review threads `4146838604` and `4148985175` concern - implementation-report validation evidence. Their Route-B disposition is the - approval-state report update required by Amendment 3 C5 (literal - candidate-stage and final validation commands and results, the candidate ADR - blob, the candidate head as approval-state parent, the six-path and four-path - footprints and CI applicability, without self-referential containing-commit - metadata). + implementation-report validation evidence. Their Route-B disposition under + Amendment 3 C5 is this approval-state report update: section 9 records the + literal candidate-stage and approval-state validation commands and results, + the candidate ADR blob, the candidate head as approval-state parent, the + six-path and four-path footprints and CI applicability, without + self-referential containing-commit metadata. - Thread `4146838614` concerns the disposition-to-job mapping; the prior blob corrected section 3.5 and invariant 8, and section 3.2 now states the same model. @@ -822,9 +1080,11 @@ Suggested review focus: is a faithful expression of A2.8 rather than a new mechanism; - that the emission-floor, rollback and recovery text matches base-specification section 3; -- that no current approver, approval date or approval statement presents the - corrected bytes as approved, and that section 12 remains true after the - approval-state commit apart from the enumerated approval-state delta; +- that the approval-state delta from candidate + `95769537cdc9871e42f9f3e5443604a96beb74da` is limited to the approval + metadata and approval-state wording listed in section 4, with no + architecture change, and that the ADR header and section 12 carry the + content approval's approver and date (Javi, CTO, 2026-10-05); - that section 12's amendment clause mirrors `docs/engineering/decisions/README.md` without loosening it; - that ADR text unrelated to route activation, the disposition model and diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index a88aed0a0..4848470a2 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-05 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -1967,11 +1969,13 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +This ADR is **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-05 + +The CTO approved this exact corrected content on 2026-10-05 under the +repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index ce8811566..b19b1404d 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-09-30 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012 and does not approve this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and the CTO approved its exact corrected content on 2026-10-05 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012 and does not approve this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 9db91af7bf3d9f969556c819187420fc31d25c10 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 6 Oct 2026 10:55:15 +0100 Subject: [PATCH 28/48] THOTH-ASYNC-01-ADR-01: reconcile final-head review metadata --- ...H-ASYNC-01-ADR-01-implementation-report.md | 545 ++++++++++++++---- .../decisions/decision-register.md | 2 +- 2 files changed, 440 insertions(+), 107 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index cd97eb441..64326e564 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -4,14 +4,16 @@ This report records the bounded ADR-0012 architecture task for THOTH-ASYNC-01: the original architecture authoring, its independent-review correction rounds, the historical approval-state reconciliation, the programme-integration compatibility reconciliation, the route-disposition -clarification and the Route-B material activation-epoch correction. It records -durable task evidence only. Live head, CI, review, authorization and merge state -is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. +clarification, the Route-B material activation-epoch correction and the +bounded final-head recovery authorized by Specification Amendments 4-5. It +records durable task evidence only. Live head, CI, review, authorization and +merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not +copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction. Appendix A preserves the earlier authoring, review, -approval-state, reconciliation and validation record. Appendix B records the -durable control conditions. +Route-B correction and its final-head recovery. Appendix A preserves the +earlier authoring, review, approval-state, reconciliation and validation +record. Appendix B records the durable control conditions. ## 1. Repository state @@ -29,10 +31,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the approval-state commit that contains this report, a direct -child of the content-approved candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`; it is not self-pinned here -(section 3). Its publication as the PR #960 head is a post-commit, +Head commit: the Amendment-4/5 recovery commit that contains this report, an +ordinary single-parent additive commit whose direct parent is the historical +Route-B approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`; its +SHA is deliberately not self-pinned here (section 3). The historical +approval-state head is itself the direct child of the historical +content-approved candidate `95769537cdc9871e42f9f3e5443604a96beb74da`. The +recovery commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting `feature/worker`; its live state, head and checks are GitHub-owned lifecycle @@ -41,7 +46,8 @@ Expected branch deletion after merge: YES Final programme PR required: YES - `feature/worker -> develop` is a separate programme-level gate. Implementing model: Route-B candidate and approval-state stages - Claude -(Opus 5.5), bounded implementing agent. Earlier rounds are recorded in +(Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude +(Fable 5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard @@ -90,6 +96,16 @@ content-approved Route-B candidate / approval-state parent: 95769537cdc9871e42f9f3e5443604a96beb74da tree 5084c6951a1b57b449f6045b139aacfbc488e534 ADR-0012 blob a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c + +historical Route-B approval-state head / recovery parent: +c046e6bf6510f24ee1231f86293a3d499bceed7e +tree 801e12ffc010470928da45919ede8d7239a491ce +ADR-0012 blob 4848470a252076268a9fac9da5c40f0b4ebff474 (unchanged by the recovery) +decision-register blob b19b1404dafa62a7aaebcd33708841cf44ce1831 +implementation-report blob cd97eb441a38ff1885cf621e1af04519c470dffc + +feature/worker at the recovery (unchanged): +b44303c214498baf76c9d9a0a7cab374377f9cbe ``` Both `feature/worker` refreshes were separately authorized control actions @@ -110,10 +126,18 @@ Approved specification: `5992836778`; - candidate-stage implementation authorization: #958 comment `5993634460`; - replacement-candidate authorization: #958 comment `5998119577` (section 5); -- approval-state authorization: #958 comment `5999579357`. +- approval-state authorization: #958 comment `5999579357`; +- Specification Amendment 4 / final-head review recovery: #958 comment + `6000543428`; +- Specification Amendment 5 / Amendment-4 review closure: #958 comment + `6001012674`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-5: #958 comment `6013265532`; +- bounded recovery implementation authorization: #958 comment `6013267369`. Implemented objective: the Route-B material architectural correction of -ADR-0012 before repository authority, in two source stages. +ADR-0012 before repository authority, in two source stages, followed by one +bounded final-head recovery stage. - **Candidate stage.** The candidate implements the CTO-selected append-only activation-epoch architecture (#958 comment `5935475916`) that resolves @@ -122,15 +146,32 @@ ADR-0012 before repository authority, in two source stages. or changing the route identity. It carried `Status: PROPOSED` together with a `PROPOSED` decision-register row, one `PROPOSED` changelog correction entry and the candidate-stage report. -- **Approval-state stage (this commit).** The CTO approved the exact candidate - content - head `95769537cdc9871e42f9f3e5443604a96beb74da`, ADR-0012 blob - `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`, inspected against prior blob - `dec6665353804e42f22474954bf878308092b717` - on 2026-10-05 in a durable record - on #958. This commit records that approval as durable decision state: - ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and - `Approval date: 2026-10-05`, the ADR-0012 register row and the correction - changelog entry moved to the approved state, and this report's approval-state - evidence. No architecture content changes. +- **Approval-state stage (historical head + `c046e6bf6510f24ee1231f86293a3d499bceed7e`).** The CTO approved the exact + candidate content - head `95769537cdc9871e42f9f3e5443604a96beb74da`, + ADR-0012 blob `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`, inspected against + prior blob `dec6665353804e42f22474954bf878308092b717` - on 2026-10-05 in a + durable record on #958. The approval-state commit recorded that approval as + durable decision state: ADR-0012 `PROPOSED -> APPROVED` with + `Approved by: Javi, CTO` and `Approval date: 2026-10-05`, the ADR-0012 + register row and the correction changelog entry moved to the approved state, + and this report's approval-state evidence. No architecture content changed. +- **Final-head recovery stage (the commit that contains this report).** After + PR #960 was separately marked Ready, the configured final-head automated + review of the approval-state head raised two findings. A route-parentage + concern rested on a premise that did not match the authoritative PR #960 + commit graph (the approval-state head is the direct child of the + content-approved candidate) and required no source correction. The second + finding identified that the active decision register still carried + `Last updated: 2026-09-30` while its ADR-0012 row recorded an approval on + 2026-10-05; that freshness finding was accepted as blocking and caused this + bounded recovery under Specification Amendments 4-5. The recovery commit + advances the register header to `Last updated: 2026-10-05` and reconciles + this report; it is one bounded post-final-review factual/control correction, + not another candidate and not another approval-state commit. ADR-0012 + (blob `4848470a252076268a9fac9da5c40f0b4ebff474`), its approval date and + approver, the changelog and every other decision and control file are + byte-identical to the approval-state head. Out-of-scope changes made: NONE @@ -202,26 +243,49 @@ the corrected version: Those records remain historical evidence for their exact versions. They are not approval of the corrected version. Under Route B the corrected version received fresh CTO content approval on 2026-10-05, recorded as `APPROVED` by -this approval-state commit; CTO exact-final-blob approval, fresh independent -exact-head review and separate merge authorization remain separate later gates. +the historical approval-state commit +`c046e6bf6510f24ee1231f86293a3d499bceed7e`. The CTO's exact-final-blob +approval record for ADR-0012 blob `4848470a252076268a9fac9da5c40f0b4ebff474` +was bound to that superseded source head as well as to the blob, so Amendments +4-5 require a fresh head-bound confirmation of the same unchanged blob at the +recovery head; that confirmation, Draft -> Ready, fresh final-head automated +review, fresh independent exact-head review and separate merge authorization +remain separate later gates. ## 3. Commits -Route-B approval-state commit (the commit that contains this report): - -- exactly one ordinary single-parent commit on `feature/async/adr-0012` - +Amendment-4/5 recovery commit (the commit that contains this report): + +- exactly one ordinary single-parent additive commit on + `feature/async/adr-0012` - + `THOTH-ASYNC-01-ADR-01: reconcile final-head review metadata` + - direct parent: the historical Route-B approval-state head + `c046e6bf6510f24ee1231f86293a3d499bceed7e` + - changes only the two authorized recovery paths (section 4): the + decision-register `Last updated` header line and this report + +Its SHA, its tree and this report's final blob are deliberately not embedded +here because this report is part of that tree (Amendment 5 F2). They are +verified against the remote task-branch head after push and recorded in the +post-publication handoff; once published, the PR #960 head and head tree are +GitHub-owned lifecycle evidence under `ADR-0005`. The authorized topology is a +non-force fast-forward of the task branch from the approval-state head to the +recovery commit; no merge commit, rebase, squash, amend, force update or +further source commit is authorized, and no further source commit exists +implicitly after it. + +Route-B approval-state commit (historical): + +- `c046e6bf6510f24ee1231f86293a3d499bceed7e` - `THOTH-ASYNC-01-ADR-01: record ADR-0012 approval state` - - direct parent: the content-approved candidate - `95769537cdc9871e42f9f3e5443604a96beb74da` - - changes only the four authorized paths, and only their approval state - (section 4) - -Its SHA and tree are not embedded here because this report is part of that -tree. They are recorded in the post-publication handoff; once the commit is -published, the PR #960 head and head tree are GitHub-owned lifecycle evidence -under `ADR-0005`. The authorized topology is a fast-forward of the task branch -from the candidate to this commit; no merge commit, rebase, squash, amend, -force update or further source commit is authorized. + - ordinary single-parent commit; direct parent: the content-approved + candidate `95769537cdc9871e42f9f3e5443604a96beb74da` + - tree `801e12ffc010470928da45919ede8d7239a491ce` + - changed only the four authorized correction paths, and only their approval + state (section 4) + - published by a non-force fast-forward of `feature/async/adr-0012` from the + candidate; the remote head, tree and parent matched the locally validated + object Route-B candidate commit (content-approved): @@ -234,7 +298,7 @@ Route-B candidate commit (content-approved): `e15607878e66d2ff12b819dcea13293a46674f13` overlaid only by the four correction paths in section 4 - published by a non-force fast-forward of `feature/async/adr-0012` from - parent 1 before this approval-state commit was created + parent 1 before the approval-state commit was created Earlier task-branch history from `923545d5c9028bc04c40e38efeb7de674efed3fd`, first-parent order, newest first: @@ -269,15 +333,22 @@ first-parent order, newest first: ## 4. Files changed -Authorized correction write paths (base specification section 5, unchanged by -Amendments 1-3): +Historical Route-B material-correction and approval-state write budget (base +specification section 5, unchanged by Amendments 1-3; four correction paths): - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/decisions/decision-register.md` -Authorized cumulative PR footprint against refreshed `feature/worker`: +Amendment-4/5 final-head recovery write budget (Amendment 4 E5, Amendment 5 +F3.4; exactly two paths): + +- `docs/engineering/decisions/decision-register.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + +Authorized cumulative PR footprint against refreshed `feature/worker` (exactly +six paths at every stage): - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -286,7 +357,8 @@ Authorized cumulative PR footprint against refreshed `feature/worker`: - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/decisions/decision-register.md` -Authorized new-file paths: NONE for this stage; this report already existed. +Authorized new-file paths: NONE at the approval-state and recovery stages; this +report already existed. Candidate-stage changes (content-approved candidate `95769537cdc9871e42f9f3e5443604a96beb74da`) relative to the unmodified @@ -341,7 +413,8 @@ merge-tree baseline `e15607878e66d2ff12b819dcea13293a46674f13`: only the tense or framing changes needed to keep it truthful as history. - within authorized write budget: YES -Approval-state changes in this commit relative to the content-approved +Approval-state changes in the historical approval-state commit +`c046e6bf6510f24ee1231f86293a3d499bceed7e` relative to the content-approved candidate `95769537cdc9871e42f9f3e5443604a96beb74da` (the same four paths, approval state only): @@ -383,6 +456,45 @@ approval state only): candidate-stage and historical evidence. - within authorized write budget: YES +Recovery changes in the commit that contains this report relative to the +historical approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e` +(exactly the two authorized recovery paths): + +- `docs/engineering/decisions/decision-register.md` + - reason: final-head automated review found the active register's own + freshness metadata stale after the ADR-0012 row recorded an approval on + 2026-10-05 (Amendment 4 E1/E6). + - behavioural effect: exactly one header line, `Last updated: 2026-09-30` + becomes `Last updated: 2026-10-05`. The ADR-0012 row and every other row + are byte-identical to the approval-state head; no other wording or metadata + changes. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: Amendment 4 E7 / Amendment 5 F3 reconciliation so the durable + record stays truthful once the report moves into the recovery commit. + - behavioural effect: documentation only. Supersedes the earlier statement + that leaving the register header unchanged was the correct final state; + distinguishes the historical content-approved candidate, the historical + approval-state head and the recovery commit; distinguishes the historical + four-path budget from the two-path recovery budget; records the final-head + review outcome substantively, without copying review identifiers; reframes + statements that would otherwise describe the recovery commit as the + approval-state commit; and records the recovery validation (section 9). + All still-true candidate-stage, content-approval and approval-state + evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the approval-state head through the recovery: + +- ADR-0012 - blob `4848470a252076268a9fac9da5c40f0b4ebff474` (architecture, + approval date, approver and route/disposition semantics unchanged); +- `CHANGELOG.md` - blob `ff7089a4a93e6a8c2c27c75a0ebf56ce43405c27`; +- ADR-0005 - blob `bdaa976e4893b1fc45f994236f9e56d433212d63`; +- `docs/engineering/AGENTS.md` - blob `e194b14e8c4fbb298db7ca6c38aebeb69547a29a`; +- `docs/engineering/ai-delivery/implementation-report-template.md` - blob + `0ae39d3892bbca5b0ff90dc7ffa70038662351bc`; +- the inherited and byte-preserved paths listed below. + Inherited unchanged from refreshed `develop` through parent 2 (the closed inherited set between the task head and the merge-tree baseline): @@ -414,17 +526,21 @@ The candidate differs from the unmodified merge-tree baseline in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9). The approval-state commit differs from the content-approved candidate in exactly the same four paths and from -refreshed `feature/worker` in exactly the six cumulative PR paths. No ADR-0008, +refreshed `feature/worker` in exactly the six cumulative PR paths. The recovery +commit differs from the approval-state head in exactly the two authorized +recovery paths and from refreshed `feature/worker` in exactly the same six +cumulative PR paths (section 9, "Recovery validation"). No ADR-0008, ADR-0010, ADR-0013, engineering-control doctrine, workflow, `AGENTS.md`, -runtime, migration or schema path is edited. +runtime, migration or schema path is edited at any stage. ## 4.2 Authorized actions actually used -For the Route-B candidate stage (#958 comments `5993634460` and `5998119577`) -and the approval-state stage (#958 comment `5999579357`). This matrix states -only what had been done when the commit that contains this report was created. +For the Route-B candidate stage (#958 comments `5993634460` and `5998119577`), +the approval-state stage (#958 comment `5999579357`) and the Amendment-4/5 +recovery stage (#958 comment `6013267369`). This matrix states only what had +been done when the commit that contains this report was created. -Candidate stage - completed before this commit: +Candidate stage - historical, completed before the approval-state commit: - repository inspection: USED (Git history, refs, doctrine and the #957/#958/PR #960 records, read-only) @@ -442,34 +558,62 @@ Candidate stage - completed before this commit: constructed, independently verified by control, and the PR description updated for the published candidate); PR #960 remained a draft -Approval-state stage - done up to and including the creation of this commit: +Approval-state stage - historical, completed before the recovery commit: - repository inspection: USED (read-only re-verification of refs, PR #960 state and the #958 approval records) - source edit: USED (the same four paths, approval state only) -- commit: USED (the single-parent approval-state commit that contains this - report) +- commit: USED (the single-parent approval-state commit + `c046e6bf6510f24ee1231f86293a3d499bceed7e`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `95769537cdc9871e42f9f3e5443604a96beb74da` to the approval-state commit; + the remote head, tree, direct parent and ADR-0012 blob were verified) +- PR creation/update: USED (the PR #960 description was updated for the + published approval-state head while the PR remained a draft) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + authorized and performed it, which triggered the configured final-head + automated review of the approval-state head (section 2) + +Recovery stage - done up to and including the creation of the commit that +contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 state, commit topology and every protected blob named by the + recovery authorization; all matched) +- PR creation/update: USED (PR #960 Ready -> Draft, the one authorized + pre-publication PR-state mutation, performed after that verification and + before any recovery source was staged; the read-back showed the PR open, + draft and unmerged at head `c046e6bf6510f24ee1231f86293a3d499bceed7e`) +- source edit: USED (the two authorized recovery paths only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (a detached local worktree at the approval-state + head was used for construction; the existing task branch is the + fast-forward target) +- commit: USED (the single-parent recovery commit that contains this report) -Authorized post-commit actions for this commit - GitHub-owned lifecycle -evidence under `ADR-0005`, not self-recorded here: +Authorized post-commit actions for the recovery commit - GitHub-owned +lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `95769537cdc9871e42f9f3e5443604a96beb74da` to this commit; -- post-push verification of the remote head, tree, direct parent and ADR-0012 - blob; -- the truthful PR #960 description update for the published approval-state - head; -- the automatic PR CI and any automatic review activity those actions cause. + `c046e6bf6510f24ee1231f86293a3d499bceed7e` to the recovery commit; +- post-push verification of the remote head, tree, direct parent, ADR-0012 + blob, decision-register blob and report blob; +- the one authorized state-neutral PR #960 description update for the + published recovery head (no title change); +- the automatic PR CI those actions cause. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized at either stage, and separate later gates: +Not authorized at any stage for the implementing agent, and separate later +gates: -- CTO exact-final-blob approval, Draft -> Ready, final-head Codex/app review - and the final independent review: later Route-B gates; Draft -> Ready is not - authorized for the implementing agent at either stage +- fresh CTO exact-final-blob confirmation at the recovery head, Draft -> Ready, + fresh final-head Codex/app review and the fresh independent exact-head + review: later gates; Draft -> Ready is not authorized for the implementing + agent at any stage - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -500,13 +644,13 @@ Pushing a new head to the PR triggers the repository's classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push -and no migration execution is expected. Under Amendment 3 C2/C11, configured -Codex/app review is not expected from a push to a draft PR; final-head -Codex/app review is expected at the separately authorized Draft -> Ready -transition after the approval-state commit. The actual job outcomes and any -automatic review for this approval-state commit exist only after its -publication; they are GitHub-owned lifecycle evidence recorded in the pull -request and the post-publication handoff, not in this file. +and no migration execution is expected. Under Amendment 3 C2/C11 and +Amendments 4-5, configured Codex/app review is not expected from a push to a +draft PR; fresh final-head Codex/app review is expected at the separately +authorized Draft -> Ready transition after the recovery commit. The actual job +outcomes for the recovery commit exist only after its publication; they are +GitHub-owned lifecycle evidence recorded in the pull request and the +post-publication handoff, not in this file. Historical automatic CI for earlier PR heads followed the same documentation-only classification, with no manual dispatch or rerun: @@ -516,6 +660,7 @@ documentation-only classification, with no manual dispatch or rerun: 227057cae8a4c10f7f7795e3afe68271e56681b9: 4 success, 6 skipped b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20: 4 success (classify x3, check-changelog), 6 skipped 95769537cdc9871e42f9f3e5443604a96beb74da: 4 success (classify x3, check-changelog), 6 skipped (content-approved candidate) +c046e6bf6510f24ee1231f86293a3d499bceed7e: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (approval-state head) ``` Manually initiated external actions (anything the implementing agent @@ -563,8 +708,15 @@ List decisions made within the approved design: true in both the `PROPOSED` and `APPROVED` states, so the later approval-state delta is limited to the status line, the approver/date and the sentence that says the corrected content is not yet approved. -10. The decision-register header `Last updated` line is not changed, because - the authorization permits only the ADR-0012 row to change. +10. At the approval-state stage the decision-register header `Last updated` + line was left at `2026-09-30`, because that authorization permitted only + the ADR-0012 row to change. Final-head automated review of the + approval-state head showed that this left the active register's own + freshness metadata stale once the row recorded an approval on 2026-10-05; + that finding was accepted as blocking. Leaving the header unchanged was + therefore not the correct final state: the recovery commit that contains + this report advances it to `Last updated: 2026-10-05` as its only register + change (Amendment 4 E6). 11. The report is restructured to the template rather than appended to, so its durable current state is readable without implying that historical approvals cover the corrected version. @@ -575,6 +727,14 @@ List decisions made within the approved design: 13. The approval date carried by the ADR is the date of the durable CTO content-approval record, 2026-10-05, as Route B requires; it is neither back-dated nor anticipated. +14. The recovery commit's SHA, its tree and this report's final blob are not + self-pinned in this report (Amendment 5 F2); they are verified against the + remote task-branch head after push and recorded in the post-publication + handoff. +15. The final-head automated review outcome is recorded here substantively + (section 2, section 14); its review and comment identifiers remain + GitHub-owned lifecycle evidence under `ADR-0005` and are not transcribed + into source (Amendment 5 F3.5). List any deviation from the specification requiring authorization: @@ -594,9 +754,14 @@ List any deviation from the specification requiring authorization: not copy content-approval, exact-final-blob approval, review or merge identifiers into repository files, and that repository records reference the owning issue and pull request instead. Control selected the - README-conforming resolution before this commit: the report identifies the - content approval by its owning issue (#958), its date and the exact candidate - head and ADR blob it approved, without restating its comment identifier. + README-conforming resolution before the approval-state commit: the report + identifies the content approval by its owning issue (#958), its date and the + exact candidate head and ADR blob it approved, without restating its comment + identifier. +- Recovery stage: NONE. The recovery followed Amendments 4-5 and its separate + implementation authorization exactly; no third path, second commit, amend, + rebase, squash, force push or history rewrite was needed or performed before + the commit that contains this report was created. ## 6. Database and migration effects @@ -632,8 +797,10 @@ Record exact commands and outcomes. The subsections "Formatting" through "Content checks" record the candidate-stage validation of candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, preserved as candidate-stage -evidence. "Approval-state validation" at the end of this section records the -validation of the commit that contains this report. +evidence. "Approval-state validation" records the validation of the historical +approval-state commit `c046e6bf6510f24ee1231f86293a3d499bceed7e`. "Recovery +validation" at the end of this section records the validation of the commit +that contains this report. Validation environment: a full local Git worktree of `thoth-pub/thoth`, detached at `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20`, with every object @@ -836,9 +1003,9 @@ origin` and the PR #960 record showed `develop` and `feature/worker` at `95769537cdc9871e42f9f3e5443604a96beb74da` and PR #960 open, draft and unmerged with that head. The four paths were edited and staged, and the checks below ran against that exact staged index before the approval-state commit was created -from it. Verification of the created commit's parent and tree, and of the -remote head after push, follows the creation of the commit that contains this -report and is recorded in the post-publication handoff. +from it. The created commit was `c046e6bf6510f24ee1231f86293a3d499bceed7e`; +its single parent and tree, and the remote head after push, were verified as +recorded at the end of this subsection. Whitespace: @@ -950,20 +1117,152 @@ CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df52 CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c ``` -This report's own blob and the approval-state tree depend on this report's -bytes, so they are not self-pinned here; they are recorded in the -post-publication handoff and, once published, are GitHub-owned lifecycle -evidence. +The approval-state report blob was `cd97eb441a38ff1885cf621e1af04519c470dffc` +and the approval-state tree `801e12ffc010470928da45919ede8d7239a491ce`. They +were not self-pinned by the approval-state report; they are recorded here as +historical facts because that commit is now the recovery commit's parent. + +Topology of the approval-state commit, checked after it was created and before +it was pushed, and re-read from the repository at the recovery stage: + +```text +git rev-list --parents -n 1 c046e6bf6510f24ee1231f86293a3d499bceed7e +c046e6bf6510f24ee1231f86293a3d499bceed7e 95769537cdc9871e42f9f3e5443604a96beb74da + +git rev-parse c046e6bf6510f24ee1231f86293a3d499bceed7e^{tree} +801e12ffc010470928da45919ede8d7239a491ce (equal to the git write-tree output of the validated index) +``` + +After publication, the remote task-branch head, tree and parent matched these +values, and PR #960 carried that head when it was later marked Ready and +received the configured final-head automated review. + +### Recovery validation + +Environment: a full local Git worktree of `thoth-pub/thoth`, detached at the +historical approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e` +(tree `801e12ffc010470928da45919ede8d7239a491ce`), with every object fetched +from `origin`. Immediately before construction, read-only GitHub inspection +showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`c046e6bf6510f24ee1231f86293a3d499bceed7e`, PR #960 open, ready, unmerged, +mergeable and clean with that head and target `feature/worker`, the +approval-state commit's single parent `95769537cdc9871e42f9f3e5443604a96beb74da` +and tree, and every protected blob named by the recovery authorization at its +required identity. PR #960 was then converted Ready -> Draft and the read-back +confirmed it open, draft and unmerged at the same head. The two recovery paths +were edited and staged, and every check below ran against that exact final +staged index before the recovery commit was created from it; the report's +numeric line counts below were filled in and the complete check set rerun with +identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check c046e6bf6510f24ee1231f86293a3d499bceed7e +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Recovery delta against the approval-state head (exactly the two authorized +paths): + +```text +git diff --cached --name-only c046e6bf6510f24ee1231f86293a3d499bceed7e +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status c046e6bf6510f24ee1231f86293a3d499bceed7e +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat c046e6bf6510f24ee1231f86293a3d499bceed7e +439 106 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register recovery delta (every changed line; the one authorized +header line and nothing else): + +```text +git diff --cached -U0 c046e6bf6510f24ee1231f86293a3d499bceed7e -- docs/engineering/decisions/decision-register.md +@@ -5 +5 @@ Owner: CTO +-Last updated: 2026-09-30 ++Last updated: 2026-10-05 +``` -Required topology of the approval-state commit, checked after it is created and +ADR-0012 register row byte comparison against the approval-state head (the +row is line 20 of the register at both versions): + +```text +diff <(git show c046e6bf6510f24ee1231f86293a3d499bceed7e:docs/engineering/decisions/decision-register.md | sed -n 20p) <(git show :docs/engineering/decisions/decision-register.md | sed -n 20p) +exit 0; no output (byte-identical) + +git diff --cached --numstat c046e6bf6510f24ee1231f86293a3d499bceed7e -- docs/engineering/decisions/decision-register.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Protected-blob checks on the staged index (condensed `git ls-files -s` output; +mode and stage omitted, paths abbreviated) and the ADR-0012 +`git hash-object --no-filters` result: + +```text +ADR-0012 (APPROVED, unchanged) 4848470a252076268a9fac9da5c40f0b4ebff474 +CHANGELOG.md ff7089a4a93e6a8c2c27c75a0ebf56ce43405c27 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc +decision-register.md (recovered header) 247d077786a9176e628baa8de64c0c3a5e9b09d3 + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +4848470a252076268a9fac9da5c40f0b4ebff474 +``` + +The recovered decision-register blob is deterministic and not self-referential, +so it is recorded above. This report's own final blob, the recovery tree and the +recovery commit SHA depend on this report's bytes and are not self-pinned +(Amendment 5 F2); they are verified against the remote head after push and +recorded in the post-publication handoff. + +Required topology of the recovery commit, checked after it is created and before any push, with results recorded in the post-publication handoff: ```text git rev-list --parents -n 1 HEAD -required: 95769537cdc9871e42f9f3e5443604a96beb74da +required: c046e6bf6510f24ee1231f86293a3d499bceed7e (exactly one parent) git rev-parse HEAD^{tree} required: equal to the git write-tree output of the validated index + +git diff --name-only c046e6bf6510f24ee1231f86293a3d499bceed7e HEAD +required: exactly the two recovery paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths ``` ## 10. Manual verification @@ -982,10 +1281,18 @@ Approval-state stage: compared the staged ADR-0012 with candidate blob `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`, and the staged register and changelog with the candidate blobs; the observed changes are exactly the approval-state lines listed in section 4 and in "Approval-state validation". +Recovery stage: compared the staged register with the approval-state blob +`b19b1404dafa62a7aaebcd33708841cf44ce1831`; the only changed line is the +`Last updated` header line and the ADR-0012 row is byte-identical; confirmed +that ADR-0012, the changelog and every protected decision and control file +keep their approval-state blob identities; read this report for statements +that would become false once it moves into the recovery commit and reframed +them (section 4). Evidence link/screenshot/log reference: the local validation in section 9. -Pull-request diffs of heads before this commit, including the published -candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, are historical evidence; -the live PR #960 diff of this commit exists only after its publication and is +Pull-request diffs of earlier heads, including the published candidate +`95769537cdc9871e42f9f3e5443604a96beb74da` and the approval-state head +`c046e6bf6510f24ee1231f86293a3d499bceed7e`, are historical evidence; the live +PR #960 diff of the recovery commit exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -994,8 +1301,9 @@ CI status: GitHub-owned lifecycle evidence for each PR head; expected documentation-only classification (`classify` and `check-changelog` pass; build, test, lint, format, migration and Docker jobs skipped). Checks: as listed in section 4.3. -Failures or warnings: NONE EXPECTED. Under Amendment 3 C10 an automatic CI -failure, or any migration execution or Docker publication, is a HOLD condition. +Failures or warnings: NONE EXPECTED. Under Amendment 3 C10 and the Amendment-4/5 +recovery authorization an automatic CI failure, or any migration execution or +Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -1004,23 +1312,29 @@ approved ADR-0012 version is reachable from the programme integration branch only. It is not repository-authoritative until its authority condition, including reachability from `develop`, is met, and ADR-0013 programme-local reliance additionally requires the programme-pin conditions on #957. Merge -itself still requires CTO exact-final-blob approval, Draft -> Ready, final-head -CI and Codex/app review, fresh independent CRITICAL exact-head review and -separate CTO merge authorization. +itself still requires, in order: fresh CTO exact-final-blob confirmation of the +unchanged ADR-0012 blob at the recovery head; separately authorized +Draft -> Ready; fresh automatic final-head CI and Codex/app review; fresh +independent CRITICAL exact-head review; separate CTO exact-head merge +authorization; then the guarded merge and programme-pin/reliance +reconciliation. Activation required: NONE. Merging the eventual approved version would make no runtime, schema, provider or deployment change. Feature flag/configuration: NONE Migration sequence: NONE Rollback/disable procedure: before merge, stop at the current gate; no history rewrite, force push or amend is permitted, and any further source change needs a -fresh CTO specification amendment/authorization (Amendment 3 C10). +fresh CTO specification amendment/authorization (Amendment 3 C10; Amendments +4-5 authorized exactly one recovery commit after the final-head HOLD, and no +further source commit exists implicitly after it). Monitoring required: NONE ## 13. Known limitations and deferred work -- The candidate and approval-state stages do not include the exact-final-blob - approval, Draft -> Ready, the final-head Codex/app review, the final - independent review or merge. +- The candidate, approval-state and recovery stages do not include the fresh + exact-final-blob confirmation at the recovery head, Draft -> Ready, the fresh + final-head Codex/app review, the fresh independent exact-head review or + merge. - Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The stale #957 pin must be superseded using the corrected exact ADR-0012 blob and the fresh independently reviewed PR #960 head; dependent `feature/worker` @@ -1032,7 +1346,10 @@ Monitoring required: NONE `4168223564`) is not resolved here. For #958, Amendment 3 C10 fails closed: any correction needing another candidate commit, another approval-state commit, amend, rebase, force push or history rewrite requires a fresh CTO - specification amendment/authorization. + specification amendment/authorization. The final-head register-freshness + finding was handled exactly that way: Amendments 4-5 and a separate + implementation authorization permitted one additive recovery commit, and + nothing further. - The projection-evidence limitation of the historical heads `227057cae8a4c10f7f7795e3afe68271e56681b9` and `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; @@ -1043,7 +1360,8 @@ Monitoring required: NONE - The PR #960 review threads `4146838604` and `4148985175` concern implementation-report validation evidence. Their Route-B disposition under - Amendment 3 C5 is this approval-state report update: section 9 records the + Amendment 3 C5 was the approval-state report update, preserved and extended + by the recovery: section 9 records the literal candidate-stage and approval-state validation commands and results, the candidate ADR blob, the candidate head as approval-state parent, the six-path and four-path footprints and CI applicability, without @@ -1058,9 +1376,18 @@ Monitoring required: NONE `feature/async/adr-0012` (section 1). - Thread `4157508169` concerns repeated route activation; it is the `R-b467-01` defect that the activation-epoch correction addresses. +- The configured final-head automated review of the approval-state head opened + two further threads, identified here substantively only. One asserted that + the approval-state commit was not the direct child of the content-approved + candidate; its premise did not match the authoritative PR #960 commit graph + (the approval-state head's single parent is the candidate) and it required + no source correction. The other identified the stale `Last updated` register + header; it was accepted as blocking and is addressed by the recovery commit + that contains this report. Adjudication of every thread belongs to the fresh independent CRITICAL -final-head review. This report neither resolves nor dismisses any thread. +exact-head review of the recovery head. This report neither resolves nor +dismisses any thread. ## 15. Agent self-assessment @@ -1068,8 +1395,9 @@ The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own work. It also prepared the earlier uncommitted activation-epoch candidate -referred to in Amendment 1 A10, so it is not eligible to act as an independent -reviewer of this task. +referred to in Amendment 1 A10 and implemented the recovery commit that +contains this report, so it is not eligible to act as an independent reviewer +of this task (Amendment 5 F8). Suggested review focus: @@ -1088,7 +1416,12 @@ Suggested review focus: - that section 12's amendment clause mirrors `docs/engineering/decisions/README.md` without loosening it; - that ADR text unrelated to route activation, the disposition model and - approval state is byte-identical to the prior blob. + approval state is byte-identical to the prior blob; +- that the recovery delta from the approval-state head + `c046e6bf6510f24ee1231f86293a3d499bceed7e` is exactly the register + `Last updated` header line plus this report, that the ADR-0012 row and every + other register row are byte-identical, and that ADR-0012 remains blob + `4848470a252076268a9fac9da5c40f0b4ebff474`. ## Appendix A - Historical authoring and correction record diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index b19b1404d..247d07778 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-09-30 +Last updated: 2026-10-05 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| From b596536e7db0d6b8ace0a42fdd26bff42cabf104 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 6 Oct 2026 18:57:52 +0100 Subject: [PATCH 29/48] THOTH-ASYNC-01-ADR-01: propose route-contract revision correction Propose the Amendment-6/7 material pre-authority correction of ADR-0012 as a PROPOSED Route-B candidate (#958), closing R-9DB-01 in source. - ADR-0012: Status APPROVED -> PROPOSED with no current approver or approval date. Every activation epoch references exactly one immutable route-contract revision with a canonical semantic signature; eligibility requires epoch membership and a match to that revision; continuous producer-version coverage with expand/contract and cross-route fan-out; durable IN_EPOCH_CONTRACT_MISMATCH anomalies; field-by-field historical-enrollment validation; atomic database-level revision equivalence; deterministic materializer compatibility; per-revision route health; safe revision withdrawal and route retirement. Superseded route-level-contract wording is reconciled across sections 3.2, 3.3, 3.9, 3.15, 7-12. - decision register: ADR-0012 row PROPOSED; Last updated 2026-10-06. - CHANGELOG: one new PROPOSED correction entry; history preserved. - implementation report: candidate stage, Amendments 6-7 history and candidate validation evidence. ADR-0008, ADR-0010, ADR-0013 and shared control files unchanged. Architecture proposal only: no runtime, migration, provider, deployment, release or activation change. --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1040 +++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 883 +++++++++++--- .../decisions/decision-register.md | 4 +- 4 files changed, 1488 insertions(+), 440 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7089a4a..db70c9827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `CTRL-ADR-PR-FIRST-01`: add a PR-first staged approval route for a material pre-authority ADR correction to the engineering-decision amendment process in [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md) (issue [966](https://github.com/thoth-pub/thoth/issues/966)). The existing pre-commit exact-final-blob approval route (now Route A) is retained unchanged. Route B lets the implementing agent push the corrected candidate to a real task branch and draft PR as `PROPOSED`, without presenting the earlier version's approver or approval date as approval of the corrected bytes; the CTO then records architecture-content approval against the exact candidate blob/head from the PR; only afterwards may exactly one bounded approval-state commit record `PROPOSED -> APPROVED` with the fresh approver/date and authorized companion metadata; the CTO records exact-final-blob approval of the final ADR blob before merge; fresh independent exact-head review binds to the final PR head; and HIGH/CRITICAL merge authorization and the guarded merge remain separate gates. The doctrine records why that approval-state commit is durable decision state rather than the lifecycle transcription `ADR-0005` prohibits. Material-correction eligibility, the stale-control list, `ADR-0013` exact-version reliance/drift rules and the repository-authority condition are unchanged. **Documentation/control only: no ADR is changed by this task, and no runtime, schema, migration, API, authorization, workflow, provider/IAM, deployment, release or production behaviour is authorized or affected.** - `CTRL-ADR-AMEND-01`: correct the engineering-decision authority and amendment process so `APPROVED` records exact CTO/decision-owner approval while repository authority additionally requires independent exact-head review and reachability from `develop`; permit a material in-place correction only for an approved ADR number that has never been repository-authoritative, under explicit CTO classification/authorization, exact-final-blob approval before commit, stale exact-version control invalidation, and fresh independent exact-head review. Factual clarifications remain separately classified and do not alter the selected decision; `ADR-0013` programme-local reliance remains exact-version-bound and fail-closed on decision drift. **Documentation/control only: no ADR is changed by this task, no runtime/schema/API/migration/provider/IAM/deployment/release/production behaviour is authorized or modified.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 64326e564..a47c299a2 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -4,16 +4,19 @@ This report records the bounded ADR-0012 architecture task for THOTH-ASYNC-01: the original architecture authoring, its independent-review correction rounds, the historical approval-state reconciliation, the programme-integration compatibility reconciliation, the route-disposition -clarification, the Route-B material activation-epoch correction and the -bounded final-head recovery authorized by Specification Amendments 4-5. It -records durable task evidence only. Live head, CI, review, authorization and -merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not -copied here. +clarification, the Route-B material activation-epoch correction, the bounded +final-head recovery authorized by Specification Amendments 4-5 and the +`PROPOSED` Route-B candidate for the immutable route-contract-revision +correction specified by Specification Amendments 6-7. It records durable task +evidence only. Live head, CI, review, authorization and merge state is +GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction and its final-head recovery. Appendix A preserves the -earlier authoring, review, approval-state, reconciliation and validation -record. Appendix B records the durable control conditions. +Route-B correction stages, with the Amendment-6/7 candidate stage as the +current stage and the earlier candidate, approval-state and recovery stages +recorded as history. Appendix A preserves the earlier authoring, review, +approval-state, reconciliation and validation record. Appendix B records the +durable control conditions. ## 1. Repository state @@ -31,23 +34,27 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-4/5 recovery commit that contains this report, an -ordinary single-parent additive commit whose direct parent is the historical -Route-B approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`; its -SHA is deliberately not self-pinned here (section 3). The historical -approval-state head is itself the direct child of the historical -content-approved candidate `95769537cdc9871e42f9f3e5443604a96beb74da`. The -recovery commit's publication as the PR #960 head is a post-commit, -GitHub-owned lifecycle fact under `ADR-0005`. +Head commit: the Amendment-6/7 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical Amendment-4/5 recovery head +`9db91af7bf3d9f969556c819187420fc31d25c10`; its SHA is deliberately not +self-pinned here (section 3). The recovery head is itself the direct child of +the historical Route-B approval-state head +`c046e6bf6510f24ee1231f86293a3d499bceed7e`, which is the direct child of the +historical content-approved activation-epoch candidate +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact +under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting `feature/worker`; its live state, head and checks are GitHub-owned lifecycle evidence. Expected branch deletion after merge: YES Final programme PR required: YES - `feature/worker -> develop` is a separate programme-level gate. -Implementing model: Route-B candidate and approval-state stages - Claude -(Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude -(Fable 5.1), bounded implementing agent. Earlier rounds are recorded in +Implementing model: activation-epoch candidate and approval-state stages - +Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - +Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate stage - +Claude (Opus 5.5), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard @@ -80,19 +87,19 @@ historical approval-state source and stale earlier programme pin: bd5223deee44b465dd860ff96b75d81ad41cbf2d ADR-0012 blob cffee08b0c4326823f36387e6cec2abe49594273 -current prior ADR-0012 blob (task head before this correction): +prior ADR-0012 blob before the activation-epoch correction: dec6665353804e42f22474954bf878308092b717 -task head before this correction / candidate parent 1: +task head before the activation-epoch correction / its candidate parent 1: b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20 -refreshed feature/worker / candidate parent 2: +refreshed feature/worker / activation-epoch candidate parent 2: b44303c214498baf76c9d9a0a7cab374377f9cbe unmodified Route-B merge-tree baseline: e15607878e66d2ff12b819dcea13293a46674f13 -content-approved Route-B candidate / approval-state parent: +content-approved activation-epoch candidate / approval-state parent: 95769537cdc9871e42f9f3e5443604a96beb74da tree 5084c6951a1b57b449f6045b139aacfbc488e534 ADR-0012 blob a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c @@ -104,7 +111,15 @@ ADR-0012 blob 4848470a252076268a9fac9da5c40f0b4ebff474 (unchanged by the recover decision-register blob b19b1404dafa62a7aaebcd33708841cf44ce1831 implementation-report blob cd97eb441a38ff1885cf621e1af04519c470dffc -feature/worker at the recovery (unchanged): +historical Amendment-4/5 recovery head / Amendment-6/7 candidate parent: +9db91af7bf3d9f969556c819187420fc31d25c10 +tree fd9a300265c310388652ff94a4ef9f95224ce018 +ADR-0012 blob 4848470a252076268a9fac9da5c40f0b4ebff474 (APPROVED activation-epoch version; historical) +decision-register blob 247d077786a9176e628baa8de64c0c3a5e9b09d3 +implementation-report blob 64326e564b3f27f94aa9ced7dd7572bb30244bb8 +CHANGELOG blob ff7089a4a93e6a8c2c27c75a0ebf56ce43405c27 + +feature/worker at the recovery and at this candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -133,19 +148,35 @@ Approved specification: `6001012674`; - fresh independent CRITICAL specification approval of the base specification and Amendments 1-5: #958 comment `6013265532`; -- bounded recovery implementation authorization: #958 comment `6013267369`. +- bounded recovery implementation authorization: #958 comment `6013267369`; +- Specification Amendment 6 / immutable route-contract history: #958 comment + `6018536323`; +- independent CRITICAL review of Amendment 6 (`CHANGES REQUIRED`, findings + accepted as binding blocking evidence): #958 comment `6020055747`; +- Specification Amendment 7 / Amendment-6 review closure: #958 comment + `6020074189`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-7, including eight candidate-inspection clarifications: + #958 comment `6021556329`; +- Amendment-6/7 candidate implementation authorization: #958 comment + `6021574322`. Implemented objective: the Route-B material architectural correction of -ADR-0012 before repository authority, in two source stages, followed by one -bounded final-head recovery stage. - -- **Candidate stage.** The candidate implements the CTO-selected append-only - activation-epoch architecture (#958 comment `5935475916`) that resolves - review finding `R-b467-01` - a single activation/deactivation pair cannot - represent repeated route activation without rewriting historical eligibility - or changing the route identity. It carried `Status: PROPOSED` together with a - `PROPOSED` decision-register row, one `PROPOSED` changelog correction entry - and the candidate-stage report. +ADR-0012 before repository authority. A first correction (activation epochs) +passed through candidate and approval-state stages and one bounded final-head +recovery stage, all now historical. Final-head review of the recovery head then +exposed a further architecture defect, specified by Amendments 6-7; the commit +that contains this report is the `PROPOSED` Route-B candidate for that further +correction. + +- **Activation-epoch candidate stage (historical candidate + `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the + CTO-selected append-only activation-epoch architecture (#958 comment + `5935475916`) that resolves review finding `R-b467-01` - a single + activation/deactivation pair cannot represent repeated route activation + without rewriting historical eligibility or changing the route identity. It + carried `Status: PROPOSED` together with a `PROPOSED` decision-register row, + one `PROPOSED` changelog correction entry and the candidate-stage report. - **Approval-state stage (historical head `c046e6bf6510f24ee1231f86293a3d499bceed7e`).** The CTO approved the exact candidate content - head `95769537cdc9871e42f9f3e5443604a96beb74da`, @@ -156,22 +187,64 @@ bounded final-head recovery stage. `Approved by: Javi, CTO` and `Approval date: 2026-10-05`, the ADR-0012 register row and the correction changelog entry moved to the approved state, and this report's approval-state evidence. No architecture content changed. -- **Final-head recovery stage (the commit that contains this report).** After - PR #960 was separately marked Ready, the configured final-head automated - review of the approval-state head raised two findings. A route-parentage - concern rested on a premise that did not match the authoritative PR #960 - commit graph (the approval-state head is the direct child of the - content-approved candidate) and required no source correction. The second - finding identified that the active decision register still carried - `Last updated: 2026-09-30` while its ADR-0012 row recorded an approval on - 2026-10-05; that freshness finding was accepted as blocking and caused this - bounded recovery under Specification Amendments 4-5. The recovery commit - advances the register header to `Last updated: 2026-10-05` and reconciles - this report; it is one bounded post-final-review factual/control correction, - not another candidate and not another approval-state commit. ADR-0012 - (blob `4848470a252076268a9fac9da5c40f0b4ebff474`), its approval date and - approver, the changelog and every other decision and control file are - byte-identical to the approval-state head. +- **Final-head recovery stage (historical recovery head + `9db91af7bf3d9f969556c819187420fc31d25c10`).** After PR #960 was + separately marked Ready, the configured final-head automated review of the + approval-state head raised two findings. A route-parentage concern rested on + a premise that did not match the authoritative PR #960 commit graph (the + approval-state head is the direct child of the content-approved candidate) + and required no source correction. The second finding identified that the + active decision register still carried `Last updated: 2026-09-30` while its + ADR-0012 row recorded an approval on 2026-10-05; that freshness finding was + accepted as blocking and caused the bounded recovery under Specification + Amendments 4-5. The recovery commit advanced the register header to + `Last updated: 2026-10-05` and reconciled this report; it was one bounded + post-final-review factual/control correction, not another candidate and not + another approval-state commit. ADR-0012 (blob + `4848470a252076268a9fac9da5c40f0b4ebff474`), its approval date and approver, + the changelog and every other decision and control file were byte-identical + to the approval-state head. Control then verified the recovery as conforming + and the CTO confirmed the unchanged exact final ADR-0012 blob at the recovery + head in durable records on #958. +- **Final-head review of the recovery head and Amendments 6-7 (historical + control stage).** PR #960 was then marked Ready by control, which triggered + the configured final-head automated review of the recovery head. It raised + one new finding, recorded by control as `R-9DB-01` (HIGH, valid and + blocking): ADR-0012 gave each logical route one route-level event + kind/version contract that was not immutable and not versioned with its + activation epochs, so a later contract change could reinterpret closed-epoch + obligations or make outstanding obligations appear to need a different + materializer. The CTO approved Specification Amendment 6 (immutable + route-contract revisions bound to activation epochs). Its independent + CRITICAL review returned `CHANGES REQUIRED` with three accepted blocking + findings - an undefined in-epoch contract mismatch, unvalidated backfill + revision selection and incomplete revision-failure validation - plus + non-blocking clarifications. The CTO approved Specification Amendment 7 to + close them, and a fresh independent CRITICAL review of the base specification + and Amendments 1-7 returned `APPROVED`, closing all three findings and + `R-9DB-01` at specification level and accepting eight non-blocking + candidate-inspection clarifications. `R-9DB-01` remained open in source at + the recovery head. +- **Amendment-6/7 candidate stage (the commit that contains this report).** + Authorized by #958 comment `6021574322`. PR #960 was converted Ready -> + Draft before any candidate source was staged. The candidate is one ordinary + single-parent commit on the recovery head that changes exactly the four + authorized correction paths. ADR-0012 returns to `Status: PROPOSED` with no + current approver or approval date and implements Amendments 6-7 as one + coherent architecture: every activation epoch references exactly one + immutable route-contract revision with a canonical semantic signature; + eligibility requires both epoch membership and a match to that revision; + continuous producer-version coverage with expand/contract transitions and + cross-route fan-out; durable `IN_EPOCH_CONTRACT_MISMATCH` anomalies; + field-by-field validation of historical-enrollment revision selection; + atomic database-level revision equivalence; deterministic materializer + compatibility; per-revision route health; and safe revision withdrawal and + route retirement. The superseded route-level-contract wording in sections + 3.2, 3.3, 3.9, 3.15, 7, 8, 9, 10, 11 and 12 is reconciled. The + decision-register ADR-0012 row, one new changelog entry and this report + describe the corrected version as `PROPOSED`; the 2026-09-30 and 2026-10-05 + CTO approvals remain historical evidence bound to the earlier exact + versions. Out-of-scope changes made: NONE @@ -187,11 +260,12 @@ Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) Eligibility for the material pre-authority correction is keyed to the ADR number, not to one pathname (Amendment 1 A1; -`docs/engineering/decisions/README.md`). At candidate validation time -`b44303c214498baf76c9d9a0a7cab374377f9cbe` was simultaneously `develop` and the -refreshed `feature/worker`, and `master` was -`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`. The following literal commands were -run against both commits: +`docs/engineering/decisions/README.md`). At activation-epoch candidate +validation time `b44303c214498baf76c9d9a0a7cab374377f9cbe` was simultaneously +`develop` and the refreshed `feature/worker`, and `master` was +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`. Those refs were unchanged at the +Amendment-6/7 candidate stage, and the same literal commands were run again +against both commits with identical results: ```text git log --format=%h -- ':(glob)**/*ADR-0012*' @@ -224,8 +298,8 @@ that such a removed repository-authoritative ADR-0012 version existed. ### 2.2 Stale controls -Under the repository material-correction rules, this correction makes stale, for -the corrected version: +Under the repository material-correction rules, the material correction makes +stale, for every corrected version: - the CTO architecture-content approval bound to `631e28d1f05495f24ce88369d4557f1033f970b3` / ADR blob @@ -241,38 +315,74 @@ the corrected version: `APPROVED`. Those records remain historical evidence for their exact versions. They are -not approval of the corrected version. Under Route B the corrected version -received fresh CTO content approval on 2026-10-05, recorded as `APPROVED` by -the historical approval-state commit -`c046e6bf6510f24ee1231f86293a3d499bceed7e`. The CTO's exact-final-blob -approval record for ADR-0012 blob `4848470a252076268a9fac9da5c40f0b4ebff474` -was bound to that superseded source head as well as to the blob, so Amendments -4-5 require a fresh head-bound confirmation of the same unchanged blob at the -recovery head; that confirmation, Draft -> Ready, fresh final-head automated -review, fresh independent exact-head review and separate merge authorization -remain separate later gates. +not approval of the corrected version. Under Route B the activation-epoch +corrected version received CTO content approval on 2026-10-05, recorded as +`APPROVED` by the historical approval-state commit +`c046e6bf6510f24ee1231f86293a3d499bceed7e` in ADR-0012 blob +`4848470a252076268a9fac9da5c40f0b4ebff474`, and that unchanged blob later +received a fresh head-bound exact-final-blob confirmation at the recovery head +`9db91af7bf3d9f969556c819187420fc31d25c10`. + +The Amendment-6/7 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every one of those activation-epoch-version records +as well: + +- the CTO content approval of candidate + `95769537cdc9871e42f9f3e5443604a96beb74da` / ADR blob + `a88aed0a0a17b6dd7a7b852a0e82d8da3fa19b2c`; +- the 2026-10-05 approval state carried by ADR blob + `4848470a252076268a9fac9da5c40f0b4ebff474`, its exact-final-blob approval + and its head-bound confirmation at the recovery head; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the recovery head; +- the recovery-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-CORRECTION` changelog entry that record the + activation-epoch version as `APPROVED`; the changelog entry is preserved + and remains true only of that historical version, while the register row now + records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. The +new candidate requires its own independent control inspection, CTO +exact-candidate content approval, approval-state commit, exact-final-blob +approval, Draft -> Ready, fresh final-head automated review, fresh independent +CRITICAL exact-head review and separate merge authorization, all of which are +later gates. ## 3. Commits -Amendment-4/5 recovery commit (the commit that contains this report): +Amendment-6/7 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: reconcile final-head review metadata` - - direct parent: the historical Route-B approval-state head - `c046e6bf6510f24ee1231f86293a3d499bceed7e` - - changes only the two authorized recovery paths (section 4): the - decision-register `Last updated` header line and this report + `THOTH-ASYNC-01-ADR-01: propose route-contract revision correction` + - direct parent: the historical Amendment-4/5 recovery head + `9db91af7bf3d9f969556c819187420fc31d25c10` + - changes only the four authorized correction paths (section 4) Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree (Amendment 5 F2). They are -verified against the remote task-branch head after push and recorded in the -post-publication handoff; once published, the PR #960 head and head tree are -GitHub-owned lifecycle evidence under `ADR-0005`. The authorized topology is a -non-force fast-forward of the task branch from the approval-state head to the -recovery commit; no merge commit, rebase, squash, amend, force update or -further source commit is authorized, and no further source commit exists -implicitly after it. +here because this report is part of that tree. They are verified against the +remote task-branch head after push and recorded in the post-publication +handoff; once published, the PR #960 head and head tree are GitHub-owned +lifecycle evidence under `ADR-0005`. The authorized topology is a non-force +fast-forward of the task branch from the recovery head to the candidate; no +merge commit, rebase, squash, amend, force update or second candidate commit +is authorized. The only further source commit the Route-B sequence +contemplates is one direct-child approval-state commit, which requires its own +later CTO content approval and separate authorization; no source commit exists +implicitly after this candidate. + +Amendment-4/5 recovery commit (historical): + +- `9db91af7bf3d9f969556c819187420fc31d25c10` - + `THOTH-ASYNC-01-ADR-01: reconcile final-head review metadata` + - ordinary single-parent additive commit; direct parent: the historical + Route-B approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e` + - tree `fd9a300265c310388652ff94a4ef9f95224ce018` + - changed only the two authorized recovery paths (section 4): the + decision-register `Last updated` header line and this report + - published by a non-force fast-forward of `feature/async/adr-0012` from the + approval-state head; the remote head, tree and parent matched the locally + validated object Route-B approval-state commit (historical): @@ -287,7 +397,7 @@ Route-B approval-state commit (historical): candidate; the remote head, tree and parent matched the locally validated object -Route-B candidate commit (content-approved): +Route-B activation-epoch candidate commit (content-approved; historical): - `95769537cdc9871e42f9f3e5443604a96beb74da` - `THOTH-ASYNC-01-ADR-01: propose Route-B activation-epoch correction` @@ -333,16 +443,21 @@ first-parent order, newest first: ## 4. Files changed -Historical Route-B material-correction and approval-state write budget (base -specification section 5, unchanged by Amendments 1-3; four correction paths): +Amendment-6/7 candidate write budget for the commit that contains this report +(Amendment 6 G10, Amendment 7 H12, authorization #958 comment `6021574322`; +exactly four existing paths, no new file, no deletion): - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/decisions/decision-register.md` -Amendment-4/5 final-head recovery write budget (Amendment 4 E5, Amendment 5 -F3.4; exactly two paths): +Historical Route-B activation-epoch material-correction and approval-state +write budget (base specification section 5, unchanged by Amendments 1-3; the +same four correction paths). + +Historical Amendment-4/5 final-head recovery write budget (Amendment 4 E5, +Amendment 5 F3.4; exactly two paths): - `docs/engineering/decisions/decision-register.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -357,10 +472,90 @@ six paths at every stage): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/decisions/decision-register.md` -Authorized new-file paths: NONE at the approval-state and recovery stages; this -report already existed. +Authorized new-file paths: NONE at the approval-state, recovery and +Amendment-6/7 candidate stages; this report already existed. + +Amendment-6/7 candidate changes in the commit that contains this report, +relative to the recovery head `9db91af7bf3d9f969556c819187420fc31d25c10` +(exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement the Amendment-6/7 immutable route-contract-revision + architecture and record the corrected version as `PROPOSED`. + - behavioural effect: architecture proposal only. The header carries + `Status: PROPOSED` and no current approver or approval date. Section 3.2 + replaces the route-level event kind/version contract with append-only + immutable route-contract revisions under the stable `route_key`; binds + every epoch to exactly one revision; makes eligibility require both epoch + membership and the epoch revision's kind/accepted-version predicate; + defines the canonical semantic signature and database-atomic equivalent + revision convergence; adds the `IN_EPOCH_CONTRACT_MISMATCH` fail-safe, + field-by-field historical-enrollment validation, revision withdrawal and + route-retirement rules, same-route versus new-route guidance and new-route + duplicate-effect protection; and turns the event-emission floor into the + event-emission floor and continuous producer-version coverage, with + expand/contract transitions, cross-route fan-out and non-binary writers. + Route backlog and materializer health become per revision and per + emittable or outstanding accepted version (sections 3.2 and 3.15). Section + 3.3 binds route version acceptance to the revision's accepted-version set; + section 3.9 adds explicit materializer capability declarations, + deterministic compatibility and the materializer-retirement rule; + invariants 3-6 and 8 are reconciled and invariants 35-40 added; sections + 8, 9, 10 and 11 are aligned, and section 11 adds the Amendment-6/7 + validation set. Section 12 records the `PROPOSED` state and binds the + 2026-09-30 and 2026-10-05 approvals to the historical earlier versions. + All unrelated architecture is unchanged. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the correction makes the ADR-0012 row's `APPROVED` assertion + untrue for the new version. + - behavioural effect: the ADR-0012 row records `PROPOSED` / `Pending`, binds + the 2026-09-30 approval to the pre-correction versions and the 2026-10-05 + approval to the earlier activation-epoch corrected version, and adds one + summary of the route-contract-revision correction; its partial + supersession, authority-condition, ADR-0013 and + implementation-authorization sentences are unchanged. The header + `Last updated` line advances from `2026-10-05` to `2026-10-06`, the date of + this row change (section 5, decision 25). No other row changes. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: required changelog entry. + - behavioural effect: exactly one new + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry at the top of + `[Unreleased] -> Changed`, describing the corrected version as `PROPOSED` + and stating that the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-APPROVAL` and + `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries and the approvals they + record apply only to the historical earlier exact versions. No heading is + added and every existing entry is byte-preserved. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-6/7 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate the current + stage; records the recovery, the final-head review of the recovery head, + Amendments 6-7 and their review sequence as history; records the + candidate's validation (section 9); and reframes statements that would + otherwise describe the recovery commit as the commit containing this + report. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the recovery head through this candidate: + +- ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; +- ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; +- ADR-0013 - blob `d928f957bdf775d03e99fc73888ec8e2dec5f80b`; +- `docs/engineering/decisions/README.md` - blob + `de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7`; +- `docs/engineering/ai-delivery/implementation-reports/CTRL-ADR-AMEND-01-implementation-report.md` + - blob `7bf7425f0527dbaa72e718df5264e399035a148b`; +- `docs/engineering/ai-delivery/implementation-reports/CTRL-ADR-PR-FIRST-01-implementation-report.md` + - blob `d86cf215866750979eafea040426c51374d5155c`; +- ADR-0005 - blob `bdaa976e4893b1fc45f994236f9e56d433212d63`; +- `docs/engineering/AGENTS.md` - blob `e194b14e8c4fbb298db7ca6c38aebeb69547a29a`; +- `docs/engineering/ai-delivery/implementation-report-template.md` - blob + `0ae39d3892bbca5b0ff90dc7ffa70038662351bc`. -Candidate-stage changes (content-approved candidate +Activation-epoch candidate-stage changes (content-approved candidate `95769537cdc9871e42f9f3e5443604a96beb74da`) relative to the unmodified merge-tree baseline `e15607878e66d2ff12b819dcea13293a46674f13`: @@ -456,9 +651,10 @@ approval state only): candidate-stage and historical evidence. - within authorized write budget: YES -Recovery changes in the commit that contains this report relative to the -historical approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e` -(exactly the two authorized recovery paths): +Recovery changes in the historical recovery commit +`9db91af7bf3d9f969556c819187420fc31d25c10` relative to the historical +approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e` (exactly the +two authorized recovery paths): - `docs/engineering/decisions/decision-register.md` - reason: final-head automated review found the active register's own @@ -522,25 +718,30 @@ Files deleted, moved or renamed: NONE PASS -The candidate differs from the unmodified merge-tree baseline in exactly the -four authorized correction paths and from refreshed `feature/worker` in exactly -the six cumulative PR paths (section 9). The approval-state commit differs -from the content-approved candidate in exactly the same four paths and from -refreshed `feature/worker` in exactly the six cumulative PR paths. The recovery -commit differs from the approval-state head in exactly the two authorized -recovery paths and from refreshed `feature/worker` in exactly the same six -cumulative PR paths (section 9, "Recovery validation"). No ADR-0008, +The Amendment-6/7 candidate differs from the recovery head in exactly the four +authorized correction paths and from refreshed `feature/worker` in exactly the +six cumulative PR paths (section 9, "Amendment-6/7 candidate validation"). +Historically, the activation-epoch candidate differed from the unmodified +merge-tree baseline in exactly the four authorized correction paths and from +refreshed `feature/worker` in exactly the six cumulative PR paths; the +approval-state commit differed from the content-approved candidate in exactly +the same four paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths; and the recovery commit differed from the approval-state +head in exactly the two authorized recovery paths and from refreshed +`feature/worker` in exactly the same six cumulative PR paths. No ADR-0008, ADR-0010, ADR-0013, engineering-control doctrine, workflow, `AGENTS.md`, runtime, migration or schema path is edited at any stage. ## 4.2 Authorized actions actually used -For the Route-B candidate stage (#958 comments `5993634460` and `5998119577`), -the approval-state stage (#958 comment `5999579357`) and the Amendment-4/5 -recovery stage (#958 comment `6013267369`). This matrix states only what had +For the Route-B activation-epoch candidate stage (#958 comments `5993634460` +and `5998119577`), its approval-state stage (#958 comment `5999579357`), the +Amendment-4/5 recovery stage (#958 comment `6013267369`) and the Amendment-6/7 +candidate stage (#958 comment `6021574322`). This matrix states only what had been done when the commit that contains this report was created. -Candidate stage - historical, completed before the approval-state commit: +Activation-epoch candidate stage - historical, completed before the +approval-state commit: - repository inspection: USED (Git history, refs, doctrine and the #957/#958/PR #960 records, read-only) @@ -574,48 +775,68 @@ Approval-state stage - historical, completed before the recovery commit: authorized and performed it, which triggered the configured final-head automated review of the approval-state head (section 2) -Recovery stage - done up to and including the creation of the commit that -contains this report: +Amendment-4/5 recovery stage - historical, completed before this candidate: - repository inspection: USED (read-only verification of the exact refs, PR #960 state, commit topology and every protected blob named by the recovery authorization; all matched) +- PR creation/update: USED (PR #960 Ready -> Draft before any recovery source + was staged, and one state-neutral PR #960 description update for the + published recovery head; no title change) +- source edit: USED (the two authorized recovery paths only) +- new file creation, file deletion/move/rename, branch creation: NOT USED +- commit: USED (the single-parent recovery commit + `9db91af7bf3d9f969556c819187420fc31d25c10`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `c046e6bf6510f24ee1231f86293a3d499bceed7e` to the recovery commit; the + remote head, tree, parent and protected blobs were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later marked + PR #960 Ready, which triggered the configured final-head automated review of + the recovery head (section 2) + +Amendment-6/7 candidate stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 state, mergeability, the six-path cumulative footprint, the absence + of any source commit after the recovery head, the ADR-0012 blob at the + recovery head and the nine unresolved PR #960 review threads; all matched + the authorization) - PR creation/update: USED (PR #960 Ready -> Draft, the one authorized pre-publication PR-state mutation, performed after that verification and - before any recovery source was staged; the read-back showed the PR open, - draft and unmerged at head `c046e6bf6510f24ee1231f86293a3d499bceed7e`) -- source edit: USED (the two authorized recovery paths only) + before any candidate source was staged; the read-back showed the PR open, + draft and unmerged at head `9db91af7bf3d9f969556c819187420fc31d25c10` with + target `feature/worker`) +- source edit: USED (the four authorized correction paths only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) -- branch creation: NOT USED (a detached local worktree at the approval-state - head was used for construction; the existing task branch is the +- branch creation: NOT USED (not authorized; a detached local worktree at the + recovery head was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent recovery commit that contains this report) +- commit: USED (the single-parent candidate commit that contains this report) -Authorized post-commit actions for the recovery commit - GitHub-owned +Authorized post-commit actions for the candidate commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `c046e6bf6510f24ee1231f86293a3d499bceed7e` to the recovery commit; -- post-push verification of the remote head, tree, direct parent, ADR-0012 - blob, decision-register blob and report blob; -- the one authorized state-neutral PR #960 description update for the - published recovery head (no title change); -- the automatic PR CI those actions cause. + `9db91af7bf3d9f969556c819187420fc31d25c10` to the candidate commit; +- post-push read-only verification of the remote head, tree, direct parent, + ADR-0012 blob, cumulative footprint and PR #960 state; +- the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized at any stage for the implementing agent, and separate later -gates: +Not authorized for the implementing agent at the Amendment-6/7 candidate +stage, and separate later gates where applicable: -- fresh CTO exact-final-blob confirmation at the recovery head, Draft -> Ready, - fresh final-head Codex/app review and the fresh independent exact-head - review: later gates; Draft -> Ready is not authorized for the implementing - agent at any stage +- PR #960 title or description update: NOT USED (not authorized at this stage) +- Draft -> Ready: NOT USED (not authorized) +- approval-state commit, `APPROVED` status or any second candidate commit: + NOT USED (not authorized; later gates) - issue/comment mutation: NOT USED (not authorized) -- review-thread resolution/dismissal: NOT USED (not authorized) +- review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) - manual Codex/app review request: NOT USED (not authorized) - provider/runtime read: NOT USED (not authorized) @@ -644,13 +865,16 @@ Pushing a new head to the PR triggers the repository's classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push -and no migration execution is expected. Under Amendment 3 C2/C11 and -Amendments 4-5, configured Codex/app review is not expected from a push to a -draft PR; fresh final-head Codex/app review is expected at the separately -authorized Draft -> Ready transition after the recovery commit. The actual job -outcomes for the recovery commit exist only after its publication; they are +and no migration execution is expected. Under Amendment 3 C2/C11, Amendments +4-5 and Amendments 6-7, configured Codex/app review is not expected from a push +to a draft PR and is not relied upon at this candidate stage; fresh final-head +Codex/app review is expected only at a later, separately authorized +Draft -> Ready transition after the approval-state commit. The actual job +outcomes for the candidate commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request and the -post-publication handoff, not in this file. +post-publication handoff, not in this file. If an automatic workflow +unexpectedly intends to execute a migration or publish an image rather than +skip, that is a HOLD condition. Historical automatic CI for earlier PR heads followed the same documentation-only classification, with no manual dispatch or rerun: @@ -661,6 +885,7 @@ documentation-only classification, with no manual dispatch or rerun: b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20: 4 success (classify x3, check-changelog), 6 skipped 95769537cdc9871e42f9f3e5443604a96beb74da: 4 success (classify x3, check-changelog), 6 skipped (content-approved candidate) c046e6bf6510f24ee1231f86293a3d499bceed7e: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (approval-state head) +9db91af7bf3d9f969556c819187420fc31d25c10: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (recovery head) ``` Manually initiated external actions (anything the implementing agent @@ -672,7 +897,9 @@ services): NONE ## 5. Implementation decisions -List decisions made within the approved design: +List decisions made within the approved design. + +Activation-epoch correction, approval-state and recovery stages (historical): 1. An activation epoch is expressed as the half-open routing-generation interval `[activation_generation, deactivation_generation)`; an open epoch @@ -691,7 +918,7 @@ List decisions made within the approved design: epoch; reactivation after retirement is rejected. 5. Any epoch identifier is storage/audit metadata subordinate to `route_key` and is excluded from the `(event_id, route_key)` disposition key (A2.11). -6. Section 3.2 now states the disposition model already ruled by the CTO and +6. Section 3.2 states the disposition model already ruled by the CTO and recorded in section 3.5 and invariant 8: exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job. @@ -699,55 +926,126 @@ List decisions made within the approved design: logical route with its own epochs and dispositions and receives earlier events only through explicitly authorized backfill/replay; the ADR defines no successor mechanism. -8. At the candidate stage the header dropped the earlier `Approved by` / - `Approval date` fields; the approval-state commit adds fresh values - (decision 12). Section 12 binds the 2026-09-30 approval to the historical pre-correction - versions without embedding their identities; those identities live in this - report and the owning issue. +8. At the activation-epoch candidate stage the header dropped the earlier + `Approved by` / `Approval date` fields, and the approval-state commit added + fresh values (decision 12). Section 12 binds earlier approvals to the + historical versions without embedding their identities; those identities + live in this report and the owning issue. 9. Section 12's approval, authority and supersession text is written to remain - true in both the `PROPOSED` and `APPROVED` states, so the later - approval-state delta is limited to the status line, the approver/date and - the sentence that says the corrected content is not yet approved. + true in both the `PROPOSED` and `APPROVED` states, so an approval-state + delta is limited to the status line, the approver/date and the sentence + that says the corrected content is not yet approved. 10. At the approval-state stage the decision-register header `Last updated` line was left at `2026-09-30`, because that authorization permitted only the ADR-0012 row to change. Final-head automated review of the approval-state head showed that this left the active register's own freshness metadata stale once the row recorded an approval on 2026-10-05; that finding was accepted as blocking. Leaving the header unchanged was - therefore not the correct final state: the recovery commit that contains - this report advances it to `Last updated: 2026-10-05` as its only register - change (Amendment 4 E6). + therefore not the correct final state: the recovery commit advanced it to + `Last updated: 2026-10-05` as its only register change (Amendment 4 E6). 11. The report is restructured to the template rather than appended to, so its durable current state is readable without implying that historical - approvals cover the corrected version. -12. The approval-state header uses the same field order as the earlier approved - ADR-0012 versions (`Status`, `Date`, `Approved by`, `Approval date`, - `Decision owner`), and section 12 repeats the approver and date, so the - approval metadata appears in both places as before. -13. The approval date carried by the ADR is the date of the durable CTO - content-approval record, 2026-10-05, as Route B requires; it is neither - back-dated nor anticipated. -14. The recovery commit's SHA, its tree and this report's final blob are not - self-pinned in this report (Amendment 5 F2); they are verified against the - remote task-branch head after push and recorded in the post-publication - handoff. -15. The final-head automated review outcome is recorded here substantively - (section 2, section 14); its review and comment identifiers remain + approvals cover a later corrected version. +12. The approval-state header used the same field order as the earlier + approved ADR-0012 versions (`Status`, `Date`, `Approved by`, + `Approval date`, `Decision owner`), and section 12 repeated the approver and + date, so the approval metadata appeared in both places as before. +13. The approval date carried by the activation-epoch version was the date of + the durable CTO content-approval record, 2026-10-05, as Route B requires; + it was neither back-dated nor anticipated. +14. The recovery report did not self-pin the recovery commit's SHA, its tree or + its own final blob (Amendment 5 F2); they were verified after push and + recorded in the post-publication handoff. Section 1 records them here as + historical facts because the recovery head is this candidate's parent. +15. Final-head automated review outcomes are recorded here substantively + (section 2, section 14); their review and comment identifiers remain GitHub-owned lifecycle evidence under `ADR-0005` and are not transcribed into source (Amendment 5 F3.5). +Amendment-6/7 candidate stage (the commit that contains this report). Items 16, +19, 20, 22 and 25 are interpretations of the approved specification recorded +for explicit control inspection: + +16. The `IN_EPOCH_CONTRACT_MISMATCH` predicate is keyed to events of the epoch + revision's consumed event kind whose payload/schema version is outside its + accepted-version set. An event of a kind the revision does not consume is + outside that route's contract and is neither an obligation nor an anomaly + for it; otherwise every event of every unrelated kind would be a mismatch + for every route. Whether producers still emit the consumed kind at all is + governed by the event-emission floor, which already requires every serving + and supported rollback producer to emit it. +17. The semantic signature records the absence of a compatibility policy as + part of the signature, so a revision with a policy and one without remain + distinct (Amendment 7 H6). +18. The database-level atomic property is stated as two acceptable forms: a + database-enforced uniqueness constraint over the route's canonical + semantic signature (or a deterministic canonical encoding of it), or + revision creation serialized through the route's durable serialization + mechanism with the equivalence check and insert in one transaction. An + application-level read-then-insert is stated to be insufficient. Exact + table, index and encoding names remain implementation-specification detail + (Amendment 7 H6, clarification 4). +19. Historical-enrollment validation is enumerated field by field over the + semantic signature (clarification 8). For the route-to-materializer/job + contract version, the check is that deterministic compatibility identifies + at least one compatible materializer for the event's kind and version, so + enrollment never creates a disposition that no materializer can honour; a + refusal leaves the `(event_id, route_key)` slot free for a later attempt. +20. Withdrawal from future selection is defined to prevent only new epochs from + referencing the revision and its selection for new historical enrollment; + it never removes the revision from materializing or reporting obligations + already eligible through epochs that reference it. It is refused or held + while it would strand undispositioned obligations or unresolved mismatch + anomalies (Amendment 7 H8, clarification 5). +21. Materializer coverage is stated per revision and per accepted version that + is currently emittable or has outstanding obligations; a newly emittable + accepted version must be verified before producers may emit it + (Amendment 7 H10, clarification 1 and its control clarification). +22. In-epoch mismatch resolution is append-only evidence linked to the anomaly; + the anomaly and its detection evidence are never deleted (clarification 6). + A mismatch that enrollment resolves receives the one allowed disposition + under the selected compatible revision; one resolved by approved + reconciliation/divergence receives none. +23. New invariants 35-40 are appended after invariant 34 rather than inserted + after invariant 8, so the numbering of invariants 9-34, which other text + and earlier review records cite, is unchanged. Invariants 3-6 and 8 are + reconciled in place. +24. Section 3.2 gains five unnumbered subsections - route-contract revisions, + in-epoch contract mismatch, historical enrollment/backfill/replay, revision + withdrawal and route retirement, and the renamed "Event-emission floor and + producer-version coverage" - following the existing unnumbered-subsection + style of section 3.8. The "route introduced by a later release" paragraph + moves into the historical-enrollment subsection. +25. The decision-register header `Last updated` line advances to + `2026-10-06`, the date on which this candidate changes the ADR-0012 row. + This keeps the active register's freshness metadata truthful, the property + whose absence caused the accepted blocking final-head finding in decision + 10. It is a header line in the authorized register path, not another row. +26. The changelog gains one new entry instead of rewording the existing + `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, so every historical entry, + including the record of the 2026-10-05 approval, stays byte-preserved; the + new entry states which exact earlier versions those entries describe + (Amendment 3 C8 pattern). +27. Section 12 binds both the 2026-09-30 approval and the 2026-10-05 approval + to the historical earlier versions without embedding their identities, + consistent with decision 8. +28. This report does not self-pin the candidate commit's SHA, its tree or its + own final blob; it records only non-self-referential evidence, and the + ADR-0012, decision-register and changelog blobs, which do not depend on + this report's bytes. + List any deviation from the specification requiring authorization: -- An earlier unpublished local candidate, +- An earlier unpublished local activation-epoch candidate, `a0e0c2cd6809fb2b88d15235d07086f32e4a699c`, carried a version of this report that described the push, the task-branch fast-forward, the PR description update, the PR head and the PR diff as already complete before they had - happened. Control rejected it before publication and authorized this + happened. Control rejected it before publication and authorized a replacement in #958 comment `5998119577`. The rejected candidate was not - pushed and is not a parent of this candidate. This replacement is rebuilt - from the same two authorized parents with the same ADR-0012, - decision-register and changelog bytes; only this report differs, to correct - that lifecycle wording. + pushed and is not a parent of any published commit. The replacement + `95769537cdc9871e42f9f3e5443604a96beb74da` was rebuilt from the same two + authorized parents with the same ADR-0012, decision-register and changelog + bytes; only the report differed, to correct that lifecycle wording. - The approval-state authorization asked this report to record the identifier of the CTO content-approval comment. The repository-authoritative Route-B rule in `docs/engineering/decisions/README.md` says the approval-state commit must @@ -759,17 +1057,22 @@ List any deviation from the specification requiring authorization: exact candidate head and ADR blob it approved, without restating its comment identifier. - Recovery stage: NONE. The recovery followed Amendments 4-5 and its separate - implementation authorization exactly; no third path, second commit, amend, - rebase, squash, force push or history rewrite was needed or performed before - the commit that contains this report was created. + implementation authorization exactly. +- Amendment-6/7 candidate stage: NONE. No fifth path, new file, deletion, + second commit, amend, rebase, squash, force push or history rewrite was + needed or performed before the commit that contains this report was + created. Decisions 16, 19, 20, 22 and 25 are recorded for control inspection + as interpretations within the authorized scope, not as deviations. ## 6. Database and migration effects Migration added: NO No schema, data, index, constraint or `thoth-api/src/schema.rs` effect. The -activation-epoch model is architecture for a future separately authorized -shared-engine implementation; it creates no migration obligation by itself. +activation-epoch and route-contract-revision model, including its required +database-level revision-equivalence property, is architecture for a future +separately authorized shared-engine implementation; it creates no migration +obligation by itself and names no table, column or index. ## 7. API and compatibility effects @@ -779,8 +1082,12 @@ Backwards compatibility: unaffected; documentation/architecture only Deprecations: NONE Cross-repository dependencies: NONE for this correction. The verified BE-04 per-consumer impact record for the ADR's later legacy-contract retirement is -preserved unchanged in Appendix A; the activation-epoch correction changes no -released contract and adds no consumer. +preserved unchanged in Appendix A; neither the activation-epoch correction nor +the route-contract-revision correction changes a released contract or adds a +consumer. The route, revision, anomaly and materializer-compatibility concepts +are internal to the future shared engine; any later external read or executor +contract derived from them needs its own specification, merged upstream +contract and downstream task. ## 8. Authorization and security @@ -795,14 +1102,17 @@ Security limitations: NONE Record exact commands and outcomes. The subsections "Formatting" through "Content checks" record the -candidate-stage validation of candidate +candidate-stage validation of the historical activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, preserved as candidate-stage evidence. "Approval-state validation" records the validation of the historical approval-state commit `c046e6bf6510f24ee1231f86293a3d499bceed7e`. "Recovery +validation" records the validation of the historical recovery commit +`9db91af7bf3d9f969556c819187420fc31d25c10`. "Amendment-6/7 candidate validation" at the end of this section records the validation of the commit that contains this report. -Validation environment: a full local Git worktree of `thoth-pub/thoth`, +Validation environment for the activation-epoch candidate: a full local Git +worktree of `thoth-pub/thoth`, detached at `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20`, with every object fetched from `origin`. Immediately before construction, `git ls-remote origin` returned `develop` and `feature/worker` at @@ -912,8 +1222,9 @@ docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution. docs/engineering/decisions/decision-register.md ``` -Delta against the task head before this correction (four correction paths plus -the closed inherited set; `CHANGELOG.md` belongs to both): +Delta against the task head before the activation-epoch correction (four +correction paths plus the closed inherited set; `CHANGELOG.md` belongs to +both): ```text git diff --cached --name-only b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20 @@ -1243,13 +1554,17 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro ``` The recovered decision-register blob is deterministic and not self-referential, -so it is recorded above. This report's own final blob, the recovery tree and the -recovery commit SHA depend on this report's bytes and are not self-pinned -(Amendment 5 F2); they are verified against the remote head after push and -recorded in the post-publication handoff. - -Required topology of the recovery commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +so it was recorded above. The recovery report's own final blob, the recovery +tree and the recovery commit SHA depended on that report's bytes and were not +self-pinned in it (Amendment 5 F2); they were verified against the remote head +after push and recorded in the post-publication handoff, and section 1 now +records them as historical facts. + +Required topology of the recovery commit, checked after it was created and +before any push (results recorded in that post-publication handoff; the +published recovery head `9db91af7bf3d9f969556c819187420fc31d25c10` has the +single parent `c046e6bf6510f24ee1231f86293a3d499bceed7e` and tree +`fd9a300265c310388652ff94a4ef9f95224ce018`): ```text git rev-list --parents -n 1 HEAD @@ -1265,9 +1580,179 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-6/7 candidate validation + +Environment: a full local Git worktree of `thoth-pub/thoth`, detached at the +historical recovery head `9db91af7bf3d9f969556c819187420fc31d25c10` (tree +`fd9a300265c310388652ff94a4ef9f95224ce018`), with every required object +present locally. Immediately before construction, read-only GitHub inspection +showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`9db91af7bf3d9f969556c819187420fc31d25c10` with single parent +`c046e6bf6510f24ee1231f86293a3d499bceed7e`, PR #960 open, ready, unmerged, +mergeable and clean with that head and target `feature/worker`, the cumulative +PR footprint exactly the six paths, ADR-0012 blob +`4848470a252076268a9fac9da5c40f0b4ebff474` at that head and nine unresolved +review threads. PR #960 was then converted Ready -> Draft and the read-back +confirmed it open, draft and unmerged at the same head. The four correction +paths were edited and staged, and every check below ran against that exact +final staged index before the candidate commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 9db91af7bf3d9f969556c819187420fc31d25c10 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the recovery head (exactly the four authorized +paths): + +```text +git diff --cached --name-only 9db91af7bf3d9f969556c819187420fc31d25c10 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 9db91af7bf3d9f969556c819187420fc31d25c10 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 9db91af7bf3d9f969556c819187420fc31d25c10 +1 0 CHANGELOG.md +781 259 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +704 179 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (only the `Last updated` header line and the ADR-0012 +row; every other line byte-identical): + +```text +git diff --cached -U0 9db91af7bf3d9f969556c819187420fc31d25c10 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-05 + +git diff --cached -U0 9db91af7bf3d9f969556c819187420fc31d25c10 -- docs/engineering/decisions/decision-register.md | grep '^[-+]Last updated' +-Last updated: 2026-10-05 ++Last updated: 2026-10-06 + +diff <(git show 9db91af7bf3d9f969556c819187420fc31d25c10:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n 20p | grep -o '^| `ADR-0012` | [^|]* | [A-Z]* |' +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 9db91af7bf3d9f969556c819187420fc31d25c10 -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state and outline: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +2492:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'supported event kind/version contract' +0 (exit 1: no line matches) + +diff <(git show 9db91af7bf3d9f969556c819187420fc31d25c10:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +10c10,14 +< ### Event-emission deployment floor +--- +> ### Route-contract revisions +> ### In-epoch contract mismatch +> ### Historical enrollment, backfill and replay +> ### Revision withdrawal and route retirement +> ### Event-emission floor and producer-version coverage +exit 1 (the differences above are the complete outline change) +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) cd897d576522a741c3dfa82f8de2f8d80d4288f6 +decision-register.md 18e3708d4247a9732778b8e71e5b52b95e9de776 +CHANGELOG.md db70c9827cde16887c11dbb4ef19258c36251d6f +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +cd897d576522a741c3dfa82f8de2f8d80d4288f6 +``` + +The candidate ADR-0012, decision-register and changelog blobs are deterministic +and do not depend on this report's bytes, so they are recorded above. This +report's own final blob, the candidate tree and the candidate commit SHA depend +on this report's bytes and are not self-pinned; they are verified against the +remote head after push and recorded in the post-publication handoff. + +Required topology of the candidate commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 9db91af7bf3d9f969556c819187420fc31d25c10 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 9db91af7bf3d9f969556c819187420fc31d25c10 HEAD +required: exit 0; no output + +git diff --name-only 9db91af7bf3d9f969556c819187420fc31d25c10 HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification -Environment: the local worktree described in section 9. +Environment: the local worktrees described in section 9. Steps: compare the corrected ADR against the prior blob `dec6665353804e42f22474954bf878308092b717` for every Amendment 1 A2 invariant, Amendment 3 C7 and base-specification section 3; confirm that no unrelated @@ -1285,14 +1770,30 @@ Recovery stage: compared the staged register with the approval-state blob `b19b1404dafa62a7aaebcd33708841cf44ce1831`; the only changed line is the `Last updated` header line and the ADR-0012 row is byte-identical; confirmed that ADR-0012, the changelog and every protected decision and control file -keep their approval-state blob identities; read this report for statements -that would become false once it moves into the recovery commit and reframed -them (section 4). +keep their approval-state blob identities. +Amendment-6/7 candidate stage: compared the staged ADR-0012 with the recovery +head's ADR blob `4848470a252076268a9fac9da5c40f0b4ebff474` against every +requirement of Amendment 6 G3-G9, Amendment 7 H3-H11 and the eight +candidate-inspection clarifications. Observed result: each is expressed in +section 3.2 and its subsections, sections 3.3, 3.9 and 3.15, invariants 3-6, +8 and 35-40, and sections 8-12, and each required validation case appears in +section 11. Every changed ADR hunk concerns route contracts, eligibility, +mismatch, enrollment, withdrawal, emission/producer coverage, materializer +compatibility, route health, rollout/rollback/validation of those concerns or +the approval state; the ADR-0008/ADR-0010 partial-supersession header text and +all other architecture are byte-identical to the recovery head. The phrase +"supported event kind/version contract" and every other route-level +kind/version contract statement are gone; the only remaining "kind/version" +phrases describe the revision predicate, recorded anomaly facts or job +kind/version retirement. The register, changelog and this report describe the +corrected version as `PROPOSED` and bind both earlier approvals to the earlier +exact versions; ADR-0012 carries no current approver or approval date. Evidence link/screenshot/log reference: the local validation in section 9. -Pull-request diffs of earlier heads, including the published candidate -`95769537cdc9871e42f9f3e5443604a96beb74da` and the approval-state head -`c046e6bf6510f24ee1231f86293a3d499bceed7e`, are historical evidence; the live -PR #960 diff of the recovery commit exists only after its publication and is +Pull-request diffs of earlier heads, including the published activation-epoch +candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, the approval-state head +`c046e6bf6510f24ee1231f86293a3d499bceed7e` and the recovery head +`9db91af7bf3d9f969556c819187420fc31d25c10`, are historical evidence; the live +PR #960 diff of the candidate commit exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -1301,40 +1802,43 @@ CI status: GitHub-owned lifecycle evidence for each PR head; expected documentation-only classification (`classify` and `check-changelog` pass; build, test, lint, format, migration and Docker jobs skipped). Checks: as listed in section 4.3. -Failures or warnings: NONE EXPECTED. Under Amendment 3 C10 and the Amendment-4/5 -recovery authorization an automatic CI failure, or any migration execution or -Docker publication, is a HOLD condition. +Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 +recovery authorization and the Amendment-6/7 candidate authorization, an +automatic CI failure, or any migration execution or Docker publication, is a +HOLD condition. ## 12. Rollout and rollback Initial state after merge: after a guarded merge into `feature/worker`, the -approved ADR-0012 version is reachable from the programme integration branch -only. It is not repository-authoritative until its authority condition, +approved ADR-0012 version would be reachable from the programme integration +branch only. It is not repository-authoritative until its authority condition, including reachability from `develop`, is met, and ADR-0013 programme-local reliance additionally requires the programme-pin conditions on #957. Merge -itself still requires, in order: fresh CTO exact-final-blob confirmation of the -unchanged ADR-0012 blob at the recovery head; separately authorized -Draft -> Ready; fresh automatic final-head CI and Codex/app review; fresh -independent CRITICAL exact-head review; separate CTO exact-head merge -authorization; then the guarded merge and programme-pin/reliance -reconciliation. +itself still requires, in order: independent control inspection of this exact +candidate; CTO exact-candidate content approval; exactly one direct-child +approval-state commit under its own separate authorization; CTO +exact-final-blob approval of the resulting ADR-0012 blob; separately +authorized Draft -> Ready; fresh automatic final-head CI and Codex/app review +and adjudication of every new finding; fresh independent CRITICAL exact-head +review; separate CTO exact-head merge authorization; then the guarded merge +and programme-pin/reliance reconciliation. Activation required: NONE. Merging the eventual approved version would make no runtime, schema, provider or deployment change. Feature flag/configuration: NONE Migration sequence: NONE Rollback/disable procedure: before merge, stop at the current gate; no history -rewrite, force push or amend is permitted, and any further source change needs a -fresh CTO specification amendment/authorization (Amendment 3 C10; Amendments -4-5 authorized exactly one recovery commit after the final-head HOLD, and no -further source commit exists implicitly after it). +rewrite, force push or amend is permitted. The Amendment-6/7 authorization +permits exactly one candidate commit; if this candidate needs correction, that +authorization is exhausted and a fresh CTO specification amendment or +authorization is required (Amendment 3 C10; Amendments 6-7). Monitoring required: NONE ## 13. Known limitations and deferred work -- The candidate, approval-state and recovery stages do not include the fresh - exact-final-blob confirmation at the recovery head, Draft -> Ready, the fresh - final-head Codex/app review, the fresh independent exact-head review or - merge. +- This candidate stage does not include independent control inspection, CTO + candidate-content approval, the approval-state commit, exact-final-blob + approval, Draft -> Ready, the fresh final-head Codex/app review, the fresh + independent exact-head review or merge. - Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The stale #957 pin must be superseded using the corrected exact ADR-0012 blob and the fresh independently reviewed PR #960 head; dependent `feature/worker` @@ -1347,27 +1851,29 @@ Monitoring required: NONE any correction needing another candidate commit, another approval-state commit, amend, rebase, force push or history rewrite requires a fresh CTO specification amendment/authorization. The final-head register-freshness - finding was handled exactly that way: Amendments 4-5 and a separate - implementation authorization permitted one additive recovery commit, and - nothing further. + finding was handled that way (Amendments 4-5 and one recovery commit), and + so was the final-head route-contract finding (Amendments 6-7 and this one + candidate commit). +- Exact table, column, index and encoding names for route-contract revisions, + anomalies and capability declarations are deliberately left to the future + shared-engine implementation specification. - The projection-evidence limitation of the historical heads `227057cae8a4c10f7f7795e3afe68271e56681b9` and `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; - section 9 is exact-worktree evidence for the candidate and the - approval-state commit. + section 9 is exact-worktree evidence for the activation-epoch candidate, the + approval-state commit, the recovery commit and this candidate. ## 14. Unresolved issues - The PR #960 review threads `4146838604` and `4148985175` concern implementation-report validation evidence. Their Route-B disposition under Amendment 3 C5 was the approval-state report update, preserved and extended - by the recovery: section 9 records the - literal candidate-stage and approval-state validation commands and results, - the candidate ADR blob, the candidate head as approval-state parent, the - six-path and four-path footprints and CI applicability, without + by the recovery and by this candidate: section 9 records the literal + validation commands and results of every source stage, the candidate ADR + blobs, the six-path and four-path footprints and CI applicability, without self-referential containing-commit metadata. - Thread `4146838614` concerns the disposition-to-job mapping; the prior blob - corrected section 3.5 and invariant 8, and section 3.2 now states the same + corrected section 3.5 and invariant 8, and section 3.2 states the same model. - Thread `4148985154` concerns BE-04 consumer impact; the verified per-consumer record is in Appendix A. @@ -1382,58 +1888,67 @@ Monitoring required: NONE candidate; its premise did not match the authoritative PR #960 commit graph (the approval-state head's single parent is the candidate) and it required no source correction. The other identified the stale `Last updated` register - header; it was accepted as blocking and is addressed by the recovery commit - that contains this report. + header; it was accepted as blocking and was addressed by the recovery commit. +- The configured final-head automated review of the recovery head opened one + further thread, identified here substantively only: the route-level event + kind/version contract could reinterpret historical epochs (`R-9DB-01`). It + was accepted as valid and blocking and closed at specification level by + Amendments 6-7; this candidate implements that correction in source. -Adjudication of every thread belongs to the fresh independent CRITICAL -exact-head review of the recovery head. This report neither resolves nor -dismisses any thread. +All nine PR #960 review threads remain unresolved. Adjudication of every thread +belongs to the fresh independent CRITICAL exact-head review of the eventual +final head. This report neither resolves nor dismisses any thread. ## 15. Agent self-assessment The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. It also prepared the earlier uncommitted activation-epoch candidate -referred to in Amendment 1 A10 and implemented the recovery commit that -contains this report, so it is not eligible to act as an independent reviewer -of this task (Amendment 5 F8). +work. The implementing agent of this candidate also implemented earlier Route-B +source stages of this task and prepared the earlier uncommitted +activation-epoch candidate referred to in Amendment 1 A10, so it is not +eligible to act as an independent reviewer of this task (Amendment 5 F8; +Amendment 6 G12; Amendment 7 H13). Suggested review focus: -- that every Amendment 1 A2 invariant and the Amendment 3 C7 write-once close - rule are expressed in section 3.2 and invariant 3 without changing route - identity or the `(event_id, route_key)` disposition boundary; -- that the deactivation-boundary allocation requirement (section 5, decision 3) - is a faithful expression of A2.8 rather than a new mechanism; -- that the emission-floor, rollback and recovery text matches - base-specification section 3; -- that the approval-state delta from candidate - `95769537cdc9871e42f9f3e5443604a96beb74da` is limited to the approval - metadata and approval-state wording listed in section 4, with no - architecture change, and that the ADR header and section 12 carry the - content approval's approver and date (Javi, CTO, 2026-10-05); -- that section 12's amendment clause mirrors - `docs/engineering/decisions/README.md` without loosening it; -- that ADR text unrelated to route activation, the disposition model and - approval state is byte-identical to the prior blob; -- that the recovery delta from the approval-state head - `c046e6bf6510f24ee1231f86293a3d499bceed7e` is exactly the register - `Last updated` header line plus this report, that the ADR-0012 row and every - other register row are byte-identical, and that ADR-0012 remains blob - `4848470a252076268a9fac9da5c40f0b4ebff474`. +- that every Amendment 6 G3-G9 and Amendment 7 H3-H11 requirement, and each of + the eight candidate-inspection clarifications, is expressed in ADR-0012 + without retaining contradictory route-level-contract wording; +- the interpretations recorded in section 5 as items 16, 19, 20, 22 and 25: + the scope of the in-epoch mismatch predicate, the materializer check in + enrollment validation, the meaning of withdrawal from future selection, + anomaly-resolution recording and the register `Last updated` header; +- that the database-level revision-equivalence property (section 3.2) rules + out an application-level check-then-insert race; +- that the producer-version coverage text covers rollback targets, cross-route + fan-out, newly emittable versions and non-binary writers; +- that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree + with section 3.2; +- that section 12 carries `PROPOSED` with no current approver or approval date + and binds both earlier approvals to the earlier exact versions, and that an + approval-state delta can be limited to the status line, approver/date and the + not-yet-approved sentence; +- that the register row, the new changelog entry and this report describe the + corrected version as `PROPOSED` while every historical changelog entry is + byte-preserved; +- that ADR-0008, ADR-0010, ADR-0013, the decision README and the other + protected control files are byte-identical to the recovery head. ## Appendix A - Historical authoring and correction record This appendix preserves the earlier task record. Headings are demoted and only the tense or framing needed to keep it truthful as history is changed. Statements such as "the ADR now requires" describe ADR-0012 as of the round in -which they were written; later rounds and the Route-B activation-epoch -correction supersede them where they differ. In particular, the round-4 route -model of one activation boundary and one optional deactivation boundary per -route is superseded by the append-only activation-epoch model in the current -section 3.2, and the 2026-09-30 approval and every review recorded here are -bound to the exact historical versions and heads they name. +which they were written; later rounds and the Route-B corrections supersede them +where they differ. In particular, the round-4 route model of one activation +boundary and one optional deactivation boundary per route is superseded by the +append-only activation-epoch model, and the route-level event kind/version +contract and "emits K" emission floor described in earlier rounds are +superseded by immutable route-contract revisions and producer-version coverage +in the current section 3.2. The 2026-09-30 and 2026-10-05 approvals and every +review recorded here are bound to the exact historical versions and heads they +name. ### Initial candidate and review round 1 @@ -2230,7 +2745,14 @@ stage: explicitly authorized backfill/replay under the same `(event_id, route_key)` deduplication boundary. 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO - approval of that exact corrected content; earlier approvals and reviews - remain bound to the earlier exact versions and heads they name. + approval of that exact corrected content; earlier approvals and reviews, + including the 2026-09-30 and 2026-10-05 approvals, remain bound to the + earlier exact versions and heads they name. +11. The route-contract-revision model does not permit silent reinterpretation + or loss either: every epoch is bound to one immutable revision, an event + outside its epoch revision's accepted versions becomes a visible anomaly + rather than disappearing, historical enrollment validates its selected + revision before consuming the `(event_id, route_key)` slot, and revision + withdrawal or route retirement never strands obligations or anomalies. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 4848470a2..cd897d576 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-05 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -237,11 +235,18 @@ not assume sequence allocation order equals commit order. Every event consumer has a durable **logical route** registration with: - a stable `route_key` that identifies the logical route for its whole life; -- the supported event kind/version contract; -- an append-only, totally ordered history of **activation epochs**; +- an append-only history of immutable **route-contract revisions** that define + how the route interprets and materializes the events it consumes (below); +- an append-only, totally ordered history of **activation epochs**, each bound + to exactly one of those revisions; - a route lifecycle state; - enough durable state to decide whether an event is eligible for that route. +A logical route has no mutable route-level event kind/version contract. Any +"current contract" that an implementation exposes is only a projection of the +immutable revision referenced by the route's currently open epoch; it never +determines how an event captured inside an earlier epoch is interpreted. + An activation epoch is one continuous active interval of the logical route. It is the half-open routing-generation interval: @@ -249,21 +254,25 @@ is the half-open routing-generation interval: [activation_generation, deactivation_generation) ``` -An open epoch has no deactivation boundary yet. Each epoch also records the -authorizing actor/authority and the deployment evidence that satisfied the -activation gates below. A route may be activated, deactivated and reactivated -any number of times; every activation creates a new epoch beneath the same -`route_key`. A new `route_key` is never created merely to reactivate a route. +An open epoch has no deactivation boundary yet. Each epoch references exactly +one immutable route-contract revision, fixed when the epoch opens, and records +the authorizing actor/authority and the deployment evidence that satisfied the +activation gates below for that exact revision. A route may be activated, +deactivated and reactivated any number of times; every activation creates a new +epoch beneath the same `route_key`. A new `route_key` is never created merely to +reactivate a route or to move it to another revision of its contract. -An implementation may assign an epoch identifier for storage and audit. That -identifier is subordinate to the logical `route_key`: it never replaces -`route_key` as the route identity and never enters the route-disposition -uniqueness key. +An implementation may assign epoch and route-contract-revision identifiers for +storage and audit. Those identifiers are subordinate to the logical +`route_key`: they never replace `route_key` as the route identity and never +enter the route-disposition uniqueness key. Epochs are immutable, append-only history: - at most one epoch of a route is open at any time; -- an epoch's identity and activation boundary never change once recorded; +- an epoch's identity, activation boundary and referenced route-contract + revision never change once recorded; opening, closing, reactivating or + retiring the route never changes the revision attached to any epoch; - closing an open epoch is a write-once durable close/deactivation fact, or an equivalently strong append-only close record, that records its deactivation boundary. Once an epoch is closed, its deactivation boundary cannot change @@ -304,51 +313,71 @@ or deleting handler code. Epoch boundaries use a durable routing generation, or an equivalently strong serialized database mechanism, that an event captures inside its creating -transaction and a route records when it opens or closes an epoch. An event is -historically eligible for a route exactly when its captured routing generation -lies inside **any** activation epoch of that route, whether that epoch is -currently open or already closed: at or after that epoch's activation -generation and, when the epoch has been closed, strictly before its deactivation -generation. Because epochs are pairwise-disjoint, an eligible event belongs to -exactly one epoch. An event whose captured generation falls before the route's -first epoch, in an inactive gap between two epochs, or after its most recent -closed epoch while no later epoch is open is not eligible for that route. +transaction and a route records when it opens or closes an epoch. A captured +routing generation lies inside an epoch when it is at or after that epoch's +activation generation and, when the epoch has been closed, strictly before its +deactivation generation. Because epochs are pairwise-disjoint, a captured +generation lies inside at most one epoch of a route. + +An event is historically **eligible** for a route exactly when both: + +1. its captured routing generation lies inside **any** activation epoch of that + route, whether that epoch is currently open or already closed; and +2. the event satisfies the immutable contract predicate of the route-contract + revision referenced by that epoch: its durable event kind is the revision's + consumed event kind and its durable payload/schema version is a member of + the revision's accepted-version set. + +An eligible event therefore belongs to exactly one epoch and is interpreted +under that epoch's revision. An event whose captured generation falls before +the route's first epoch, in an inactive gap between two epochs, or after its +most recent closed epoch while no later epoch is open is not eligible for that +route. An event of the consumed event kind whose captured generation lies inside +an epoch but whose version fails condition 2 is not eligible either, and is +never silently ignored: it is an in-epoch contract mismatch (below). An event of +a kind that the epoch's revision does not consume is outside that route's +contract and creates neither an obligation nor an anomaly for it. Eligibility is fixed history. The mechanism must allocate each deactivation boundary so that closing an epoch never removes or rewrites eligibility for an -event that captured a routing generation inside that epoch. Opening a later -epoch never retroactively covers routing generations in an inactive inter-epoch -gap, and retiring the route never changes whether an already-committed event -was eligible. +event that captured a routing generation inside that epoch. Because an epoch's +revision is immutable, no later revision, current-contract projection or +executor change can rewrite whether, or under which contract, an +already-committed event was eligible. Opening a later epoch never retroactively +covers routing generations in an inactive inter-epoch gap, and retiring the +route never changes whether an already-committed event was eligible. These intervals must partition events deterministically even when transactions commit out of sequence. Sequence allocation order and commit observation order alone are insufficient. For every committed event, the engine must eventually establish a durable route -disposition for every route for which that event is eligible. Exactly one -durable disposition exists for each `(event_id, route_key)`. The normal -disposition materializes a job: a materialized disposition points to exactly one -job and remains unique on `(event_id, route_key)` regardless of whether that job -is pending, running, waiting or terminal. The only alternative is the explicit, -authorized and audited terminal non-materialized disposition described below, -which points to no job (section 3.5 and invariant 8). - -Disposition uniqueness is per logical route, not per epoch: `(event_id, -route_key)` is the complete uniqueness boundary across every epoch of that -route. A disposition may record the epoch through which the event became -eligible, or that an explicit backfill/replay enrolled it, for audit and -metrics, but that attribute is never part of the uniqueness key. Reactivating a -route therefore cannot rematerialize, duplicate or reset a disposition that -already exists for that route and event. - -A route may not be "fixed" by deleting its registration or any of its epochs. -Deactivation and retirement affect only events at or after the closing -boundary. Events already eligible through any earlier epoch remain owed after -deactivation and after retirement. If an eligible route can no longer be -materialized safely, each affected event requires an explicit, authorized, -audited terminal route disposition describing why it was not -materialized and what reconciliation/backfill, if any, covers it. Each such +disposition for every route for which that event is eligible, and a durable +in-epoch contract-mismatch anomaly for every route for which it is mismatched. +Exactly one durable disposition exists for each `(event_id, route_key)`. The +normal disposition materializes a job: a materialized disposition points to +exactly one job and remains unique on `(event_id, route_key)` regardless of +whether that job is pending, running, waiting or terminal. The only alternative +is the explicit, authorized and audited terminal non-materialized disposition +described below, which points to no job (section 3.5 and invariant 8). + +Disposition uniqueness is per logical route, not per epoch or per revision: +`(event_id, route_key)` is the complete uniqueness boundary across every epoch +and every route-contract revision of that route. A disposition may record the +epoch and revision through which the event became eligible, or the revision +selected by an explicit historical enrollment, for audit and metrics, but those +attributes are never part of the uniqueness key. Reactivating a route or moving +it to another revision therefore cannot rematerialize, duplicate or reset a +disposition that already exists for that route and event. + +A route may not be "fixed" by deleting its registration or any of its epochs or +revisions. Deactivation, a revision transition and retirement affect only +events at or after the closing boundary. Events already eligible through any +earlier epoch remain owed, under that epoch's revision, after deactivation, +after a newer revision becomes active and after retirement. If an eligible +route can no longer be materialized safely, each affected event requires an +explicit, authorized, audited terminal route disposition describing why it was +not materialized and what reconciliation/backfill, if any, covers it. Each such disposition remains one durable `(event_id, route_key)` record even when an operator authorizes a bulk action, and records the actor/authority and reason. Where a staff/operator action creates that disposition, it uses ADR-0010's @@ -360,76 +389,351 @@ successful routing. It records an explicit accepted divergence for the affected domain/effect target and raises durable operator attention; an operator cannot make required work disappear merely by supplying a reason. -The engine exposes route-level backlog independently of job-level queue metrics. -For every route key it must make queryable at least its lifecycle state (active, -temporarily inactive or permanently retired), the count of events eligible -through any of its epochs that still have no disposition, the age of the oldest -such event, and whether a compatible materializer for the route kind/version is -deployed and eligible to perform the materialization. Obligations from closed -epochs and from retired routes remain in that backlog until dispositioned. A -route may not open any epoch - its first activation or a later reactivation - -until deployment evidence establishes both the event-emission floor below and at -least one compatible materializer in the intended environment. If compatible -materialization later becomes unavailable, outstanding obligations remain owed and -the unavailable-materializer and route-backlog signals raise attention, whether -the route is active, temporarily inactive or permanently retired. +The engine exposes route-level backlog independently of job-level queue +metrics, because an undispositioned route obligation may not yet have any job. +For every route key, and separately for each route-contract revision of that +route, it must make queryable at least: + +- the route's lifecycle state (active, temporarily inactive or permanently + retired) and its activation-epoch and revision history; +- per revision, the count of eligible events that still have no disposition and + the age of the oldest such event; +- per revision and per accepted payload/schema version that can currently be + emitted or that has an undispositioned eligible event, whether a compatible + materializer (section 3.9) is deployed and eligible to perform the + materialization; +- the count and oldest age of open in-epoch contract-mismatch anomalies, per + epoch and revision. + +Route-level totals may aggregate these values, but no aggregate may hide an +undispositioned backlog, an open anomaly or an unavailable compatible +materializer of an older revision behind a healthy current revision. +Obligations from closed epochs, from older revisions and from retired routes +remain in that backlog until dispositioned, each bound to the revision of the +epoch that made it eligible. + +A route may not open any epoch - its first activation, a later reactivation or +a transition to another revision - until deployment evidence establishes, for +that epoch's exact revision, the event-emission floor and producer-version +coverage below and compatible materialization for every accepted version that +can actually be emitted. If compatible materialization later becomes +unavailable for any revision or version with outstanding obligations, those +obligations remain owed and the unavailable-materializer and route-backlog +signals raise attention, whether the route is active, temporarily inactive or +permanently retired. + +Outstanding obligations are interpreted against the immutable revision of the +epoch that made each event eligible. After a newer revision becomes active, an +undispositioned event from an older epoch remains owed under its older revision +and may be materialized only by a materializer that deterministic compatibility +(section 3.9) admits for that exact revision and event version. A newer +materializer never silently reinterprets an older obligation. A crash after some consumers are materialized therefore resumes from durable route records. A worker must never mark an event fully routed merely because it processed all routes known to its own binary. During a mixed-version deployment, -if a worker encounters an eligible route whose kind/version it cannot -materialize, it leaves that route incomplete for a compatible worker rather than -silently completing the event. - -A route introduced by a later release, or reactivated after an inactive gap, -receives only events within its activation epochs. Historical events from before -its first epoch and events from an inactive inter-epoch gap are processed only -through an explicitly authorized historical backfill/replay under the same -stable `route_key` and the same `(event_id, route_key)` deduplication boundary; -backfill cannot bypass route deduplication and cannot create a second -disposition for an event that already has one for that route. Deliberately -re-executing work for an already-dispositioned event is an explicit replay or -current-state job under section 3.5, carrying replay/correlation metadata, not a -second route disposition. - -### Event-emission deployment floor - -A route consuming event kind/version `K` may open an activation epoch - on its -first activation and on every reactivation - only after deployment evidence -proves that **every running binary that can perform the relevant business -mutation, and every binary still retained as a supported production rollback -target, emits K atomically with that mutation**. - -The event-emission floor is therefore established before every epoch opens. -Opening an epoch while some serving/rollback binary can still perform the -business mutation without emitting K is prohibited. - -If production must roll back below that emission-capable floor while an epoch is -open, the route's current epoch is closed before the older binary can perform -relevant writes, or the rollback is accompanied by an explicitly authorized -current-state reconciliation/backfill that covers the precise emission gap. +if a worker encounters an eligible obligation whose revision and event version +it is not compatible with, it leaves that obligation incomplete for a +compatible worker rather than silently completing the event. + +### Route-contract revisions + +A route-contract revision is a durable, immutable record subordinate to one +`route_key`. It binds everything needed to determine and execute the +obligations of the epochs that reference it. Its **semantic signature** is the +complete set of immutable fields that affect interpretation or executor +compatibility, at minimum: + +- the `route_key`; +- the consumed event kind; +- the accepted-version set: an explicit, closed and immutable set of + payload/schema versions of the consumed event kind, held in one canonical + representation so that the order or repetition in which versions are + supplied cannot produce a different signature; +- the route-to-materializer/job contract version that determines which + materializers are compatible; +- the semantic consumer / owning-domain identity that shows the revision belongs + to the same logical route; +- the identity and version of the immutable compatibility policy where the + revision declares one; the absence of a policy is itself part of the + signature. + +Exact table, column and encoding names are implementation-specification +details. + +A revision may be prepared before any epoch references it. Once an epoch +references it or a historical enrollment has selected it, no operation may +rewrite any field of its semantic signature, and the revision is never deleted. +The same immutable revision may be referenced again by a later epoch when +rollback or recovery intentionally returns to that exact contract and the +activation gates are re-established for it. + +Two revisions of one route with the same semantic signature are equivalent, and +the engine must not hold ambiguous duplicate-equivalent revisions. Equivalence +is decided by semantic content, never by revision identifier. Revision creation +must converge atomically at the database: either a database-enforced +uniqueness constraint over the route's canonical semantic signature, or over a +deterministic canonical encoding of it, admits exactly one revision per +signature; or revision creation for a route is serialized through that route's +durable serialization mechanism with the equivalence check and the insert in the +same transaction. Concurrent requests to create equivalent revisions therefore +resolve to one canonical effective revision, and a request that does not create +it receives that canonical revision instead of a competing identity. An +application-level read-then-insert check without such a database-enforced +uniqueness or serialization guarantee does not satisfy this rule. Revisions +whose semantic signatures differ always remain distinct. + +A revision identifier is a storage/audit identity, not a compatibility key. +Materializer compatibility derives from the revision's semantic content +(section 3.9). + +Creating, selecting, withdrawing or otherwise changing the availability of a +revision for activation or historical enrollment is a durable, audited control +operation recording actor, authority and reason. Where staff/operators initiate +it, it uses ADR-0010's protected, audited staff-command seam. + +Contract evolution remains under the same `route_key` only while the route is +still the same semantic consumer and effect obligation and the existing +`(event_id, route_key)` disposition identity remains correct: + +- the same semantic consumer/effect with an evolved compatible event or + materialization contract is a new revision under the same `route_key`; +- the same consumer returning to an older compatible contract opens a later + epoch that references the existing immutable revision, after the normal + gates; +- a change under which an event already dispositioned for the route could + legitimately require a second independent disposition or effect is not a + revision: it requires a new `route_key` or a separately approved architecture + decision. + +### In-epoch contract mismatch + +Producer-version coverage (below) prohibits an event from being emitted inside +an open epoch in a version that the epoch's revision does not accept. The engine +nevertheless fails safe if that invariant is violated. + +An event is an **in-epoch contract mismatch** for a route when its captured +routing generation lies inside one of the route's epochs and its durable event +kind is that epoch revision's consumed event kind, but its durable +payload/schema version is not a member of the revision's accepted-version set, +so that it fails the revision's immutable kind/version predicate. Such an event +becomes a durable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly, or an +equivalently explicit durable routing anomaly whose exact representation is an +implementation-specification detail. The anomaly: + +- durably identifies the event, the `route_key`, the epoch, the expected + immutable revision, and the actual durable event kind and payload/schema + version that failed matching; +- is queryable, counted and age-visible, and raises durable operator attention; +- is never classified as successful routing; +- creates no route disposition and no job merely because it was detected, and + does not consume the `(event_id, route_key)` disposition uniqueness slot; +- is never reinterpreted automatically under a later revision or any "current" + contract. + +The anomaly remains open until, under explicit authorization, either: + +1. historical enrollment (below) selects a revision of the same logical route + that is validated as compatible with the event and creates the one allowed + `(event_id, route_key)` disposition; or +2. an explicitly approved reconciliation/divergence procedure records why no + route disposition will be created and what compensating state or effect + covers the event. + +Resolution is recorded as append-only, audited evidence linked to the anomaly, +including actor, authority, reason and the resulting disposition or divergence +record. Resolving an anomaly never deletes it or its detection evidence. + +Route health exposes open anomalies separately from, and alongside, the +undispositioned eligible-event backlog. An in-epoch contract mismatch is +therefore never an invisible fourth eligibility state. + +### Historical enrollment, backfill and replay + +A route introduced by a later release, reactivated after an inactive gap or +moved to another revision receives through ordinary routing only events that are +eligible through its epochs. Historical events from before its first epoch, +events from an inactive inter-epoch gap and events held as in-epoch +contract-mismatch anomalies are processed only through explicitly authorized +historical enrollment (backfill/replay) under the same stable `route_key` and +the same `(event_id, route_key)` deduplication boundary. + +Historical enrollment explicitly selects, for each event, the immutable revision +of the same logical route under which that event is interpreted; it never +implicitly uses whichever revision is current. Enrollment spanning events from +several epochs or revisions makes that selection per event and +deterministically. Enrollment may select a revision that was not active at the +event's routing generation, because enrollment is explicit and outside ordinary +epoch eligibility, but only when that revision is validated as semantically +compatible with the event. + +Before enrollment creates the first route disposition for an event, the selected +revision is validated against the event's durable facts, field by field across +its semantic signature: + +- `route_key`: the revision belongs to the logical route being enrolled; a + revision of another route is never selected; +- consumed event kind: it equals the event's durable event kind; +- accepted-version set: it contains the event's durable payload/schema version; +- semantic consumer / owning-domain identity: it is the identity of the logical + route being enrolled; +- route-to-materializer/job contract version: deterministic compatibility + (section 3.9) identifies at least one materializer compatible with that + contract version for the event's kind and version; +- compatibility policy, where the revision declares one: the policy admits the + event's kind and version. + +If any check fails, the enrollment is refused before any route disposition or +job is created. The `(event_id, route_key)` uniqueness slot remains unused, so a +later enrollment under a compatible revision remains possible. The refused +attempt and its reason are durably audited, and any existing anomaly, backlog or +attention state for the event remains visible. + +Enrollment cannot bypass route deduplication and cannot create a second +disposition for an event that already has one for that route, whichever revision +either enrollment selects. Deliberately re-executing work for an +already-dispositioned event is an explicit replay or current-state job under +section 3.5, carrying replay/correlation metadata; it is not historical +enrollment and not a second route disposition. + +A new logical route may enroll historical events that another route has already +dispositioned. The authorization of that enrollment must state explicitly +whether a second external/domain effect is intended. Where the new route can +write the same canonical external effect target as the earlier route, the +target-scoped idempotency, concurrency and applied-revision/fingerprint evidence +rules of sections 3.3 and 3.5 remain mandatory, so historical enrollment cannot +produce an unintended duplicate or regressive effect. A new route identity does +not by itself grant permission to duplicate an external effect. + +### Revision withdrawal and route retirement + +A route-contract revision that an epoch references, or that an enrollment, +disposition or anomaly uses, is immutable historical state: it is never deleted +or rewritten. + +A revision may be marked **withdrawn from future selection** by an append-only, +audited control operation. Withdrawal only prevents a new epoch from referencing +the revision and prevents its selection for new historical enrollment. It does +not remove or rewrite epoch references, does not change how any event is +interpreted, does not remove the revision from materializing or reporting +obligations already eligible through epochs that reference it, and does not hide +backlog, anomaly or attention state. + +Withdrawal is refused, or held, while it would strand work: while any +undispositioned obligation that depends on the revision would be left without a +compatible materialization path, or while any unresolved in-epoch +contract-mismatch anomaly would be left without an approved resolution path (a +selectable compatible revision or an approved reconciliation/divergence +procedure), unless an explicitly approved migration, reconciliation or +disposition plan closes or transfers that work safely. + +Permanent retirement of a logical route does not retire, withdraw or erase its +revisions. Its historical obligations and anomalies remain visible and +interpretable under their revisions until they are resolved. + +### Event-emission floor and producer-version coverage + +For each consumed event kind in an environment, the **producer emission set** +is the union of the payload/schema versions of that kind that may be emitted for +the relevant business mutation by every currently serving producer binary and +every binary retained as a supported production rollback target. + +A route may open an activation epoch - on its first activation, on every +reactivation and on every transition to another revision - only after +deployment evidence for that epoch's exact revision proves all of: + +1. **every running binary that can perform the relevant business mutation, and + every binary still retained as a supported production rollback target, emits + the revision's consumed event kind atomically with that mutation**; +2. every version that any of those binaries may emit is a member of the + revision's accepted-version set; +3. compatible materialization (section 3.9) exists for every accepted version + that can actually be emitted in that deployment state; +4. the revision is not withdrawn from future selection; and +5. the route's lifecycle and retirement state permit the epoch. + +The event-emission floor and producer-version coverage are therefore +established before every epoch opens. Opening an epoch while some +serving/rollback binary can still perform the business mutation without +emitting the consumed kind, or can emit a version outside the revision's +accepted-version set, is prohibited. A route cannot open an epoch for a revision +accepting only `v2` merely because binaries emit `v1`. + +Producer-version coverage is also a continuous deployment invariant, not only an +activation check. For every route with an open epoch consuming an event kind: + +```text +producer emission set ⊆ accepted-version set of that epoch's revision +``` + +No deployment, rollback or producer configuration change may introduce a version +outside the accepted-version set of any affected open epoch before each such +route has been transitioned safely. Where several routes consume the same event +kind with different accepted-version sets, the producer emission set may widen +only when every affected open route already accepts the widened set: if route A +accepts `{v1}` and route B accepts `{v1, v2}`, producers may not emit `v2` until +route A has also moved to a revision accepting `v2` or its epoch is closed. +Before a change makes an accepted version emittable that was not emittable +before, compatible materialization for that version is verified for every +affected open epoch's revision. + +A compatible version evolution such as `v1 -> v2` uses expand/contract: + +1. create or select an immutable revision accepting the transition set, for + example `{v1, v2}`; +2. establish compatible materialization for every accepted version that can be + emitted; +3. close the `{v1}` epoch and open the `{v1, v2}` epoch under the route's + serialized boundary, using adjacent generation boundaries - the new epoch's + activation generation equal to the closed epoch's deactivation generation - + where a zero-gap transition is intended; +4. only then allow serving or supported rollback producer state to include + `v2`; +5. once no serving or supported rollback producer can emit `v1`, a later epoch + may narrow to a `{v2}` revision after its own gates are satisfied. + +Obligations already eligible under an earlier revision remain owed and are +interpreted under that revision throughout. + +If an affected route cannot satisfy coverage, its current epoch is closed before +the incompatible producer writes occur, or a separately authorized exact-scope +reconciliation/backfill plan covers the resulting gap. If coverage is +nevertheless violated, the in-epoch contract-mismatch anomaly above is the +fail-safe: an event outside the accepted-version set is never silently dropped. + +If production must roll back below the emission-capable floor, or to a producer +set whose emission set an open epoch's revision does not accept, while that +epoch is open, the route's current epoch is closed before the older binary can +perform relevant writes, or the rollback is accompanied by an explicitly +authorized current-state reconciliation/backfill that covers the precise gap. Silent loss of business changes is not an acceptable rollback behaviour. Closing the epoch makes the rollback interval an explicit inactive gap that -remains visible in the route's epoch history. Forward recovery above the floor -may open a new epoch under the same `route_key` only after the event-emission -floor and compatible-materializer gates are re-established for the recovered -deployment. Opening that new epoch does not make gap events eligible; business -changes made during the gap that the route must still reflect are covered only -by an explicitly authorized current-state reconciliation/backfill through the -same route identity. +remains visible in the route's epoch history. Forward or rollback recovery may +open a new epoch under the same `route_key` only after these gates are +re-established for the exact revision that epoch references. Reusing an older +immutable revision is permitted only where its accepted-version set covers the +then-current producer emission set, including supported rollback targets, and +compatible materialization is available for every emittable accepted version. +Opening that new epoch does not make gap events eligible; business changes made +during the gap that the route must still reflect are covered only by an +explicitly authorized current-state reconciliation/backfill through the same +route identity. The binary emission floor does not exempt canonical writes performed outside -those binaries. An authorized data migration, administrative repair or exceptional -operator write that changes state consumed by an active route must either emit the -same required event transactionally through an approved database/application -mechanism or be followed by an explicitly authorized current-state -reconciliation/backfill covering the exact affected write set. Direct production -SQL is not an event-emission mechanism by implication. +those binaries. An authorized data migration, administrative repair or +exceptional operator write that changes state consumed by an active route must +either emit the same required event transactionally, in a version accepted by +the revision of every affected open epoch, through an approved +database/application mechanism, or be followed by an explicitly authorized +current-state reconciliation/backfill covering the exact affected write set. +Such writers are bound by producer-version coverage; they are not a bypass +around it. Direct production SQL is not an event-emission mechanism by +implication. The implementation may optimize discovery/scanning, but routing completeness -must be proven from durable event/route/disposition records. Any cursor or -high-water optimization must prove that no lower/in-flight event can commit +must be proven from durable event/route/disposition/anomaly records. Any cursor +or high-water optimization must prove that no lower/in-flight event can commit later and be skipped; absent such proof, it is not authoritative. ### 3.3 Versioned contracts, payload minimization and revision semantics @@ -447,6 +751,14 @@ Changes that require old in-flight jobs to remain executable must preserve the required compatibility window or provide an explicit migration/reconciliation plan. +For event routing, whether a route accepts an event payload/schema version is +defined only by the immutable accepted-version set of the route-contract +revision referenced by the relevant epoch or selected for historical enrollment +(section 3.2). An event kind's general compatibility policy never widens, +narrows or reinterprets that set once the revision is in use. An event version +outside it is held as an in-epoch contract mismatch or refused for enrollment +rather than guessed. + Event/job payloads default to **canonical references**, not copied domain snapshots. A payload should normally contain only: @@ -940,13 +1252,47 @@ risk class are configured for that pool. Capability declarations are runtime/configuration boundaries and scheduling primitives; they do not imply one IAM role per ordinary handler. +The registry also carries, for every route materializer a worker provides, an +explicit capability declaration that is immutable for that worker version and +covers at least: + +- the route-to-materializer/job contract versions it implements; +- the consumed event kind and the payload/schema versions it can interpret; +- any forward or backward compatibility it declares, and the + compatibility-policy identities/versions it honours; +- the domain, trust/risk-class and capability constraints already required by + this ADR. + +Whether a materializer is compatible with a route obligation is derived +deterministically from the immutable contents of the obligation's +route-contract revision - its route-to-materializer/job contract version, +consumed event kind, accepted-version set and compatibility policy - together +with those declarations and the obligation's actual event version. It is never +inferred from equality with a revision identifier, and matching job +kind/version alone does not establish compatibility when event or +materialization contracts differ. A newer materializer may handle an older +revision, and an older materializer a newer revision, only where its +declarations explicitly state that backward or forward compatibility and the +revision's contract and policy make it semantically valid. For a revision whose +accepted-version set has several versions, compatibility is decided separately +for each version. During a mixed-version deployment each worker materializes +only obligations that its own declarations make compatible and leaves every +other obligation incomplete for a compatible worker. Mutable worker registry or +deployment state may change which compatible materializers are **available**; it +never changes what a revision means or which events it made eligible. + A job kind/version cannot be removed from every compatible executor while non-terminal durable jobs of that kind/version remain. Retirement first stops new admission, then drains, migrates, reconciles or explicitly dispositions existing PENDING/RUNNING/WAITING/RETRY_SCHEDULED/RECONCILIATION_REQUIRED work under its approved domain rules. A compatible executor remains available until that obligation is closed; retirement must not orphan a retained concurrency key or -make a durable job uninterpretable. +make a durable job uninterpretable. Likewise, compatible materialization for a +route-contract revision and event version cannot be removed from every worker +while undispositioned obligations depend on it, unless those obligations are +explicitly migrated, reconciled or dispositioned under an approved rule; if it +nevertheless becomes unavailable, the obligations remain owed and visible +(section 3.2). External/domain executors use the same kind scoping: their authorization permits only the job kinds assigned to that domain executor. A dissemination executor @@ -1105,18 +1451,25 @@ job kind: - execution latency/attempt distribution; - worker liveness and last successful claim/heartbeat activity. -It also exposes route-materialization health at least by route key: +It also exposes route-materialization health at least by route key and by +route-contract revision, without relying on job metrics for obligations that +have no job yet: - lifecycle state - active, temporarily inactive or permanently retired - and - the route's activation-epoch history; -- count of events eligible through any epoch with no durable route disposition; -- age of the oldest such event; -- active routes, and temporarily inactive or permanently retired routes with - outstanding obligations, that have no compatible deployed/eligible - materializer; + the route's activation-epoch and revision history; +- per revision, the count of events eligible through any epoch with no durable + route disposition, and the age of the oldest such event; +- per revision and per accepted version that can be emitted or has outstanding + obligations, whether a compatible deployed/eligible materializer exists, for + active routes and for temporarily inactive or permanently retired routes with + outstanding obligations; +- the count and oldest age of open in-epoch contract-mismatch anomalies; - terminal route dispositions that represent accepted divergence rather than covered reconciliation/backfill. +An aggregate route view never hides an unhealthy older revision behind a +healthy current revision. + Each job kind defines bounded attempts, backoff and concurrency limits. Backoff must prevent tight retry loops; exact algorithms and jitter are implementation details. The scheduler must provide bounded fairness so one enabled kind cannot @@ -1434,21 +1787,39 @@ execution until a separate approved architecture decision says otherwise. fact, so a closed epoch's boundary never changes and the epoch never reopens. Opening, closing and permanent retirement for one route are serialized through the same durable database mechanism and resolve into one total order. - Eligibility is membership in any epoch, open or closed; closing an epoch - never rewrites eligibility, and inactive-gap events are not eligible. Any - epoch identifier is subordinate to `route_key`. Permanent retirement is + Every epoch references exactly one immutable route-contract revision, fixed + when it opens; a revision that an epoch references or an enrollment has + selected is never rewritten or deleted, and the route has no mutable + route-level contract. Eligibility is membership in any epoch, open or + closed, together with a match to that epoch's revision; closing an epoch or + activating a newer revision never rewrites eligibility or its + interpretation, and inactive-gap events are not eligible. Any epoch or + revision identifier is subordinate to `route_key`. Permanent retirement is terminal and prohibits any later epoch for that route identity. -4. Every route activation, including each reactivation epoch, occurs only after - all serving and supported rollback binaries that can perform the relevant - business mutation emit the consumed event kind/version atomically and a - compatible materializer is deployed. -5. Deactivation and retirement never erase previously eligible work: events - eligible through any epoch remain owed until materialized or given an - explicit authorized/audited route disposition. -6. A newly opened epoch, whether a first activation or a reactivation, does not - consume events from before its activation boundary, including inactive-gap - events, without an explicitly authorized historical backfill/replay under the - same `route_key` and `(event_id, route_key)` deduplication boundary. +4. Every epoch - first activation, reactivation or revision transition - opens + only after all serving and supported rollback binaries that can perform the + relevant business mutation emit the revision's consumed event kind + atomically, every version they may emit is in the revision's + accepted-version set, compatible materialization exists for every emittable + accepted version and the revision is not withdrawn. While an epoch is open, + the producer emission set remains a subset of its revision's + accepted-version set, for every route consuming that event kind; producers + widen only after every affected open route accepts the widened set. +5. Deactivation, revision transitions and retirement never erase previously + eligible work: events eligible through any epoch remain owed, under that + epoch's revision, until materialized or given an explicit authorized/audited + route disposition. In-epoch contract-mismatch anomalies remain open and + visible until resolved through compatible enrollment or an approved + reconciliation/divergence, recorded append-only. +6. A newly opened epoch, whether a first activation, a reactivation or a + revision transition, does not consume events from before its activation + boundary, including inactive-gap events, without explicitly authorized + historical enrollment (backfill/replay) under the same `route_key` and + `(event_id, route_key)` deduplication boundary. Enrollment explicitly selects + an immutable revision of the same logical route and validates the event + against every field of its semantic signature before creating a disposition; + a refused enrollment creates no disposition or job and leaves + `(event_id, route_key)` unused. 7. Job idempotency identifies a specific intended effect; a resource-only key must not suppress later revisions/schedule slots/commands. 8. Exactly one durable disposition exists for each `(event_id, route_key)`. @@ -1459,9 +1830,9 @@ execution until a separate approved architecture decision says otherwise. reason and any covering reconciliation/backfill, and shares the same uniqueness boundary so it cannot coexist with a materialized disposition for the same event/route pair. The uniqueness boundary spans every activation - epoch of the route and excludes any epoch identifier, so neither - reactivation nor backfill/replay can rematerialize an event that already has - a disposition. + epoch and every route-contract revision of the route and excludes any epoch + or revision identifier, so neither reactivation, a revision transition nor + backfill/replay can rematerialize an event that already has a disposition. 9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. A CURRENT_STATE fingerprint covers all canonical inputs determining its effect. A REVISION_BOUND effect must not regress a newer already-applied revision @@ -1527,6 +1898,36 @@ execution until a separate approved architecture decision says otherwise. can query or write it. 34. Merge, migration, deployment, handler activation, provider access and production activation remain separate gates. +35. An event of an epoch revision's consumed event kind, captured inside that + epoch, whose version is outside the revision's accepted-version set is a + durable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that is queryable, + counted, age-visible and attention-raising. Detection creates no route + disposition or job, consumes no `(event_id, route_key)` slot, is never + successful routing and is never reinterpreted under a later contract. +36. Route-contract revisions with the same canonical semantic signature + converge atomically to one canonical revision through database-enforced + uniqueness or serialization, never an application-level check-then-insert; + revisions with different signatures remain distinct, and a revision + identifier is not a compatibility key. +37. Materializer compatibility is derived deterministically from immutable + revision content and policy plus explicit materializer capability + declarations; matching job kind/version alone is insufficient, and mutable + registry state changes availability only, never historical meaning. +38. Route health exposes, independently of job metrics and per revision, the + undispositioned eligible-event count and oldest age, compatible-materializer + availability for every emittable or outstanding accepted version, and open + contract-mismatch anomaly count and oldest age; no aggregate hides an + unhealthy older revision behind a healthy current one. +39. Revision withdrawal is append-only and audited, affects only future + selection and is refused or held while it would strand undispositioned + obligations or unresolved contract-mismatch anomalies; permanent route + retirement never erases revisions, obligations or anomalies. +40. Semantic evolution under which an already-dispositioned event could + require a second independent disposition or effect uses a new `route_key`, + not a revision. A new route identity grants no permission to duplicate or + regress an external effect: its historical enrollment states whether a + second effect is intended and obeys target-scoped idempotency, concurrency + and applied-revision evidence. ## 8. Implementation impact and decomposition @@ -1535,7 +1936,10 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: 1. **Release N shared schema/engine/API in `thoth`** - additive generic async migration; - durable logical-route registration, append-only activation-epoch, - retirement and materialization model; + immutable route-contract-revision, retirement and materialization model, + including producer-version coverage evidence, in-epoch contract-mismatch + anomalies, revision-validated historical enrollment, atomic revision + equivalence and deterministic materializer compatibility; - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - effect-scoped idempotency and explicit coalescing; @@ -1640,10 +2044,13 @@ worker runtime. Legacy BE-04 schema/API remains present and unchanged, with legacy automatic job creation/activation OFF. Any new event route remains INACTIVE until every serving binary and supported -rollback target capable of the relevant business mutation emits the event -kind/version atomically. Route activation, including every later reactivation -epoch, is a separate authorized operation after deployment evidence establishes -that emission floor and a compatible materializer. +rollback target capable of the relevant business mutation emits the consumed +event kind atomically, in versions within the accepted-version set of the +route-contract revision the epoch will reference. Route activation, including +every later reactivation or revision-transition epoch, is a separate authorized +operation after deployment evidence establishes that emission floor and +producer-version coverage, and compatible materialization for every emittable +accepted version of that exact revision. Release N must be fully deployed and become a valid rollback target before any downstream consumer migration starts. @@ -1714,11 +2121,15 @@ The migration test matrix must include: - pre-v1.7 upgrade applying v1.7.0 plus additive generic schema; - full empty-database migration chain; - rolling deploy with mixed old/new application versions during Phase A; -- event-route activation, and every reactivation epoch, only after the - emission-capable deployment/rollback floor; +- event-route activation, and every reactivation or revision-transition epoch, + only after the emission-capable deployment/rollback floor and + producer-version coverage for that epoch's exact revision; +- producer deployments and rollbacks that keep the producer emission set within + the accepted-version set of every affected open epoch, including the + `{v1} -> {v1, v2} -> {v2}` expand/contract transition; - closing the current activation epoch before rollback below an event-emission - floor, and opening a new epoch only after forward recovery re-establishes the - activation gates; + floor or to an uncovered producer version, and opening a new epoch only after + recovery re-establishes the activation gates; - current-state reconciliation/backfill covering an authorized emission gap; - later API/runtime-retirement rollback window; - proof Phase-C binaries have zero runtime legacy-table access; @@ -1737,8 +2148,9 @@ Rollout should prove in order: 1. schema/event-route/job concurrency without external writes; 2. mixed-version route completeness and out-of-order transaction commits; -3. activation, deactivation, reactivation-epoch, retirement and - event-emission-floor behaviour; +3. activation, deactivation, reactivation-epoch, route-contract-revision + transition, retirement, event-emission-floor and producer-version-coverage + behaviour, including in-epoch contract-mismatch detection; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing; 6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; @@ -1761,7 +2173,8 @@ Once implemented, rollback must distinguish: - stopping new event/job creation; - closing each affected route's current activation epoch before rolling below - its event-emission floor; + its event-emission floor or to a producer set that can emit a version outside + that epoch revision's accepted-version set; - stopping worker claiming; - preserving durable event/route/job/attempt/checkpoint records; - rolling back individual handler activation; @@ -1771,14 +2184,19 @@ Once implemented, rollback must distinguish: During the expand/migrate phases, rollback to a previous Thoth image is permitted only while all schema/API that image needs remains present **and** route -activation remains compatible with that image's event-emission behaviour. - -If rollback below an already-active route's emission floor is unavoidable, the -route's current epoch is closed before old writers resume or a specifically -authorized current-state reconciliation/backfill covers the exact gap. Existing -obligations from every epoch, including the one being closed, remain durable; -closing an epoch does not delete them. Forward recovery may open a new epoch under the same `route_key` -only after the activation gates are re-established; it does not make +activation remains compatible with that image's event-emission behaviour: the +image emits each consumed event kind, and every version it may emit is in the +accepted-version set of every affected open epoch's revision. + +If rollback below an already-active route's emission floor, or to a producer +version its open epoch's revision does not accept, is unavoidable, the route's +current epoch is closed before old writers resume or a specifically authorized +current-state reconciliation/backfill covers the exact gap. Existing +obligations from every epoch, including the one being closed, remain durable +and bound to their epochs' revisions; closing an epoch does not delete them. +Recovery may open a new epoch under the same `route_key`, referencing a new +revision or reusing an older immutable revision, only after the activation +gates are re-established for that exact revision; it does not make inactive-gap events eligible. Legacy BE-04 storage contraction cannot occur until every supported rollback @@ -1813,7 +2231,8 @@ Before shared implementation can be approved, evidence must include: boundary never change, its write-once close fact cannot be rewritten and a closed epoch cannot be reopened; - historical eligibility tests proving that an event inside any epoch, open or - closed, remains eligible after that epoch closes, after later epochs open and + closed, that matches that epoch's revision remains eligible, under that + revision, after that epoch closes, after later epochs and revisions open and after retirement; - inactive-gap exclusion tests proving events captured before the first epoch or between epochs are not eligible and are processed only through explicitly @@ -1831,28 +2250,129 @@ Before shared implementation can be approved, evidence must include: ADR-0010 protected staff-command authorization/audit where staff acts; - accepted-divergence attention when a terminal disposition has no covering reconciliation/backfill; -- queryable per-route undisposed-event count/oldest-age across every epoch, - active / temporarily inactive / permanently retired state and unavailable - compatible-materializer signals; -- activation and reactivation refusal until the event-emission floor and at - least one compatible materializer are established for that epoch; +- queryable per-route and per-revision undisposed-event count/oldest-age across + every epoch and revision, active / temporarily inactive / permanently retired + state and per-revision, per-version unavailable compatible-materializer + signals, including for obligations that have no job yet; +- activation, reactivation and revision-transition refusal until the + event-emission floor, producer-version coverage and compatible + materialization for every emittable accepted version are established for that + epoch's exact revision, and refusal for a revision withdrawn from future + selection; - event-emission-floor tests proving a route cannot activate while any serving or supported rollback binary can perform the mutation without emitting its - event kind/version; + consumed event kind, or can emit a version outside the revision's + accepted-version set; - rollback-below-emission-floor tests requiring the current epoch to be closed first or explicit current-state reconciliation/backfill, and forward recovery opening a new epoch only after the activation gates are re-established; -- migration/administrative-write tests proving non-emitting canonical writes are - paired with transactional emission or exact-scope reconciliation/backfill; +- migration/administrative-write tests proving non-emitting canonical writes, + and exceptional writes that would emit a version outside an affected open + epoch's accepted-version set, are paired with transactional emission of an + accepted version or exact-scope reconciliation/backfill; - mixed worker-version routing where an older worker cannot silently complete an unknown eligible route; - out-of-order transaction commit tests proving no event can be skipped by a sequence/cursor optimization; - crash during partial fan-out with exact resume and no duplicate route record; - explicit historical backfill through the same `(event_id, route_key)` - materialization identity, including across epochs and inactive gaps, proving - that neither reactivation nor backfill/replay rematerializes an - already-dispositioned event; + materialization identity, including across epochs, revisions and inactive + gaps, proving that neither reactivation, a revision transition nor + backfill/replay rematerializes an already-dispositioned event; +- `v1` epoch -> close -> `v2` epoch under one stable `route_key`, proving that + outstanding `v1` obligations survive `v2` activation, still require a + `v1`-compatible materializer, and are refused or left unclaimed by a + `v2`-only materializer; +- the `{v1} -> {v1, v2} -> {v2}` expand/contract transition under one stable + `route_key`, proving that at every step, including supported rollback + targets, the producer emission set stays a subset of every affected open + epoch's accepted-version set; +- refusal or blocking of a producer deployment, rollback or configuration change + that would emit `v2` while only a `{v1}` epoch is open; +- cross-route fan-out tests in which several routes consume one event kind with + different accepted-version sets, proving producers cannot widen the emission + set until every affected open route accepts the widened set; +- zero-gap revision transitions using adjacent epoch boundaries where the + serialized generation mechanism permits them, with events on both sides of + the boundary interpreted under the correct revision; +- concurrent close / new-revision / open operations preserving one durable + total order and at most one open epoch; +- eligibility tests requiring both epoch-generation membership and a match to + the epoch revision's consumed event kind and accepted-version set; +- immutable closed-epoch revision bindings, and refusal of any in-place + mutation of a revision referenced by an epoch or selected by an enrollment; +- reuse of the same immutable revision by a later epoch after rollback, only + after the exact accepted-set producer and materializer gates are + re-established for it; +- multi-version accepted-set tests proving every accepted version is eligible, + materializer compatibility is decided per version, and versions outside the + closed set are never eligible; +- materializer-coverage tests proving an accepted version that becomes newly + emittable is verified as compatibly materializable for every affected open + epoch's revision before producers may emit it; +- durable in-epoch contract-mismatch detection for an event of the consumed + kind with an unaccepted version, recording the event, route, epoch, expected + revision and actual kind/version, and proving the anomaly is queryable, + counted, age-visible and attention-raising; +- proof that detecting a mismatch creates no route disposition or job, does not + consume the `(event_id, route_key)` slot, is never counted as successful + routing and is never reinterpreted under a later revision or current-contract + projection; +- mismatch resolution by compatible authorized enrollment that creates the one + allowed disposition, and by approved reconciliation/divergence that creates + none, each recorded append-only without deleting the anomaly or its + detection evidence; +- aggregate route backlog/health tests proving that neither a mismatch anomaly + nor an older revision's backlog or unavailable materializer is hidden behind a + healthy current revision; +- concurrent creation of semantically equivalent revisions converging, through + the database-enforced uniqueness or serialization property, to one canonical + revision, including concurrent requests that both pass any application-level + pre-check; +- canonical accepted-version-set tests proving that different orderings or + repetitions of the same versions produce the same semantic signature; +- distinct-signature tests proving revisions that differ in any signature field, + including the presence or absence of a compatibility policy, remain distinct; +- revision-withdrawal tests proving withdrawal is append-only and audited, + affects only future selection, leaves historical interpretation, epoch + references, backlog and anomalies unchanged, and is refused or held while it + would strand undispositioned obligations or unresolved mismatch anomalies; +- a backward-compatible materializer handling an older revision only where + explicitly declared, and a forward-compatible materializer handling a newer + revision only where explicitly declared and semantically valid; +- refusal of an incompatible materializer despite a matching job kind/version; +- proof that compatibility is derived from immutable revision content and + policy plus capability declarations rather than revision identifiers, and that + changing mutable registry state changes only availability; +- mixed materializer-version tests proving no worker silently reinterprets an + old revision; +- loss of every compatible materializer for a revision leaving its obligations + visible and raising attention; +- valid historical-enrollment revision selection creating exactly one + disposition; +- enrollment refusal before disposition creation for each signature-field + mismatch: event kind, payload/schema version, a revision of another route, + semantic consumer/owning-domain identity, an incompatible + route-to-materializer/job contract version and a compatibility-policy + rejection; +- proof that a refused enrollment creates no disposition or job, leaves + `(event_id, route_key)` unused for a later compatible enrollment, and records + a durable audit while existing attention remains visible; +- enrollment spanning several epochs and revisions selecting each event's + revision explicitly and deterministically, and undispositioned scans across + several closed and open revisions preserving each obligation's revision + binding; +- in-epoch mismatch recovery through explicit compatible revision selection; +- same-route replay creating no second disposition; +- new-route historical enrollment that states explicitly whether a second effect + is intended, and that obeys target-scoped idempotency, concurrency and + applied-revision evidence when it shares an external effect target with an + older route; +- a new route identity required where semantic evolution would require a second + independent disposition or effect for an already-dispositioned event; +- old-revision obligations and anomalies remaining visible and executable after + newer revisions and after permanent route retirement, with retirement neither + deleting nor withdrawing historical revisions; - effect-scoped idempotency tests across newer source revisions/schedule slots; - CURRENT_STATE tests that read canonical latest state and prove the effect fingerprint covers every canonical input affecting the external result before @@ -1969,26 +2489,31 @@ shared-engine tests. ## 12. Approval and authority -This ADR is **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-05 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-05 under the -repository decision process. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the material-correction rules in `docs/engineering/decisions/README.md`. It replaces the single activation interval per route with the append-only -activation-epoch model in section 3.2. +activation-epoch model in section 3.2 and binds every activation epoch to an +immutable route-contract revision, so that no mutable route-level contract can +reinterpret historical obligations. It also defines producer-version coverage, +in-epoch contract-mismatch anomalies, revision-validated historical enrollment, +atomic revision equivalence, deterministic materializer compatibility, +per-revision route health and safe revision withdrawal. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO -approval dated 2026-09-30. That approval, and every review of those earlier -versions, remains historical evidence bound to those exact earlier versions and -source heads only; it does not approve this corrected version. The exact -historical identities are recorded in the task implementation report and the -owning issue. +approval dated 2026-09-30. A later corrected version, which introduced the +activation-epoch model without immutable route-contract revisions, carried +`Status: APPROVED` with CTO approval dated 2026-10-05. Those approvals, and +every review of those earlier versions, remain historical evidence bound to +those exact earlier versions and source heads only; they do not approve this +version. The exact historical identities are recorded in the task +implementation report and the owning issue. CTO approval of this ADR adopts the architecture decision and invariants recorded here. It is architecture approval only: it does not authorize diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 247d07778..18e3708d4 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-05 +Last updated: 2026-10-06 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-05 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and the CTO approved its exact corrected content on 2026-10-05 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012 and does not approve this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, and the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; neither approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; and revision withdrawal or route retirement never strands obligations or anomalies. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 609a01427847233b7280557baa61825ac2079865 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 6 Oct 2026 19:21:25 +0100 Subject: [PATCH 30/48] THOTH-ASYNC-01-ADR-01: record route-contract correction approval Record the CTO exact candidate-content approval of 2026-10-06 for the Amendment-6/7 Route-B candidate (head b596536e, ADR-0012 blob cd897d57) as durable decision state, as the single approval-state commit Route B permits (#958). - ADR-0012: Status PROPOSED -> APPROVED, Approved by: Javi, CTO, Approval date: 2026-10-06, and the leading section 12 approval-state paragraphs; no architecture text changes (normalized comparison with the candidate blob shows no difference). - decision register: ADR-0012 row approval state only. - CHANGELOG: route-contract correction entry approval-state wording only. - implementation report: approval-state stage, no-drift proof and validation evidence. ADR-0008, ADR-0010, ADR-0013 and shared control files unchanged. Architecture approval only: no runtime, migration, provider, deployment, release or activation change. --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 781 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 17 +- .../decisions/decision-register.md | 2 +- 4 files changed, 637 insertions(+), 165 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db70c9827..e2947275d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `CTRL-ADR-PR-FIRST-01`: add a PR-first staged approval route for a material pre-authority ADR correction to the engineering-decision amendment process in [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md) (issue [966](https://github.com/thoth-pub/thoth/issues/966)). The existing pre-commit exact-final-blob approval route (now Route A) is retained unchanged. Route B lets the implementing agent push the corrected candidate to a real task branch and draft PR as `PROPOSED`, without presenting the earlier version's approver or approval date as approval of the corrected bytes; the CTO then records architecture-content approval against the exact candidate blob/head from the PR; only afterwards may exactly one bounded approval-state commit record `PROPOSED -> APPROVED` with the fresh approver/date and authorized companion metadata; the CTO records exact-final-blob approval of the final ADR blob before merge; fresh independent exact-head review binds to the final PR head; and HIGH/CRITICAL merge authorization and the guarded merge remain separate gates. The doctrine records why that approval-state commit is durable decision state rather than the lifecycle transcription `ADR-0005` prohibits. Material-correction eligibility, the stale-control list, `ADR-0013` exact-version reliance/drift rules and the repository-authority condition are unchanged. **Documentation/control only: no ADR is changed by this task, and no runtime, schema, migration, API, authorization, workflow, provider/IAM, deployment, release or production behaviour is authorized or affected.** - `CTRL-ADR-AMEND-01`: correct the engineering-decision authority and amendment process so `APPROVED` records exact CTO/decision-owner approval while repository authority additionally requires independent exact-head review and reachability from `develop`; permit a material in-place correction only for an approved ADR number that has never been repository-authoritative, under explicit CTO classification/authorization, exact-final-blob approval before commit, stale exact-version control invalidation, and fresh independent exact-head review. Factual clarifications remain separately classified and do not alter the selected decision; `ADR-0013` programme-local reliance remains exact-version-bound and fail-closed on decision drift. **Documentation/control only: no ADR is changed by this task, no runtime/schema/API/migration/provider/IAM/deployment/release/production behaviour is authorized or modified.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index a47c299a2..6b1a0c2e5 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -5,15 +5,16 @@ THOTH-ASYNC-01: the original architecture authoring, its independent-review correction rounds, the historical approval-state reconciliation, the programme-integration compatibility reconciliation, the route-disposition clarification, the Route-B material activation-epoch correction, the bounded -final-head recovery authorized by Specification Amendments 4-5 and the -`PROPOSED` Route-B candidate for the immutable route-contract-revision -correction specified by Specification Amendments 6-7. It records durable task -evidence only. Live head, CI, review, authorization and merge state is -GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. +final-head recovery authorized by Specification Amendments 4-5, and the +Route-B candidate and approval-state stages of the immutable +route-contract-revision correction specified by Specification Amendments 6-7. +It records durable task evidence only. Live head, CI, review, authorization and +merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not +copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-6/7 candidate stage as the -current stage and the earlier candidate, approval-state and recovery stages +Route-B correction stages, with the Amendment-6/7 approval-state stage as the +current stage and every earlier candidate, approval-state and recovery stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the durable control conditions. @@ -34,15 +35,17 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-6/7 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the +Head commit: the Amendment-6/7 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-6/7 candidate +`b596536e7db0d6b8ace0a42fdd26bff42cabf104`; its SHA is deliberately not +self-pinned here (section 3). That candidate is the direct child of the historical Amendment-4/5 recovery head -`9db91af7bf3d9f969556c819187420fc31d25c10`; its SHA is deliberately not -self-pinned here (section 3). The recovery head is itself the direct child of -the historical Route-B approval-state head -`c046e6bf6510f24ee1231f86293a3d499bceed7e`, which is the direct child of the +`9db91af7bf3d9f969556c819187420fc31d25c10`, which is the direct child of the +historical activation-epoch approval-state head +`c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -53,9 +56,9 @@ Final programme PR required: YES - `feature/worker -> develop` is a separate programme-level gate. Implementing model: activation-epoch candidate and approval-state stages - Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - -Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate stage - -Claude (Opus 5.5), bounded implementing agent. Earlier rounds are recorded in -Appendix A. +Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate and +approval-state stages - Claude (Opus 5.5), bounded implementing agent. Earlier +rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -119,7 +122,16 @@ decision-register blob 247d077786a9176e628baa8de64c0c3a5e9b09d3 implementation-report blob 64326e564b3f27f94aa9ced7dd7572bb30244bb8 CHANGELOG blob ff7089a4a93e6a8c2c27c75a0ebf56ce43405c27 -feature/worker at the recovery and at this candidate (unchanged): +content-approved Amendment-6/7 candidate / approval-state parent: +b596536e7db0d6b8ace0a42fdd26bff42cabf104 +tree d4d7dd359c3e20d37fcb5f664721b87cd2e480a8 +ADR-0012 blob cd897d576522a741c3dfa82f8de2f8d80d4288f6 (PROPOSED; content-approved) +decision-register blob 18e3708d4247a9732778b8e71e5b52b95e9de776 +implementation-report blob a47c299a2f9d4a8ab4938ed958050dcb531a3455 +CHANGELOG blob db70c9827cde16887c11dbb4ef19258c36251d6f + +feature/worker at the recovery, the candidate and this approval-state commit +(unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -159,15 +171,20 @@ Approved specification: and Amendments 1-7, including eight candidate-inspection clarifications: #958 comment `6021556329`; - Amendment-6/7 candidate implementation authorization: #958 comment - `6021574322`. + `6021574322`; +- CTO exact candidate-content approval of the Amendment-6/7 candidate: #958 + comment `6022418236`; +- Amendment-6/7 approval-state implementation authorization: #958 comment + `6022486352`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) passed through candidate and approval-state stages and one bounded final-head recovery stage, all now historical. Final-head review of the recovery head then -exposed a further architecture defect, specified by Amendments 6-7; the commit -that contains this report is the `PROPOSED` Route-B candidate for that further -correction. +exposed a further architecture defect, specified by Amendments 6-7. Its +`PROPOSED` Route-B candidate received CTO exact candidate-content approval; the +commit that contains this report is the one bounded Route-B approval-state +commit that records that approval. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -225,13 +242,14 @@ correction. `R-9DB-01` at specification level and accepting eight non-blocking candidate-inspection clarifications. `R-9DB-01` remained open in source at the recovery head. -- **Amendment-6/7 candidate stage (the commit that contains this report).** +- **Amendment-6/7 candidate stage (historical content-approved candidate + `b596536e7db0d6b8ace0a42fdd26bff42cabf104`).** Authorized by #958 comment `6021574322`. PR #960 was converted Ready -> Draft before any candidate source was staged. The candidate is one ordinary single-parent commit on the recovery head that changes exactly the four - authorized correction paths. ADR-0012 returns to `Status: PROPOSED` with no - current approver or approval date and implements Amendments 6-7 as one - coherent architecture: every activation epoch references exactly one + authorized correction paths. Its ADR-0012 returned to `Status: PROPOSED` + with no current approver or approval date and implements Amendments 6-7 as + one coherent architecture: every activation epoch references exactly one immutable route-contract revision with a canonical semantic signature; eligibility requires both epoch membership and a match to that revision; continuous producer-version coverage with expand/contract transitions and @@ -240,11 +258,27 @@ correction. atomic database-level revision equivalence; deterministic materializer compatibility; per-revision route health; and safe revision withdrawal and route retirement. The superseded route-level-contract wording in sections - 3.2, 3.3, 3.9, 3.15, 7, 8, 9, 10, 11 and 12 is reconciled. The - decision-register ADR-0012 row, one new changelog entry and this report - describe the corrected version as `PROPOSED`; the 2026-09-30 and 2026-10-05 - CTO approvals remain historical evidence bound to the earlier exact - versions. + 3.2, 3.3, 3.9, 3.15, 7, 8, 9, 10, 11 and 12 is reconciled. Its + decision-register ADR-0012 row, one new changelog entry and candidate-stage + report described the corrected version as `PROPOSED`; the 2026-09-30 and + 2026-10-05 CTO approvals remain historical evidence bound to the earlier + exact versions. Control independently inspected the candidate, accepted it as + conforming together with the five interpretations recorded in section 5 + (items 16, 19, 20, 22 and 25), and the CTO approved its exact content - head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, ADR-0012 blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6` - on 2026-10-06 in #958 comment + `6022418236`. +- **Amendment-6/7 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6022486352`. One ordinary + single-parent commit directly on the content-approved candidate records that + approval as durable decision state, changing only the four authorized paths: + ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and + `Approval date: 2026-10-06` and the leading section-12 approval paragraphs; + the ADR-0012 register row; the + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry; and this + report. No architecture content changes (section 9, "Amendment-6/7 + approval-state validation"). PR #960 remains a draft; this stage authorizes + no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -342,34 +376,49 @@ as well: records the candidate as `PROPOSED`. They remain historical evidence bound to their exact versions and heads. The -new candidate requires its own independent control inspection, CTO -exact-candidate content approval, approval-state commit, exact-final-blob -approval, Draft -> Ready, fresh final-head automated review, fresh independent -CRITICAL exact-head review and separate merge authorization, all of which are -later gates. +new version received its own independent control inspection and CTO exact +candidate-content approval on 2026-10-06, and the commit that contains this +report is its approval-state commit. The resulting exact final ADR-0012 blob +and head still require CTO exact-final-blob confirmation; any separately +authorized PR metadata reconciliation, Draft -> Ready, fresh final-head +automated review, adjudication of every review thread, fresh independent +CRITICAL exact-head review and separate merge authorization are later gates. ## 3. Commits -Amendment-6/7 Route-B candidate commit (the commit that contains this report): +Amendment-6/7 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose route-contract revision correction` - - direct parent: the historical Amendment-4/5 recovery head - `9db91af7bf3d9f969556c819187420fc31d25c10` - - changes only the four authorized correction paths (section 4) + `THOTH-ASYNC-01-ADR-01: record route-contract correction approval` + - direct parent: the content-approved Amendment-6/7 candidate + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` + - changes only the four authorized paths, and only to record the approval + state (section 4) Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree. They are verified against the -remote task-branch head after push and recorded in the post-publication -handoff; once published, the PR #960 head and head tree are GitHub-owned -lifecycle evidence under `ADR-0005`. The authorized topology is a non-force -fast-forward of the task branch from the recovery head to the candidate; no -merge commit, rebase, squash, amend, force update or second candidate commit -is authorized. The only further source commit the Route-B sequence -contemplates is one direct-child approval-state commit, which requires its own -later CTO content approval and separate authorization; no source commit exists -implicitly after this candidate. +here because this report is part of that tree; the ADR-0012, decision-register +and changelog blobs, which do not depend on this report, are recorded in +section 9. The commit identities are verified against the remote task-branch head after +push and recorded in the post-publication handoff; once published, the PR #960 +head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The +authorized topology is a non-force fast-forward of the task branch from the +content-approved candidate to the approval-state commit; no merge commit, +rebase, squash, amend, force update or second approval-state commit is +authorized, and no further source commit exists implicitly after it. + +Amendment-6/7 Route-B candidate commit (content-approved; historical): + +- `b596536e7db0d6b8ace0a42fdd26bff42cabf104` - + `THOTH-ASYNC-01-ADR-01: propose route-contract revision correction` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-4/5 recovery head `9db91af7bf3d9f969556c819187420fc31d25c10` + - tree `d4d7dd359c3e20d37fcb5f664721b87cd2e480a8` + - changed only the four authorized correction paths (section 4) + - published by a non-force fast-forward of `feature/async/adr-0012` from the + recovery head; the remote head, tree, parent and ADR-0012 blob matched the + locally validated object Amendment-4/5 recovery commit (historical): @@ -443,9 +492,11 @@ first-parent order, newest first: ## 4. Files changed -Amendment-6/7 candidate write budget for the commit that contains this report -(Amendment 6 G10, Amendment 7 H12, authorization #958 comment `6021574322`; -exactly four existing paths, no new file, no deletion): +Amendment-6/7 approval-state write budget for the commit that contains this +report (authorization #958 comment `6022486352`), identical to the +Amendment-6/7 candidate write budget (Amendment 6 G10, Amendment 7 H12, +authorization #958 comment `6021574322`); exactly four existing paths, no new +file, no deletion: - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -472,12 +523,63 @@ six paths at every stage): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/decisions/decision-register.md` -Authorized new-file paths: NONE at the approval-state, recovery and -Amendment-6/7 candidate stages; this report already existed. +Authorized new-file paths: NONE at the approval-state, recovery, +Amendment-6/7 candidate and Amendment-6/7 approval-state stages; this report +already existed. + +Amendment-6/7 approval-state changes in the commit that contains this report, +relative to the content-approved candidate +`b596536e7db0d6b8ace0a42fdd26bff42cabf104` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate-content approval of 2026-10-06. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-06`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + `This ADR is **APPROVED**.`, the same approver and date, and a paragraph + stating that the CTO approved this exact corrected content on 2026-10-06, + that this approval does not by itself make the ADR repository-authoritative + and that any ADR-0013 programme-local reliance on this exact version is a + separate state not effective until its own conditions are satisfied. Every + other line, including all architecture, the historical-approval paragraph, + the authority condition and the architecture-versus-implementation + separation, is byte-identical to the candidate (section 9). + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row only - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, and "its corrected content awaits CTO + approval" becomes "the CTO approved its exact corrected content on + 2026-10-06". The `Last updated: 2026-10-06` header already carries the + date of this change and is unchanged; every other row is unchanged. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the route-contract correction entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry only - "propose" + becomes "record a CTO-approved", "`PROPOSED`: the corrected content is not + approved" becomes "`APPROVED`: the CTO approved the exact corrected content + on 2026-10-06", and "proposal only" becomes "decision only". Its statements + that the earlier entries and the 2026-09-30 and 2026-10-05 approvals apply + only to historical exact versions are unchanged, and every other entry is + byte-identical. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-6/7 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit the + current stage; records the content-approved candidate, the CTO + candidate-content approval and the approval-state authorization; records + the approval-state validation and no-drift proof (section 9); and reframes + statements that would otherwise describe the candidate as the commit + containing this report. All still-true earlier evidence is preserved. + - within authorized write budget: YES -Amendment-6/7 candidate changes in the commit that contains this report, -relative to the recovery head `9db91af7bf3d9f969556c819187420fc31d25c10` -(exactly the four authorized paths): +Amendment-6/7 candidate changes in the historical content-approved candidate +`b596536e7db0d6b8ace0a42fdd26bff42cabf104`, relative to the recovery head +`9db91af7bf3d9f969556c819187420fc31d25c10` (exactly the four authorized paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement the Amendment-6/7 immutable route-contract-revision @@ -531,15 +633,16 @@ relative to the recovery head `9db91af7bf3d9f969556c819187420fc31d25c10` - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-6/7 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate the current - stage; records the recovery, the final-head review of the recovery head, - Amendments 6-7 and their review sequence as history; records the - candidate's validation (section 9); and reframes statements that would - otherwise describe the recovery commit as the commit containing this - report. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the candidate the current + stage; recorded the recovery, the final-head review of the recovery head, + Amendments 6-7 and their review sequence as history; recorded the + candidate's validation (section 9); and reframed statements that would + otherwise have described the recovery commit as the commit containing the + report. All still-true earlier evidence was preserved. - within authorized write budget: YES -Byte-identical to the recovery head through this candidate: +Byte-identical to the recovery head through the Amendment-6/7 candidate and +this approval-state commit: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -718,10 +821,13 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-6/7 candidate differs from the recovery head in exactly the four -authorized correction paths and from refreshed `feature/worker` in exactly the -six cumulative PR paths (section 9, "Amendment-6/7 candidate validation"). -Historically, the activation-epoch candidate differed from the unmodified +The Amendment-6/7 approval-state commit differs from the content-approved +candidate in exactly the four authorized paths and from refreshed +`feature/worker` in exactly the six cumulative PR paths (section 9, +"Amendment-6/7 approval-state validation"). The content-approved candidate +differed from the recovery head in exactly the four authorized correction paths +and from refreshed `feature/worker` in exactly the six cumulative PR paths +(section 9, "Amendment-6/7 candidate validation"). Historically, the activation-epoch candidate differed from the unmodified merge-tree baseline in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths; the approval-state commit differed from the content-approved candidate in exactly @@ -736,9 +842,10 @@ runtime, migration or schema path is edited at any stage. For the Route-B activation-epoch candidate stage (#958 comments `5993634460` and `5998119577`), its approval-state stage (#958 comment `5999579357`), the -Amendment-4/5 recovery stage (#958 comment `6013267369`) and the Amendment-6/7 -candidate stage (#958 comment `6021574322`). This matrix states only what had -been done when the commit that contains this report was created. +Amendment-4/5 recovery stage (#958 comment `6013267369`), the Amendment-6/7 +candidate stage (#958 comment `6021574322`) and the Amendment-6/7 +approval-state stage (#958 comment `6022486352`). This matrix states only what +had been done when the commit that contains this report was created. Activation-epoch candidate stage - historical, completed before the approval-state commit: @@ -775,7 +882,8 @@ Approval-state stage - historical, completed before the recovery commit: authorized and performed it, which triggered the configured final-head automated review of the approval-state head (section 2) -Amendment-4/5 recovery stage - historical, completed before this candidate: +Amendment-4/5 recovery stage - historical, completed before the Amendment-6/7 +candidate: - repository inspection: USED (read-only verification of the exact refs, PR #960 state, commit topology and every protected blob named by the @@ -794,8 +902,8 @@ Amendment-4/5 recovery stage - historical, completed before this candidate: PR #960 Ready, which triggered the configured final-head automated review of the recovery head (section 2) -Amendment-6/7 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-6/7 candidate stage - historical, completed before this +approval-state commit: - repository inspection: USED (read-only verification of the exact refs, PR #960 state, mergeability, the six-path cumulative footprint, the absence @@ -804,37 +912,58 @@ commit that contains this report: the authorization) - PR creation/update: USED (PR #960 Ready -> Draft, the one authorized pre-publication PR-state mutation, performed after that verification and - before any candidate source was staged; the read-back showed the PR open, - draft and unmerged at head `9db91af7bf3d9f969556c819187420fc31d25c10` with - target `feature/worker`) + before any candidate source was staged; no PR title or description change) - source edit: USED (the four authorized correction paths only) +- new file creation, file deletion/move/rename, branch creation: NOT USED +- commit: USED (the single-parent candidate commit + `b596536e7db0d6b8ace0a42fdd26bff42cabf104`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `9db91af7bf3d9f969556c819187420fc31d25c10` to the candidate; the remote head, + tree, parent and ADR-0012 blob were verified) + +Amendment-6/7 approval-state stage - done up to and including the creation of +the commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and target `feature/worker`, the + candidate's tree, single parent and ADR-0012 blob, the six-path cumulative + footprint and the nine unresolved PR #960 review threads unchanged since the + candidate push; all matched the authorization) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) -- branch creation: NOT USED (not authorized; a detached local worktree at the - recovery head was used for construction, and the existing task branch is the - fast-forward target) -- commit: USED (the single-parent candidate commit that contains this report) +- branch creation: NOT USED (not authorized; the same detached local worktree, + at the content-approved candidate, was used for construction, and the + existing task branch is the fast-forward target) +- commit: USED (the single-parent approval-state commit that contains this + report) -Authorized post-commit actions for the candidate commit - GitHub-owned +Authorized post-commit actions for the approval-state commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `9db91af7bf3d9f969556c819187420fc31d25c10` to the candidate commit; + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` to the approval-state commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012 blob, cumulative footprint and PR #960 state; + ADR-0012 blob and state, cumulative footprint, review threads and PR #960 + state; - the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-6/7 candidate +Not authorized for the implementing agent at the Amendment-6/7 approval-state stage, and separate later gates where applicable: +- CTO exact-final-blob confirmation: NOT USED (a later CTO gate; never on the + implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- approval-state commit, `APPROVED` status or any second candidate commit: - NOT USED (not authorized; later gates) +- any architecture change or second approval-state commit: NOT USED (not + authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -867,17 +996,20 @@ are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments 4-5 and Amendments 6-7, configured Codex/app review is not expected from a push -to a draft PR and is not relied upon at this candidate stage; fresh final-head -Codex/app review is expected only at a later, separately authorized -Draft -> Ready transition after the approval-state commit. The actual job -outcomes for the candidate commit exist only after its publication; they are -GitHub-owned lifecycle evidence recorded in the pull request and the -post-publication handoff, not in this file. If an automatic workflow +to a draft PR and is not relied upon at the Amendment-6/7 candidate or +approval-state stage; fresh final-head Codex/app review is expected only at a +later, separately authorized Draft -> Ready transition. The actual job outcomes +for the approval-state commit exist only after its publication; they, and the +completed automatic CI of the content-approved candidate +`b596536e7db0d6b8ace0a42fdd26bff42cabf104`, are GitHub-owned lifecycle +evidence recorded in the pull request, the post-publication handoffs and the +owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than skip, that is a HOLD condition. -Historical automatic CI for earlier PR heads followed the same -documentation-only classification, with no manual dispatch or rerun: +Historical automatic CI for earlier PR heads, as recorded by earlier stages of +this report, followed the same documentation-only classification, with no +manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -956,15 +1088,18 @@ Activation-epoch correction, approval-state and recovery stages (historical): 14. The recovery report did not self-pin the recovery commit's SHA, its tree or its own final blob (Amendment 5 F2); they were verified after push and recorded in the post-publication handoff. Section 1 records them here as - historical facts because the recovery head is this candidate's parent. + historical facts because the recovery head is the Amendment-6/7 candidate's + parent. 15. Final-head automated review outcomes are recorded here substantively (section 2, section 14); their review and comment identifiers remain GitHub-owned lifecycle evidence under `ADR-0005` and are not transcribed into source (Amendment 5 F3.5). -Amendment-6/7 candidate stage (the commit that contains this report). Items 16, -19, 20, 22 and 25 are interpretations of the approved specification recorded -for explicit control inspection: +Amendment-6/7 candidate stage (historical content-approved candidate +`b596536e7db0d6b8ace0a42fdd26bff42cabf104`). Items 16, 19, 20, 22 and 25 were +interpretations of the approved specification recorded for explicit control +inspection; control inspected and accepted all five before the CTO +candidate-content approval: 16. The `IN_EPOCH_CONTRACT_MISMATCH` predicate is keyed to events of the epoch revision's consumed event kind whose payload/schema version is outside its @@ -1017,7 +1152,7 @@ for explicit control inspection: style of section 3.8. The "route introduced by a later release" paragraph moves into the historical-enrollment subsection. 25. The decision-register header `Last updated` line advances to - `2026-10-06`, the date on which this candidate changes the ADR-0012 row. + `2026-10-06`, the date on which the candidate changed the ADR-0012 row. This keeps the active register's freshness metadata truthful, the property whose absence caused the accepted blocking final-head finding in decision 10. It is a header line in the authorized register path, not another row. @@ -1029,10 +1164,49 @@ for explicit control inspection: 27. Section 12 binds both the 2026-09-30 approval and the 2026-10-05 approval to the historical earlier versions without embedding their identities, consistent with decision 8. -28. This report does not self-pin the candidate commit's SHA, its tree or its - own final blob; it records only non-self-referential evidence, and the +28. The candidate-stage report did not self-pin the candidate commit's SHA, its + tree or its own final blob; it recorded only non-self-referential evidence + and the ADR-0012, decision-register and changelog blobs, which did not + depend on its bytes. Section 1 now records those candidate identities as + historical facts because the candidate is the approval-state parent. + +Amendment-6/7 approval-state stage (the commit that contains this report): + +29. The approval-state header uses the same field order as the earlier approved + ADR-0012 versions (`Status`, `Date`, `Approved by`, `Approval date`, + `Decision owner`), and section 12 repeats the approver and date. +30. The approval date carried by the ADR is the date of the CTO exact + candidate-content approval record, 2026-10-06, as Route B requires; it is + neither back-dated nor anticipated. +31. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by the approval statement, the + approver/date and one paragraph stating that the approval does not by + itself make the ADR repository-authoritative and that ADR-0013 + programme-local reliance on this exact version is separate and not yet + effective. The historical-approval, architecture-only, authority-condition, + partial-supersession and amendment paragraphs were already true in the + approved state (decision 9) and are unchanged. +32. The decision-register `Last updated: 2026-10-06` header already carries the + date of this approval-state change, so it is unchanged; only the ADR-0012 + row changes. +33. Only the `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry + changes, following the earlier approval-state wording pattern; every other + entry, including the historical approval entries, is byte-identical. +34. Architecture no-drift is proven by an exact normalized comparison rather + than by assertion (section 9, "Amendment-6/7 approval-state validation"). +35. This report does not self-pin the approval-state commit's SHA, its tree or + its own final blob; it records only non-self-referential evidence and the ADR-0012, decision-register and changelog blobs, which do not depend on this report's bytes. +36. This report records the CTO candidate-content approval by its #958 comment + identifier, as the approval-state authorization requires (section 2). The + Route-B rule in `docs/engineering/decisions/README.md` says the + approval-state commit must not copy content-approval identifiers into + repository files, and an earlier instance of the same conflict was resolved + by omitting the identifier. When the implementing agent raised the conflict + before committing, it was instructed to record the identifier as the + authorization requires. This is recorded as a deviation below for + independent review. List any deviation from the specification requiring authorization: @@ -1060,9 +1234,17 @@ List any deviation from the specification requiring authorization: implementation authorization exactly. - Amendment-6/7 candidate stage: NONE. No fifth path, new file, deletion, second commit, amend, rebase, squash, force push or history rewrite was - needed or performed before the commit that contains this report was - created. Decisions 16, 19, 20, 22 and 25 are recorded for control inspection - as interpretations within the authorized scope, not as deviations. + needed or performed. Decisions 16, 19, 20, 22 and 25 were recorded for + control inspection as interpretations within the authorized scope, not as + deviations, and control accepted them. +- Amendment-6/7 approval-state stage: the CTO content-approval comment + identifier is recorded in this report as the approval-state authorization + requires, which departs from the Route-B README rule against copying + content-approval identifiers into repository files and from the earlier + README-conforming resolution of the same conflict (decision 36). No fifth + path, new file, deletion, architecture change, second commit, amend, rebase, + squash, force push or history rewrite was needed or performed before the + commit that contains this report was created. ## 6. Database and migration effects @@ -1108,8 +1290,10 @@ evidence. "Approval-state validation" records the validation of the historical approval-state commit `c046e6bf6510f24ee1231f86293a3d499bceed7e`. "Recovery validation" records the validation of the historical recovery commit `9db91af7bf3d9f969556c819187420fc31d25c10`. "Amendment-6/7 candidate -validation" at the end of this section records the validation of the commit -that contains this report. +validation" records the validation of the historical content-approved +candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`. "Amendment-6/7 +approval-state validation" at the end of this section records the validation +of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -1724,14 +1908,19 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro cd897d576522a741c3dfa82f8de2f8d80d4288f6 ``` -The candidate ADR-0012, decision-register and changelog blobs are deterministic -and do not depend on this report's bytes, so they are recorded above. This -report's own final blob, the candidate tree and the candidate commit SHA depend -on this report's bytes and are not self-pinned; they are verified against the -remote head after push and recorded in the post-publication handoff. +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate report's bytes, so they were +recorded above. The candidate report's own final blob, the candidate tree and +the candidate commit SHA depended on that report's bytes and were not +self-pinned in it; they were verified against the remote head after push and +recorded in the post-publication handoff, and section 1 now records them as +historical facts. -Required topology of the candidate commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the candidate commit, checked after it was created and +before any push (results recorded in that post-publication handoff; the +published candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` has the single +parent `9db91af7bf3d9f969556c819187420fc31d25c10` and tree +`d4d7dd359c3e20d37fcb5f664721b87cd2e480a8`): ```text git rev-list --parents -n 1 HEAD @@ -1750,6 +1939,257 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-6/7 approval-state validation + +Environment: the same full local Git worktree, detached at the content-approved +candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` (tree +`d4d7dd359c3e20d37fcb5f664721b87cd2e480a8`). Immediately before construction, +read-only GitHub inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`b596536e7db0d6b8ace0a42fdd26bff42cabf104` with single parent +`9db91af7bf3d9f969556c819187420fc31d25c10`, ADR-0012 blob +`cd897d576522a741c3dfa82f8de2f8d80d4288f6` at that head, PR #960 open, draft, +unmerged and mergeable with that head and target `feature/worker`, the +cumulative PR footprint exactly the six paths, and the nine unresolved review +threads unchanged since the candidate push. The four paths were edited and +staged, and every check below ran against that exact final staged index before +the approval-state commit was created from it; the report's numeric line +counts below were filled in and the complete check set rerun with identical +results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check b596536e7db0d6b8ace0a42fdd26bff42cabf104 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths): + +```text +git diff --cached --name-only b596536e7db0d6b8ace0a42fdd26bff42cabf104 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status b596536e7db0d6b8ace0a42fdd26bff42cabf104 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat b596536e7db0d6b8ace0a42fdd26bff42cabf104 +1 1 CHANGELOG.md +623 158 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +12 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 candidate -> approval state (every changed line, diff header lines +omitted): + +```text +git diff --cached -U0 b596536e7db0d6b8ace0a42fdd26bff42cabf104 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +@@ -3 +3 @@ +-Status: PROPOSED ++Status: APPROVED +@@ -4,0 +5,2 @@ Date: 2026-09-29 ++Approved by: Javi, CTO ++Approval date: 2026-10-06 +@@ -2492 +2494 @@ shared-engine tests. +-Current decision state: **PROPOSED**. ++This ADR is **APPROVED**. +@@ -2494,3 +2496,8 @@ Current decision state: **PROPOSED**. +-The corrected content of this version has not been approved. It may carry +-`APPROVED`, an approver and an approval date only after the CTO approves this +-exact corrected content under the repository decision process. ++Approved by: Javi, CTO ++Approval date: 2026-10-06 ++ ++The CTO approved this exact corrected content on 2026-10-06 under the ++repository decision process. That approval does not by itself make this ADR ++repository-authoritative: the authority condition below still applies. Any ++ADR-0013 programme-local reliance on this exact version is a separate state ++that is not effective until its own conditions are satisfied for this version. +``` + +Architecture no-drift proof. The normalization `N` removes only the authorized +approval-state representation: the header `Status`, `Approved by` and +`Approval date` fields before the first `---` rule, and the section-12 +approval-state paragraphs between the `## 12. Approval and authority` heading +and the paragraph beginning "This version is a material architectural +correction". Everything else, including every other section-12 paragraph, is +kept: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p cd897d576522a741c3dfa82f8de2f8d80d4288f6 | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p cd897d576522a741c3dfa82f8de2f8d80d4288f6 | awk "$N" | shasum -a 256 +96e46cfe968b47cc56c05a6038a5732565c9e6cb17254971bd999cda7efddeae - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +96e46cfe968b47cc56c05a6038a5732565c9e6cb17254971bd999cda7efddeae - +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display): + +```text +diff <(git cat-file -p cd897d576522a741c3dfa82f8de2f8d80d4288f6) <(git cat-file -p cd897d576522a741c3dfa82f8de2f8d80d4288f6 | awk "$N") | sed 's/[[:space:]]*$//' +3d2 +< Status: PROPOSED +2491,2497d2489 +< +< Current decision state: **PROPOSED**. +< +< The corrected content of this version has not been approved. It may carry +< `APPROVED`, an approver and an approval date only after the CTO approves this +< exact corrected content under the repository decision process. +< + +diff <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") | sed 's/[[:space:]]*$//' +3d2 +< Status: APPROVED +5,6d3 +< Approved by: Javi, CTO +< Approval date: 2026-10-06 +2493,2504d2489 +< +< This ADR is **APPROVED**. +< +< Approved by: Javi, CTO +< Approval date: 2026-10-06 +< +< The CTO approved this exact corrected content on 2026-10-06 under the +< repository decision process. That approval does not by itself make this ADR +< repository-authoritative: the authority condition below still applies. Any +< ADR-0013 programme-local reliance on this exact version is a separate state +< that is not effective until its own conditions are satisfied for this version. +< +``` + +ADR-0012 approval state: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n 3,6p +Status: APPROVED +Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-06 +``` + +Decision-register candidate -> approval state (one line, the ADR-0012 row; +every other line byte-identical; word-level changes, condensed to one pair per +line): + +```text +git diff --cached -U0 b596536e7db0d6b8ace0a42fdd26bff42cabf104 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-06 + +diff <(git show b596536e7db0d6b8ace0a42fdd26bff42cabf104:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 b596536e7db0d6b8ace0a42fdd26bff42cabf104 -- docs/engineering/decisions/decision-register.md +[-PROPOSED-] {+APPROVED+} +[-Pending-] {+Satisfied+} +{+the CTO approved+} {+exact+} +[-awaits CTO approval-] {+on 2026-10-06+} +``` + +Changelog candidate -> approval state (one line, the +`THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry; every other line +byte-identical; word-level changes, one pair per line): + +```text +git diff --cached -U0 b596536e7db0d6b8ace0a42fdd26bff42cabf104 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show b596536e7db0d6b8ace0a42fdd26bff42cabf104:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 b596536e7db0d6b8ace0a42fdd26bff42cabf104 -- CHANGELOG.md +[-propose-] {+record+} {+CTO-approved+} +[-`PROPOSED`:-] {+`APPROVED`:+} +{+CTO approved the exact+} +[-is not approved.**-] {+on 2026-10-06.**+} +[-proposal-] {+decision+} +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) a7d95ca918f418d596432071c8ba78a33c2630a9 +decision-register.md b358acd5bbedf7482cfdc7b71866815c852773af +CHANGELOG.md e2947275d19d3abb351a98854268d7db213370d7 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +a7d95ca918f418d596432071c8ba78a33c2630a9 +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above; the ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is the +exact final blob that still requires CTO exact-final-blob confirmation. This +report's own final blob, the approval-state tree and the approval-state commit +SHA depend on this report's bytes and are not self-pinned; they are verified +against the remote head after push and recorded in the post-publication +handoff. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: b596536e7db0d6b8ace0a42fdd26bff42cabf104 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check b596536e7db0d6b8ace0a42fdd26bff42cabf104 HEAD +required: exit 0; no output + +git diff --name-only b596536e7db0d6b8ace0a42fdd26bff42cabf104 HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -1785,16 +2225,26 @@ all other architecture are byte-identical to the recovery head. The phrase "supported event kind/version contract" and every other route-level kind/version contract statement are gone; the only remaining "kind/version" phrases describe the revision predicate, recorded anomaly facts or job -kind/version retirement. The register, changelog and this report describe the -corrected version as `PROPOSED` and bind both earlier approvals to the earlier -exact versions; ADR-0012 carries no current approver or approval date. +kind/version retirement. The candidate's register, changelog and report +described the corrected version as `PROPOSED` and bound both earlier approvals +to the earlier exact versions; its ADR-0012 carried no current approver or +approval date. +Amendment-6/7 approval-state stage: compared the staged ADR-0012 with the +content-approved candidate blob `cd897d576522a741c3dfa82f8de2f8d80d4288f6` by +the exact normalized comparison in section 9 (no difference), and read the +raw delta line by line; compared the staged register and changelog with the +candidate blobs (only the ADR-0012 row and the route-contract correction entry +change); confirmed the protected decision and control blobs are unchanged; and +read this report for statements that would become false once it moves into the +approval-state commit and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, the approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e` and the recovery head -`9db91af7bf3d9f969556c819187420fc31d25c10`, are historical evidence; the live -PR #960 diff of the candidate commit exists only after its publication and is -GitHub-owned lifecycle evidence. +`9db91af7bf3d9f969556c819187420fc31d25c10` and the content-approved +Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, are +historical evidence; the live PR #960 diff of the approval-state commit exists +only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -1803,9 +2253,9 @@ documentation-only classification (`classify` and `check-changelog` pass; build, test, lint, format, migration and Docker jobs skipped). Checks: as listed in section 4.3. Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 -recovery authorization and the Amendment-6/7 candidate authorization, an -automatic CI failure, or any migration execution or Docker publication, is a -HOLD condition. +recovery authorization and the Amendment-6/7 candidate and approval-state +authorizations, an automatic CI failure, or any migration execution or Docker +publication, is a HOLD condition. ## 12. Rollout and rollback @@ -1814,12 +2264,11 @@ approved ADR-0012 version would be reachable from the programme integration branch only. It is not repository-authoritative until its authority condition, including reachability from `develop`, is met, and ADR-0013 programme-local reliance additionally requires the programme-pin conditions on #957. Merge -itself still requires, in order: independent control inspection of this exact -candidate; CTO exact-candidate content approval; exactly one direct-child -approval-state commit under its own separate authorization; CTO -exact-final-blob approval of the resulting ADR-0012 blob; separately -authorized Draft -> Ready; fresh automatic final-head CI and Codex/app review -and adjudication of every new finding; fresh independent CRITICAL exact-head +itself still requires, in order: CTO exact-final-blob confirmation of the +approval-state ADR-0012 blob at the approval-state head; any separately +authorized PR #960 metadata reconciliation; separately authorized +Draft -> Ready; fresh automatic final-head CI and configured Codex/app review; +adjudication of every review thread; fresh independent CRITICAL exact-head review; separate CTO exact-head merge authorization; then the guarded merge and programme-pin/reliance reconciliation. Activation required: NONE. Merging the eventual approved version would make no @@ -1827,18 +2276,21 @@ runtime, schema, provider or deployment change. Feature flag/configuration: NONE Migration sequence: NONE Rollback/disable procedure: before merge, stop at the current gate; no history -rewrite, force push or amend is permitted. The Amendment-6/7 authorization -permits exactly one candidate commit; if this candidate needs correction, that -authorization is exhausted and a fresh CTO specification amendment or -authorization is required (Amendment 3 C10; Amendments 6-7). +rewrite, force push or amend is permitted. The Amendment-6/7 authorizations +permitted exactly one candidate commit and exactly one approval-state commit; +if the approval-state head needs correction, those authorizations are +exhausted and a fresh CTO specification amendment or authorization is required +(Amendment 3 C10; Amendments 6-7). Monitoring required: NONE ## 13. Known limitations and deferred work -- This candidate stage does not include independent control inspection, CTO - candidate-content approval, the approval-state commit, exact-final-blob - approval, Draft -> Ready, the fresh final-head Codex/app review, the fresh - independent exact-head review or merge. +- This approval-state stage does not include CTO exact-final-blob + confirmation, PR #960 title/description reconciliation, Draft -> Ready, the + fresh final-head Codex/app review, adjudication of the review threads, the + fresh independent exact-head review or merge. The PR #960 title and + description still describe the historical approval/recovery state; their + reconciliation requires a separate authorization. - Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The stale #957 pin must be superseded using the corrected exact ADR-0012 blob and the fresh independently reviewed PR #960 head; dependent `feature/worker` @@ -1852,23 +2304,24 @@ Monitoring required: NONE commit, amend, rebase, force push or history rewrite requires a fresh CTO specification amendment/authorization. The final-head register-freshness finding was handled that way (Amendments 4-5 and one recovery commit), and - so was the final-head route-contract finding (Amendments 6-7 and this one - candidate commit). + so was the final-head route-contract finding (Amendments 6-7, one candidate + commit and this one approval-state commit). - Exact table, column, index and encoding names for route-contract revisions, anomalies and capability declarations are deliberately left to the future shared-engine implementation specification. - The projection-evidence limitation of the historical heads `227057cae8a4c10f7f7795e3afe68271e56681b9` and `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; - section 9 is exact-worktree evidence for the activation-epoch candidate, the - approval-state commit, the recovery commit and this candidate. + section 9 is exact-worktree evidence for the activation-epoch candidate, its + approval-state commit, the recovery commit, the Amendment-6/7 candidate and + this approval-state commit. ## 14. Unresolved issues - The PR #960 review threads `4146838604` and `4148985175` concern implementation-report validation evidence. Their Route-B disposition under Amendment 3 C5 was the approval-state report update, preserved and extended - by the recovery and by this candidate: section 9 records the literal + by the recovery and the Amendment-6/7 stages: section 9 records the literal validation commands and results of every source stage, the candidate ADR blobs, the six-path and four-path footprints and CI applicability, without self-referential containing-commit metadata. @@ -1893,7 +2346,8 @@ Monitoring required: NONE further thread, identified here substantively only: the route-level event kind/version contract could reinterpret historical epochs (`R-9DB-01`). It was accepted as valid and blocking and closed at specification level by - Amendments 6-7; this candidate implements that correction in source. + Amendments 6-7; the content-approved candidate implements that correction + in source and this commit records its approval. All nine PR #960 review threads remain unresolved. Adjudication of every thread belongs to the fresh independent CRITICAL exact-head review of the eventual @@ -1904,14 +2358,25 @@ final head. This report neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this candidate also implemented earlier Route-B -source stages of this task and prepared the earlier uncommitted -activation-epoch candidate referred to in Amendment 1 A10, so it is not -eligible to act as an independent reviewer of this task (Amendment 5 F8; -Amendment 6 G12; Amendment 7 H13). +work. The implementing agent of this approval-state commit also implemented +the Amendment-6/7 candidate and earlier Route-B source stages of this task and +prepared the earlier uncommitted activation-epoch candidate referred to in +Amendment 1 A10, so it is not eligible to act as an independent reviewer of +this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Suggested review focus: +- that the approval-state delta from the content-approved candidate + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` is limited to the ADR header + approval fields, the two leading section-12 paragraphs, the ADR-0012 + register row and the route-contract correction changelog entry, and that the + normalized comparison in section 9 shows no architecture drift; +- that the approval date 2026-10-06 matches the CTO candidate-content approval + record and that the final ADR-0012 blob + `a7d95ca918f418d596432071c8ba78a33c2630a9` is the blob presented for + exact-final-blob confirmation; +- the recording of the content-approval comment identifier against the + Route-B README rule (section 5, decision 36); - that every Amendment 6 G3-G9 and Amendment 7 H3-H11 requirement, and each of the eight candidate-inspection clarifications, is expressed in ADR-0012 without retaining contradictory route-level-contract wording; @@ -1925,15 +2390,15 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that section 12 carries `PROPOSED` with no current approver or approval date - and binds both earlier approvals to the earlier exact versions, and that an - approval-state delta can be limited to the status line, approver/date and the - not-yet-approved sentence; -- that the register row, the new changelog entry and this report describe the - corrected version as `PROPOSED` while every historical changelog entry is - byte-preserved; +- that section 12 carries `APPROVED` with Javi, CTO and 2026-10-06, binds both + earlier approvals to the earlier exact versions and keeps the repository + authority condition and the ADR-0013 reliance distinction; +- that the register row, the route-contract correction changelog entry and this + report describe the corrected version as `APPROVED` on 2026-10-06 while every + historical changelog entry is byte-preserved; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other - protected control files are byte-identical to the recovery head. + protected control files are byte-identical to the recovery head and the + content-approved candidate. ## Appendix A - Historical authoring and correction record diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index cd897d576..a7d95ca91 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-06 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -2489,11 +2491,16 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +This ADR is **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-06 + +The CTO approved this exact corrected content on 2026-10-06 under the +repository decision process. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 18e3708d4..b358acd5b 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-06 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, and the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; neither approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; and revision withdrawal or route retirement never strands obligations or anomalies. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and the CTO approved its exact corrected content on 2026-10-06 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, and the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; neither approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; and revision withdrawal or route retirement never strands obligations or anomalies. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 22bd94dc959f11fe49c8bef8cd2a836140aadff7 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 6 Oct 2026 19:39:12 +0100 Subject: [PATCH 31/48] THOTH-ASYNC-01-ADR-01: correct approval-state control record Report-only control-record recovery of the Amendment-6/7 approval-state report (#958). The approval-state authorization erroneously required the report to copy the GitHub identifier of the CTO content-approval record into repository source; repository-authoritative Route-B doctrine (docs/engineering/decisions/README.md) prohibits that and takes precedence. - implementation report only: remove the copied identifier, record the content approval through owning issue #958, approval date 2026-10-06, candidate head b596536e and candidate ADR-0012 blob cd897d57, replace the deviation framing with the reconciled control fact, and record the recovery and its validation. ADR-0012 (a7d95ca9), its approval state, approver and date, the decision register and CHANGELOG are unchanged. No runtime, migration, provider, deployment, release or activation change. --- ...H-ASYNC-01-ADR-01-implementation-report.md | 639 +++++++++++++----- 1 file changed, 474 insertions(+), 165 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 6b1a0c2e5..74845b91d 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -5,15 +5,16 @@ THOTH-ASYNC-01: the original architecture authoring, its independent-review correction rounds, the historical approval-state reconciliation, the programme-integration compatibility reconciliation, the route-disposition clarification, the Route-B material activation-epoch correction, the bounded -final-head recovery authorized by Specification Amendments 4-5, and the -Route-B candidate and approval-state stages of the immutable -route-contract-revision correction specified by Specification Amendments 6-7. -It records durable task evidence only. Live head, CI, review, authorization and +final-head recovery authorized by Specification Amendments 4-5, the Route-B +candidate and approval-state stages of the immutable route-contract-revision +correction specified by Specification Amendments 6-7, and the one-path +report-only control-record recovery of that approval-state report. It records +durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-6/7 approval-state stage as the +Route-B correction stages, with the report-only control-record recovery as the current stage and every earlier candidate, approval-state and recovery stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -35,17 +36,19 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-6/7 Route-B approval-state commit that contains +Head commit: the report-only control-record recovery commit that contains this report, an ordinary single-parent additive commit whose direct parent is +the historical Amendment-6/7 approval-state head +`609a01427847233b7280557baa61825ac2079865`; its SHA is deliberately not +self-pinned here (section 3). That approval-state head is the direct child of the content-approved Amendment-6/7 candidate -`b596536e7db0d6b8ace0a42fdd26bff42cabf104`; its SHA is deliberately not -self-pinned here (section 3). That candidate is the direct child of the +`b596536e7db0d6b8ace0a42fdd26bff42cabf104`, which is the direct child of the historical Amendment-4/5 recovery head `9db91af7bf3d9f969556c819187420fc31d25c10`, which is the direct child of the historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The recovery commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -56,9 +59,9 @@ Final programme PR required: YES - `feature/worker -> develop` is a separate programme-level gate. Implementing model: activation-epoch candidate and approval-state stages - Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - -Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate and -approval-state stages - Claude (Opus 5.5), bounded implementing agent. Earlier -rounds are recorded in Appendix A. +Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, +approval-state and control-record recovery stages - Claude (Opus 5.5), bounded +implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -130,8 +133,16 @@ decision-register blob 18e3708d4247a9732778b8e71e5b52b95e9de776 implementation-report blob a47c299a2f9d4a8ab4938ed958050dcb531a3455 CHANGELOG blob db70c9827cde16887c11dbb4ef19258c36251d6f -feature/worker at the recovery, the candidate and this approval-state commit -(unchanged): +historical Amendment-6/7 approval-state head / control-record recovery parent: +609a01427847233b7280557baa61825ac2079865 +tree abc024bd16ebf8198475ef196c1f829228760abe +ADR-0012 blob a7d95ca918f418d596432071c8ba78a33c2630a9 (APPROVED; unchanged by the recovery) +decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by the recovery) +CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the recovery) +implementation-report blob 6b1a0c2e5f353c6974bf011726eb7beac804a3bd (superseded by the recovery) + +feature/worker at the recovery, the candidate, the approval-state commit and +the control-record recovery (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -172,19 +183,27 @@ Approved specification: #958 comment `6021556329`; - Amendment-6/7 candidate implementation authorization: #958 comment `6021574322`; -- CTO exact candidate-content approval of the Amendment-6/7 candidate: #958 - comment `6022418236`; +- CTO exact candidate-content approval of the Amendment-6/7 candidate: + recorded on #958 on 2026-10-06 for candidate head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; - Amendment-6/7 approval-state implementation authorization: #958 comment - `6022486352`. + `6022486352`; +- report-only control-record recovery authorization: #958 comment + `6022850916`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) passed through candidate and approval-state stages and one bounded final-head recovery stage, all now historical. Final-head review of the recovery head then exposed a further architecture defect, specified by Amendments 6-7. Its -`PROPOSED` Route-B candidate received CTO exact candidate-content approval; the -commit that contains this report is the one bounded Route-B approval-state -commit that records that approval. +`PROPOSED` Route-B candidate received CTO exact candidate-content approval, and +the one bounded Route-B approval-state commit recorded that approval. The +commit that contains this report is a one-path report-only control-record +recovery that corrects how the approval-state report recorded that content +approval; it changes no ADR, decision-register or changelog byte. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -266,19 +285,40 @@ commit that records that approval. conforming together with the five interpretations recorded in section 5 (items 16, 19, 20, 22 and 25), and the CTO approved its exact content - head `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, ADR-0012 blob - `cd897d576522a741c3dfa82f8de2f8d80d4288f6` - on 2026-10-06 in #958 comment - `6022418236`. -- **Amendment-6/7 approval-state stage (the commit that contains this - report).** Authorized by #958 comment `6022486352`. One ordinary - single-parent commit directly on the content-approved candidate records that - approval as durable decision state, changing only the four authorized paths: - ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and - `Approval date: 2026-10-06` and the leading section-12 approval paragraphs; - the ADR-0012 register row; the - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry; and this - report. No architecture content changes (section 9, "Amendment-6/7 - approval-state validation"). PR #960 remains a draft; this stage authorizes - no PR-state or PR-metadata change. + `cd897d576522a741c3dfa82f8de2f8d80d4288f6` - on 2026-10-06 in a durable + record on #958. +- **Amendment-6/7 approval-state stage (historical approval-state head + `609a01427847233b7280557baa61825ac2079865`).** Authorized by #958 comment + `6022486352`. One ordinary single-parent commit directly on the + content-approved candidate recorded that approval as durable decision state, + changing only the four authorized paths: ADR-0012 `PROPOSED -> APPROVED` with + `Approved by: Javi, CTO` and `Approval date: 2026-10-06` and the leading + section-12 approval paragraphs; the ADR-0012 register row; the + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry; and the + report. No architecture content changed (section 9, "Amendment-6/7 + approval-state validation"). That authorization also contained an erroneous + instruction requiring the report to copy the GitHub identifier of the CTO + content-approval record into repository source, which the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` prohibits; the approval-state report + followed that instruction. +- **Report-only control-record recovery (the commit that contains this + report).** Authorized by #958 comment `6022850916` after control adjudication + on #958 found the approval-state architecture conforming and that one + control-record defect nonconforming. Repository-authoritative Route-B doctrine + takes precedence over the erroneous task instruction, so control required this + bounded recovery. One ordinary single-parent commit directly on the + approval-state head changes only this report: it removes the copied + content-approval identifier, records the content approval through owning + issue #958, approval date 2026-10-06, candidate head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6`, and restores conformity with + Route-B doctrine. No ADR-0012 architecture, approval state, approver, + approval date, decision-register state or changelog state changes; ADR-0012 + remains blob `a7d95ca918f418d596432071c8ba78a33c2630a9`. It is not another + candidate, not another approval-state commit and not an ADR amendment. PR + #960 remains a draft; this stage authorizes no PR-state or PR-metadata + change. Out-of-scope changes made: NONE @@ -377,37 +417,53 @@ as well: They remain historical evidence bound to their exact versions and heads. The new version received its own independent control inspection and CTO exact -candidate-content approval on 2026-10-06, and the commit that contains this -report is its approval-state commit. The resulting exact final ADR-0012 blob -and head still require CTO exact-final-blob confirmation; any separately +candidate-content approval on 2026-10-06, the historical approval-state +commit `609a01427847233b7280557baa61825ac2079865` recorded it, and the +report-only control-record recovery that contains this report leaves ADR-0012 +unchanged. The exact final ADR-0012 blob +`a7d95ca918f418d596432071c8ba78a33c2630a9` at the recovery head still requires +independent inspection of the recovery head and CTO exact-final-blob +confirmation; any separately authorized PR metadata reconciliation, Draft -> Ready, fresh final-head automated review, adjudication of every review thread, fresh independent CRITICAL exact-head review and separate merge authorization are later gates. ## 3. Commits -Amendment-6/7 Route-B approval-state commit (the commit that contains this +Report-only control-record recovery commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: record route-contract correction approval` - - direct parent: the content-approved Amendment-6/7 candidate - `b596536e7db0d6b8ace0a42fdd26bff42cabf104` - - changes only the four authorized paths, and only to record the approval - state (section 4) + `THOTH-ASYNC-01-ADR-01: correct approval-state control record` + - direct parent: the historical Amendment-6/7 approval-state head + `609a01427847233b7280557baa61825ac2079865` + - changes only this report (section 4) Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the ADR-0012, decision-register -and changelog blobs, which do not depend on this report, are recorded in -section 9. The commit identities are verified against the remote task-branch head after -push and recorded in the post-publication handoff; once published, the PR #960 -head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The -authorized topology is a non-force fast-forward of the task branch from the -content-approved candidate to the approval-state commit; no merge commit, -rebase, squash, amend, force update or second approval-state commit is +here because this report is part of that tree; every other file, including +ADR-0012, the decision register and the changelog, keeps its approval-state +blob (section 9). The commit identities are verified against the remote +task-branch head after push and recorded in the post-publication handoff; once +published, the PR #960 head and head tree are GitHub-owned lifecycle evidence +under `ADR-0005`. The authorized topology is a non-force fast-forward of the +task branch from the approval-state head to the recovery commit; no merge +commit, rebase, squash, amend, force update or second recovery commit is authorized, and no further source commit exists implicitly after it. +Amendment-6/7 Route-B approval-state commit (historical): + +- `609a01427847233b7280557baa61825ac2079865` - + `THOTH-ASYNC-01-ADR-01: record route-contract correction approval` + - ordinary single-parent additive commit; direct parent: the content-approved + Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` + - tree `abc024bd16ebf8198475ef196c1f829228760abe` + - changed only the four authorized paths, and only to record the approval + state (section 4) + - published by a non-force fast-forward of `feature/async/adr-0012` from the + candidate; the remote head, tree, parent and ADR-0012 blob matched the + locally validated object + Amendment-6/7 Route-B candidate commit (content-approved; historical): - `b596536e7db0d6b8ace0a42fdd26bff42cabf104` - @@ -492,11 +548,17 @@ first-parent order, newest first: ## 4. Files changed -Amendment-6/7 approval-state write budget for the commit that contains this -report (authorization #958 comment `6022486352`), identical to the -Amendment-6/7 candidate write budget (Amendment 6 G10, Amendment 7 H12, -authorization #958 comment `6021574322`); exactly four existing paths, no new -file, no deletion: +Report-only control-record recovery write budget for the commit that contains +this report (authorization #958 comment `6022850916`); exactly one existing +path, no new file, no deletion, and every other path byte-identical to the +approval-state head: + +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + +Historical Amendment-6/7 approval-state write budget (authorization #958 +comment `6022486352`), identical to the Amendment-6/7 candidate write budget +(Amendment 6 G10, Amendment 7 H12, authorization #958 comment `6021574322`); +exactly four existing paths, no new file, no deletion: - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -524,11 +586,39 @@ six paths at every stage): - `docs/engineering/decisions/decision-register.md` Authorized new-file paths: NONE at the approval-state, recovery, -Amendment-6/7 candidate and Amendment-6/7 approval-state stages; this report -already existed. +Amendment-6/7 candidate, Amendment-6/7 approval-state and control-record +recovery stages; this report already existed. + +Control-record recovery changes in the commit that contains this report, +relative to the approval-state head `609a01427847233b7280557baa61825ac2079865` +(exactly the one authorized path): + +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: the approval-state report copied the GitHub identifier of the CTO + content-approval record into repository source, contrary to the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md`; control adjudication on #958 + required a report-only correction. + - behavioural effect: documentation only. Removes every occurrence of that + identifier; records the content approval through owning issue #958, + approval date 2026-10-06, candidate head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6`; replaces the approval-state + deviation framing with the reconciled control fact (section 5, decisions + 36-39); removes the review-focus item that asked a reviewer to accept the + transcription; makes this recovery the current stage; and records its + validation (section 9). All other historical evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the approval-state head through the control-record recovery: +every path other than this report, including ADR-0012 +`a7d95ca918f418d596432071c8ba78a33c2630a9`, the decision register +`b358acd5bbedf7482cfdc7b71866815c852773af` and `CHANGELOG.md` +`e2947275d19d3abb351a98854268d7db213370d7`. -Amendment-6/7 approval-state changes in the commit that contains this report, -relative to the content-approved candidate +Amendment-6/7 approval-state changes in the historical approval-state commit +`609a01427847233b7280557baa61825ac2079865`, relative to the content-approved +candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` (exactly the four authorized paths, approval state only): @@ -569,12 +659,14 @@ approval state only): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-6/7 approval-state stage truthfully. - - behavioural effect: documentation only. Makes the approval-state commit the - current stage; records the content-approved candidate, the CTO - candidate-content approval and the approval-state authorization; records - the approval-state validation and no-drift proof (section 9); and reframes - statements that would otherwise describe the candidate as the commit - containing this report. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the approval-state commit the + current stage; recorded the content-approved candidate, the CTO + candidate-content approval and the approval-state authorization; recorded + the approval-state validation and no-drift proof (section 9); and reframed + statements that would otherwise have described the candidate as the commit + containing the report. It also copied the content-approval record's GitHub + identifier into the report, the control-record defect corrected by the + recovery above. All still-true earlier evidence was preserved. - within authorized write budget: YES Amendment-6/7 candidate changes in the historical content-approved candidate @@ -641,8 +733,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate report. All still-true earlier evidence was preserved. - within authorized write budget: YES -Byte-identical to the recovery head through the Amendment-6/7 candidate and -this approval-state commit: +Byte-identical to the recovery head through the Amendment-6/7 candidate, the +approval-state commit and the control-record recovery: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -821,13 +913,17 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-6/7 approval-state commit differs from the content-approved +The control-record recovery differs from the approval-state head in exactly +the one authorized report path and from refreshed `feature/worker` in exactly +the six cumulative PR paths (section 9, "Control-record recovery validation"). +The Amendment-6/7 approval-state commit differed from the content-approved candidate in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-6/7 approval-state validation"). The content-approved candidate differed from the recovery head in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths -(section 9, "Amendment-6/7 candidate validation"). Historically, the activation-epoch candidate differed from the unmodified +(section 9, "Amendment-6/7 candidate validation"). Historically, the +activation-epoch candidate differed from the unmodified merge-tree baseline in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths; the approval-state commit differed from the content-approved candidate in exactly @@ -843,9 +939,10 @@ runtime, migration or schema path is edited at any stage. For the Route-B activation-epoch candidate stage (#958 comments `5993634460` and `5998119577`), its approval-state stage (#958 comment `5999579357`), the Amendment-4/5 recovery stage (#958 comment `6013267369`), the Amendment-6/7 -candidate stage (#958 comment `6021574322`) and the Amendment-6/7 -approval-state stage (#958 comment `6022486352`). This matrix states only what -had been done when the commit that contains this report was created. +candidate stage (#958 comment `6021574322`), the Amendment-6/7 approval-state +stage (#958 comment `6022486352`) and the control-record recovery stage (#958 +comment `6022850916`). This matrix states only what had been done when the +commit that contains this report was created. Activation-epoch candidate stage - historical, completed before the approval-state commit: @@ -902,7 +999,7 @@ candidate: PR #960 Ready, which triggered the configured final-head automated review of the recovery head (section 2) -Amendment-6/7 candidate stage - historical, completed before this +Amendment-6/7 candidate stage - historical, completed before the approval-state commit: - repository inspection: USED (read-only verification of the exact refs, @@ -921,49 +1018,69 @@ approval-state commit: `9db91af7bf3d9f969556c819187420fc31d25c10` to the candidate; the remote head, tree, parent and ADR-0012 blob were verified) -Amendment-6/7 approval-state stage - done up to and including the creation of -the commit that contains this report: +Amendment-6/7 approval-state stage - historical, completed before the +control-record recovery: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and target `feature/worker`, the candidate's tree, single parent and ADR-0012 blob, the six-path cumulative - footprint and the nine unresolved PR #960 review threads unchanged since the - candidate push; all matched the authorization) + footprint and the nine unresolved PR #960 review threads; all matched the + authorization) - PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, - title or description change is authorized at this stage) + title or description change) - source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, branch creation: NOT USED +- commit: USED (the single-parent approval-state commit + `609a01427847233b7280557baa61825ac2079865`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` to the approval-state commit; the + remote head, tree, parent and ADR-0012 blob and state were verified) + +Control-record recovery stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `609a01427847233b7280557baa61825ac2079865` and target `feature/worker`, the + ADR-0012, report, decision-register and changelog blobs at that head, the + six-path cumulative footprint and the nine unresolved PR #960 review threads + unchanged since the approval-state push; all matched the authorization) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (this report only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) - branch creation: NOT USED (not authorized; the same detached local worktree, - at the content-approved candidate, was used for construction, and the - existing task branch is the fast-forward target) -- commit: USED (the single-parent approval-state commit that contains this - report) + at the approval-state head, was used for construction, and the existing task + branch is the fast-forward target) +- commit: USED (the single-parent recovery commit that contains this report) -Authorized post-commit actions for the approval-state commit - GitHub-owned +Authorized post-commit actions for the recovery commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `b596536e7db0d6b8ace0a42fdd26bff42cabf104` to the approval-state commit; + `609a01427847233b7280557baa61825ac2079865` to the recovery commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012 blob and state, cumulative footprint, review threads and PR #960 - state; + ADR-0012 blob, changed path set, cumulative footprint, review threads and + PR #960 state; - the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-6/7 approval-state +Not authorized for the implementing agent at the control-record recovery stage, and separate later gates where applicable: -- CTO exact-final-blob confirmation: NOT USED (a later CTO gate; never on the - implementing agent's authority) +- any edit to ADR-0012, the decision register, the changelog or any path other + than this report: NOT USED (not authorized) +- independent inspection of the recovery head and CTO exact-final-blob + confirmation: NOT USED (later gates; never on the implementing agent's + authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any architecture change or second approval-state commit: NOT USED (not - authorized) +- any second recovery commit or architecture change: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -996,12 +1113,13 @@ are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments 4-5 and Amendments 6-7, configured Codex/app review is not expected from a push -to a draft PR and is not relied upon at the Amendment-6/7 candidate or -approval-state stage; fresh final-head Codex/app review is expected only at a -later, separately authorized Draft -> Ready transition. The actual job outcomes -for the approval-state commit exist only after its publication; they, and the -completed automatic CI of the content-approved candidate -`b596536e7db0d6b8ace0a42fdd26bff42cabf104`, are GitHub-owned lifecycle +to a draft PR and is not relied upon at the Amendment-6/7 candidate, +approval-state or control-record recovery stage; fresh final-head Codex/app +review is expected only at a later, separately authorized Draft -> Ready +transition. The actual job outcomes for the recovery commit exist only after +its publication; they, and the completed automatic CI of the content-approved +candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and the approval-state +head `609a01427847233b7280557baa61825ac2079865`, are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoffs and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than @@ -1170,7 +1288,8 @@ candidate-content approval: depend on its bytes. Section 1 now records those candidate identities as historical facts because the candidate is the approval-state parent. -Amendment-6/7 approval-state stage (the commit that contains this report): +Amendment-6/7 approval-state stage (historical approval-state head +`609a01427847233b7280557baa61825ac2079865`): 29. The approval-state header uses the same field order as the earlier approved ADR-0012 versions (`Status`, `Date`, `Approved by`, `Approval date`, @@ -1194,19 +1313,47 @@ Amendment-6/7 approval-state stage (the commit that contains this report): entry, including the historical approval entries, is byte-identical. 34. Architecture no-drift is proven by an exact normalized comparison rather than by assertion (section 9, "Amendment-6/7 approval-state validation"). -35. This report does not self-pin the approval-state commit's SHA, its tree or - its own final blob; it records only non-self-referential evidence and the - ADR-0012, decision-register and changelog blobs, which do not depend on - this report's bytes. -36. This report records the CTO candidate-content approval by its #958 comment - identifier, as the approval-state authorization requires (section 2). The - Route-B rule in `docs/engineering/decisions/README.md` says the - approval-state commit must not copy content-approval identifiers into - repository files, and an earlier instance of the same conflict was resolved - by omitting the identifier. When the implementing agent raised the conflict - before committing, it was instructed to record the identifier as the - authorization requires. This is recorded as a deviation below for - independent review. +35. The approval-state report did not self-pin the approval-state commit's + SHA, its tree or its own final blob; it recorded only non-self-referential + evidence and the ADR-0012, decision-register and changelog blobs, which did + not depend on its bytes. Section 1 now records those approval-state + identities as historical facts because that head is the recovery parent. +36. Approval-state implementation authorization #958 comment `6022486352` + contained an erroneous instruction requiring the report to copy the GitHub + identifier of the CTO content-approval record into repository source. The + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` says the approval-state commit must + not copy content-approval, exact-final-blob approval, review or merge + identifiers into repository files; those remain GitHub-owned lifecycle + evidence under `ADR-0005`, and repository records reference the owning + issue and pull request. The implementing agent raised the conflict before + committing and, on instruction, followed the task instruction, so the + approval-state report copied the identifier. Repository doctrine takes + precedence; control adjudication on #958 found that control-record defect + nonconforming and required the report-only recovery below. + +Report-only control-record recovery (the commit that contains this report): + +37. The recovery removes every occurrence of the copied content-approval + identifier and records the content approval only through durable facts: + owning issue #958, approval date 2026-10-06, candidate head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6`. No other content-approval, + exact-final-blob approval, review or merge identifier is substituted, and + the adjudication record that required the recovery is identified only + through owning issue #958. +38. The recovery changes only this report. ADR-0012 + (`a7d95ca918f418d596432071c8ba78a33c2630a9`), its architecture, approval + state, approver and approval date, the decision register and the changelog + are byte-identical to the approval-state head; the recovery restores + conformity with Route-B doctrine and is not an ADR amendment, another + candidate or another approval-state commit. +39. Because this report now lives in the recovery commit, statements that + described the approval-state commit as the commit containing this report + are reframed as history, and the approval-state commit's identities, now + the recovery parent, are recorded in section 1 as historical facts. This + report does not self-pin the recovery commit's SHA, its tree or its own + final blob. List any deviation from the specification requiring authorization: @@ -1220,9 +1367,10 @@ List any deviation from the specification requiring authorization: `95769537cdc9871e42f9f3e5443604a96beb74da` was rebuilt from the same two authorized parents with the same ADR-0012, decision-register and changelog bytes; only the report differed, to correct that lifecycle wording. -- The approval-state authorization asked this report to record the identifier - of the CTO content-approval comment. The repository-authoritative Route-B rule - in `docs/engineering/decisions/README.md` says the approval-state commit must +- Activation-epoch approval-state stage: the approval-state authorization asked + the report to record the identifier of the CTO content-approval comment. The + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` says the approval-state commit must not copy content-approval, exact-final-blob approval, review or merge identifiers into repository files, and that repository records reference the owning issue and pull request instead. Control selected the @@ -1237,14 +1385,20 @@ List any deviation from the specification requiring authorization: needed or performed. Decisions 16, 19, 20, 22 and 25 were recorded for control inspection as interpretations within the authorized scope, not as deviations, and control accepted them. -- Amendment-6/7 approval-state stage: the CTO content-approval comment - identifier is recorded in this report as the approval-state authorization - requires, which departs from the Route-B README rule against copying - content-approval identifiers into repository files and from the earlier - README-conforming resolution of the same conflict (decision 36). No fifth - path, new file, deletion, architecture change, second commit, amend, rebase, - squash, force push or history rewrite was needed or performed before the - commit that contains this report was created. +- Amendment-6/7 approval-state stage (historical, corrected): the + approval-state report copied the CTO content-approval record's GitHub + identifier into repository source because the approval-state authorization + erroneously required it, contrary to the repository-authoritative Route-B + rule (decision 36). Control adjudication on #958 found that control-record + defect nonconforming, and the report-only recovery that contains this report + corrects it; it is not a current source deviation. No fifth path, new file, + deletion, architecture change, second commit, amend, rebase, squash, force + push or history rewrite occurred at that stage. +- Control-record recovery stage: NONE. The recovery follows its separate + authorization exactly; no second path, new file, deletion, ADR, register or + changelog change, second commit, amend, rebase, squash, force push or history + rewrite was needed or performed before the commit that contains this report + was created. ## 6. Database and migration effects @@ -1292,8 +1446,10 @@ validation" records the validation of the historical recovery commit `9db91af7bf3d9f969556c819187420fc31d25c10`. "Amendment-6/7 candidate validation" records the validation of the historical content-approved candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`. "Amendment-6/7 -approval-state validation" at the end of this section records the validation -of the commit that contains this report. +approval-state validation" records the validation of the historical +approval-state commit `609a01427847233b7280557baa61825ac2079865`. +"Control-record recovery validation" at the end of this section records the +validation of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -2161,17 +2317,21 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro a7d95ca918f418d596432071c8ba78a33c2630a9 ``` -The approval-state ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above; the ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is the -exact final blob that still requires CTO exact-final-blob confirmation. This -report's own final blob, the approval-state tree and the approval-state commit -SHA depend on this report's bytes and are not self-pinned; they are verified +The approval-state ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the approval-state report's bytes, so they +were recorded above; the ADR-0012 blob +`a7d95ca918f418d596432071c8ba78a33c2630a9` is the exact final blob that still +requires CTO exact-final-blob confirmation. The approval-state report's own +final blob, the approval-state tree and the approval-state commit SHA depended +on that report's bytes and were not self-pinned in it; they were verified against the remote head after push and recorded in the post-publication -handoff. +handoff, and section 1 now records them as historical facts. -Required topology of the approval-state commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the approval-state commit, checked after it was created +and before any push (results recorded in that post-publication handoff; the +published approval-state head `609a01427847233b7280557baa61825ac2079865` has +the single parent `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and tree +`abc024bd16ebf8198475ef196c1f829228760abe`): ```text git rev-list --parents -n 1 HEAD @@ -2190,6 +2350,132 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Control-record recovery validation + +Environment: the same full local Git worktree, detached at the historical +approval-state head `609a01427847233b7280557baa61825ac2079865` (tree +`abc024bd16ebf8198475ef196c1f829228760abe`). Immediately before construction, +read-only GitHub inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`609a01427847233b7280557baa61825ac2079865` with single parent +`b596536e7db0d6b8ace0a42fdd26bff42cabf104`, the ADR-0012, report, +decision-register and changelog blobs named by the recovery authorization at +that head, PR #960 open, draft, unmerged and mergeable with that head and +target `feature/worker`, the cumulative PR footprint exactly the six paths, and +the nine unresolved review threads unchanged since the approval-state push. +Only this report was edited and staged, and every check below ran against that +exact final staged index before the recovery commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 609a01427847233b7280557baa61825ac2079865 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Recovery delta against the approval-state head (exactly the one authorized +path): + +```text +git diff --cached --name-only 609a01427847233b7280557baa61825ac2079865 +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md + +git diff --cached --name-status 609a01427847233b7280557baa61825ac2079865 +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md + +git diff --cached --numstat 609a01427847233b7280557baa61825ac2079865 +474 165 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Removed content-approval identifier (the command is shown with the identifier +replaced by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Every other path byte-identical to the approval-state head: + +```text +git diff --cached --name-only 609a01427847233b7280557baa61825ac2079865 -- . ':!docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md' +(no output) +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED, unchanged) a7d95ca918f418d596432071c8ba78a33c2630a9 +decision-register.md (unchanged) b358acd5bbedf7482cfdc7b71866815c852773af +CHANGELOG.md (unchanged) e2947275d19d3abb351a98854268d7db213370d7 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +a7d95ca918f418d596432071c8ba78a33c2630a9 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n 3,6p +Status: APPROVED +Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-06 +``` + +This report's own final blob, the recovery tree and the recovery commit SHA +depend on this report's bytes and are not self-pinned; they are verified +against the remote head after push and recorded in the post-publication +handoff. + +Required topology of the recovery commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 609a01427847233b7280557baa61825ac2079865 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 609a01427847233b7280557baa61825ac2079865 HEAD +required: exit 0; no output + +git diff --name-only 609a01427847233b7280557baa61825ac2079865 HEAD +required: exactly this report + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -2235,15 +2521,23 @@ the exact normalized comparison in section 9 (no difference), and read the raw delta line by line; compared the staged register and changelog with the candidate blobs (only the ADR-0012 row and the route-contract correction entry change); confirmed the protected decision and control blobs are unchanged; and -read this report for statements that would become false once it moves into the +read the report for statements that would become false once it moved into the approval-state commit and reframed them. +Control-record recovery stage: confirmed that only this report is staged and +that ADR-0012, the decision register, the changelog and every protected file +keep their approval-state blobs; confirmed by a fixed-string search that the +removed content-approval identifier no longer occurs in this report; and read +the report for statements that would become false once it moves into the +recovery commit, or that treated the identifier transcription as an accepted +deviation or a review item, and reframed or removed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch -candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, the approval-state head -`c046e6bf6510f24ee1231f86293a3d499bceed7e` and the recovery head -`9db91af7bf3d9f969556c819187420fc31d25c10` and the content-approved -Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, are -historical evidence; the live PR #960 diff of the approval-state commit exists +candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head +`c046e6bf6510f24ee1231f86293a3d499bceed7e`, the recovery head +`9db91af7bf3d9f969556c819187420fc31d25c10`, the content-approved +Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and the +Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865`, +are historical evidence; the live PR #960 diff of the recovery commit exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -2253,9 +2547,10 @@ documentation-only classification (`classify` and `check-changelog` pass; build, test, lint, format, migration and Docker jobs skipped). Checks: as listed in section 4.3. Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 -recovery authorization and the Amendment-6/7 candidate and approval-state -authorizations, an automatic CI failure, or any migration execution or Docker -publication, is a HOLD condition. +recovery authorization, the Amendment-6/7 candidate and approval-state +authorizations and the control-record recovery authorization, an automatic CI +failure, or any migration execution or Docker publication, is a HOLD +condition. ## 12. Rollout and rollback @@ -2264,10 +2559,12 @@ approved ADR-0012 version would be reachable from the programme integration branch only. It is not repository-authoritative until its authority condition, including reachability from `develop`, is met, and ADR-0013 programme-local reliance additionally requires the programme-pin conditions on #957. Merge -itself still requires, in order: CTO exact-final-blob confirmation of the -approval-state ADR-0012 blob at the approval-state head; any separately -authorized PR #960 metadata reconciliation; separately authorized -Draft -> Ready; fresh automatic final-head CI and configured Codex/app review; +itself still requires, in order: independent inspection of the exact +control-record recovery head; CTO exact-final-blob confirmation of the +unchanged ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` at that +head; any separately authorized PR #960 metadata reconciliation; separately +authorized Draft -> Ready; fresh automatic final-head CI and configured +Codex/app review; adjudication of every review thread; fresh independent CRITICAL exact-head review; separate CTO exact-head merge authorization; then the guarded merge and programme-pin/reliance reconciliation. @@ -2277,18 +2574,20 @@ Feature flag/configuration: NONE Migration sequence: NONE Rollback/disable procedure: before merge, stop at the current gate; no history rewrite, force push or amend is permitted. The Amendment-6/7 authorizations -permitted exactly one candidate commit and exactly one approval-state commit; -if the approval-state head needs correction, those authorizations are -exhausted and a fresh CTO specification amendment or authorization is required -(Amendment 3 C10; Amendments 6-7). +permitted exactly one candidate commit and exactly one approval-state commit, +and the control-record recovery authorization permits exactly one report-only +recovery commit; if the recovery head needs correction, those authorizations +are exhausted and a fresh CTO authorization is required (Amendment 3 C10; +Amendments 6-7). Monitoring required: NONE ## 13. Known limitations and deferred work -- This approval-state stage does not include CTO exact-final-blob - confirmation, PR #960 title/description reconciliation, Draft -> Ready, the - fresh final-head Codex/app review, adjudication of the review threads, the - fresh independent exact-head review or merge. The PR #960 title and +- This control-record recovery stage does not include independent + inspection of the recovery head, CTO exact-final-blob confirmation, PR #960 + title/description reconciliation, Draft -> Ready, the fresh final-head + Codex/app review, adjudication of the review threads, the fresh independent + exact-head review or merge. The PR #960 title and description still describe the historical approval/recovery state; their reconciliation requires a separate authorization. - Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The @@ -2305,7 +2604,9 @@ Monitoring required: NONE specification amendment/authorization. The final-head register-freshness finding was handled that way (Amendments 4-5 and one recovery commit), and so was the final-head route-contract finding (Amendments 6-7, one candidate - commit and this one approval-state commit). + commit and one approval-state commit). The approval-state control-record + defect was likewise corrected only under a separate authorization for this + one report-only recovery commit. - Exact table, column, index and encoding names for route-contract revisions, anomalies and capability declarations are deliberately left to the future shared-engine implementation specification. @@ -2313,15 +2614,15 @@ Monitoring required: NONE `227057cae8a4c10f7f7795e3afe68271e56681b9` and `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; section 9 is exact-worktree evidence for the activation-epoch candidate, its - approval-state commit, the recovery commit, the Amendment-6/7 candidate and - this approval-state commit. + approval-state commit, the recovery commit, the Amendment-6/7 candidate, its + approval-state commit and this control-record recovery. ## 14. Unresolved issues - The PR #960 review threads `4146838604` and `4148985175` concern implementation-report validation evidence. Their Route-B disposition under Amendment 3 C5 was the approval-state report update, preserved and extended - by the recovery and the Amendment-6/7 stages: section 9 records the literal + by the later stages: section 9 records the literal validation commands and results of every source stage, the candidate ADR blobs, the six-path and four-path footprints and CI applicability, without self-referential containing-commit metadata. @@ -2347,7 +2648,8 @@ Monitoring required: NONE kind/version contract could reinterpret historical epochs (`R-9DB-01`). It was accepted as valid and blocking and closed at specification level by Amendments 6-7; the content-approved candidate implements that correction - in source and this commit records its approval. + in source and the approval-state commit + `609a01427847233b7280557baa61825ac2079865` records its approval. All nine PR #960 review threads remain unresolved. Adjudication of every thread belongs to the fresh independent CRITICAL exact-head review of the eventual @@ -2358,8 +2660,9 @@ final head. This report neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this approval-state commit also implemented -the Amendment-6/7 candidate and earlier Route-B source stages of this task and +work. The implementing agent of this control-record recovery also implemented +the Amendment-6/7 approval-state commit and candidate and earlier Route-B +source stages of this task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). @@ -2371,12 +2674,18 @@ Suggested review focus: approval fields, the two leading section-12 paragraphs, the ADR-0012 register row and the route-contract correction changelog entry, and that the normalized comparison in section 9 shows no architecture drift; -- that the approval date 2026-10-06 matches the CTO candidate-content approval - record and that the final ADR-0012 blob +- that the content approval is recorded only through owning issue #958, the + approval date 2026-10-06, candidate head + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6`, that the approval date matches + the CTO candidate-content approval record, and that the final ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is the blob presented for exact-final-blob confirmation; -- the recording of the content-approval comment identifier against the - Route-B README rule (section 5, decision 36); +- that the control-record recovery changes only this report, that ADR-0012, + the decision register and the changelog keep their approval-state blobs, and + that no GitHub content-approval, exact-final-blob approval, review or merge + identifier is copied into the report (section 5, decisions 36-39; section 9, + "Control-record recovery validation"); - that every Amendment 6 G3-G9 and Amendment 7 H3-H11 requirement, and each of the eight candidate-inspection clarifications, is expressed in ADR-0012 without retaining contradictory route-level-contract wording; From 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 7 Oct 2026 14:50:23 +0100 Subject: [PATCH 32/48] THOTH-ASYNC-01-ADR-01: make report review-thread record durable Report-only correction of the control-record recovery report (#958, PR #960) after the configured final-head automated review of the recovery head 22bd94dc. Control adjudicated its two findings on #958: the topology finding rested on a non-head object and was rejected against the authoritative PR head/parent graph (no lineage change); the transient review-thread-state finding was accepted. - implementation report only: stage-time review-thread inspections are rephrased as historical stage evidence, the live assertion about the number and resolved state of PR #960 review threads is replaced by durable wording that points to PR #960 as the authority for current thread state, the final-head review outcome is recorded substantively without review identifiers, and the correction and its validation are recorded as the current stage. ADR-0012 (a7d95ca9), its approval state, approver and date, the decision register and CHANGELOG are unchanged. No runtime, migration, provider, deployment, release or activation change. --- ...H-ASYNC-01-ADR-01-implementation-report.md | 721 ++++++++++++++---- 1 file changed, 555 insertions(+), 166 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 74845b91d..81db6d33b 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -7,17 +7,20 @@ programme-integration compatibility reconciliation, the route-disposition clarification, the Route-B material activation-epoch correction, the bounded final-head recovery authorized by Specification Amendments 4-5, the Route-B candidate and approval-state stages of the immutable route-contract-revision -correction specified by Specification Amendments 6-7, and the one-path -report-only control-record recovery of that approval-state report. It records +correction specified by Specification Amendments 6-7, the one-path +report-only control-record recovery of that approval-state report, and the +one-path report-only transient-state correction that followed the configured +final-head automated review of that recovery head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the report-only control-record recovery as the -current stage and every earlier candidate, approval-state and recovery stage -recorded as history. Appendix A preserves the earlier authoring, review, -approval-state, reconciliation and validation record. Appendix B records the +Route-B correction stages, with the report-only transient-state correction as +the current stage and every earlier candidate, approval-state, recovery and +control-record recovery stage recorded as history. Appendix A preserves the +earlier authoring, review, approval-state, reconciliation and validation +record. Appendix B records the durable control conditions. ## 1. Repository state @@ -36,11 +39,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the report-only control-record recovery commit that contains +Head commit: the report-only transient-state correction commit that contains this report, an ordinary single-parent additive commit whose direct parent is -the historical Amendment-6/7 approval-state head -`609a01427847233b7280557baa61825ac2079865`; its SHA is deliberately not -self-pinned here (section 3). That approval-state head is the direct child of +the historical report-only control-record recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`; its SHA is deliberately not +self-pinned here (section 3). That recovery head is the direct child of the +historical Amendment-6/7 approval-state head +`609a01427847233b7280557baa61825ac2079865`, which is the direct child of the content-approved Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, which is the direct child of the historical Amendment-4/5 recovery head @@ -48,7 +53,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The recovery commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The correction commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -61,7 +66,8 @@ Implementing model: activation-epoch candidate and approval-state stages - Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded -implementing agent. Earlier rounds are recorded in Appendix A. +implementing agent; transient-state correction stage - Claude (Fable 5.1), +bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -141,8 +147,16 @@ decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by th CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the recovery) implementation-report blob 6b1a0c2e5f353c6974bf011726eb7beac804a3bd (superseded by the recovery) -feature/worker at the recovery, the candidate, the approval-state commit and -the control-record recovery (unchanged): +historical control-record recovery head / transient-state correction parent: +22bd94dc959f11fe49c8bef8cd2a836140aadff7 +tree 7af3e0b8007eb6686e94043f57db63acc4edb4ab +ADR-0012 blob a7d95ca918f418d596432071c8ba78a33c2630a9 (APPROVED; unchanged by the recovery and by the correction) +decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by the recovery and by the correction) +CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the recovery and by the correction) +implementation-report blob 74845b91dede2ba066bd0720598651e539196eb1 (superseded by the correction) + +feature/worker at the recovery, the candidate, the approval-state commit, the +control-record recovery and the transient-state correction (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -192,7 +206,9 @@ Approved specification: - Amendment-6/7 approval-state implementation authorization: #958 comment `6022486352`; - report-only control-record recovery authorization: #958 comment - `6022850916`. + `6022850916`; +- final-head review report-only transient-state correction authorization: + #958 comment `6036961945`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -200,10 +216,14 @@ passed through candidate and approval-state stages and one bounded final-head recovery stage, all now historical. Final-head review of the recovery head then exposed a further architecture defect, specified by Amendments 6-7. Its `PROPOSED` Route-B candidate received CTO exact candidate-content approval, and -the one bounded Route-B approval-state commit recorded that approval. The -commit that contains this report is a one-path report-only control-record -recovery that corrects how the approval-state report recorded that content -approval; it changes no ADR, decision-register or changelog byte. +the one bounded Route-B approval-state commit recorded that approval. A +one-path report-only control-record recovery then corrected how the +approval-state report recorded that content approval. The commit that contains +this report is a second one-path report-only correction, made after the +configured final-head automated review of that recovery head: it removes +statements that treated the live number and resolved/unresolved state of +PR #960 review threads as durable repository truth; it changes no ADR, +decision-register or changelog byte. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -302,23 +322,55 @@ approval; it changes no ADR, decision-register or changelog byte. repository-authoritative Route-B rule in `docs/engineering/decisions/README.md` prohibits; the approval-state report followed that instruction. -- **Report-only control-record recovery (the commit that contains this - report).** Authorized by #958 comment `6022850916` after control adjudication - on #958 found the approval-state architecture conforming and that one - control-record defect nonconforming. Repository-authoritative Route-B doctrine - takes precedence over the erroneous task instruction, so control required this - bounded recovery. One ordinary single-parent commit directly on the - approval-state head changes only this report: it removes the copied - content-approval identifier, records the content approval through owning - issue #958, approval date 2026-10-06, candidate head - `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob - `cd897d576522a741c3dfa82f8de2f8d80d4288f6`, and restores conformity with - Route-B doctrine. No ADR-0012 architecture, approval state, approver, - approval date, decision-register state or changelog state changes; ADR-0012 - remains blob `a7d95ca918f418d596432071c8ba78a33c2630a9`. It is not another - candidate, not another approval-state commit and not an ADR amendment. PR - #960 remains a draft; this stage authorizes no PR-state or PR-metadata - change. +- **Report-only control-record recovery (historical recovery head + `22bd94dc959f11fe49c8bef8cd2a836140aadff7`).** Authorized by #958 comment + `6022850916` after control adjudication on #958 found the approval-state + architecture conforming and that one control-record defect nonconforming. + Repository-authoritative Route-B doctrine takes precedence over the erroneous + task instruction, so control required that bounded recovery. One ordinary + single-parent commit directly on the approval-state head changed only this + report: it removed the copied content-approval identifier, recorded the + content approval through owning issue #958, approval date 2026-10-06, + candidate head `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate + ADR-0012 blob `cd897d576522a741c3dfa82f8de2f8d80d4288f6`, and restored + conformity with Route-B doctrine. No ADR-0012 architecture, approval state, + approver, approval date, decision-register state or changelog state changed; + ADR-0012 remained blob `a7d95ca918f418d596432071c8ba78a33c2630a9`. It was + not another candidate, not another approval-state commit and not an ADR + amendment, and that stage authorized no PR-state or PR-metadata change. +- **Final-head review of the recovery head and report-only transient-state + correction (the commit that contains this report).** Authorized by #958 + comment `6036961945`. Control separately marked PR #960 Ready at the + recovery head, which triggered the configured final-head automated review of + `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, and returned PR #960 to Draft + before any correction source was staged. That review raised two findings, + both adjudicated by control on #958 (section 14). The first asserted that + the recovery head's lineage matched a cited object whose sole parent was the + refreshed `feature/worker` base and which changed all six PR paths; that + object is not the PR #960 head, and its premise did not match the + authoritative GitHub PR head/parent graph, under which the recovery head's + single parent is the approval-state head + `609a01427847233b7280557baa61825ac2079865` and its exact delta from that + parent is this report alone. Control rejected it as an invalid premise and + non-blocking; no source or lineage correction was required and the cited + object is not adopted here. The second identified that the committed recovery + report treated the current number and resolved/unresolved state of PR #960 + review threads as durable repository truth, contrary to + `docs/engineering/AGENTS.md`, under which committed files record durable + repository state while GitHub records transient workflow state. Control + accepted it as a valid blocking control-record defect. One ordinary + single-parent commit directly on the recovery head changes only this report: + stage-time review-thread inspections are rephrased as historical stage + evidence, the current-state assertion in section 14 is replaced by durable + wording that points to PR #960 for live thread state, the final-head review + outcome is recorded substantively without its review identifiers, and this + correction is recorded as the current stage. No ADR-0012 architecture, + approval state, approver, approval date, decision-register state or + changelog state changes; ADR-0012 remains blob + `a7d95ca918f418d596432071c8ba78a33c2630a9`, so the existing CTO + exact-final-blob approval of that unchanged blob remains valid. It is not + another candidate, not another approval-state commit and not an ADR + amendment. This stage authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -418,38 +470,54 @@ as well: They remain historical evidence bound to their exact versions and heads. The new version received its own independent control inspection and CTO exact candidate-content approval on 2026-10-06, the historical approval-state -commit `609a01427847233b7280557baa61825ac2079865` recorded it, and the -report-only control-record recovery that contains this report leaves ADR-0012 -unchanged. The exact final ADR-0012 blob -`a7d95ca918f418d596432071c8ba78a33c2630a9` at the recovery head still requires -independent inspection of the recovery head and CTO exact-final-blob -confirmation; any separately -authorized PR metadata reconciliation, Draft -> Ready, fresh final-head -automated review, adjudication of every review thread, fresh independent -CRITICAL exact-head review and separate merge authorization are later gates. +commit `609a01427847233b7280557baa61825ac2079865` recorded it, and neither +the historical report-only control-record recovery +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` nor the report-only +transient-state correction that contains this report changes ADR-0012. The +exact final ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is +unchanged, so the existing CTO exact-final-blob approval of that blob remains +valid; the correction head still requires independent inspection, and any +separately authorized PR metadata reconciliation, Draft -> Ready, fresh +final-head automated review, adjudication of every historical and current +review thread, fresh independent CRITICAL exact-head review and separate merge +authorization are later gates. ## 3. Commits -Report-only control-record recovery commit (the commit that contains this +Report-only transient-state correction commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: correct approval-state control record` - - direct parent: the historical Amendment-6/7 approval-state head - `609a01427847233b7280557baa61825ac2079865` + `THOTH-ASYNC-01-ADR-01: make report review-thread record durable` + - direct parent: the historical report-only control-record recovery head + `22bd94dc959f11fe49c8bef8cd2a836140aadff7` - changes only this report (section 4) Its SHA, its tree and this report's final blob are deliberately not embedded here because this report is part of that tree; every other file, including -ADR-0012, the decision register and the changelog, keeps its approval-state -blob (section 9). The commit identities are verified against the remote -task-branch head after push and recorded in the post-publication handoff; once -published, the PR #960 head and head tree are GitHub-owned lifecycle evidence -under `ADR-0005`. The authorized topology is a non-force fast-forward of the -task branch from the approval-state head to the recovery commit; no merge -commit, rebase, squash, amend, force update or second recovery commit is -authorized, and no further source commit exists implicitly after it. +ADR-0012, the decision register and the changelog, keeps its recovery-head +blob, which is its approval-state blob (section 9). The commit identities are +verified against the remote task-branch head after push and recorded in the +post-publication handoff; once published, the PR #960 head and head tree are +GitHub-owned lifecycle evidence under `ADR-0005`. The authorized topology is a +non-force fast-forward of the task branch from the recovery head to the +correction commit; no merge commit, rebase, squash, amend, force update or +second correction commit is authorized, and no further source commit exists +implicitly after it. + +Report-only control-record recovery commit (historical): + +- `22bd94dc959f11fe49c8bef8cd2a836140aadff7` - + `THOTH-ASYNC-01-ADR-01: correct approval-state control record` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-6/7 approval-state head + `609a01427847233b7280557baa61825ac2079865` + - tree `7af3e0b8007eb6686e94043f57db63acc4edb4ab` + - changed only this report (section 4) + - published by a non-force fast-forward of `feature/async/adr-0012` from the + approval-state head; the remote head, tree, parent and ADR-0012 blob + matched the locally validated object Amendment-6/7 Route-B approval-state commit (historical): @@ -548,13 +616,17 @@ first-parent order, newest first: ## 4. Files changed -Report-only control-record recovery write budget for the commit that contains -this report (authorization #958 comment `6022850916`); exactly one existing -path, no new file, no deletion, and every other path byte-identical to the -approval-state head: +Report-only transient-state correction write budget for the commit that +contains this report (authorization #958 comment `6036961945`); exactly one +existing path, no new file, no deletion, and every other path byte-identical +to the recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7`: - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +Historical report-only control-record recovery write budget (authorization +#958 comment `6022850916`): the same single existing path, no new file, no +deletion, and every other path byte-identical to the approval-state head. + Historical Amendment-6/7 approval-state write budget (authorization #958 comment `6022486352`), identical to the Amendment-6/7 candidate write budget (Amendment 6 G10, Amendment 7 H12, authorization #958 comment `6021574322`); @@ -586,13 +658,42 @@ six paths at every stage): - `docs/engineering/decisions/decision-register.md` Authorized new-file paths: NONE at the approval-state, recovery, -Amendment-6/7 candidate, Amendment-6/7 approval-state and control-record -recovery stages; this report already existed. +Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record +recovery and transient-state correction stages; this report already existed. -Control-record recovery changes in the commit that contains this report, -relative to the approval-state head `609a01427847233b7280557baa61825ac2079865` +Transient-state correction changes in the commit that contains this report, +relative to the recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7` (exactly the one authorized path): +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: the configured final-head automated review of the recovery head + found that the recovery report treated the current number and + resolved/unresolved state of PR #960 review threads as durable repository + truth; control adjudication on #958 accepted that finding as a blocking + control-record defect and required a report-only correction. + - behavioural effect: documentation only. Rephrases every stage-time + review-thread inspection as historical stage evidence; replaces the + current-state assertion in section 14 with durable wording that points to + PR #960 for live thread state; records the final-head review of the + recovery head substantively, without its review identifiers, and the + rejection of its topology premise (section 5, decisions 40-43); makes this + correction the current stage; reframes statements that would otherwise + describe the recovery commit as the commit containing this report; and + records the correction's validation (section 9). All other historical + evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the recovery head through the transient-state correction: +every path other than this report, including ADR-0012 +`a7d95ca918f418d596432071c8ba78a33c2630a9`, the decision register +`b358acd5bbedf7482cfdc7b71866815c852773af` and `CHANGELOG.md` +`e2947275d19d3abb351a98854268d7db213370d7`. + +Historical control-record recovery changes in the recovery commit +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`, relative to the approval-state +head `609a01427847233b7280557baa61825ac2079865` (exactly the one authorized +path): + - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: the approval-state report copied the GitHub identifier of the CTO content-approval record into repository source, contrary to the @@ -606,11 +707,13 @@ relative to the approval-state head `609a01427847233b7280557baa61825ac2079865` `cd897d576522a741c3dfa82f8de2f8d80d4288f6`; replaces the approval-state deviation framing with the reconciled control fact (section 5, decisions 36-39); removes the review-focus item that asked a reviewer to accept the - transcription; makes this recovery the current stage; and records its - validation (section 9). All other historical evidence is preserved. + transcription; made the recovery the then-current stage of this report; + and recorded its validation (section 9). All other historical evidence + was preserved. - within authorized write budget: YES -Byte-identical to the approval-state head through the control-record recovery: +Byte-identical to the approval-state head through the control-record recovery +and the transient-state correction: every path other than this report, including ADR-0012 `a7d95ca918f418d596432071c8ba78a33c2630a9`, the decision register `b358acd5bbedf7482cfdc7b71866815c852773af` and `CHANGELOG.md` @@ -734,7 +837,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate - within authorized write budget: YES Byte-identical to the recovery head through the Amendment-6/7 candidate, the -approval-state commit and the control-record recovery: +approval-state commit, the control-record recovery and the transient-state +correction: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -913,9 +1017,13 @@ Files deleted, moved or renamed: NONE PASS -The control-record recovery differs from the approval-state head in exactly -the one authorized report path and from refreshed `feature/worker` in exactly -the six cumulative PR paths (section 9, "Control-record recovery validation"). +The transient-state correction differs from the recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` in exactly the one authorized +report path and from refreshed `feature/worker` in exactly the six cumulative +PR paths (section 9, "Transient-state correction validation"). The +control-record recovery differed from the approval-state head in exactly the +one authorized report path and from refreshed `feature/worker` in exactly the +six cumulative PR paths (section 9, "Control-record recovery validation"). The Amendment-6/7 approval-state commit differed from the content-approved candidate in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, @@ -940,9 +1048,10 @@ For the Route-B activation-epoch candidate stage (#958 comments `5993634460` and `5998119577`), its approval-state stage (#958 comment `5999579357`), the Amendment-4/5 recovery stage (#958 comment `6013267369`), the Amendment-6/7 candidate stage (#958 comment `6021574322`), the Amendment-6/7 approval-state -stage (#958 comment `6022486352`) and the control-record recovery stage (#958 -comment `6022850916`). This matrix states only what had been done when the -commit that contains this report was created. +stage (#958 comment `6022486352`), the control-record recovery stage (#958 +comment `6022850916`) and the transient-state correction stage (#958 comment +`6036961945`). This matrix states only what had been done when the commit that +contains this report was created. Activation-epoch candidate stage - historical, completed before the approval-state commit: @@ -1005,8 +1114,9 @@ approval-state commit: - repository inspection: USED (read-only verification of the exact refs, PR #960 state, mergeability, the six-path cumulative footprint, the absence of any source commit after the recovery head, the ADR-0012 blob at the - recovery head and the nine unresolved PR #960 review threads; all matched - the authorization) + recovery head and, as stage-time evidence, the PR #960 review-thread state + in GitHub; all matched the authorization; current review-thread state + remains GitHub-owned) - PR creation/update: USED (PR #960 Ready -> Draft, the one authorized pre-publication PR-state mutation, performed after that verification and before any candidate source was staged; no PR title or description change) @@ -1025,8 +1135,9 @@ control-record recovery: PR #960 open, draft, unmerged and mergeable with head `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and target `feature/worker`, the candidate's tree, single parent and ADR-0012 blob, the six-path cumulative - footprint and the nine unresolved PR #960 review threads; all matched the - authorization) + footprint and, as stage-time evidence, the PR #960 review-thread state in + GitHub; all matched the authorization; current review-thread state remains + GitHub-owned) - PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, title or description change) - source edit: USED (the four authorized paths, approval state only) @@ -1037,50 +1148,82 @@ control-record recovery: `b596536e7db0d6b8ace0a42fdd26bff42cabf104` to the approval-state commit; the remote head, tree, parent and ADR-0012 blob and state were verified) -Control-record recovery stage - done up to and including the creation of the -commit that contains this report: +Control-record recovery stage - historical, completed before the +transient-state correction: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head `609a01427847233b7280557baa61825ac2079865` and target `feature/worker`, the ADR-0012, report, decision-register and changelog blobs at that head, the - six-path cumulative footprint and the nine unresolved PR #960 review threads - unchanged since the approval-state push; all matched the authorization) + six-path cumulative footprint and, as stage-time evidence, the PR #960 + review-thread state in GitHub; all matched the authorization; current + review-thread state remains GitHub-owned) - PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, - title or description change is authorized at this stage) + title or description change was authorized at that stage) - source edit: USED (this report only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) - branch creation: NOT USED (not authorized; the same detached local worktree, at the approval-state head, was used for construction, and the existing task - branch is the fast-forward target) -- commit: USED (the single-parent recovery commit that contains this report) + branch was the fast-forward target) +- commit: USED (the single-parent recovery commit + `22bd94dc959f11fe49c8bef8cd2a836140aadff7`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `609a01427847233b7280557baa61825ac2079865` to the recovery commit; the + remote head, tree, parent and protected blobs were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the recovery head, and returned it to Draft before the + transient-state correction (section 2) + +Transient-state correction stage - done up to and including the creation of +the commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `22bd94dc959f11fe49c8bef8cd2a836140aadff7` and target `feature/worker`, the + recovery head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the six-path cumulative + footprint and, as stage-time evidence, the PR #960 review-thread state in + GitHub; all matched the authorization; current review-thread state remains + GitHub-owned) +- PR creation/update: NOT USED (control had already returned PR #960 to + draft; no PR-state, title or description change is authorized at this + stage) +- source edit: USED (this report only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; a detached local worktree at the + recovery head was used for construction, and the existing task branch is + the fast-forward target) +- commit: USED (the single-parent correction commit that contains this + report) -Authorized post-commit actions for the recovery commit - GitHub-owned +Authorized post-commit actions for the correction commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `609a01427847233b7280557baa61825ac2079865` to the recovery commit; + `22bd94dc959f11fe49c8bef8cd2a836140aadff7` to the correction commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012 blob, changed path set, cumulative footprint, review threads and - PR #960 state; + ADR-0012 blob, changed path set, cumulative footprint and PR #960 state, + and that no review thread was replied to, resolved or dismissed; - the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the control-record recovery +Not authorized for the implementing agent at the transient-state correction stage, and separate later gates where applicable: - any edit to ADR-0012, the decision register, the changelog or any path other than this report: NOT USED (not authorized) -- independent inspection of the recovery head and CTO exact-final-blob - confirmation: NOT USED (later gates; never on the implementing agent's - authority) +- independent inspection of the correction head: NOT USED (later gate; never + on the implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second recovery commit or architecture change: NOT USED (not authorized) +- any second correction commit or architecture change: NOT USED (not + authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -1112,22 +1255,21 @@ classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments -4-5 and Amendments 6-7, configured Codex/app review is not expected from a push -to a draft PR and is not relied upon at the Amendment-6/7 candidate, -approval-state or control-record recovery stage; fresh final-head Codex/app +4-5, Amendments 6-7 and the transient-state correction authorization, +configured Codex/app review is not expected from a push to a draft PR and is +not relied upon at the Amendment-6/7 candidate, approval-state, control-record +recovery or transient-state correction stage; fresh final-head Codex/app review is expected only at a later, separately authorized Draft -> Ready -transition. The actual job outcomes for the recovery commit exist only after -its publication; they, and the completed automatic CI of the content-approved -candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and the approval-state -head `609a01427847233b7280557baa61825ac2079865`, are GitHub-owned lifecycle -evidence recorded in the pull request, the post-publication handoffs and the -owning issue, not in this file. If an automatic workflow -unexpectedly intends to execute a migration or publish an image rather than -skip, that is a HOLD condition. +transition. The actual job outcomes for the correction commit exist only after +its publication; they are GitHub-owned lifecycle evidence recorded in the pull +request, the post-publication handoff and the owning issue, not in this file. +If an automatic workflow unexpectedly intends to execute a migration or +publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of -this report, followed the same documentation-only classification, with no -manual dispatch or rerun: +this report or read back from GitHub at the transient-state correction stage, +followed the same documentation-only classification, with no manual dispatch +or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -1136,6 +1278,9 @@ b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20: 4 success (classify x3, check-changelo 95769537cdc9871e42f9f3e5443604a96beb74da: 4 success (classify x3, check-changelog), 6 skipped (content-approved candidate) c046e6bf6510f24ee1231f86293a3d499bceed7e: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (approval-state head) 9db91af7bf3d9f969556c819187420fc31d25c10: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (recovery head) +b596536e7db0d6b8ace0a42fdd26bff42cabf104: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-6/7 candidate) +609a01427847233b7280557baa61825ac2079865: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-6/7 approval-state head) +22bd94dc959f11fe49c8bef8cd2a836140aadff7: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (control-record recovery head) ``` Manually initiated external actions (anything the implementing agent @@ -1332,7 +1477,8 @@ Amendment-6/7 approval-state stage (historical approval-state head precedence; control adjudication on #958 found that control-record defect nonconforming and required the report-only recovery below. -Report-only control-record recovery (the commit that contains this report): +Report-only control-record recovery (historical recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`): 37. The recovery removes every occurrence of the copied content-approval identifier and records the content approval only through durable facts: @@ -1348,11 +1494,50 @@ Report-only control-record recovery (the commit that contains this report): are byte-identical to the approval-state head; the recovery restores conformity with Route-B doctrine and is not an ADR amendment, another candidate or another approval-state commit. -39. Because this report now lives in the recovery commit, statements that - described the approval-state commit as the commit containing this report - are reframed as history, and the approval-state commit's identities, now - the recovery parent, are recorded in section 1 as historical facts. This - report does not self-pin the recovery commit's SHA, its tree or its own +39. Because the report then lived in the recovery commit, statements that + described the approval-state commit as the commit containing the report + were reframed as history, and the approval-state commit's identities, the + recovery parent, were recorded in section 1 as historical facts. The + recovery report did not self-pin the recovery commit's SHA, its tree or + its own final blob; section 1 records them here as historical facts + because the recovery head is the transient-state correction's parent. + +Report-only transient-state correction (the commit that contains this +report): + +40. The committed report records review-thread evidence only as durable + substantive dispositions and as historical stage-time inspection evidence + phrased as such. GitHub, through PR #960, owns the current number and + resolved/unresolved state of review threads and their later adjudication + (`docs/engineering/AGENTS.md`; `ADR-0005`). No sentence of this report + depends for its truth on a thread remaining unresolved, and no later + reply, resolution, dismissal or adjudication of a thread requires a + further source edit to keep this report truthful. +41. The final-head automated review of the recovery head is recorded + substantively (section 2, section 14) without its review, thread or + comment identifiers, consistent with decision 15 (Amendment 5 F3.5). The + control adjudication that classified its findings is identified only + through owning issue #958. +42. The topology finding is recorded as rejected because its premise did not + match the authoritative GitHub PR head/parent graph. The report's Route-B + source lineage - the correction commit on + `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, on + `609a01427847233b7280557baa61825ac2079865`, on + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` - is unchanged, and the + non-head object cited by that finding is not adopted as source topology. + The cumulative six-path PR footprint against refreshed `feature/worker` + does not mean that any single commit changed six paths from its parent. +43. The correction changes only this report. ADR-0012 + (`a7d95ca918f418d596432071c8ba78a33c2630a9`), its architecture, approval + state, approver and approval date, the decision register and the changelog + are byte-identical to the recovery head, so the existing CTO + exact-final-blob approval of that blob remains valid; the correction is + not an ADR amendment, another candidate or another approval-state commit. + Because this report now lives in the correction commit, statements that + described the recovery commit as the commit containing this report are + reframed as history, and the recovery commit's identities, now the + correction parent, are recorded in section 1 as historical facts. This + report does not self-pin the correction commit's SHA, its tree or its own final blob. List any deviation from the specification requiring authorization: @@ -1390,11 +1575,16 @@ List any deviation from the specification requiring authorization: identifier into repository source because the approval-state authorization erroneously required it, contrary to the repository-authoritative Route-B rule (decision 36). Control adjudication on #958 found that control-record - defect nonconforming, and the report-only recovery that contains this report - corrects it; it is not a current source deviation. No fifth path, new file, + defect nonconforming, and the historical report-only recovery + `22bd94dc959f11fe49c8bef8cd2a836140aadff7` corrected it; it is not a current + source deviation. No fifth path, new file, deletion, architecture change, second commit, amend, rebase, squash, force push or history rewrite occurred at that stage. -- Control-record recovery stage: NONE. The recovery follows its separate +- Control-record recovery stage: NONE. The recovery followed its separate + authorization exactly; no second path, new file, deletion, ADR, register or + changelog change, second commit, amend, rebase, squash, force push or history + rewrite was needed or performed at that stage. +- Transient-state correction stage: NONE. The correction follows its separate authorization exactly; no second path, new file, deletion, ADR, register or changelog change, second commit, amend, rebase, squash, force push or history rewrite was needed or performed before the commit that contains this report @@ -1448,8 +1638,10 @@ validation" records the validation of the historical content-approved candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`. "Amendment-6/7 approval-state validation" records the validation of the historical approval-state commit `609a01427847233b7280557baa61825ac2079865`. -"Control-record recovery validation" at the end of this section records the -validation of the commit that contains this report. +"Control-record recovery validation" records the validation of the historical +recovery commit `22bd94dc959f11fe49c8bef8cd2a836140aadff7`. "Transient-state +correction validation" at the end of this section records the validation of +the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -1932,8 +2124,10 @@ showed `develop` and `feature/worker` at `c046e6bf6510f24ee1231f86293a3d499bceed7e`, PR #960 open, ready, unmerged, mergeable and clean with that head and target `feature/worker`, the cumulative PR footprint exactly the six paths, ADR-0012 blob -`4848470a252076268a9fac9da5c40f0b4ebff474` at that head and nine unresolved -review threads. PR #960 was then converted Ready -> Draft and the read-back +`4848470a252076268a9fac9da5c40f0b4ebff474` at that head, and PR #960 +review-thread state that matched the stage authorization (stage-time evidence; +current review-thread state remains GitHub-owned). PR #960 was then converted +Ready -> Draft and the read-back confirmed it open, draft and unmerged at the same head. The four correction paths were edited and staged, and every check below ran against that exact final staged index before the candidate commit was created from it; the @@ -2106,8 +2300,9 @@ read-only GitHub inspection showed `develop` and `feature/worker` at `9db91af7bf3d9f969556c819187420fc31d25c10`, ADR-0012 blob `cd897d576522a741c3dfa82f8de2f8d80d4288f6` at that head, PR #960 open, draft, unmerged and mergeable with that head and target `feature/worker`, the -cumulative PR footprint exactly the six paths, and the nine unresolved review -threads unchanged since the candidate push. The four paths were edited and +cumulative PR footprint exactly the six paths, and PR #960 review-thread state +that matched the stage authorization (stage-time evidence; current +review-thread state remains GitHub-owned). The four paths were edited and staged, and every check below ran against that exact final staged index before the approval-state commit was created from it; the report's numeric line counts below were filled in and the complete check set rerun with identical @@ -2362,8 +2557,9 @@ read-only GitHub inspection showed `develop` and `feature/worker` at decision-register and changelog blobs named by the recovery authorization at that head, PR #960 open, draft, unmerged and mergeable with that head and target `feature/worker`, the cumulative PR footprint exactly the six paths, and -the nine unresolved review threads unchanged since the approval-state push. -Only this report was edited and staged, and every check below ran against that +PR #960 review-thread state that matched the stage authorization (stage-time +evidence; current review-thread state remains GitHub-owned). Only this report +was edited and staged, and every check below ran against that exact final staged index before the recovery commit was created from it; the report's numeric line counts below were filled in and the complete check set rerun with identical results. @@ -2451,13 +2647,15 @@ Approved by: Javi, CTO Approval date: 2026-10-06 ``` -This report's own final blob, the recovery tree and the recovery commit SHA -depend on this report's bytes and are not self-pinned; they are verified -against the remote head after push and recorded in the post-publication -handoff. +The recovery report did not self-pin its own final blob, the recovery tree or +the recovery commit SHA; they were verified against the remote head after push +and recorded in the post-publication handoff, and section 1 now records them +as historical facts: commit `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, tree +`7af3e0b8007eb6686e94043f57db63acc4edb4ab`, report blob +`74845b91dede2ba066bd0720598651e539196eb1`. -Required topology of the recovery commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the recovery commit, checked after it was created and +before push; the published recovery commit satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -2476,6 +2674,151 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Transient-state correction validation + +Environment: a full local Git worktree of `thoth-pub/thoth`, detached at the +historical control-record recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` (tree +`7af3e0b8007eb6686e94043f57db63acc4edb4ab`), with every required object +present locally. Immediately before construction, read-only GitHub inspection +showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` with single parent +`609a01427847233b7280557baa61825ac2079865`, the ADR-0012, report, +decision-register and changelog blobs named by the correction authorization at +that head, PR #960 open, draft, unmerged and mergeable with that head and +target `feature/worker`, the cumulative PR footprint exactly the six paths, and +PR #960 review-thread state that matched the stage authorization (stage-time +evidence; current review-thread state remains GitHub-owned). Only this report +was edited and staged, and every check below ran against that exact final +staged index before the correction commit was created from it; the report's +numeric line counts below were filled in and the complete check set rerun with +identical results. + +Whitespace: + +```text +git diff --cached --check +exit 0; no output + +git diff --cached --check 22bd94dc959f11fe49c8bef8cd2a836140aadff7 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Correction delta against the recovery head (exactly the one authorized path): + +```text +git diff --cached --name-only 22bd94dc959f11fe49c8bef8cd2a836140aadff7 +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md + +git diff --cached --name-status 22bd94dc959f11fe49c8bef8cd2a836140aadff7 +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md + +git diff --cached --numstat 22bd94dc959f11fe49c8bef8cd2a836140aadff7 +555 166 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Transient review-thread state (the searched phrases are the exact-count and +current-state wordings named by the correction authorization; they are +described rather than restated so that this report does not contain them): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -c -i -F -e '' -e '' -e '' -e '' +0 + +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -n -i -E 'review.thread|unresolved' +every match read: each is historical stage-time evidence phrased as such, +durable substantive finding or disposition information, or the statement that +current review-thread state is GitHub-owned; none asserts a current thread +count or a current resolved/unresolved state +``` + +Final-head review identifiers (the review, thread and review-comment +identifiers of the final-head automated review of the recovery head, and the +identifier of the control adjudication that classified it, are described +rather than restated so that this report does not contain them): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -c -F -e '' -e '' -e '' -e '' -e '' -e '' +0 +``` + +Every other path byte-identical to the recovery head: + +```text +git diff --cached --name-only 22bd94dc959f11fe49c8bef8cd2a836140aadff7 -- . ':!docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md' +(no output) +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED, unchanged) a7d95ca918f418d596432071c8ba78a33c2630a9 +decision-register.md (unchanged) b358acd5bbedf7482cfdc7b71866815c852773af +CHANGELOG.md (unchanged) e2947275d19d3abb351a98854268d7db213370d7 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +a7d95ca918f418d596432071c8ba78a33c2630a9 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n 3,6p +Status: APPROVED +Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-06 +``` + +This report's own final blob, the correction tree and the correction commit +SHA depend on this report's bytes and are not self-pinned; they are verified +against the remote head after push and recorded in the post-publication +handoff. + +Required topology of the correction commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 22bd94dc959f11fe49c8bef8cd2a836140aadff7 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 22bd94dc959f11fe49c8bef8cd2a836140aadff7 HEAD +required: exit 0; no output + +git diff --name-only 22bd94dc959f11fe49c8bef8cd2a836140aadff7 HEAD +required: exactly this report + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -2530,15 +2873,30 @@ removed content-approval identifier no longer occurs in this report; and read the report for statements that would become false once it moves into the recovery commit, or that treated the identifier transcription as an accepted deviation or a review item, and reframed or removed them. +Transient-state correction stage: confirmed that only this report is staged +and that ADR-0012, the decision register, the changelog and every protected +file keep their recovery-head blobs; searched the whole report for every +exact-count and current-state review-thread wording named by the correction +authorization and for equivalents, and read every remaining mention of review +threads to confirm it is historical stage-time evidence phrased as such, +durable substantive finding or disposition information, or the statement that +current thread state is GitHub-owned; confirmed by fixed-string search that no +review, thread or comment identifier of the final-head review of the recovery +head, and no identifier of the control adjudication that classified it, occurs +in this report; confirmed that the report's source lineage is unchanged; and +read the report for statements that would become false once it moves into the +correction commit and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, the recovery head `9db91af7bf3d9f969556c819187420fc31d25c10`, the content-approved -Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and the -Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865`, -are historical evidence; the live PR #960 diff of the recovery commit exists -only after its publication and is GitHub-owned lifecycle evidence. +Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, the +Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865` +and the control-record recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`, are historical evidence; the live +PR #960 diff of the correction commit exists only after its publication and is +GitHub-owned lifecycle evidence. ## 11. CI @@ -2548,9 +2906,9 @@ build, test, lint, format, migration and Docker jobs skipped). Checks: as listed in section 4.3. Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 recovery authorization, the Amendment-6/7 candidate and approval-state -authorizations and the control-record recovery authorization, an automatic CI -failure, or any migration execution or Docker publication, is a HOLD -condition. +authorizations, the control-record recovery authorization and the +transient-state correction authorization, an automatic CI failure, or any +migration execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -2560,12 +2918,12 @@ branch only. It is not repository-authoritative until its authority condition, including reachability from `develop`, is met, and ADR-0013 programme-local reliance additionally requires the programme-pin conditions on #957. Merge itself still requires, in order: independent inspection of the exact -control-record recovery head; CTO exact-final-blob confirmation of the -unchanged ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` at that -head; any separately authorized PR #960 metadata reconciliation; separately -authorized Draft -> Ready; fresh automatic final-head CI and configured -Codex/app review; -adjudication of every review thread; fresh independent CRITICAL exact-head +transient-state correction head, at which the unchanged ADR-0012 blob +`a7d95ca918f418d596432071c8ba78a33c2630a9` keeps its existing CTO +exact-final-blob approval; any separately authorized PR #960 metadata +reconciliation; separately authorized Draft -> Ready; fresh automatic +final-head CI and configured Codex/app review; adjudication of every +historical and current review thread; fresh independent CRITICAL exact-head review; separate CTO exact-head merge authorization; then the guarded merge and programme-pin/reliance reconciliation. Activation required: NONE. Merging the eventual approved version would make no @@ -2575,21 +2933,23 @@ Migration sequence: NONE Rollback/disable procedure: before merge, stop at the current gate; no history rewrite, force push or amend is permitted. The Amendment-6/7 authorizations permitted exactly one candidate commit and exactly one approval-state commit, -and the control-record recovery authorization permits exactly one report-only -recovery commit; if the recovery head needs correction, those authorizations -are exhausted and a fresh CTO authorization is required (Amendment 3 C10; -Amendments 6-7). +the control-record recovery authorization permitted exactly one report-only +recovery commit, and the transient-state correction authorization permits +exactly one report-only correction commit; if the correction head needs +correction, those authorizations are exhausted and a fresh CTO authorization +is required (Amendment 3 C10; Amendments 6-7). Monitoring required: NONE ## 13. Known limitations and deferred work -- This control-record recovery stage does not include independent - inspection of the recovery head, CTO exact-final-blob confirmation, PR #960 - title/description reconciliation, Draft -> Ready, the fresh final-head - Codex/app review, adjudication of the review threads, the fresh independent - exact-head review or merge. The PR #960 title and - description still describe the historical approval/recovery state; their - reconciliation requires a separate authorization. +- This transient-state correction stage does not include independent + inspection of the correction head, PR #960 title/description + reconciliation, Draft -> Ready, the fresh final-head Codex/app review, + adjudication of the historical and current review threads, the fresh + independent exact-head review or merge. The PR #960 title and description + were last reconciled at an earlier stage; whether they match the correction + head is GitHub-owned lifecycle state, and their reconciliation requires a + separate authorization. - Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The stale #957 pin must be superseded using the corrected exact ADR-0012 blob and the fresh independently reviewed PR #960 head; dependent `feature/worker` @@ -2605,8 +2965,10 @@ Monitoring required: NONE finding was handled that way (Amendments 4-5 and one recovery commit), and so was the final-head route-contract finding (Amendments 6-7, one candidate commit and one approval-state commit). The approval-state control-record - defect was likewise corrected only under a separate authorization for this - one report-only recovery commit. + defect was likewise corrected only under a separate authorization for one + report-only recovery commit, and the transient review-thread-state defect + found by the final-head review of that recovery head only under a separate + authorization for this one report-only correction commit. - Exact table, column, index and encoding names for route-contract revisions, anomalies and capability declarations are deliberately left to the future shared-engine implementation specification. @@ -2615,7 +2977,8 @@ Monitoring required: NONE `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; section 9 is exact-worktree evidence for the activation-epoch candidate, its approval-state commit, the recovery commit, the Amendment-6/7 candidate, its - approval-state commit and this control-record recovery. + approval-state commit, the control-record recovery and this transient-state + correction. ## 14. Unresolved issues @@ -2650,19 +3013,37 @@ Monitoring required: NONE Amendments 6-7; the content-approved candidate implements that correction in source and the approval-state commit `609a01427847233b7280557baa61825ac2079865` records its approval. - -All nine PR #960 review threads remain unresolved. Adjudication of every thread -belongs to the fresh independent CRITICAL exact-head review of the eventual -final head. This report neither resolves nor dismisses any thread. +- The configured final-head automated review of the control-record recovery + head `22bd94dc959f11fe49c8bef8cd2a836140aadff7` opened two further threads, + identified here substantively only. One asserted that the recovery head's + lineage matched a cited object whose sole parent was the refreshed + `feature/worker` base and which changed all six PR paths; that object is not + the PR #960 head, its premise did not match the authoritative GitHub PR + head/parent graph (the recovery head's single parent is the approval-state + head `609a01427847233b7280557baa61825ac2079865` and its delta from that + parent is this report alone), and control rejected it as an invalid premise + and non-blocking; no source or lineage correction was made. The other + identified that the recovery report treated the current number and + resolved/unresolved state of PR #960 review threads as durable repository + truth; control accepted it as a valid blocking control-record defect, and + the correction commit that contains this report addresses only that defect. + +PR #960 holds the historical and current review threads covering the findings +summarized above. GitHub is authoritative for their current number and +resolved/unresolved state; this report records only their durable substantive +dispositions and source evidence. Every applicable thread must be adjudicated +under the final exact-head review controls before merge, through the fresh +independent CRITICAL exact-head review of the eventual final head. This report +itself neither resolves nor dismisses any thread. ## 15. Agent self-assessment The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this control-record recovery also implemented -the Amendment-6/7 approval-state commit and candidate and earlier Route-B -source stages of this task and +work. The implementing agent of this transient-state correction also +implemented the control-record recovery, the Amendment-6/7 approval-state +commit and candidate and earlier Route-B source stages of this task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). @@ -2681,7 +3062,15 @@ Suggested review focus: the CTO candidate-content approval record, and that the final ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is the blob presented for exact-final-blob confirmation; -- that the control-record recovery changes only this report, that ADR-0012, +- that the transient-state correction changes only this report, that + ADR-0012, the decision register and the changelog keep their recovery-head + blobs, that no sentence presents the current count or resolved/unresolved + state of PR #960 review threads as durable repository truth, that the + report's source lineage is unchanged, and that no review, thread or comment + identifier of the final-head review of the recovery head is copied into the + report (section 5, decisions 40-43; section 9, "Transient-state correction + validation"); +- that the control-record recovery changed only this report, that ADR-0012, the decision register and the changelog keep their approval-state blobs, and that no GitHub content-approval, exact-final-blob approval, review or merge identifier is copied into the report (section 5, decisions 36-39; section 9, From eb3129f13a9c858b3c012ee182315801f0b8563a Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 7 Oct 2026 17:10:14 +0100 Subject: [PATCH 33/48] THOTH-ASYNC-01-ADR-01: clarify route-disposition cardinality Two-path correction after the configured final-head automated review of the transient-state correction head 9b4bb1b0 (#958, PR #960). Both findings were accepted by control; the CTO classified the ADR change as a FACTUAL CLARIFICATION of prior approved ADR-0012 blob a7d95ca9. - ADR-0012: replace the unconditional "exactly one durable disposition exists for each (event_id, route_key)" sentence in section 3.2, section 3.5 and invariant 8 with the at-most-one uniqueness rule, each paired with the already-selected rule that an eligible route obligation remains owed until it receives exactly one disposition. No-disposition cases (ineligible events, in-epoch mismatch, refused enrollment, approved divergence), validated enrollment, duplicate prevention, job idempotency, header approval fields and section 12 are unchanged; no architecture decision changes. - implementation report: replace statements about which live PR #960 lifecycle gate is pending with the durable lifecycle model (GitHub, through #958 and PR #960, owns current gate state), record the clarification and its validation, and advance the stage record. Decision register and CHANGELOG unchanged. No runtime, migration, provider, deployment, release or activation change. --- ...H-ASYNC-01-ADR-01-implementation-report.md | 934 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 29 +- 2 files changed, 755 insertions(+), 208 deletions(-) diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 81db6d33b..c47b874f1 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -8,19 +8,21 @@ clarification, the Route-B material activation-epoch correction, the bounded final-head recovery authorized by Specification Amendments 4-5, the Route-B candidate and approval-state stages of the immutable route-contract-revision correction specified by Specification Amendments 6-7, the one-path -report-only control-record recovery of that approval-state report, and the +report-only control-record recovery of that approval-state report, the one-path report-only transient-state correction that followed the configured -final-head automated review of that recovery head. It records +final-head automated review of that recovery head, and the two-path factual +clarification of ADR-0012 and durable-report correction that followed the +configured final-head automated review of that correction head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the report-only transient-state correction as -the current stage and every earlier candidate, approval-state, recovery and -control-record recovery stage recorded as history. Appendix A preserves the -earlier authoring, review, approval-state, reconciliation and validation -record. Appendix B records the +Route-B correction stages, with the factual clarification and durable-report +correction as the current stage and every earlier candidate, approval-state, +recovery and report-only correction stage recorded as history. Appendix A +preserves the earlier authoring, review, approval-state, reconciliation and +validation record. Appendix B records the durable control conditions. ## 1. Repository state @@ -39,11 +41,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the report-only transient-state correction commit that contains -this report, an ordinary single-parent additive commit whose direct parent is -the historical report-only control-record recovery head -`22bd94dc959f11fe49c8bef8cd2a836140aadff7`; its SHA is deliberately not -self-pinned here (section 3). That recovery head is the direct child of the +Head commit: the factual-clarification commit that contains this report, an +ordinary single-parent additive commit whose direct parent is the historical +report-only transient-state correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`; its SHA is deliberately not +self-pinned here (section 3). That correction head is the direct child of the +historical report-only control-record recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`, which is the direct child of the historical Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865`, which is the direct child of the content-approved Amendment-6/7 candidate @@ -53,7 +57,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The correction commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The clarification commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -66,8 +70,9 @@ Implementing model: activation-epoch candidate and approval-state stages - Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded -implementing agent; transient-state correction stage - Claude (Fable 5.1), -bounded implementing agent. Earlier rounds are recorded in Appendix A. +implementing agent; transient-state correction and factual-clarification +stages - Claude (Fable 5.1), bounded implementing agent. Earlier rounds are +recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -150,13 +155,25 @@ implementation-report blob 6b1a0c2e5f353c6974bf011726eb7beac804a3bd (superseded historical control-record recovery head / transient-state correction parent: 22bd94dc959f11fe49c8bef8cd2a836140aadff7 tree 7af3e0b8007eb6686e94043f57db63acc4edb4ab -ADR-0012 blob a7d95ca918f418d596432071c8ba78a33c2630a9 (APPROVED; unchanged by the recovery and by the correction) -decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by the recovery and by the correction) -CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the recovery and by the correction) -implementation-report blob 74845b91dede2ba066bd0720598651e539196eb1 (superseded by the correction) +ADR-0012 blob a7d95ca918f418d596432071c8ba78a33c2630a9 (APPROVED; unchanged by the recovery and by the transient-state correction) +decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by the recovery and by the transient-state correction) +CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the recovery and by the transient-state correction) +implementation-report blob 74845b91dede2ba066bd0720598651e539196eb1 (superseded by the transient-state correction) + +historical transient-state correction head / factual-clarification parent: +9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a +tree 862a8e32207f1e0f61cab532b08b65034979f5ac +ADR-0012 blob a7d95ca918f418d596432071c8ba78a33c2630a9 (APPROVED; the exact prior approved blob clarified by the factual clarification) +decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by the factual clarification) +CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the factual clarification) +implementation-report blob 81db6d33bc0d103e149dd99e1f35150f35c079e3 (superseded by the factual clarification) + +factual clarification (the commit that contains this report): +ADR-0012 blob 2db08058552ce901548ef3f71af4a294f5af7e61 (APPROVED; Javi, CTO; 2026-10-06; factual clarification of a7d95ca918f418d596432071c8ba78a33c2630a9) feature/worker at the recovery, the candidate, the approval-state commit, the -control-record recovery and the transient-state correction (unchanged): +control-record recovery, the transient-state correction and the factual +clarification (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -208,7 +225,13 @@ Approved specification: - report-only control-record recovery authorization: #958 comment `6022850916`; - final-head review report-only transient-state correction authorization: - #958 comment `6036961945`. + #958 comment `6036961945`; +- CTO factual-clarification classification of ADR-0012 blob + `a7d95ca918f418d596432071c8ba78a33c2630a9` and adjudication of the + final-head review of the transient-state correction head: #958 comment + `6041284313`; +- factual-clarification and durable-report correction authorization: #958 + comment `6041301223`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -218,12 +241,18 @@ exposed a further architecture defect, specified by Amendments 6-7. Its `PROPOSED` Route-B candidate received CTO exact candidate-content approval, and the one bounded Route-B approval-state commit recorded that approval. A one-path report-only control-record recovery then corrected how the -approval-state report recorded that content approval. The commit that contains -this report is a second one-path report-only correction, made after the -configured final-head automated review of that recovery head: it removes -statements that treated the live number and resolved/unresolved state of -PR #960 review threads as durable repository truth; it changes no ADR, -decision-register or changelog byte. +approval-state report recorded that content approval, and a second one-path +report-only correction, made after the configured final-head automated review +of that recovery head, removed statements that treated the live number and +resolved/unresolved state of PR #960 review threads as durable repository +truth. The commit that contains this report is a two-path correction made +after the configured final-head automated review of that correction head: it +applies the CTO-classified factual clarification of ADR-0012's +route-disposition cardinality wording to the prior approved ADR blob +`a7d95ca918f418d596432071c8ba78a33c2630a9`, and removes the report's remaining +statements about which live PR #960 lifecycle gate was pending. It changes no +decision-register or changelog byte and makes no material architecture +change. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -339,7 +368,8 @@ decision-register or changelog byte. not another candidate, not another approval-state commit and not an ADR amendment, and that stage authorized no PR-state or PR-metadata change. - **Final-head review of the recovery head and report-only transient-state - correction (the commit that contains this report).** Authorized by #958 + correction (historical correction head + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`).** Authorized by #958 comment `6036961945`. Control separately marked PR #960 Ready at the recovery head, which triggered the configured final-head automated review of `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, and returned PR #960 to Draft @@ -359,18 +389,52 @@ decision-register or changelog byte. `docs/engineering/AGENTS.md`, under which committed files record durable repository state while GitHub records transient workflow state. Control accepted it as a valid blocking control-record defect. One ordinary - single-parent commit directly on the recovery head changes only this report: - stage-time review-thread inspections are rephrased as historical stage - evidence, the current-state assertion in section 14 is replaced by durable + single-parent commit directly on the recovery head changed only this report: + stage-time review-thread inspections were rephrased as historical stage + evidence, the current-state assertion in section 14 was replaced by durable wording that points to PR #960 for live thread state, the final-head review - outcome is recorded substantively without its review identifiers, and this - correction is recorded as the current stage. No ADR-0012 architecture, + outcome was recorded substantively without its review identifiers, and that + correction became the then-current stage. No ADR-0012 architecture, approval state, approver, approval date, decision-register state or - changelog state changes; ADR-0012 remains blob - `a7d95ca918f418d596432071c8ba78a33c2630a9`, so the existing CTO - exact-final-blob approval of that unchanged blob remains valid. It is not + changelog state changed; ADR-0012 remained blob + `a7d95ca918f418d596432071c8ba78a33c2630a9` at that head. It was not another candidate, not another approval-state commit and not an ADR - amendment. This stage authorizes no PR-state or PR-metadata change. + amendment, and that stage authorized no PR-state or PR-metadata change. +- **Final-head review of the transient-state correction head, factual + clarification and durable-report correction (the commit that contains this + report).** Authorized by #958 comment `6041301223` after the CTO + classification and adjudication record #958 comment `6041284313`. Control + separately marked PR #960 Ready at the transient-state correction head, + which triggered the configured final-head automated review of + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, and returned PR #960 to Draft + before any correction source was staged. That review raised two findings, + both accepted by control as valid and blocking (section 14). The first found + that ADR-0012 stated unconditionally, in section 3.2, section 3.5 and + invariant 8, that exactly one durable disposition exists for each + `(event_id, route_key)`, which is broader than the eligibility, backlog, + in-epoch mismatch, historical-enrollment and reconciliation/divergence + semantics the same ADR already selects. The CTO classified the required + change, under the Amendments rules of + `docs/engineering/decisions/README.md`, as a `FACTUAL CLARIFICATION` of the + exact prior approved ADR blob `a7d95ca918f418d596432071c8ba78a33c2630a9`: + the cardinality rule becomes at most one durable disposition per + `(event_id, route_key)`, an eligible route obligation remains owed until it + receives exactly one, and every existing no-disposition case and duplicate + prevention rule is unchanged. The second found that the report still + recorded which live PR #960 lifecycle gates were pending. One ordinary + single-parent commit directly on the transient-state correction head changes + only ADR-0012 and this report: the three unconditional cardinality + sentences are replaced by the at-most-one rule with the eventual-completion + clause (section 4; section 9, "Factual-clarification validation"), and the + report's current-gate statements are replaced by the durable lifecycle + model under which GitHub, through #958 and PR #960, is authoritative for + the current gate. ADR-0012 keeps `Status: APPROVED`, `Approved by: Javi, + CTO` and `Approval date: 2026-10-06`; under the factual-clarification rule + the existing architecture approval remains applicable within the classified + scope. No decision-register or changelog byte changes. It is not a + Specification Amendment, not another Route-B candidate or approval-state + commit and not a material architectural correction. This stage authorizes + no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -382,6 +446,11 @@ MATERIAL ARCHITECTURAL CORRECTION (base specification section 3) selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) + +ADR-0012 change in the commit that contains this report: +FACTUAL CLARIFICATION of prior approved blob a7d95ca918f418d596432071c8ba78a33c2630a9 +(docs/engineering/decisions/README.md, Amendments; CTO classification on #958; +no Specification Amendment and no new Route-B cycle) ``` Eligibility for the material pre-authority correction is keyed to the ADR @@ -472,39 +541,59 @@ new version received its own independent control inspection and CTO exact candidate-content approval on 2026-10-06, the historical approval-state commit `609a01427847233b7280557baa61825ac2079865` recorded it, and neither the historical report-only control-record recovery -`22bd94dc959f11fe49c8bef8cd2a836140aadff7` nor the report-only -transient-state correction that contains this report changes ADR-0012. The -exact final ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is -unchanged, so the existing CTO exact-final-blob approval of that blob remains -valid; the correction head still requires independent inspection, and any -separately authorized PR metadata reconciliation, Draft -> Ready, fresh -final-head automated review, adjudication of every historical and current -review thread, fresh independent CRITICAL exact-head review and separate merge -authorization are later gates. +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` nor the historical report-only +transient-state correction `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` changed +ADR-0012; both left the approved blob +`a7d95ca918f418d596432071c8ba78a33c2630a9`. The factual clarification that +contains this report changes that blob to +`2db08058552ce901548ef3f71af4a294f5af7e61` within the CTO-classified +clarification scope only; under the factual-clarification rule the selected +architecture and its approval are unchanged, while any source-head-bound +review or merge authorization and any ADR-0013 exact-version programme pin +bound to an earlier blob or head are stale for the clarified version until +separately reconciled. Independent exact-head review, CTO merge authorization, +guarded merge, programme-local reliance and later migration, deployment, +activation and observation are distinct controlled lifecycle categories; +GitHub, through #958 and PR #960, is authoritative for which of them is +pending, satisfied or blocked for any head. ## 3. Commits -Report-only transient-state correction commit (the commit that contains this -report): +Factual-clarification commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: make report review-thread record durable` - - direct parent: the historical report-only control-record recovery head - `22bd94dc959f11fe49c8bef8cd2a836140aadff7` - - changes only this report (section 4) + `THOTH-ASYNC-01-ADR-01: clarify route-disposition cardinality` + - direct parent: the historical report-only transient-state correction head + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` + - changes only ADR-0012 and this report (section 4); the clarified ADR-0012 + blob is `2db08058552ce901548ef3f71af4a294f5af7e61` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; every other file, including -ADR-0012, the decision register and the changelog, keeps its recovery-head -blob, which is its approval-state blob (section 9). The commit identities are -verified against the remote task-branch head after push and recorded in the -post-publication handoff; once published, the PR #960 head and head tree are -GitHub-owned lifecycle evidence under `ADR-0005`. The authorized topology is a -non-force fast-forward of the task branch from the recovery head to the -correction commit; no merge commit, rebase, squash, amend, force update or -second correction commit is authorized, and no further source commit exists -implicitly after it. +here because this report is part of that tree; the clarified ADR-0012 blob +does not depend on this report and is recorded above; every other file, +including the decision register and the changelog, keeps its transient-state +correction-head blob, which is its approval-state blob (section 9). The commit +identities are verified against the remote task-branch head after push and +recorded in the post-publication handoff; once published, the PR #960 head and +head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The authorized +topology is a non-force fast-forward of the task branch from the +transient-state correction head to the clarification commit; no merge commit, +rebase, squash, amend, force update or second clarification commit is +authorized, and no further source commit exists implicitly after it. + +Report-only transient-state correction commit (historical): + +- `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` - + `THOTH-ASYNC-01-ADR-01: make report review-thread record durable` + - ordinary single-parent additive commit; direct parent: the historical + report-only control-record recovery head + `22bd94dc959f11fe49c8bef8cd2a836140aadff7` + - tree `862a8e32207f1e0f61cab532b08b65034979f5ac` + - changed only this report (section 4) + - published by a non-force fast-forward of `feature/async/adr-0012` from the + recovery head; the remote head, tree, parent and ADR-0012 blob matched the + locally validated object Report-only control-record recovery commit (historical): @@ -616,13 +705,19 @@ first-parent order, newest first: ## 4. Files changed -Report-only transient-state correction write budget for the commit that -contains this report (authorization #958 comment `6036961945`); exactly one -existing path, no new file, no deletion, and every other path byte-identical -to the recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7`: +Factual-clarification write budget for the commit that contains this report +(authorization #958 comment `6041301223`); exactly two existing paths, no new +file, no deletion, and every other path byte-identical to the transient-state +correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`: +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +Historical report-only transient-state correction write budget +(authorization #958 comment `6036961945`): exactly the report path, no new +file, no deletion, and every other path byte-identical to the recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`. + Historical report-only control-record recovery write budget (authorization #958 comment `6022850916`): the same single existing path, no new file, no deletion, and every other path byte-identical to the approval-state head. @@ -659,11 +754,68 @@ six paths at every stage): Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record -recovery and transient-state correction stages; this report already existed. +recovery, transient-state correction and factual-clarification stages; this +report and ADR-0012 already existed. + +Factual-clarification changes in the commit that contains this report, +relative to the transient-state correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` (exactly the two authorized +paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: the configured final-head automated review of the transient-state + correction head found that the unconditional sentence "exactly one durable + disposition exists for each `(event_id, route_key)`" in section 3.2, + section 3.5 and invariant 8 conflicted with the eligibility, backlog, + in-epoch mismatch, historical-enrollment and reconciliation/divergence + semantics already selected by the same ADR; the CTO classified the + correction as a factual clarification of prior approved blob + `a7d95ca918f418d596432071c8ba78a33c2630a9`. + - behavioural effect: factual clarification only; no architecture decision + changes. Three sentences change and nothing else: section 3.2 now states + that at most one durable disposition may exist for a given + `(event_id, route_key)` and that an eligible route obligation remains + owed, visible in the route backlog, until it has received that one + disposition; section 3.5 states the same at-most-one rule with eventual + receipt of exactly one for an eligible route obligation; invariant 8 + states the same rule in its first sentence. The surrounding text of each + sentence, the materialized/terminal non-materialized disposition model, + the uniqueness boundary across epochs and revisions, the no-disposition + cases (ineligible events, in-epoch contract mismatch, refused historical + enrollment, approved reconciliation/divergence), the one allowed + disposition from validated enrollment, duplicate prevention and job + idempotency are byte-identical. The header keeps `Status: APPROVED`, + `Approved by: Javi, CTO` and `Approval date: 2026-10-06`; section 12 is + unchanged. Clarified blob: `2db08058552ce901548ef3f71af4a294f5af7e61`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: the same review found that the report still recorded which live + PR #960 lifecycle gates were pending ("still requires", "does not + include", a next-step list of Ready, review, thread-adjudication and + merge-authorization gates); control accepted that as a blocking + control-record defect. + - behavioural effect: documentation only. Replaces every current-gate + statement with the durable lifecycle model (the ADR-0012 authority + condition; independent exact-head review, CTO merge authorization, + guarded merge, programme-local reliance, migration, deployment, + activation and observation as distinct controlled lifecycle categories; + GitHub, through #958 and PR #960, as the authority for the current gate); + records the final-head review of the transient-state correction head and + the CTO classification substantively, without review identifiers; records + the factual clarification and its validation (section 9); makes this + stage the current stage; and reframes statements that would otherwise + describe the transient-state correction commit as the commit containing + this report. All other historical evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the transient-state correction head through the factual +clarification: every path other than ADR-0012 and this report, including the +decision register `b358acd5bbedf7482cfdc7b71866815c852773af` and +`CHANGELOG.md` `e2947275d19d3abb351a98854268d7db213370d7`. -Transient-state correction changes in the commit that contains this report, -relative to the recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7` -(exactly the one authorized path): +Historical transient-state correction changes in the correction commit +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, relative to the recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` (exactly the one authorized path): - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: the configured final-head automated review of the recovery head @@ -676,11 +828,11 @@ relative to the recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7` current-state assertion in section 14 with durable wording that points to PR #960 for live thread state; records the final-head review of the recovery head substantively, without its review identifiers, and the - rejection of its topology premise (section 5, decisions 40-43); makes this - correction the current stage; reframes statements that would otherwise - describe the recovery commit as the commit containing this report; and - records the correction's validation (section 9). All other historical - evidence is preserved. + rejection of its topology premise (section 5, decisions 40-43); made that + correction the then-current stage of this report; reframed statements + that would otherwise describe the recovery commit as the commit containing + the report; and recorded the correction's validation (section 9). All + other historical evidence was preserved. - within authorized write budget: YES Byte-identical to the recovery head through the transient-state correction: @@ -837,8 +989,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate - within authorized write budget: YES Byte-identical to the recovery head through the Amendment-6/7 candidate, the -approval-state commit, the control-record recovery and the transient-state -correction: +approval-state commit, the control-record recovery, the transient-state +correction and the factual clarification: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -1017,7 +1169,11 @@ Files deleted, moved or renamed: NONE PASS -The transient-state correction differs from the recovery head +The factual clarification differs from the transient-state correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` in exactly the two authorized +paths and from refreshed `feature/worker` in exactly the six cumulative PR +paths (section 9, "Factual-clarification validation"). The transient-state +correction differed from the recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7` in exactly the one authorized report path and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Transient-state correction validation"). The @@ -1049,9 +1205,11 @@ and `5998119577`), its approval-state stage (#958 comment `5999579357`), the Amendment-4/5 recovery stage (#958 comment `6013267369`), the Amendment-6/7 candidate stage (#958 comment `6021574322`), the Amendment-6/7 approval-state stage (#958 comment `6022486352`), the control-record recovery stage (#958 -comment `6022850916`) and the transient-state correction stage (#958 comment -`6036961945`). This matrix states only what had been done when the commit that -contains this report was created. +comment `6022850916`), the transient-state correction stage (#958 comment +`6036961945`) and the factual-clarification stage (#958 comment +`6041301223`). This matrix states only what had been done when the commit that +contains this report was created; it is historical stage-time evidence and +does not describe the current PR #960 lifecycle gate. Activation-epoch candidate stage - historical, completed before the approval-state commit: @@ -1176,8 +1334,8 @@ transient-state correction: review of the recovery head, and returned it to Draft before the transient-state correction (section 2) -Transient-state correction stage - done up to and including the creation of -the commit that contains this report: +Transient-state correction stage - historical, completed before the factual +clarification: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -1188,42 +1346,78 @@ the commit that contains this report: GitHub; all matched the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (control had already returned PR #960 to - draft; no PR-state, title or description change is authorized at this + draft; no PR-state, title or description change was authorized at that stage) - source edit: USED (this report only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) - branch creation: NOT USED (not authorized; a detached local worktree at the - recovery head was used for construction, and the existing task branch is + recovery head was used for construction, and the existing task branch was the fast-forward target) -- commit: USED (the single-parent correction commit that contains this +- commit: USED (the single-parent correction commit + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `22bd94dc959f11fe49c8bef8cd2a836140aadff7` to the correction commit; the + remote head, tree, parent and protected blobs were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the correction head, and returned it to Draft before the factual + clarification (section 2) + +Factual-clarification stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` and target `feature/worker`, the + correction head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the six-path cumulative + footprint, the presence of the classification and authorization records on + #958 with no later superseding control record, and, as stage-time + evidence, the PR #960 review-thread state in GitHub; all matched the + authorization; current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (control had already returned PR #960 to + draft; no PR-state, title or description change is authorized at this + stage) +- source edit: USED (ADR-0012 and this report only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; a detached local worktree at the + transient-state correction head was used for construction, and the existing + task branch is the fast-forward target) +- commit: USED (the single-parent clarification commit that contains this report) -Authorized post-commit actions for the correction commit - GitHub-owned +Authorized post-commit actions for the clarification commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `22bd94dc959f11fe49c8bef8cd2a836140aadff7` to the correction commit; + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` to the clarification commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012 blob, changed path set, cumulative footprint and PR #960 state, - and that no review thread was replied to, resolved or dismissed; + ADR-0012 and report blobs, changed path set, cumulative footprint and + PR #960 state, and that no review thread was replied to, resolved or + dismissed; - the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the transient-state correction -stage, and separate later gates where applicable: - -- any edit to ADR-0012, the decision register, the changelog or any path other - than this report: NOT USED (not authorized) -- independent inspection of the correction head: NOT USED (later gate; never - on the implementing agent's authority) +Not authorized for the implementing agent at the factual-clarification stage, +each a separate controlled lifecycle category where applicable (historical +stage-time evidence; GitHub, through #958 and PR #960, is authoritative for +the current state of each): + +- any edit to the decision register, the changelog or any path other than + ADR-0012 and this report: NOT USED (not authorized) +- any ADR-0012 change outside the classified factual-clarification scope: + NOT USED (not authorized) +- independent exact-head review or inspection: NOT USED (never on the + implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second correction commit or architecture change: NOT USED (not - authorized) +- any second clarification commit or material architecture change: NOT USED + (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -1255,21 +1449,22 @@ classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments -4-5, Amendments 6-7 and the transient-state correction authorization, -configured Codex/app review is not expected from a push to a draft PR and is -not relied upon at the Amendment-6/7 candidate, approval-state, control-record -recovery or transient-state correction stage; fresh final-head Codex/app -review is expected only at a later, separately authorized Draft -> Ready -transition. The actual job outcomes for the correction commit exist only after +4-5, Amendments 6-7, the transient-state correction authorization and the +factual-clarification authorization, configured Codex/app review is not +expected from a push to a draft PR and is not relied upon at the Amendment-6/7 +candidate, approval-state, control-record recovery, transient-state +correction or factual-clarification stage; configured Codex/app review runs on +separately authorized Ready transitions, which are GitHub-owned lifecycle +events. The actual job outcomes for the clarification commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of -this report or read back from GitHub at the transient-state correction stage, -followed the same documentation-only classification, with no manual dispatch -or rerun: +this report or read back from GitHub at the transient-state correction or +factual-clarification stage, followed the same documentation-only +classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -1281,6 +1476,7 @@ c046e6bf6510f24ee1231f86293a3d499bceed7e: 4 success (classify x3, check-changelo b596536e7db0d6b8ace0a42fdd26bff42cabf104: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-6/7 candidate) 609a01427847233b7280557baa61825ac2079865: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-6/7 approval-state head) 22bd94dc959f11fe49c8bef8cd2a836140aadff7: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (control-record recovery head) +9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (transient-state correction head) ``` Manually initiated external actions (anything the implementing agent @@ -1446,8 +1642,9 @@ Amendment-6/7 approval-state stage (historical approval-state head candidate-state paragraphs, replaced by the approval statement, the approver/date and one paragraph stating that the approval does not by itself make the ADR repository-authoritative and that ADR-0013 - programme-local reliance on this exact version is separate and not yet - effective. The historical-approval, architecture-only, authority-condition, + programme-local reliance on this exact version is separate and not + effective until its own conditions are satisfied. The + historical-approval, architecture-only, authority-condition, partial-supersession and amendment paragraphs were already true in the approved state (decision 9) and are unchanged. 32. The decision-register `Last updated: 2026-10-06` header already carries the @@ -1502,8 +1699,8 @@ Report-only control-record recovery (historical recovery head its own final blob; section 1 records them here as historical facts because the recovery head is the transient-state correction's parent. -Report-only transient-state correction (the commit that contains this -report): +Report-only transient-state correction (historical correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`): 40. The committed report records review-thread evidence only as durable substantive dispositions and as historical stage-time inspection evidence @@ -1530,15 +1727,71 @@ report): 43. The correction changes only this report. ADR-0012 (`a7d95ca918f418d596432071c8ba78a33c2630a9`), its architecture, approval state, approver and approval date, the decision register and the changelog - are byte-identical to the recovery head, so the existing CTO - exact-final-blob approval of that blob remains valid; the correction is - not an ADR amendment, another candidate or another approval-state commit. - Because this report now lives in the correction commit, statements that - described the recovery commit as the commit containing this report are - reframed as history, and the recovery commit's identities, now the - correction parent, are recorded in section 1 as historical facts. This - report does not self-pin the correction commit's SHA, its tree or its own - final blob. + were byte-identical to the recovery head; the correction was not an ADR + amendment, another candidate or another approval-state commit. Because + the report then lived in the correction commit, statements that described + the recovery commit as the commit containing the report were reframed as + history, and the recovery commit's identities, the correction parent, + were recorded in section 1 as historical facts. The correction report did + not self-pin the correction commit's SHA, its tree or its own final blob; + section 1 records them here as historical facts because the correction + head is the factual clarification's parent. + +Factual clarification and durable-report correction (the commit that contains +this report): + +44. The ADR-0012 change is confined to the CTO-classified factual + clarification of prior approved blob + `a7d95ca918f418d596432071c8ba78a33c2630a9`: the unconditional sentence + "exactly one durable disposition exists for each `(event_id, + route_key)`" in section 3.2, section 3.5 and invariant 8 is replaced by + "at most one durable disposition may exist for a given + `(event_id, route_key)`", each time paired with the already-selected + completion rule that an eligible route obligation remains owed until it + has received exactly one disposition. The smallest wording that removes + the contradiction was chosen; no other sentence of the ADR changes. +45. The clarification expresses semantics the ADR already selected rather + than selecting new behaviour: the surrounding rule that the engine must + eventually establish a durable disposition for every route for which a + committed event is eligible is unchanged (section 3.2); in-epoch + contract mismatch, refused historical enrollment and approved + reconciliation/divergence already stated that no disposition is created + and that the `(event_id, route_key)` slot stays unused; validated + historical enrollment already created "the one allowed" disposition; and + the uniqueness boundary across epochs and revisions, duplicate prevention + and job idempotency were already stated as they remain. Had the + contradiction required selecting new behaviour, the stage would have + stopped as exceeding the factual-clarification boundary. +46. ADR-0012 keeps `Status: APPROVED`, `Approved by: Javi, CTO` and + `Approval date: 2026-10-06`, and section 12 is unchanged, because the + Amendments rules of `docs/engineering/decisions/README.md` let a factual + clarification update an approved ADR in place with the existing + architecture approval remaining; no `PROPOSED` candidate, approval-state + commit, changelog entry or register change belongs to this stage, and + the clarified ADR blob is recorded in section 1 for independent control + inspection because it does not depend on this report's bytes. +47. The committed report records the ADR-0012 authority condition and the + distinct controlled lifecycle categories (independent exact-head review, + CTO merge authorization where required, guarded merge, programme-local + reliance, migration, deployment, activation and observation) but not + which PR #960 gate is currently pending, satisfied or blocked: GitHub, + through #958 and PR #960, owns that state (`docs/engineering/AGENTS.md`; + `ADR-0005`). Historical stages record what they did or did not perform + as stage-time evidence only. No sentence of this report becomes false + when PR metadata is reconciled, the PR changes Draft/Ready state, an + automated or independent review runs, a thread is adjudicated, merge + authorization is recorded or the PR merges. +48. The final-head automated review of the transient-state correction head + and the CTO classification are recorded substantively (section 2, + section 14) without review, thread or review-comment identifiers, + consistent with decisions 15 and 41; the classification and authorization + records are identified through owning issue #958 as every stage + authorization is. Because this report now lives in the clarification + commit, statements that described the transient-state correction commit + as the commit containing this report are reframed as history, and that + commit's identities, now the clarification parent, are recorded in + section 1 as historical facts. This report does not self-pin the + clarification commit's SHA, its tree or its own final blob. List any deviation from the specification requiring authorization: @@ -1584,11 +1837,15 @@ List any deviation from the specification requiring authorization: authorization exactly; no second path, new file, deletion, ADR, register or changelog change, second commit, amend, rebase, squash, force push or history rewrite was needed or performed at that stage. -- Transient-state correction stage: NONE. The correction follows its separate - authorization exactly; no second path, new file, deletion, ADR, register or - changelog change, second commit, amend, rebase, squash, force push or history - rewrite was needed or performed before the commit that contains this report - was created. +- Transient-state correction stage: NONE. The correction followed its + separate authorization exactly; no second path, new file, deletion, ADR, + register or changelog change, second commit, amend, rebase, squash, force + push or history rewrite was needed or performed at that stage. +- Factual-clarification stage: NONE. The clarification follows its separate + authorization exactly; no third path, new file, deletion, register or + changelog change, ADR change outside the classified scope, second commit, + amend, rebase, squash, force push or history rewrite was needed or performed + before the commit that contains this report was created. ## 6. Database and migration effects @@ -1606,7 +1863,8 @@ GraphQL/API changes: NONE Generated schema/client updates: NONE Backwards compatibility: unaffected; documentation/architecture only Deprecations: NONE -Cross-repository dependencies: NONE for this correction. The verified BE-04 +Cross-repository dependencies: NONE for the corrections and clarification +recorded here. The verified BE-04 per-consumer impact record for the ADR's later legacy-contract retirement is preserved unchanged in Appendix A; neither the activation-epoch correction nor the route-contract-revision correction changes a released contract or adds a @@ -1640,8 +1898,10 @@ approval-state validation" records the validation of the historical approval-state commit `609a01427847233b7280557baa61825ac2079865`. "Control-record recovery validation" records the validation of the historical recovery commit `22bd94dc959f11fe49c8bef8cd2a836140aadff7`. "Transient-state -correction validation" at the end of this section records the validation of -the commit that contains this report. +correction validation" records the validation of the historical correction +commit `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`. "Factual-clarification +validation" at the end of this section records the validation of the commit +that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -2515,8 +2775,9 @@ a7d95ca918f418d596432071c8ba78a33c2630a9 The approval-state ADR-0012, decision-register and changelog blobs were deterministic and did not depend on the approval-state report's bytes, so they were recorded above; the ADR-0012 blob -`a7d95ca918f418d596432071c8ba78a33c2630a9` is the exact final blob that still -requires CTO exact-final-blob confirmation. The approval-state report's own +`a7d95ca918f418d596432071c8ba78a33c2630a9` was the exact final blob presented +for CTO exact-final-blob confirmation at that stage. The approval-state +report's own final blob, the approval-state tree and the approval-state commit SHA depended on that report's bytes and were not self-pinned in it; they were verified against the remote head after push and recorded in the post-publication @@ -2794,13 +3055,16 @@ Approved by: Javi, CTO Approval date: 2026-10-06 ``` -This report's own final blob, the correction tree and the correction commit -SHA depend on this report's bytes and are not self-pinned; they are verified -against the remote head after push and recorded in the post-publication -handoff. +The correction report did not self-pin its own final blob, the correction +tree or the correction commit SHA; they were verified against the remote head +after push and recorded in the post-publication handoff, and section 1 now +records them as historical facts: commit +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, tree +`862a8e32207f1e0f61cab532b08b65034979f5ac`, report blob +`81db6d33bc0d103e149dd99e1f35150f35c079e3`. -Required topology of the correction commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the correction commit, checked after it was created and +before push; the published correction commit satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -2819,6 +3083,240 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Factual-clarification validation + +Environment: a full local Git worktree of `thoth-pub/thoth`, detached at the +historical transient-state correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` (tree +`862a8e32207f1e0f61cab532b08b65034979f5ac`), with every required object +present locally. Immediately before construction, read-only GitHub inspection +showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` with single parent +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`, the ADR-0012, report, +decision-register and changelog blobs named by the clarification +authorization at that head, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker`, the cumulative PR footprint exactly the +six paths, the classification and authorization records present on #958 with +no later control record, and PR #960 review-thread state that matched the +stage authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012 was edited first and its deterministic blob recorded; +then only this report was edited; both were staged, and every check below ran +against that exact final staged index before the clarification commit was +created from it; the report's numeric line counts below were filled in and +the complete check set rerun with identical results. + +Whitespace: + +```text +git diff --cached --check +exit 0; no output + +git diff --cached --check 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Clarification delta against the transient-state correction head (exactly the +two authorized paths): + +```text +git diff --cached --name-only 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md + +git diff --cached --name-status 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md + +git diff --cached --numstat 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a +738 196 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +17 12 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Exact ADR-0012 delta from the prior approved blob +`a7d95ca918f418d596432071c8ba78a33c2630a9` to the clarified blob +`2db08058552ce901548ef3f71af4a294f5af7e61` (three hunks; nothing else changes): + +```text +git diff --cached -U0 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +@@ -359,6 +359,8 @@ in-epoch contract-mismatch anomaly for every route for which it is mismatched. +-Exactly one durable disposition exists for each `(event_id, route_key)`. The +-normal disposition materializes a job: a materialized disposition points to +-exactly one job and remains unique on `(event_id, route_key)` regardless of +-whether that job is pending, running, waiting or terminal. The only alternative +-is the explicit, authorized and audited terminal non-materialized disposition +-described below, which points to no job (section 3.5 and invariant 8). ++At most one durable disposition may exist for a given `(event_id, route_key)`, ++and an eligible route obligation remains owed, visible in the route backlog, ++until it has received that one disposition. The normal disposition ++materializes a job: a materialized disposition points to exactly one job and ++remains unique on `(event_id, route_key)` regardless of whether that job is ++pending, running, waiting or terminal. The only alternative is the explicit, ++authorized and audited terminal non-materialized disposition described below, ++which points to no job (section 3.5 and invariant 8). +@@ -983,2 +985,3 @@ distinction between retry, replay and current-state redistribution. +-Event routing identity and job idempotency are related but distinct. Exactly one +-durable disposition exists for each `(event_id, route_key)`. A successfully ++Event routing identity and job idempotency are related but distinct. At most ++one durable disposition may exist for a given `(event_id, route_key)`, and an ++eligible route obligation eventually receives exactly one. A successfully +@@ -1827,4 +1830,6 @@ execution until a separate approved architecture decision says otherwise. +-8. Exactly one durable disposition exists for each `(event_id, route_key)`. +- A successfully materialized disposition maps to exactly one job; many +- materialized dispositions may map to one not-yet-started job only under an +- explicit durable coalescing rule. An explicit authorized/audited terminal ++8. At most one durable disposition may exist for a given ++ `(event_id, route_key)`; an eligible route obligation remains owed until it ++ has received exactly one. A successfully materialized disposition maps to ++ exactly one job; many materialized dispositions may map to one ++ not-yet-started job only under an explicit durable coalescing rule. An ++ explicit authorized/audited terminal + +git diff --cached 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c -E '^[-+](Status:|Approved by:|Approval date:|## 12)' +0 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n 3,6p +Status: APPROVED +Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-06 +``` + +ADR semantic validation of the clarified blob (line numbers refer to that +blob): + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n -i -E 'exactly one durable|exactly one disposition|each \(event_id|every \(event_id' +(no output) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n -i 'at most one' +lines 274, 322 (epochs, pre-existing), 359 (section 3.2), 1830 (invariant 8); +section 3.5 carries the same rule across lines 985-987 +``` + +- cardinality: at most one durable disposition per `(event_id, route_key)` + (lines 359, 985-986, 1830-1831), with the uniqueness boundary spanning every + epoch and revision and excluding their identifiers (lines 367-373, + 1838-1841); +- completion: the engine must eventually establish a durable route disposition + for every route for which a committed event is eligible (line 356), and an + eligible route obligation remains owed until it has received exactly one + (lines 360-361, 987, 1831-1832); +- case 1, ineligible event: no obligation and no anomaly (lines 334-341), so + no slot is consumed; +- case 2, eligible backlog: an eligible obligation may temporarily have no + disposition while owed and visible (line 360; undispositioned eligible-event + backlog, line 552); +- case 3, in-epoch contract mismatch: no disposition or job, slot not consumed + (lines 533-534; invariant 35, lines 1911-1912); +- case 4, refused historical enrollment: no disposition or job, slot unused + (lines 589-591; invariant 6, lines 1826-1827); +- case 5, validated enrollment: creates the one allowed disposition (lines + 540-541; validation requirement, lines 2365-2366); +- case 6, approved reconciliation/divergence: records why no route disposition + is created (lines 543-545; lines 2328-2330); +- case 7, already-dispositioned event: no second disposition through + enrollment, reactivation, revision transition or replay (lines 595-600, + 1838-1841), and materialized and terminal non-materialized dispositions + cannot coexist (lines 992-994, 1837-1838). + +None of the seven cases is ambiguous or contradicted by the clarified text, +and the clarification selects no behaviour that the prior approved blob did +not already select. + +Report lifecycle validation (the current-gate formulations named by the +clarification authorization are described rather than restated, so that this +record does not reintroduce them): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -n -i -E '||||||' +every match read: each is inside this validation record's own description, +historical stage-time evidence phrased as such, a quotation of a historical +register or ADR state, or a durable rule; none states which PR #960 +review, Ready, thread-adjudication or merge-authorization gate is currently +pending or completed +``` + +Final-head review identifiers (the review, thread and review-comment +identifiers of the final-head automated review of the transient-state +correction head are described rather than restated so that this report does +not contain them): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -c -F -e '' -e '' -e '' -e '' -e '' +0 +``` + +Every other path byte-identical to the transient-state correction head: + +```text +git diff --cached --name-only 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a -- . ':!docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md' ':!docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md' +(no output) +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED, clarified) 2db08058552ce901548ef3f71af4a294f5af7e61 +decision-register.md (unchanged) b358acd5bbedf7482cfdc7b71866815c852773af +CHANGELOG.md (unchanged) e2947275d19d3abb351a98854268d7db213370d7 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2db08058552ce901548ef3f71af4a294f5af7e61 +``` + +This report's own final blob, the clarification tree and the clarification +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the clarification commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a HEAD +required: exit 0; no output + +git diff --name-only 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a HEAD +required: exactly ADR-0012 and this report + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -2886,6 +3384,20 @@ head, and no identifier of the control adjudication that classified it, occurs in this report; confirmed that the report's source lineage is unchanged; and read the report for statements that would become false once it moves into the correction commit and reframed them. +Factual-clarification stage: compared the staged ADR-0012 with the prior +approved blob `a7d95ca918f418d596432071c8ba78a33c2630a9` hunk by hunk (three +hunks, each one cardinality sentence and its rewrapped continuation); +confirmed for each of the seven disposition cases listed in section 9 that +the clarified text states it without ambiguity and that the surrounding +mismatch, enrollment, divergence, uniqueness-boundary and idempotency text is +byte-identical; confirmed that header lines 3-6 and section 12 are unchanged; +searched the report for every current-gate formulation named by the +clarification authorization and reframed each as the durable lifecycle model +or as explicitly historical stage evidence; confirmed by fixed-string search +that no review, thread or review-comment identifier of the final-head review +of the transient-state correction head occurs in this report; and read the +report for statements that would become false once it moves into the +clarification commit and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -2893,10 +3405,11 @@ candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head `9db91af7bf3d9f969556c819187420fc31d25c10`, the content-approved Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, the Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865` -and the control-record recovery head -`22bd94dc959f11fe49c8bef8cd2a836140aadff7`, are historical evidence; the live -PR #960 diff of the correction commit exists only after its publication and is -GitHub-owned lifecycle evidence. +the control-record recovery head +`22bd94dc959f11fe49c8bef8cd2a836140aadff7` and the transient-state correction +head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, are historical evidence; the +live PR #960 diff of the clarification commit exists only after its +publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -2906,9 +3419,10 @@ build, test, lint, format, migration and Docker jobs skipped). Checks: as listed in section 4.3. Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 recovery authorization, the Amendment-6/7 candidate and approval-state -authorizations, the control-record recovery authorization and the -transient-state correction authorization, an automatic CI failure, or any -migration execution or Docker publication, is a HOLD condition. +authorizations, the control-record recovery authorization, the +transient-state correction authorization and the factual-clarification +authorization, an automatic CI failure, or any migration execution or Docker +publication, is a HOLD condition. ## 12. Rollout and rollback @@ -2916,47 +3430,47 @@ Initial state after merge: after a guarded merge into `feature/worker`, the approved ADR-0012 version would be reachable from the programme integration branch only. It is not repository-authoritative until its authority condition, including reachability from `develop`, is met, and ADR-0013 programme-local -reliance additionally requires the programme-pin conditions on #957. Merge -itself still requires, in order: independent inspection of the exact -transient-state correction head, at which the unchanged ADR-0012 blob -`a7d95ca918f418d596432071c8ba78a33c2630a9` keeps its existing CTO -exact-final-blob approval; any separately authorized PR #960 metadata -reconciliation; separately authorized Draft -> Ready; fresh automatic -final-head CI and configured Codex/app review; adjudication of every -historical and current review thread; fresh independent CRITICAL exact-head -review; separate CTO exact-head merge authorization; then the guarded merge -and programme-pin/reliance reconciliation. +reliance additionally requires the programme-pin conditions on #957. +Independent exact-head review of whichever head is presented for merge, CTO +merge authorization where the task's risk requires it, the guarded merge, +programme-pin/reliance reconciliation and any later migration, deployment, +activation and observation are distinct controlled lifecycle categories under +the repository release gates and the task specification; GitHub, through #958 +and PR #960, is authoritative for which of them is pending, satisfied or +blocked for any head, and this report does not record that state. Activation required: NONE. Merging the eventual approved version would make no runtime, schema, provider or deployment change. Feature flag/configuration: NONE Migration sequence: NONE -Rollback/disable procedure: before merge, stop at the current gate; no history -rewrite, force push or amend is permitted. The Amendment-6/7 authorizations -permitted exactly one candidate commit and exactly one approval-state commit, -the control-record recovery authorization permitted exactly one report-only -recovery commit, and the transient-state correction authorization permits -exactly one report-only correction commit; if the correction head needs -correction, those authorizations are exhausted and a fresh CTO authorization -is required (Amendment 3 C10; Amendments 6-7). +Rollback/disable procedure: before merge, stop at whichever lifecycle gate +GitHub records as pending; no history rewrite, force push or amend is +permitted. The Amendment-6/7 authorizations permitted exactly one candidate +commit and exactly one approval-state commit, the control-record recovery and +transient-state correction authorizations each permitted exactly one +report-only commit, and the factual-clarification authorization permits +exactly one two-path clarification commit; any further source correction +exhausts those authorizations and requires a fresh CTO authorization +(Amendment 3 C10; Amendments 6-7; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- This transient-state correction stage does not include independent - inspection of the correction head, PR #960 title/description - reconciliation, Draft -> Ready, the fresh final-head Codex/app review, - adjudication of the historical and current review threads, the fresh - independent exact-head review or merge. The PR #960 title and description - were last reconciled at an earlier stage; whether they match the correction - head is GitHub-owned lifecycle state, and their reconciliation requires a - separate authorization. -- Programme-local reliance on ADR-0012 under ADR-0013 is not effective. The - stale #957 pin must be superseded using the corrected exact ADR-0012 blob and - the fresh independently reviewed PR #960 head; dependent `feature/worker` - slices must be rechecked and recorded as affected or `NONE` with the inspected - evidence (Amendment 1 A9, Amendment 2 B9, Amendment 3 C9) before that - supersession; ADR-0013 conditions 7 and 8 then follow the actual merge and - reachability evidence. +- Historical stage-time evidence: at the factual-clarification stage the + implementing agent performed no independent review, PR #960 + title/description reconciliation, Draft/Ready change, automated-review + request, thread adjudication, exact-head review or merge (section 4.2). + Those are separate controlled lifecycle categories; GitHub, through #958 and + PR #960, is authoritative for their current state, including whether the + PR title and description match the current head. +- Programme-local reliance on ADR-0012 under ADR-0013 is exact-version-bound + and separately controlled. Any #957 pin bound to an earlier ADR-0012 blob or + PR #960 head is stale for the clarified version until superseded using the + exact clarified ADR-0012 blob and an independently reviewed PR #960 head; + dependent `feature/worker` slices must be rechecked and recorded as affected + or `NONE` with the inspected evidence (Amendment 1 A9, Amendment 2 B9, + Amendment 3 C9) before that supersession; ADR-0013 conditions 7 and 8 follow + the actual merge and reachability evidence. #957 is authoritative for the + current pin state. - The repository-wide Route-B recovery ambiguity raised on PR #967 (finding `4168223564`) is not resolved here. For #958, Amendment 3 C10 fails closed: any correction needing another candidate commit, another approval-state @@ -2966,9 +3480,13 @@ Monitoring required: NONE so was the final-head route-contract finding (Amendments 6-7, one candidate commit and one approval-state commit). The approval-state control-record defect was likewise corrected only under a separate authorization for one - report-only recovery commit, and the transient review-thread-state defect - found by the final-head review of that recovery head only under a separate - authorization for this one report-only correction commit. + report-only recovery commit, the transient review-thread-state defect found + by the final-head review of that recovery head only under a separate + authorization for one report-only correction commit, and the overbroad + disposition-cardinality sentence and remaining live-gate wording found by + the final-head review of that correction head only under the CTO + factual-clarification classification and a separate authorization for this + one two-path clarification commit. - Exact table, column, index and encoding names for route-contract revisions, anomalies and capability declarations are deliberately left to the future shared-engine implementation specification. @@ -2977,8 +3495,8 @@ Monitoring required: NONE `b4670b8b7cb6d61bc42fb8fd2c40b1d8acf70b20` (Appendix A) remains historical; section 9 is exact-worktree evidence for the activation-epoch candidate, its approval-state commit, the recovery commit, the Amendment-6/7 candidate, its - approval-state commit, the control-record recovery and this transient-state - correction. + approval-state commit, the control-record recovery, the transient-state + correction and this factual clarification. ## 14. Unresolved issues @@ -3026,24 +3544,38 @@ Monitoring required: NONE identified that the recovery report treated the current number and resolved/unresolved state of PR #960 review threads as durable repository truth; control accepted it as a valid blocking control-record defect, and - the correction commit that contains this report addresses only that defect. + the transient-state correction commit + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` addressed only that defect. +- The configured final-head automated review of the transient-state + correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` opened two + further threads, identified here substantively only; control accepted both + as valid and blocking. One found that ADR-0012's unconditional "exactly one + durable disposition exists for each `(event_id, route_key)`" wording in + section 3.2, section 3.5 and invariant 8 was broader than the eligibility, + backlog, mismatch, enrollment and divergence semantics the same ADR selects; + the CTO classified its correction as a factual clarification of prior + approved blob `a7d95ca918f418d596432071c8ba78a33c2630a9`, and the + clarification commit that contains this report applies it (section 4; + section 5, decisions 44-46). The other found that the report still recorded + which live PR #960 lifecycle gates were pending; the same commit replaces + that wording with the durable lifecycle model (section 5, decision 47). PR #960 holds the historical and current review threads covering the findings -summarized above. GitHub is authoritative for their current number and -resolved/unresolved state; this report records only their durable substantive -dispositions and source evidence. Every applicable thread must be adjudicated -under the final exact-head review controls before merge, through the fresh -independent CRITICAL exact-head review of the eventual final head. This report -itself neither resolves nor dismisses any thread. +summarized above. GitHub is authoritative for their current number, +resolved/unresolved state and adjudication; this report records only their +durable substantive dispositions and source evidence. Adjudication of review +threads belongs to the independent exact-head review controls that precede any +merge, not to this report, which neither resolves nor dismisses any thread. ## 15. Agent self-assessment The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this transient-state correction also -implemented the control-record recovery, the Amendment-6/7 approval-state -commit and candidate and earlier Route-B source stages of this task and +work. The implementing agent of this factual clarification also implemented +the transient-state correction, the control-record recovery, the Amendment-6/7 +approval-state commit and candidate and earlier Route-B source stages of this +task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). @@ -3059,10 +3591,20 @@ Suggested review focus: approval date 2026-10-06, candidate head `b596536e7db0d6b8ace0a42fdd26bff42cabf104` and candidate ADR-0012 blob `cd897d576522a741c3dfa82f8de2f8d80d4288f6`, that the approval date matches - the CTO candidate-content approval record, and that the final ADR-0012 blob - `a7d95ca918f418d596432071c8ba78a33c2630a9` is the blob presented for - exact-final-blob confirmation; -- that the transient-state correction changes only this report, that + the CTO candidate-content approval record, and that the approval-state + ADR-0012 blob `a7d95ca918f418d596432071c8ba78a33c2630a9` is the exact prior + approved blob that the factual clarification identifies; +- that the factual clarification changes ADR-0012 only in the three + cardinality sentences recorded in section 4 and section 9 + ("Factual-clarification validation"), that the clarified blob is + `2db08058552ce901548ef3f71af4a294f5af7e61`, that it continues to require + an eventual single disposition for every eligible route obligation, to + prevent a second disposition for any `(event_id, route_key)` and to state + every no-disposition case unchanged, that the header approval fields + and section 12 are unchanged, and that no sentence of this report states + which PR #960 lifecycle gate is currently pending (section 5, decisions + 44-48); +- that the transient-state correction changed only this report, that ADR-0012, the decision register and the changelog keep their recovery-head blobs, that no sentence presents the current count or resolved/unresolved state of PR #960 review threads as durable repository truth, that the diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index a7d95ca91..2db080585 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -356,12 +356,14 @@ alone are insufficient. For every committed event, the engine must eventually establish a durable route disposition for every route for which that event is eligible, and a durable in-epoch contract-mismatch anomaly for every route for which it is mismatched. -Exactly one durable disposition exists for each `(event_id, route_key)`. The -normal disposition materializes a job: a materialized disposition points to -exactly one job and remains unique on `(event_id, route_key)` regardless of -whether that job is pending, running, waiting or terminal. The only alternative -is the explicit, authorized and audited terminal non-materialized disposition -described below, which points to no job (section 3.5 and invariant 8). +At most one durable disposition may exist for a given `(event_id, route_key)`, +and an eligible route obligation remains owed, visible in the route backlog, +until it has received that one disposition. The normal disposition +materializes a job: a materialized disposition points to exactly one job and +remains unique on `(event_id, route_key)` regardless of whether that job is +pending, running, waiting or terminal. The only alternative is the explicit, +authorized and audited terminal non-materialized disposition described below, +which points to no job (section 3.5 and invariant 8). Disposition uniqueness is per logical route, not per epoch or per revision: `(event_id, route_key)` is the complete uniqueness boundary across every epoch @@ -980,8 +982,9 @@ current-state execution and backfill create new logical jobs with new effect identity and explicit parent/replay/correlation metadata, following ADR-0010's distinction between retry, replay and current-state redistribution. -Event routing identity and job idempotency are related but distinct. Exactly one -durable disposition exists for each `(event_id, route_key)`. A successfully +Event routing identity and job idempotency are related but distinct. At most +one durable disposition may exist for a given `(event_id, route_key)`, and an +eligible route obligation eventually receives exactly one. A successfully materialized disposition points to exactly one materialized job. Several materialized route dispositions may point to one **not-yet-started** job only when the kind defines an explicit durable coalescing rule. An explicit, @@ -1824,10 +1827,12 @@ execution until a separate approved architecture decision says otherwise. `(event_id, route_key)` unused. 7. Job idempotency identifies a specific intended effect; a resource-only key must not suppress later revisions/schedule slots/commands. -8. Exactly one durable disposition exists for each `(event_id, route_key)`. - A successfully materialized disposition maps to exactly one job; many - materialized dispositions may map to one not-yet-started job only under an - explicit durable coalescing rule. An explicit authorized/audited terminal +8. At most one durable disposition may exist for a given + `(event_id, route_key)`; an eligible route obligation remains owed until it + has received exactly one. A successfully materialized disposition maps to + exactly one job; many materialized dispositions may map to one + not-yet-started job only under an explicit durable coalescing rule. An + explicit authorized/audited terminal non-materialized disposition maps to no job, records actor, authority, reason and any covering reconciliation/backfill, and shares the same uniqueness boundary so it cannot coexist with a materialized disposition for From 42ae90a5932cf24ce90679196888cc3d83d88db8 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 7 Oct 2026 19:56:17 +0100 Subject: [PATCH 34/48] THOTH-ASYNC-01-ADR-01: propose withdrawal-selection ordering correction --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1030 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 168 ++- .../decisions/decision-register.md | 4 +- 4 files changed, 984 insertions(+), 219 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2947275d..f3c2bc797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `CTRL-ADR-PR-FIRST-01`: add a PR-first staged approval route for a material pre-authority ADR correction to the engineering-decision amendment process in [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md) (issue [966](https://github.com/thoth-pub/thoth/issues/966)). The existing pre-commit exact-final-blob approval route (now Route A) is retained unchanged. Route B lets the implementing agent push the corrected candidate to a real task branch and draft PR as `PROPOSED`, without presenting the earlier version's approver or approval date as approval of the corrected bytes; the CTO then records architecture-content approval against the exact candidate blob/head from the PR; only afterwards may exactly one bounded approval-state commit record `PROPOSED -> APPROVED` with the fresh approver/date and authorized companion metadata; the CTO records exact-final-blob approval of the final ADR blob before merge; fresh independent exact-head review binds to the final PR head; and HIGH/CRITICAL merge authorization and the guarded merge remain separate gates. The doctrine records why that approval-state commit is durable decision state rather than the lifecycle transcription `ADR-0005` prohibits. Material-correction eligibility, the stale-control list, `ADR-0013` exact-version reliance/drift rules and the repository-authority condition are unchanged. **Documentation/control only: no ADR is changed by this task, and no runtime, schema, migration, API, authorization, workflow, provider/IAM, deployment, release or production behaviour is authorized or affected.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index c47b874f1..f85da63b8 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -10,17 +10,20 @@ candidate and approval-state stages of the immutable route-contract-revision correction specified by Specification Amendments 6-7, the one-path report-only control-record recovery of that approval-state report, the one-path report-only transient-state correction that followed the configured -final-head automated review of that recovery head, and the two-path factual +final-head automated review of that recovery head, the two-path factual clarification of ADR-0012 and durable-report correction that followed the -configured final-head automated review of that correction head. It records +configured final-head automated review of that correction head, and the +Route-B candidate stage of the revision-withdrawal/selection ordering +correction specified by Specification Amendment 8 after the configured +final-head automated review of that clarification head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the factual clarification and durable-report -correction as the current stage and every earlier candidate, approval-state, -recovery and report-only correction stage recorded as history. Appendix A +Route-B correction stages, with the Amendment-8 Route-B candidate as the +current stage and every earlier candidate, approval-state, recovery, +report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the durable control conditions. @@ -41,11 +44,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the factual-clarification commit that contains this report, an -ordinary single-parent additive commit whose direct parent is the historical -report-only transient-state correction head -`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`; its SHA is deliberately not -self-pinned here (section 3). That correction head is the direct child of the +Head commit: the Amendment-8 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical factual-clarification head +`eb3129f13a9c858b3c012ee182315801f0b8563a`; its SHA is deliberately not +self-pinned here (section 3). That clarification head is the direct child of +the historical report-only transient-state correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, which is the direct child of the historical report-only control-record recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, which is the direct child of the historical Amendment-6/7 approval-state head @@ -57,7 +62,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The clarification commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -70,9 +75,9 @@ Implementing model: activation-epoch candidate and approval-state stages - Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded -implementing agent; transient-state correction and factual-clarification -stages - Claude (Fable 5.1), bounded implementing agent. Earlier rounds are -recorded in Appendix A. +implementing agent; transient-state correction, factual-clarification and +Amendment-8 candidate stages - Claude (Fable 5.1), bounded implementing agent. +Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -168,12 +173,22 @@ decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af (unchanged by th CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 (unchanged by the factual clarification) implementation-report blob 81db6d33bc0d103e149dd99e1f35150f35c079e3 (superseded by the factual clarification) -factual clarification (the commit that contains this report): -ADR-0012 blob 2db08058552ce901548ef3f71af4a294f5af7e61 (APPROVED; Javi, CTO; 2026-10-06; factual clarification of a7d95ca918f418d596432071c8ba78a33c2630a9) - -feature/worker at the recovery, the candidate, the approval-state commit, the -control-record recovery, the transient-state correction and the factual -clarification (unchanged): +historical factual-clarification head / Amendment-8 candidate parent: +eb3129f13a9c858b3c012ee182315801f0b8563a +tree 5244d6ac76578a6f78cde79671762da5b09f2fc1 +ADR-0012 blob 2db08058552ce901548ef3f71af4a294f5af7e61 (APPROVED; Javi, CTO; 2026-10-06; factual clarification of a7d95ca918f418d596432071c8ba78a33c2630a9; the exact prior approved blob that the Amendment-8 correction corrects) +decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af +CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 +implementation-report blob c47b874f1a48449e6c8f60a97f00ed1506d55fb9 (superseded by the Amendment-8 candidate) + +Amendment-8 Route-B candidate (the commit that contains this report): +ADR-0012 blob cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 (PROPOSED; material architectural correction of 2db08058552ce901548ef3f71af4a294f5af7e61) +decision-register blob 45ce935149693d0e983fac1645f5363896e23a53 +CHANGELOG blob f3c2bc7976d5fb84e1a00cbfce05748197da900b + +feature/worker at the recovery, the candidates, the approval-state commits, the +control-record recovery, the transient-state correction, the factual +clarification and the Amendment-8 candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -231,7 +246,16 @@ Approved specification: final-head review of the transient-state correction head: #958 comment `6041284313`; - factual-clarification and durable-report correction authorization: #958 - comment `6041301223`. + comment `6041301223`; +- Specification Amendment 8 / revision-withdrawal selection linearization, + which accepts the final-head finding `R-EB31-01` as valid and blocking and + classifies its correction as a material architectural correction: #958 + comment `6042399237`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-8, including five non-blocking candidate-inspection + observations: #958 comment `6044203556`; +- Amendment-8 Route-B candidate implementation authorization: #958 comment + `6044220221`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -245,14 +269,18 @@ approval-state report recorded that content approval, and a second one-path report-only correction, made after the configured final-head automated review of that recovery head, removed statements that treated the live number and resolved/unresolved state of PR #960 review threads as durable repository -truth. The commit that contains this report is a two-path correction made -after the configured final-head automated review of that correction head: it -applies the CTO-classified factual clarification of ADR-0012's -route-disposition cardinality wording to the prior approved ADR blob -`a7d95ca918f418d596432071c8ba78a33c2630a9`, and removes the report's remaining -statements about which live PR #960 lifecycle gate was pending. It changes no -decision-register or changelog byte and makes no material architecture -change. +truth. A two-path correction made after the configured final-head automated +review of that correction head then applied the CTO-classified factual +clarification of ADR-0012's route-disposition cardinality wording to the prior +approved ADR blob `a7d95ca918f418d596432071c8ba78a33c2630a9` and removed the +report's remaining statements about which live PR #960 lifecycle gate was +pending. The configured final-head automated review of that clarification head +exposed a further architecture defect, `R-EB31-01`, specified by Specification +Amendment 8. The commit that contains this report is its Route-B candidate: one +ordinary single-parent commit on the clarification head that changes exactly +the four authorized correction paths, returns ADR-0012 to `Status: PROPOSED` +and requires revision withdrawal and every new durable selection of a +route-contract revision to share one database-atomic linearized order. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -401,9 +429,10 @@ change. another candidate, not another approval-state commit and not an ADR amendment, and that stage authorized no PR-state or PR-metadata change. - **Final-head review of the transient-state correction head, factual - clarification and durable-report correction (the commit that contains this - report).** Authorized by #958 comment `6041301223` after the CTO - classification and adjudication record #958 comment `6041284313`. Control + clarification and durable-report correction (historical clarification head + `eb3129f13a9c858b3c012ee182315801f0b8563a`).** Authorized by #958 comment + `6041301223` after the CTO classification and adjudication record #958 + comment `6041284313`. Control separately marked PR #960 Ready at the transient-state correction head, which triggered the configured final-head automated review of `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, and returned PR #960 to Draft @@ -422,19 +451,59 @@ change. receives exactly one, and every existing no-disposition case and duplicate prevention rule is unchanged. The second found that the report still recorded which live PR #960 lifecycle gates were pending. One ordinary - single-parent commit directly on the transient-state correction head changes + single-parent commit directly on the transient-state correction head changed only ADR-0012 and this report: the three unconditional cardinality - sentences are replaced by the at-most-one rule with the eventual-completion + sentences were replaced by the at-most-one rule with the eventual-completion clause (section 4; section 9, "Factual-clarification validation"), and the - report's current-gate statements are replaced by the durable lifecycle + report's current-gate statements were replaced by the durable lifecycle model under which GitHub, through #958 and PR #960, is authoritative for - the current gate. ADR-0012 keeps `Status: APPROVED`, `Approved by: Javi, - CTO` and `Approval date: 2026-10-06`; under the factual-clarification rule - the existing architecture approval remains applicable within the classified - scope. No decision-register or changelog byte changes. It is not a - Specification Amendment, not another Route-B candidate or approval-state - commit and not a material architectural correction. This stage authorizes - no PR-state or PR-metadata change. + the current gate. ADR-0012 kept `Status: APPROVED`, `Approved by: Javi, + CTO` and `Approval date: 2026-10-06` (blob + `2db08058552ce901548ef3f71af4a294f5af7e61`); under the factual-clarification + rule the existing architecture approval remained applicable within the + classified scope. No decision-register or changelog byte changed. It was not + a Specification Amendment, not another Route-B candidate or approval-state + commit and not a material architectural correction, and that stage + authorized no PR-state or PR-metadata change. +- **Final-head review of the factual-clarification head, Specification + Amendment 8 and the Amendment-8 Route-B candidate (the commit that contains + this report).** Authorized by #958 comment `6044220221` after the CTO + approved Specification Amendment 8 (#958 comment `6042399237`) and a fresh + independent CRITICAL specification review of the base specification and + Amendments 1-8 returned `APPROVED` (#958 comment `6044203556`). Control + separately marked PR #960 Ready at the clarification head, which triggered + the configured final-head automated review of + `eb3129f13a9c858b3c012ee182315801f0b8563a`, and PR #960 had been returned to + Draft before any candidate source was staged. That review raised one new + finding, recorded by control as `R-EB31-01` (HIGH, valid and blocking) and + classified as a `MATERIAL ARCHITECTURAL CORRECTION`: ADR-0012 required that + a withdrawn revision not be selected for a new epoch or a new historical + enrollment, and serialized epoch opening, closing and retirement per + `route_key`, but did not require revision withdrawal to share a database + linearization boundary with the operations that newly select a revision. An + activation or enrollment could therefore read a revision as available, a + withdrawal could commit, and the activation or enrollment could then commit + a new epoch, reference or enrollment from its stale availability + observation; the reverse order was likewise undefined. Amendment 8 selects + one atomic availability/selection order: for one route, withdrawal and + every operation that creates a new durable selection of or reference to a + revision share one database-atomic serialization or constraint boundary; + both commit orders are defined; a losing transaction re-reads durable state + before any retry; an application-level check-then-insert is insufficient; + the mechanism remains an implementation-specification detail that must + compose with the existing route lifecycle serialization; and withdrawal + semantics are otherwise unchanged. The independent review confirmed the + finding, the classification and the sufficiency of the nine-case + concurrency validation matrix, and recorded five non-blocking + candidate-inspection observations. The commit that contains this report is + one ordinary single-parent commit directly on the clarification head that + changes exactly the four authorized correction paths: ADR-0012 returns to + `Status: PROPOSED` with no current approver or approval date and implements + Amendment 8 (section 4; section 5, decisions 49-60); the decision-register + ADR-0012 row, one new changelog entry and this report describe the + corrected version as `PROPOSED`; and the 2026-09-30, 2026-10-05 and + 2026-10-06 CTO approvals remain historical evidence bound to the earlier + exact versions. This stage authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -448,6 +517,10 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 2db08058552ce901548ef3f71af4a294f5af7e61 +(Specification Amendment 8; Route-B candidate stage; candidate status PROPOSED) + +historical ADR-0012 change at eb3129f13a9c858b3c012ee182315801f0b8563a: FACTUAL CLARIFICATION of prior approved blob a7d95ca918f418d596432071c8ba78a33c2630a9 (docs/engineering/decisions/README.md, Amendments; CTO classification on #958; no Specification Amendment and no new Route-B cycle) @@ -459,8 +532,9 @@ number, not to one pathname (Amendment 1 A1; validation time `b44303c214498baf76c9d9a0a7cab374377f9cbe` was simultaneously `develop` and the refreshed `feature/worker`, and `master` was `4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`. Those refs were unchanged at the -Amendment-6/7 candidate stage, and the same literal commands were run again -against both commits with identical results: +Amendment-6/7 candidate stage and at the Amendment-8 candidate stage, and the +same literal commands were run again at each stage against both commits with +identical results: ```text git log --format=%h -- ':(glob)**/*ADR-0012*' @@ -544,43 +618,81 @@ the historical report-only control-record recovery `22bd94dc959f11fe49c8bef8cd2a836140aadff7` nor the historical report-only transient-state correction `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` changed ADR-0012; both left the approved blob -`a7d95ca918f418d596432071c8ba78a33c2630a9`. The factual clarification that -contains this report changes that blob to +`a7d95ca918f418d596432071c8ba78a33c2630a9`. The historical factual +clarification `eb3129f13a9c858b3c012ee182315801f0b8563a` changed that blob to `2db08058552ce901548ef3f71af4a294f5af7e61` within the CTO-classified clarification scope only; under the factual-clarification rule the selected -architecture and its approval are unchanged, while any source-head-bound +architecture and its approval were unchanged, while any source-head-bound review or merge authorization and any ADR-0013 exact-version programme pin -bound to an earlier blob or head are stale for the clarified version until -separately reconciled. Independent exact-head review, CTO merge authorization, -guarded merge, programme-local reliance and later migration, deployment, -activation and observation are distinct controlled lifecycle categories; -GitHub, through #958 and PR #960, is authoritative for which of them is -pending, satisfied or blocked for any head. +bound to an earlier blob or head became stale for the clarified version. + +The Amendment-8 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every record bound to the route-contract-revision +version as well: + +- the CTO content approval of candidate + `b596536e7db0d6b8ace0a42fdd26bff42cabf104` / ADR blob + `cd897d576522a741c3dfa82f8de2f8d80d4288f6`; +- the 2026-10-06 approval state carried by ADR blobs + `a7d95ca918f418d596432071c8ba78a33c2630a9` and + `2db08058552ce901548ef3f71af4a294f5af7e61`, and any exact-final-blob + approval or head-bound confirmation of either; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the clarification head; +- the clarification-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry that + record the route-contract-revision version as `APPROVED`; the changelog + entry is preserved and remains true only of that historical version, while + the register row now records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. For +the candidate version, independent exact-candidate inspection, CTO exact +candidate-content approval, the approval-state commit, exact-final-blob +approval, independent exact-head review, CTO merge authorization, guarded +merge, programme-local reliance and later migration, deployment, activation +and observation are distinct controlled lifecycle categories; GitHub, through +#958 and PR #960, is authoritative for which of them is pending, satisfied or +blocked for any head. ## 3. Commits -Factual-clarification commit (the commit that contains this report): +Amendment-8 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - + `THOTH-ASYNC-01-ADR-01: propose withdrawal-selection ordering correction` + - direct parent: the historical factual-clarification head + `eb3129f13a9c858b3c012ee182315801f0b8563a` + - changes only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, the + candidate decision-register blob `45ce935149693d0e983fac1645f5363896e23a53` + and the candidate changelog blob `f3c2bc7976d5fb84e1a00cbfce05748197da900b` + +Its SHA, its tree and this report's final blob are deliberately not embedded +here because this report is part of that tree; the candidate ADR-0012, +decision-register and changelog blobs do not depend on this report and are +recorded above; every other file keeps its clarification-head blob (section +9). The commit identities are verified against the remote task-branch head +after push and recorded in the post-publication handoff; once published, the +PR #960 head and head tree are GitHub-owned lifecycle evidence under +`ADR-0005`. The authorized topology is a non-force fast-forward of the task +branch from the clarification head to the candidate commit; no merge commit, +rebase, squash, amend, force update or second candidate commit is authorized, +and no further source commit exists implicitly after it. + +Factual-clarification commit (historical): + +- `eb3129f13a9c858b3c012ee182315801f0b8563a` - `THOTH-ASYNC-01-ADR-01: clarify route-disposition cardinality` - - direct parent: the historical report-only transient-state correction head + - ordinary single-parent additive commit; direct parent: the historical + report-only transient-state correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` - - changes only ADR-0012 and this report (section 4); the clarified ADR-0012 + - tree `5244d6ac76578a6f78cde79671762da5b09f2fc1` + - changed only ADR-0012 and this report (section 4); the clarified ADR-0012 blob is `2db08058552ce901548ef3f71af4a294f5af7e61` - -Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the clarified ADR-0012 blob -does not depend on this report and is recorded above; every other file, -including the decision register and the changelog, keeps its transient-state -correction-head blob, which is its approval-state blob (section 9). The commit -identities are verified against the remote task-branch head after push and -recorded in the post-publication handoff; once published, the PR #960 head and -head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The authorized -topology is a non-force fast-forward of the task branch from the -transient-state correction head to the clarification commit; no merge commit, -rebase, squash, amend, force update or second clarification commit is -authorized, and no further source commit exists implicitly after it. + - published by a non-force fast-forward of `feature/async/adr-0012` from the + transient-state correction head; the remote head, tree, parent and + ADR-0012 blob matched the locally validated object Report-only transient-state correction commit (historical): @@ -705,10 +817,21 @@ first-parent order, newest first: ## 4. Files changed -Factual-clarification write budget for the commit that contains this report -(authorization #958 comment `6041301223`); exactly two existing paths, no new -file, no deletion, and every other path byte-identical to the transient-state -correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`: +Amendment-8 Route-B candidate write budget for the commit that contains this +report (authorization #958 comment `6044220221`; the Amendment 6 G10 / +Amendment 7 H12 budget, unchanged by Amendment 8); exactly four existing paths, +no new file, no deletion, and every other path byte-identical to the +clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical factual-clarification write budget (authorization #958 comment +`6041301223`); exactly two existing paths, no new file, no deletion, and every +other path byte-identical to the transient-state correction head +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`: - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -754,13 +877,92 @@ six paths at every stage): Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record -recovery, transient-state correction and factual-clarification stages; this -report and ADR-0012 already existed. +recovery, transient-state correction, factual-clarification and Amendment-8 +candidate stages; all four correction paths already existed. -Factual-clarification changes in the commit that contains this report, -relative to the transient-state correction head -`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` (exactly the two authorized -paths): +Amendment-8 candidate changes in the commit that contains this report, +relative to the clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a` +(exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement Specification Amendment 8 and record the corrected + version as `PROPOSED`. + - behavioural effect: architecture proposal only. The header carries + `Status: PROPOSED` and no current approver or approval date. In section + 3.2, the per-`route_key` serialization paragraph states that revision + withdrawal and every operation creating a new durable selection of a + revision share one database-atomic order that composes with that + serialization; the historical-enrollment validation requires the selected + revision to be available, decided inside that order in the transaction + that would create the disposition, and refuses a withdrawn revision before + any disposition or job exists; the "Revision withdrawal and route + retirement" subsection gains the Amendment-8 rule: the covered operations + (first activation, reactivation and revision-transition epoch opening, + first historical enrollment and the withdrawal itself as the minimum, with + the general rule covering every other new durable selection or + reference), the exclusion of non-durable preselection, both commit orders, + the re-read-before-retry rule, the insufficiency of an application-level + check-then-insert, the permitted mechanisms and composition requirements, + and the exclusion of explicit replay/current-state work for an + already-dispositioned event; and activation gate 4 is decided inside the + same order. Invariant 41 is appended; section 8 item 1 and section 9 + rollout proof 3 name the ordering; section 11 adds the nine Amendment-8 + concurrency validation cases after the revision-withdrawal tests. Section + 12 records the `PROPOSED` state, adds the ordering rule to the description + of this material correction and binds the 2026-09-30, 2026-10-05 and + 2026-10-06 approvals to the historical earlier versions. The existing + withdrawal semantics (append-only, audited, future-selection only, + non-destructive, strand prevention), route retirement and all unrelated + architecture are unchanged. Candidate blob: + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the material correction makes the ADR-0012 row's `APPROVED` + assertion untrue for the new version. + - behavioural effect: the ADR-0012 row records `PROPOSED` / `Pending`, + binds the 2026-09-30, 2026-10-05 and 2026-10-06 approvals to the + historical earlier exact versions and adds one summary of the + withdrawal/selection ordering rule; its partial-supersession, + authority-condition, ADR-0013 and implementation-authorization sentences + are unchanged. The header `Last updated` line advances from `2026-10-06` + to `2026-10-07`, the date of this row change (section 5, decision 57). No + other row changes. Candidate blob: `45ce935149693d0e983fac1645f5363896e23a53`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: required changelog entry. + - behavioural effect: exactly one new + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry at the top of + `[Unreleased] -> Changed`, describing the corrected version as `PROPOSED` + and stating that the earlier + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-CORRECTION`, `THOTH-ASYNC-01-ADR-01-APPROVAL` and + `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries and the approvals they + record apply only to the historical earlier exact versions. No heading is + added and every existing entry is byte-preserved. Candidate blob: + `f3c2bc7976d5fb84e1a00cbfce05748197da900b`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-8 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate the current + stage; records the final-head review of the clarification head, + `R-EB31-01`, Amendment 8, its independent review and the candidate + authorization as governing evidence; records the candidate's validation + (section 9); and reframes statements that would otherwise describe the + clarification commit as the commit containing this report, recording that + commit's identities as historical facts. All still-true earlier evidence + is preserved. + - within authorized write budget: YES + +Byte-identical to the clarification head through the Amendment-8 candidate: +every path other than the four correction paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical factual-clarification changes in the clarification commit +`eb3129f13a9c858b3c012ee182315801f0b8563a`, relative to the transient-state +correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` (exactly the two +authorized paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: the configured final-head automated review of the transient-state @@ -784,8 +986,8 @@ paths): cases (ineligible events, in-epoch contract mismatch, refused historical enrollment, approved reconciliation/divergence), the one allowed disposition from validated enrollment, duplicate prevention and job - idempotency are byte-identical. The header keeps `Status: APPROVED`, - `Approved by: Javi, CTO` and `Approval date: 2026-10-06`; section 12 is + idempotency were byte-identical. The header kept `Status: APPROVED`, + `Approved by: Javi, CTO` and `Approval date: 2026-10-06`; section 12 was unchanged. Clarified blob: `2db08058552ce901548ef3f71af4a294f5af7e61`. - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -794,18 +996,18 @@ paths): include", a next-step list of Ready, review, thread-adjudication and merge-authorization gates); control accepted that as a blocking control-record defect. - - behavioural effect: documentation only. Replaces every current-gate + - behavioural effect: documentation only. Replaced every current-gate statement with the durable lifecycle model (the ADR-0012 authority condition; independent exact-head review, CTO merge authorization, guarded merge, programme-local reliance, migration, deployment, activation and observation as distinct controlled lifecycle categories; GitHub, through #958 and PR #960, as the authority for the current gate); - records the final-head review of the transient-state correction head and - the CTO classification substantively, without review identifiers; records - the factual clarification and its validation (section 9); makes this - stage the current stage; and reframes statements that would otherwise - describe the transient-state correction commit as the commit containing - this report. All other historical evidence is preserved. + recorded the final-head review of the transient-state correction head and + the CTO classification substantively, without review identifiers; + recorded the factual clarification and its validation (section 9); made + that stage the then-current stage; and reframed statements that would + otherwise describe the transient-state correction commit as the commit + containing the report. All other historical evidence was preserved. - within authorized write budget: YES Byte-identical to the transient-state correction head through the factual @@ -990,7 +1192,7 @@ Amendment-6/7 candidate changes in the historical content-approved candidate Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state -correction and the factual clarification: +correction, the factual clarification and the Amendment-8 candidate: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -1169,7 +1371,11 @@ Files deleted, moved or renamed: NONE PASS -The factual clarification differs from the transient-state correction head +The Amendment-8 candidate differs from the clarification head +`eb3129f13a9c858b3c012ee182315801f0b8563a` in exactly the four authorized +correction paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-8 candidate validation"). The +factual clarification differed from the transient-state correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` in exactly the two authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Factual-clarification validation"). The transient-state @@ -1206,8 +1412,9 @@ Amendment-4/5 recovery stage (#958 comment `6013267369`), the Amendment-6/7 candidate stage (#958 comment `6021574322`), the Amendment-6/7 approval-state stage (#958 comment `6022486352`), the control-record recovery stage (#958 comment `6022850916`), the transient-state correction stage (#958 comment -`6036961945`) and the factual-clarification stage (#958 comment -`6041301223`). This matrix states only what had been done when the commit that +`6036961945`), the factual-clarification stage (#958 comment `6041301223`) +and the Amendment-8 candidate stage (#958 comment `6044220221`). This matrix +states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -1364,8 +1571,8 @@ clarification: review of the correction head, and returned it to Draft before the factual clarification (section 2) -Factual-clarification stage - done up to and including the creation of the -commit that contains this report: +Factual-clarification stage - historical, completed before the Amendment-8 +candidate: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -1377,7 +1584,7 @@ commit that contains this report: evidence, the PR #960 review-thread state in GitHub; all matched the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (control had already returned PR #960 to - draft; no PR-state, title or description change is authorized at this + draft; no PR-state, title or description change was authorized at that stage) - source edit: USED (ADR-0012 and this report only) - new file creation: NOT USED (not authorized) @@ -1385,39 +1592,73 @@ commit that contains this report: - branch creation: NOT USED (not authorized; a detached local worktree at the transient-state correction head was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent clarification commit that contains this - report) +- commit: USED (the single-parent clarification commit + `eb3129f13a9c858b3c012ee182315801f0b8563a`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` to the clarification commit; the + remote head, tree, parent and ADR-0012 blob were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the clarification head, and returned it to Draft before the + Amendment-8 candidate (section 2) + +Amendment-8 candidate stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `eb3129f13a9c858b3c012ee182315801f0b8563a` and target `feature/worker`, + the clarification head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008 + and ADR-0010 blobs, the six-path cumulative footprint, PR #960 as the only + open pull request targeting `feature/worker`, the presence of the Amendment + 8, independent-review and authorization records on #958, and, as + stage-time evidence, the PR #960 review-thread state in GitHub; all matched + the authorization; current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft at preflight, so + the one authorized Ready -> Draft mutation was not needed; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized correction paths only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; a detached local worktree at the + clarification head was used for construction, and the existing task branch + is the fast-forward target) +- commit: USED (the single-parent candidate commit that contains this report) -Authorized post-commit actions for the clarification commit - GitHub-owned +Authorized post-commit actions for the candidate commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` to the clarification commit; + `eb3129f13a9c858b3c012ee182315801f0b8563a` to the candidate commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012 and report blobs, changed path set, cumulative footprint and - PR #960 state, and that no review thread was replied to, resolved or - dismissed; + ADR-0012, decision-register, changelog and report blobs, changed path set, + cumulative footprint and PR #960 state, and that no review thread was + replied to, resolved or dismissed; - the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the factual-clarification stage, +Not authorized for the implementing agent at the Amendment-8 candidate stage, each a separate controlled lifecycle category where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): -- any edit to the decision register, the changelog or any path other than - ADR-0012 and this report: NOT USED (not authorized) -- any ADR-0012 change outside the classified factual-clarification scope: +- any edit to a path other than the four authorized correction paths: NOT USED + (not authorized) +- any ADR-0012 change outside Amendment 8 and the candidate `PROPOSED` state: NOT USED (not authorized) -- independent exact-head review or inspection: NOT USED (never on the - implementing agent's authority) +- independent exact-candidate inspection or exact-head review: NOT USED (never + on the implementing agent's authority) +- CTO candidate-content approval, approval-state commit, fresh `APPROVED` + status, approver or approval date, exact-final-blob approval: NOT USED (not + authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second clarification commit or material architecture change: NOT USED - (not authorized) +- any second candidate commit, amend, rebase, squash, force push or history + rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -1449,22 +1690,23 @@ classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments -4-5, Amendments 6-7, the transient-state correction authorization and the -factual-clarification authorization, configured Codex/app review is not -expected from a push to a draft PR and is not relied upon at the Amendment-6/7 -candidate, approval-state, control-record recovery, transient-state -correction or factual-clarification stage; configured Codex/app review runs on -separately authorized Ready transitions, which are GitHub-owned lifecycle -events. The actual job outcomes for the clarification commit exist only after -its publication; they are GitHub-owned lifecycle evidence recorded in the pull +4-5, Amendments 6-7, Amendment 8, the transient-state correction +authorization, the factual-clarification authorization and the Amendment-8 +candidate authorization, configured Codex/app review is not expected from a +push to a draft PR and is not relied upon at the Amendment-6/7 candidate, +approval-state, control-record recovery, transient-state correction, +factual-clarification or Amendment-8 candidate stage; configured Codex/app +review runs on separately authorized Ready transitions, which are GitHub-owned +lifecycle events. The actual job outcomes for the candidate commit exist only +after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of -this report or read back from GitHub at the transient-state correction or -factual-clarification stage, followed the same documentation-only -classification, with no manual dispatch or rerun: +this report or read back from GitHub at the transient-state correction, +factual-clarification or Amendment-8 candidate stage, followed the same +documentation-only classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -1477,6 +1719,7 @@ b596536e7db0d6b8ace0a42fdd26bff42cabf104: 4 success (classify x3, check-changelo 609a01427847233b7280557baa61825ac2079865: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-6/7 approval-state head) 22bd94dc959f11fe49c8bef8cd2a836140aadff7: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (control-record recovery head) 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (transient-state correction head) +eb3129f13a9c858b3c012ee182315801f0b8563a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (factual-clarification head) ``` Manually initiated external actions (anything the implementing agent @@ -1737,10 +1980,10 @@ Report-only transient-state correction (historical correction head section 1 records them here as historical facts because the correction head is the factual clarification's parent. -Factual clarification and durable-report correction (the commit that contains -this report): +Factual clarification and durable-report correction (historical clarification +head `eb3129f13a9c858b3c012ee182315801f0b8563a`): -44. The ADR-0012 change is confined to the CTO-classified factual +44. The ADR-0012 change was confined to the CTO-classified factual clarification of prior approved blob `a7d95ca918f418d596432071c8ba78a33c2630a9`: the unconditional sentence "exactly one durable disposition exists for each `(event_id, @@ -1762,14 +2005,14 @@ this report): and job idempotency were already stated as they remain. Had the contradiction required selecting new behaviour, the stage would have stopped as exceeding the factual-clarification boundary. -46. ADR-0012 keeps `Status: APPROVED`, `Approved by: Javi, CTO` and - `Approval date: 2026-10-06`, and section 12 is unchanged, because the +46. ADR-0012 kept `Status: APPROVED`, `Approved by: Javi, CTO` and + `Approval date: 2026-10-06`, and section 12 was unchanged, because the Amendments rules of `docs/engineering/decisions/README.md` let a factual clarification update an approved ADR in place with the existing architecture approval remaining; no `PROPOSED` candidate, approval-state - commit, changelog entry or register change belongs to this stage, and - the clarified ADR blob is recorded in section 1 for independent control - inspection because it does not depend on this report's bytes. + commit, changelog entry or register change belonged to that stage, and + the clarified ADR blob is recorded in section 1 because it does not + depend on the report's bytes. 47. The committed report records the ADR-0012 authority condition and the distinct controlled lifecycle categories (independent exact-head review, CTO merge authorization where required, guarded merge, programme-local @@ -1786,12 +2029,116 @@ this report): section 14) without review, thread or review-comment identifiers, consistent with decisions 15 and 41; the classification and authorization records are identified through owning issue #958 as every stage - authorization is. Because this report now lives in the clarification + authorization is. Because the report then lived in the clarification commit, statements that described the transient-state correction commit - as the commit containing this report are reframed as history, and that - commit's identities, now the clarification parent, are recorded in - section 1 as historical facts. This report does not self-pin the - clarification commit's SHA, its tree or its own final blob. + as the commit containing the report were reframed as history, and that + commit's identities, the clarification parent, were recorded in + section 1 as historical facts. The clarification report did not self-pin + the clarification commit's SHA, its tree or its own final blob; section 1 + records them here as historical facts because the clarification head is + the Amendment-8 candidate's parent. + +Amendment-8 Route-B candidate (the commit that contains this report). Items +53 and 57 are interpretations of the approved specification recorded for +explicit control inspection: + +49. The Amendment-8 rule is stated once, in the "Revision withdrawal and route + retirement" subsection of section 3.2, and the places that depend on it + point to it: the per-`route_key` serialization paragraph states that + withdrawal and new selection share one database-atomic order that + composes with that serialization; activation gate 4 states that "not + withdrawn" is decided inside that order; and historical-enrollment + validation states that the selected revision's availability is decided + inside that order in the transaction that would create the disposition. + This places the correction where the independent review located the gap + (the serialization rule covered epoch open, close and retirement but not + withdrawal) without restating the rule in several places. +50. The general rule is carried, not only its enumerated floor: the subsection + names first activation, reactivation and revision-transition epoch + opening, first historical enrollment and the withdrawal itself as the + minimum, and then states that every other operation that would create a + new durable selection of or reference to the revision is covered + (Amendment 8 I3/I4; independent-review observation 3). Non-durable + preselection, preview or other choices are excluded exactly as Amendment + 8 I4 excludes them. +51. Both commit orders are written as Amendment 8 I5 defines them, including + that a withdrawal-first epoch opening is refused before the epoch exists, + that a withdrawal-first enrollment is refused before any disposition or + job exists and leaves `(event_id, route_key)` unused, that a + selection-first reference remains valid historical state, and that a + losing transaction re-reads durable state before any retry and never + commits from a stale pre-withdrawal observation. +52. The mechanism is left to the implementation specification. The ADR names + the two forms Amendment 8 I6 permits (extending the existing + per-`route_key` serialization boundary, or another database-enforced + mechanism with equivalent atomic ordering), states that an + application-level check-then-insert without database-enforced ordering is + insufficient, and lists the composition requirements of I6 without + selecting a table, lock or constraint. +53. Withdrawal semantics are otherwise unchanged (Amendment 8 I7). The + candidate adds no withdrawal reversibility, no equivalent-signature + re-enablement rule, no second lifecycle order and no new route + abstraction (independent-review observations 2 and 4). For the reviewer's + strand-check timing observation (observation 1), the candidate reflects + only what Amendments 1-8 already determine: a selection that linearizes + first makes the later withdrawal subject to every existing + strand-prevention rule (I5), stated in the selection-first order and + exercised by a validation case covering obligations that became eligible + through an epoch whose opening linearized before the withdrawal. Whether + the strand-prevention evaluation of a withdrawal may be stale relative to + events or anomalies that commit concurrently with the withdrawal, which + are not revision selections, is not determined by Amendments 1-8; it is + left outside this correction and reported to control rather than decided + here. +54. Explicit replay or current-state work for an already-dispositioned event is + stated to be outside the first-selection rule and governed by section + 3.5, and not to become a new route disposition merely because a revision + has been withdrawn (Amendment 8 I4). No statement is added about whether + such work is refused. +55. Invariant 41 is appended after invariant 40 rather than inserted after + invariant 39, so the numbering of invariants 1-40, which other text and + earlier review records cite, is unchanged (decision 23 pattern). +56. The nine Amendment 8 I8 validation cases are added to section 11 + immediately after the existing revision-withdrawal tests, grouped so that + each numbered case is identifiable: cases 1-2 (first activation, both + orders), case 3 (reactivation and revision transition, both orders), case + 4 (first enrollment, both orders), case 5, case 6, case 7, case 8 and case + 9, the last naming equivalent-revision convergence where it shares the + per-route mechanism (independent-review observation 5). All Amendment 1-7 + validation requirements are retained. +57. The decision-register header `Last updated` line advances to + `2026-10-07`, the date on which the candidate changed the ADR-0012 row, + for the reason recorded in decision 25: leaving it stale caused the + accepted blocking final-head finding in decision 10. It is a header line + in the authorized register path, not another row. +58. The header drops `Approved by` / `Approval date` and section 12 restores + the two candidate-state paragraphs used by the earlier Route-B candidates + (decisions 8 and 9), adds the ordering rule to the description of this + material correction and binds the 2026-10-06 approval, like the + 2026-09-30 and 2026-10-05 approvals, to the historical earlier version + without embedding its identities. The architecture-only, + authority-condition, partial-supersession and amendment paragraphs are + unchanged, so an approval-state delta would again be limited to the + status line, the approver/date and the sentence that says the corrected + content is not yet approved. +59. The changelog gains one new entry instead of rewording the existing + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, so every + historical entry, including the record of the 2026-10-06 approval, stays + byte-preserved; the new entry states which exact earlier versions those + entries describe (Amendment 3 C8; decision 26). +60. This report does not self-pin the candidate commit's SHA, its tree or its + own final blob; it records only non-self-referential evidence and the + candidate ADR-0012, decision-register and changelog blobs, which do not + depend on its bytes. Because the report now lives in the candidate + commit, statements that described the clarification commit as the commit + containing this report are reframed as history, and that commit's + identities, now the candidate parent, are recorded in section 1 as + historical facts. `R-EB31-01` is recorded by its control-assigned name, + and Amendment 8, its independent review receipt and the candidate + authorization by their #958 comment identifiers, as the specification + chain in section 2 already records every amendment, review receipt and + authorization; no review, thread or review-comment identifier of the + final-head automated review is copied. List any deviation from the specification requiring authorization: @@ -1841,11 +2188,17 @@ List any deviation from the specification requiring authorization: separate authorization exactly; no second path, new file, deletion, ADR, register or changelog change, second commit, amend, rebase, squash, force push or history rewrite was needed or performed at that stage. -- Factual-clarification stage: NONE. The clarification follows its separate +- Factual-clarification stage: NONE. The clarification followed its separate authorization exactly; no third path, new file, deletion, register or changelog change, ADR change outside the classified scope, second commit, amend, rebase, squash, force push or history rewrite was needed or performed - before the commit that contains this report was created. + before the clarification commit was created. +- Amendment-8 candidate stage: NONE. No fifth path, new file, deletion, ADR + change outside Amendment 8 and the candidate state, second commit, amend, + rebase, squash, force push or history rewrite was needed or performed + before the commit that contains this report was created. Decisions 53 and + 57 are recorded for control inspection as interpretations within the + authorized scope, not as deviations. ## 6. Database and migration effects @@ -1853,7 +2206,9 @@ Migration added: NO No schema, data, index, constraint or `thoth-api/src/schema.rs` effect. The activation-epoch and route-contract-revision model, including its required -database-level revision-equivalence property, is architecture for a future +database-level revision-equivalence property and its required database-atomic +ordering of revision withdrawal against new revision selection, is +architecture for a future separately authorized shared-engine implementation; it creates no migration obligation by itself and names no table, column or index. @@ -1863,12 +2218,12 @@ GraphQL/API changes: NONE Generated schema/client updates: NONE Backwards compatibility: unaffected; documentation/architecture only Deprecations: NONE -Cross-repository dependencies: NONE for the corrections and clarification -recorded here. The verified BE-04 +Cross-repository dependencies: NONE for the corrections, clarification and +candidate recorded here. The verified BE-04 per-consumer impact record for the ADR's later legacy-contract retirement is -preserved unchanged in Appendix A; neither the activation-epoch correction nor -the route-contract-revision correction changes a released contract or adds a -consumer. The route, revision, anomaly and materializer-compatibility concepts +preserved unchanged in Appendix A; neither the activation-epoch correction, +the route-contract-revision correction nor the withdrawal/selection ordering +correction changes a released contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts are internal to the future shared engine; any later external read or executor contract derived from them needs its own specification, merged upstream contract and downstream task. @@ -1900,6 +2255,8 @@ approval-state commit `609a01427847233b7280557baa61825ac2079865`. recovery commit `22bd94dc959f11fe49c8bef8cd2a836140aadff7`. "Transient-state correction validation" records the validation of the historical correction commit `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`. "Factual-clarification +validation" records the validation of the historical clarification commit +`eb3129f13a9c858b3c012ee182315801f0b8563a`. "Amendment-8 candidate validation" at the end of this section records the validation of the commit that contains this report. @@ -3292,13 +3649,17 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 2db08058552ce901548ef3f71af4a294f5af7e61 ``` -This report's own final blob, the clarification tree and the clarification -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and recorded in the -post-publication handoff. +The clarification report did not self-pin its own final blob, the +clarification tree or the clarification commit SHA; they were verified against +the remote head after push and recorded in the post-publication handoff, and +section 1 now records them as historical facts: commit +`eb3129f13a9c858b3c012ee182315801f0b8563a`, tree +`5244d6ac76578a6f78cde79671762da5b09f2fc1`, report blob +`c47b874f1a48449e6c8f60a97f00ed1506d55fb9`. -Required topology of the clarification commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the clarification commit, checked after it was created +and before push; the published clarification commit satisfied every +requirement: ```text git rev-list --parents -n 1 HEAD @@ -3317,6 +3678,212 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-8 candidate validation + +Environment: a full local Git worktree of `thoth-pub/thoth`, detached at the +historical factual-clarification head +`eb3129f13a9c858b3c012ee182315801f0b8563a` (tree +`5244d6ac76578a6f78cde79671762da5b09f2fc1`), with every required object +present locally. Immediately before construction, read-only GitHub inspection +showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `master` at +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`, `feature/async/adr-0012` at +`eb3129f13a9c858b3c012ee182315801f0b8563a` with single parent +`9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` and tree +`5244d6ac76578a6f78cde79671762da5b09f2fc1`, the ADR-0012, report, +decision-register, changelog, ADR-0008 and ADR-0010 blobs named by the +candidate authorization at that head, PR #960 open, draft, unmerged and +mergeable with that head and target `feature/worker` and the only open pull +request targeting `feature/worker`, the cumulative PR footprint exactly the six +paths, the Amendment 8, independent-review and authorization records present on +#958 with no later control record, and PR #960 review-thread state that +matched the stage authorization (stage-time evidence; current review-thread +state remains GitHub-owned). ADR-0012, the decision register and the changelog +were edited first and their deterministic blobs recorded; then only this report +was edited; all four were staged, and every check below ran against that exact +final staged index before the candidate commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check eb3129f13a9c858b3c012ee182315801f0b8563a +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the clarification head (exactly the four authorized +paths): + +```text +git diff --cached --name-only eb3129f13a9c858b3c012ee182315801f0b8563a +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status eb3129f13a9c858b3c012ee182315801f0b8563a +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat eb3129f13a9c858b3c012ee182315801f0b8563a +1 0 CHANGELOG.md +839 191 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +142 26 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (only the `Last updated` header line and the ADR-0012 +row; every other line byte-identical): + +```text +git diff --cached -U0 eb3129f13a9c858b3c012ee182315801f0b8563a -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-06 + +git diff --cached -U0 eb3129f13a9c858b3c012ee182315801f0b8563a -- docs/engineering/decisions/decision-register.md | grep '^[-+]Last updated' +-Last updated: 2026-10-06 ++Last updated: 2026-10-07 + +diff <(git show eb3129f13a9c858b3c012ee182315801f0b8563a:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n 20p | grep -o '^| `ADR-0012` | [^|]* | [A-Z]* |' +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 eb3129f13a9c858b3c012ee182315801f0b8563a -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state, outline and hunks: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +2613:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'APPROVED' +every match is in section 12 (lines 2616-2655): the sentence that the version +may carry APPROVED only after CTO approval, the historical-approval paragraph +and the authority condition; no sentence asserts that this version is approved + +diff <(git show eb3129f13a9c858b3c012ee182315801f0b8563a:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +exit 0; no output (heading outline unchanged) + +git diff --cached -U0 eb3129f13a9c858b3c012ee182315801f0b8563a -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -5,2 +4,0 @@ Date: 2026-09-29 +@@ -295,0 +294,8 @@ or record boundaries out of order. +@@ -590,5 +596,12 @@ its semantic signature: +@@ -633,0 +647,54 @@ disposition plan closes or transfers that work safely. +@@ -656 +723,3 @@ deployment evidence for that epoch's exact revision proves all of: +@@ -1937,0 +2007,16 @@ execution until a separate approved architecture decision says otherwise. +@@ -1949 +2034,2 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: +@@ -2160 +2246,2 @@ Rollout should prove in order: +@@ -2346,0 +2434,27 @@ Before shared implementation can be approved, evidence must include: +@@ -2499,4 +2613 @@ shared-engine tests. +@@ -2504,5 +2615,3 @@ Approval date: 2026-10-06 +@@ -2519 +2628,4 @@ atomic revision equivalence, deterministic materializer compatibility, +@@ -2524,5 +2636,9 @@ activation-epoch model without immutable route-contract revisions, carried + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^41\. ' +2007:41. For one route, revision withdrawal and every operation that creates a new + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'pre-withdrawal observation' +3 (the withdrawal subsection, invariant 41 and the section-11 case) +``` + +The fourteen hunks are: the header status and approval fields (two hunks); the +section 3.2 serialization paragraph; the historical-enrollment availability +check and refusal; the withdrawal/selection ordering paragraphs of the +"Revision withdrawal and route retirement" subsection; activation gate 4; +invariant 41; section 8 item 1; section 9 rollout proof 3; the nine section-11 +validation cases; and the four section-12 hunks that record the candidate +state, extend the description of this material correction and bind the +2026-10-06 approval to the historical earlier version. No other line of the +ADR changes; in particular the withdrawal paragraphs the candidate did not +touch (append-only/audited, future-selection only, non-destructive, strand +prevention and route retirement) are byte-identical to the clarification head. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 +decision-register.md 45ce935149693d0e983fac1645f5363896e23a53 +CHANGELOG.md f3c2bc7976d5fb84e1a00cbfce05748197da900b +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 +``` + +The candidate ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the candidate tree and the candidate +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the candidate commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: eb3129f13a9c858b3c012ee182315801f0b8563a (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check eb3129f13a9c858b3c012ee182315801f0b8563a HEAD +required: exit 0; no output + +git diff --name-only eb3129f13a9c858b3c012ee182315801f0b8563a HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -3398,6 +3965,23 @@ that no review, thread or review-comment identifier of the final-head review of the transient-state correction head occurs in this report; and read the report for statements that would become false once it moves into the clarification commit and reframed them. +Amendment-8 candidate stage: compared the staged ADR-0012 with the +clarification head's ADR blob `2db08058552ce901548ef3f71af4a294f5af7e61` hunk +by hunk against every requirement of Amendment 8 I3-I8 and the five +candidate-inspection observations; confirmed each is expressed in section 3.2 +(the serialization paragraph, the historical-enrollment availability check, +the "Revision withdrawal and route retirement" subsection and activation gate +4), invariant 41, sections 8, 9 and 11, and the candidate state in the header +and section 12; confirmed that every changed hunk concerns withdrawal/selection +ordering, its validation or the candidate state, that the withdrawal and +retirement paragraphs the candidate did not change are byte-identical, and +that the ADR-0008/ADR-0010 partial-supersession header text and all other +architecture are byte-identical to the clarification head; compared the staged +register and changelog with the clarification-head blobs (only the header line +and the ADR-0012 row, and only one added entry); confirmed the protected +decision and control blobs are unchanged; and read the report for statements +that would become false once it moves into the candidate commit and reframed +them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -3406,10 +3990,11 @@ candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, the Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865` the control-record recovery head -`22bd94dc959f11fe49c8bef8cd2a836140aadff7` and the transient-state correction -head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, are historical evidence; the -live PR #960 diff of the clarification commit exists only after its -publication and is GitHub-owned lifecycle evidence. +`22bd94dc959f11fe49c8bef8cd2a836140aadff7`, the transient-state correction +head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` and the factual-clarification +head `eb3129f13a9c858b3c012ee182315801f0b8563a`, are historical evidence; the +live PR #960 diff of the candidate commit exists only after its publication +and is GitHub-owned lifecycle evidence. ## 11. CI @@ -3420,9 +4005,10 @@ Checks: as listed in section 4.3. Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 recovery authorization, the Amendment-6/7 candidate and approval-state authorizations, the control-record recovery authorization, the -transient-state correction authorization and the factual-clarification -authorization, an automatic CI failure, or any migration execution or Docker -publication, is a HOLD condition. +transient-state correction authorization, the factual-clarification +authorization and the Amendment-8 candidate authorization, an automatic CI +failure, or any migration execution or Docker publication, is a HOLD +condition. ## 12. Rollout and rollback @@ -3447,25 +4033,29 @@ GitHub records as pending; no history rewrite, force push or amend is permitted. The Amendment-6/7 authorizations permitted exactly one candidate commit and exactly one approval-state commit, the control-record recovery and transient-state correction authorizations each permitted exactly one -report-only commit, and the factual-clarification authorization permits -exactly one two-path clarification commit; any further source correction -exhausts those authorizations and requires a fresh CTO authorization -(Amendment 3 C10; Amendments 6-7; `docs/engineering/decisions/README.md`). +report-only commit, the factual-clarification authorization permitted exactly +one two-path clarification commit, and the Amendment-8 candidate +authorization permits exactly one four-path candidate commit; any further +source correction exhausts those authorizations and requires a fresh CTO +authorization (Amendment 3 C10; Amendments 6-8; +`docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the factual-clarification stage the - implementing agent performed no independent review, PR #960 - title/description reconciliation, Draft/Ready change, automated-review - request, thread adjudication, exact-head review or merge (section 4.2). +- Historical stage-time evidence: at the Amendment-8 candidate stage the + implementing agent performed no independent inspection or review, + candidate-content approval, PR #960 title/description reconciliation, + Draft/Ready change, automated-review request, thread adjudication, + exact-head review or merge (section 4.2). Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. - Programme-local reliance on ADR-0012 under ADR-0013 is exact-version-bound and separately controlled. Any #957 pin bound to an earlier ADR-0012 blob or - PR #960 head is stale for the clarified version until superseded using the - exact clarified ADR-0012 blob and an independently reviewed PR #960 head; + PR #960 head is stale for the corrected version until superseded using the + exact eventually approved ADR-0012 blob and an independently reviewed + PR #960 head; dependent `feature/worker` slices must be rechecked and recorded as affected or `NONE` with the inspected evidence (Amendment 1 A9, Amendment 2 B9, Amendment 3 C9) before that supersession; ADR-0013 conditions 7 and 8 follow @@ -3485,8 +4075,18 @@ Monitoring required: NONE authorization for one report-only correction commit, and the overbroad disposition-cardinality sentence and remaining live-gate wording found by the final-head review of that correction head only under the CTO - factual-clarification classification and a separate authorization for this - one two-path clarification commit. + factual-clarification classification and a separate authorization for one + two-path clarification commit, and the withdrawal/selection ordering defect + found by the final-head review of that clarification head only under + Specification Amendment 8, its independent review and a separate + authorization for this one four-path candidate commit. +- The strand-check timing question left outside this correction (section 5, + decision 53): Amendments 1-8 do not determine whether a withdrawal's + strand-prevention evaluation may be stale relative to events or anomalies + that commit concurrently with the withdrawal and are not revision + selections. The candidate neither decides it nor implies a mechanism for + it; it belongs to a later specification or to the implementation + specification. - Exact table, column, index and encoding names for route-contract revisions, anomalies and capability declarations are deliberately left to the future shared-engine implementation specification. @@ -3555,10 +4155,29 @@ Monitoring required: NONE backlog, mismatch, enrollment and divergence semantics the same ADR selects; the CTO classified its correction as a factual clarification of prior approved blob `a7d95ca918f418d596432071c8ba78a33c2630a9`, and the - clarification commit that contains this report applies it (section 4; - section 5, decisions 44-46). The other found that the report still recorded - which live PR #960 lifecycle gates were pending; the same commit replaces - that wording with the durable lifecycle model (section 5, decision 47). + historical clarification commit `eb3129f13a9c858b3c012ee182315801f0b8563a` + applied it (section 4; section 5, decisions 44-46). The other found that the + report still recorded which live PR #960 lifecycle gates were pending; the + same commit replaced that wording with the durable lifecycle model (section + 5, decision 47). +- The configured final-head automated review of the factual-clarification + head `eb3129f13a9c858b3c012ee182315801f0b8563a` opened one further thread, + identified here substantively and by its control-assigned finding name + `R-EB31-01`: revision withdrawal could race future revision selection, + because the ADR did not require withdrawal to share a database-atomic order + with the operations that newly select a revision for an epoch or a first + historical enrollment, so a stale availability observation could commit a + new reference after the withdrawal. Control accepted it as valid, HIGH and + blocking and classified the correction as a material architectural + correction; the CTO approved Specification Amendment 8; a fresh independent + CRITICAL specification review of the base specification and Amendments 1-8 + returned `APPROVED`, confirming the finding, the classification and the + validation matrix and recording five non-blocking candidate-inspection + observations; and the Route-B candidate that contains this report + implements the correction in source as `PROPOSED` (section 4; section 5, + decisions 49-60). `R-EB31-01` is closed at specification level by Amendment + 8; whether the candidate closes it in source is decided by the independent + inspection and review of the candidate, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -3572,16 +4191,36 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this factual clarification also implemented -the transient-state correction, the control-record recovery, the Amendment-6/7 -approval-state commit and candidate and earlier Route-B source stages of this -task and -prepared the earlier uncommitted activation-epoch candidate referred to in -Amendment 1 A10, so it is not eligible to act as an independent reviewer of -this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). +work. The implementing agent of this Amendment-8 candidate also implemented +the factual clarification, the transient-state correction, the control-record +recovery, the Amendment-6/7 approval-state commit and candidate and earlier +Route-B source stages of this task and prepared the earlier uncommitted +activation-epoch candidate referred to in Amendment 1 A10, so it is not +eligible to act as an independent inspector or reviewer of this task +(Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Suggested review focus: +- that the candidate changes exactly the four authorized paths from the + clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`, that the + candidate ADR-0012 blob is `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, and that + ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that every Amendment 8 I3-I8 requirement is expressed in ADR-0012: the + general "every new durable selection/reference" rule and its enumerated + minimum, the exclusion of non-durable preselection, both commit orders, the + re-read-before-retry rule, the insufficiency of application-level + check-then-insert, the permitted mechanisms and composition requirements, + the exclusion of explicit replay/current-state work, invariant 41 and the + nine section-11 concurrency cases, and that the five independent-review + observations are reflected as recorded in section 5, decisions 50, 53 and + 56; +- that the candidate changes no withdrawal semantics other than the ordering + rule, invents no withdrawal reversibility, equivalent-signature + re-enablement, second lifecycle order or route abstraction, and leaves the + strand-check timing question outside the correction as decision 53 records; +- that every ADR-0012 hunk outside those concerns is limited to the candidate + `PROPOSED` state (header and section 12) and that all other architecture is + byte-identical to the clarification head; - that the approval-state delta from the content-approved candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` is limited to the ADR header approval fields, the two leading section-12 paragraphs, the ADR-0012 @@ -3630,15 +4269,18 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that section 12 carries `APPROVED` with Javi, CTO and 2026-10-06, binds both - earlier approvals to the earlier exact versions and keeps the repository - authority condition and the ADR-0013 reliance distinction; -- that the register row, the route-contract correction changelog entry and this - report describe the corrected version as `APPROVED` on 2026-10-06 while every - historical changelog entry is byte-preserved; +- that the header and section 12 carry `PROPOSED` with no current approver or + approval date, bind the 2026-09-30, 2026-10-05 and 2026-10-06 approvals to + the earlier exact versions and keep the repository authority condition and + the ADR-0013 reliance distinction; +- that the register row, the new withdrawal-order correction changelog entry + and this report describe the corrected version as `PROPOSED`, that the + route-contract correction entry and every other historical changelog entry + are byte-preserved, and that the register header `Last updated` is + `2026-10-07`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other - protected control files are byte-identical to the recovery head and the - content-approved candidate. + protected control files are byte-identical to the clarification head, the + recovery head and the content-approved candidates. ## Appendix A - Historical authoring and correction record @@ -3651,9 +4293,9 @@ boundary and one optional deactivation boundary per route is superseded by the append-only activation-epoch model, and the route-level event kind/version contract and "emits K" emission floor described in earlier rounds are superseded by immutable route-contract revisions and producer-version coverage -in the current section 3.2. The 2026-09-30 and 2026-10-05 approvals and every -review recorded here are bound to the exact historical versions and heads they -name. +in the current section 3.2. The 2026-09-30, 2026-10-05 and 2026-10-06 +approvals and every review recorded here are bound to the exact historical +versions and heads they name. ### Initial candidate and review round 1 @@ -4451,13 +5093,19 @@ stage: deduplication boundary. 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO approval of that exact corrected content; earlier approvals and reviews, - including the 2026-09-30 and 2026-10-05 approvals, remain bound to the - earlier exact versions and heads they name. + including the 2026-09-30, 2026-10-05 and 2026-10-06 approvals, remain + bound to the earlier exact versions and heads they name. 11. The route-contract-revision model does not permit silent reinterpretation or loss either: every epoch is bound to one immutable revision, an event outside its epoch revision's accepted versions becomes a visible anomaly rather than disappearing, historical enrollment validates its selected revision before consuming the `(event_id, route_key)` slot, and revision withdrawal or route retirement never strands obligations or anomalies. +12. The withdrawal/selection ordering rule does not permit a stale + availability observation to create a new epoch reference or first + enrollment for a withdrawn revision: withdrawal and new selection have + one durable linearized order, a selection that linearized first remains + valid historical state, and a withdrawal that linearized first refuses + later selection before any epoch, disposition or job exists. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 2db080585..cbb31a53f 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-06 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -293,6 +291,14 @@ retirement requests for one route therefore resolve into one durable total order: they cannot open two epochs, close one epoch twice, reopen a closed epoch or record boundaries out of order. +Withdrawing a route-contract revision from future selection and every operation +that creates a new durable selection of, or reference to, a revision (opening +an epoch that references it, or enrolling an event's first route disposition +under it) share one database-atomic order for the same route; see "Revision +withdrawal and route retirement" below. Whatever mechanism provides that order +composes with this per-`route_key` lifecycle serialization: one route never has +two contradictory lifecycle orders. + A route's lifecycle state is one of: - **active** - exactly one epoch is open; @@ -587,11 +593,18 @@ its semantic signature: - compatibility policy, where the revision declares one: the policy admits the event's kind and version. -If any check fails, the enrollment is refused before any route disposition or -job is created. The `(event_id, route_key)` uniqueness slot remains unused, so a -later enrollment under a compatible revision remains possible. The refused -attempt and its reason are durably audited, and any existing anomaly, backlog or -attention state for the event remains visible. +The selected revision must also still be available for selection: it must not +have been withdrawn from future selection. That availability is decided inside +the database-atomic withdrawal/selection order of "Revision withdrawal and +route retirement" below, in the same transaction that would create the +disposition, never from an earlier separate read. + +If any check fails, or the revision has been withdrawn, the enrollment is +refused before any route disposition or job is created. The +`(event_id, route_key)` uniqueness slot remains unused, so a later enrollment +under a compatible revision remains possible. The refused attempt and its reason +are durably audited, and any existing anomaly, backlog or attention state for +the event remains visible. Enrollment cannot bypass route deduplication and cannot create a second disposition for an event that already has one for that route, whichever revision @@ -631,6 +644,60 @@ selectable compatible revision or an approved reconciliation/divergence procedure), unless an explicitly approved migration, reconciliation or disposition plan closes or transfers that work safely. +Withdrawal shares one database-atomic serialization or constraint boundary with +every operation that creates a new durable selection of, or reference to, the +revision, so that for any revision, withdrawal and new future selection have one +durable total (linearized) order. The operations covered include, at minimum: +the first activation of the route opening an epoch that references the revision; +a reactivation opening a later epoch that references it; a revision transition +opening a new epoch that references it; historical enrollment that would create +the first route disposition for an event under the selected revision; and the +withdrawal itself. Every other operation that would create a new durable +selection of or reference to the revision is covered by the same rule, not only +those enumerated. A UI or API preselection, a preview or any other non-durable +choice is not a selection; the selection is the transaction that makes the new +epoch, reference or enrollment effective. + +Both commit orders are defined: + +- selection first: when the epoch opening or the valid first historical + enrollment linearizes before the withdrawal, the new epoch reference or + enrollment is valid historical state. The later withdrawal does not rewrite, + invalidate or remove it; it prevents only subsequent selection, and every + strand-prevention rule above remains binding on that later withdrawal; +- withdrawal first: when the withdrawal linearizes first, a later epoch opening + that would reference the revision is refused before the epoch is created, a + later first historical enrollment that selects the revision is refused before + any route disposition or job is created, the `(event_id, route_key)` + uniqueness slot remains unused, and existing audit, anomaly, backlog and + attention state remains visible under the rules above. + +A transaction that loses that race may retry only after re-reading current +durable state and selecting a revision that is still available, under the +operation's normal authorization and validation; it never commits from a stale +pre-withdrawal observation. An application-level check that the revision is not +withdrawn, followed by a later insert or reference without database-enforced +atomic ordering between the two, does not satisfy this rule. + +The implementation may provide that order by extending the per-`route_key` +durable serialization boundary that already orders epoch opening, closing and +permanent retirement so that revision withdrawal and revision selection +participate in it, or by another database-enforced mechanism with equivalent +atomic ordering; the exact table, row lock, advisory lock, uniqueness or +exclusion constraint is an implementation-specification detail. Whichever +mechanism is selected must compose with the serialized open, close, reactivate +and retire lifecycle, with database-atomic equivalent-revision convergence, with +historical-enrollment validation and `(event_id, route_key)` disposition +uniqueness, with the strand-prevention rule above, with permanent route +retirement and with the engine's normal deadlock and lock-ordering controls. No +implementation may introduce a second route lifecycle order that contradicts the +per-`route_key` order. + +Explicit replay or current-state work for an event that already has a route +disposition is outside this first-selection rule: it remains governed by the +replay semantics of section 3.5 and does not become a new route disposition +merely because a revision has been withdrawn. + Permanent retirement of a logical route does not retire, withdraw or erase its revisions. Its historical obligations and anomalies remain visible and interpretable under their revisions until they are resolved. @@ -653,7 +720,9 @@ deployment evidence for that epoch's exact revision proves all of: revision's accepted-version set; 3. compatible materialization (section 3.9) exists for every accepted version that can actually be emitted in that deployment state; -4. the revision is not withdrawn from future selection; and +4. the revision is not withdrawn from future selection, decided inside the + database-atomic withdrawal/selection order above, so that a withdrawal that + linearizes first refuses the epoch before it is created; and 5. the route's lifecycle and retirement state permit the epoch. The event-emission floor and producer-version coverage are therefore @@ -1935,6 +2004,22 @@ execution until a separate approved architecture decision says otherwise. regress an external effect: its historical enrollment states whether a second effect is intended and obeys target-scoped idempotency, concurrency and applied-revision evidence. +41. For one route, revision withdrawal and every operation that creates a new + durable selection of or reference to a route-contract revision - at minimum + first activation, reactivation and revision-transition epoch opening and + first historical enrollment - share one database-atomic serialization or + constraint boundary that gives withdrawal and new future selection one + durable linearized order. A selection that linearizes first remains valid + historical state that the later withdrawal does not rewrite; after + withdrawal linearizes, no later epoch opening or first historical + enrollment may commit a selection of that revision: epoch opening is + refused before the epoch exists, enrollment before any disposition or job + exists, and the `(event_id, route_key)` slot stays unused. A losing + transaction re-reads durable state before any retry and never commits from + a stale pre-withdrawal observation; an application-level check-then-insert + without database-enforced ordering does not satisfy this. The mechanism + composes with the per-`route_key` lifecycle serialization and introduces + no second contradictory lifecycle order. ## 8. Implementation impact and decomposition @@ -1946,7 +2031,8 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: immutable route-contract-revision, retirement and materialization model, including producer-version coverage evidence, in-epoch contract-mismatch anomalies, revision-validated historical enrollment, atomic revision - equivalence and deterministic materializer compatibility; + equivalence, database-atomic ordering of revision withdrawal against new + revision selection and deterministic materializer compatibility; - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - effect-scoped idempotency and explicit coalescing; @@ -2157,7 +2243,8 @@ Rollout should prove in order: 2. mixed-version route completeness and out-of-order transaction commits; 3. activation, deactivation, reactivation-epoch, route-contract-revision transition, retirement, event-emission-floor and producer-version-coverage - behaviour, including in-epoch contract-mismatch detection; + behaviour, including in-epoch contract-mismatch detection and the ordering + of revision withdrawal against epoch opening and first enrollment; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing; 6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; @@ -2344,6 +2431,33 @@ Before shared implementation can be approved, evidence must include: affects only future selection, leaves historical interpretation, epoch references, backlog and anomalies unchanged, and is refused or held while it would strand undispositioned obligations or unresolved mismatch anomalies; +- deterministic concurrency tests of revision withdrawal racing a first + activation, in the selection-first order and in the withdrawal-first order; +- deterministic concurrency tests of revision withdrawal racing a reactivation + epoch opening and racing a revision-transition epoch opening, each in both + orders; +- deterministic concurrency tests of revision withdrawal racing a first + historical enrollment, in both orders; +- proof that a withdrawal-first enrollment refusal creates no route + disposition and no job and leaves the `(event_id, route_key)` slot unused, + and that a withdrawal-first epoch opening is refused before any epoch is + created; +- proof that a selection-first epoch reference or enrollment remains valid + historical state after the later withdrawal, which prevents only subsequent + selection; +- proof that no transaction holding a stale pre-withdrawal observation can + commit a new selection of the revision after the withdrawal linearizes, and + that a losing transaction re-reads durable state before any retry; +- proof that withdrawal is still refused or held where undispositioned + obligations or unresolved in-epoch contract-mismatch anomalies would be + stranded, including obligations that became eligible through an epoch whose + opening linearized before the withdrawal; +- proof that the selected locking or constraint order for withdrawal and + selection composes with the existing per-`route_key` open, close, reactivate + and retire serialization, with equivalent-revision convergence where it + shares that mechanism, with enrollment validation and disposition uniqueness + and with permanent route retirement, without deadlock and without a split or + contradictory lifecycle order; - a backward-compatible materializer handling an older revision only where explicitly declared, and a forward-compatible materializer handling a newer revision only where explicitly declared and semantically valid; @@ -2496,16 +2610,11 @@ shared-engine tests. ## 12. Approval and authority -This ADR is **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-06 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-06 under the -repository decision process. That approval does not by itself make this ADR -repository-authoritative: the authority condition below still applies. Any -ADR-0013 programme-local reliance on this exact version is a separate state -that is not effective until its own conditions are satisfied for this version. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the @@ -2516,16 +2625,23 @@ immutable route-contract revision, so that no mutable route-level contract can reinterpret historical obligations. It also defines producer-version coverage, in-epoch contract-mismatch anomalies, revision-validated historical enrollment, atomic revision equivalence, deterministic materializer compatibility, -per-revision route health and safe revision withdrawal. +per-revision route health and safe revision withdrawal, and it requires revision +withdrawal and every new durable selection of a revision to share one +database-atomic linearized order, so that a stale availability observation can +never commit a new selection of a withdrawn revision. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO approval dated 2026-09-30. A later corrected version, which introduced the activation-epoch model without immutable route-contract revisions, carried -`Status: APPROVED` with CTO approval dated 2026-10-05. Those approvals, and -every review of those earlier versions, remain historical evidence bound to -those exact earlier versions and source heads only; they do not approve this -version. The exact historical identities are recorded in the task -implementation report and the owning issue. +`Status: APPROVED` with CTO approval dated 2026-10-05. A further corrected +version, which introduced immutable route-contract revisions without a +database-atomic order between revision withdrawal and new revision selection, +carried `Status: APPROVED` with CTO approval dated 2026-10-06, retained through +a factual clarification of that version. Those approvals, and every review of +those earlier versions, remain historical evidence bound to those exact earlier +versions and source heads only; they do not approve this version. The exact +historical identities are recorded in the task implementation report and the +owning issue. CTO approval of this ADR adopts the architecture decision and invariants recorded here. It is architecture approval only: it does not authorize diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index b358acd5b..45ce93514 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-06 +Last updated: 2026-10-07 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-06 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and the CTO approved its exact corrected content on 2026-10-06 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, and the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; neither approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; and revision withdrawal or route retirement never strands obligations or anomalies. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, and the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From e2588f7b7e63a5c71968d87a3409f0ff48635e6d Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 7 Oct 2026 20:37:15 +0100 Subject: [PATCH 35/48] THOTH-ASYNC-01-ADR-01: record withdrawal-selection correction approval --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 910 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 20 +- .../decisions/decision-register.md | 2 +- 4 files changed, 756 insertions(+), 178 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3c2bc797..d1fbe1bdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `CTRL-ADR-PR-FIRST-01`: add a PR-first staged approval route for a material pre-authority ADR correction to the engineering-decision amendment process in [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md) (issue [966](https://github.com/thoth-pub/thoth/issues/966)). The existing pre-commit exact-final-blob approval route (now Route A) is retained unchanged. Route B lets the implementing agent push the corrected candidate to a real task branch and draft PR as `PROPOSED`, without presenting the earlier version's approver or approval date as approval of the corrected bytes; the CTO then records architecture-content approval against the exact candidate blob/head from the PR; only afterwards may exactly one bounded approval-state commit record `PROPOSED -> APPROVED` with the fresh approver/date and authorized companion metadata; the CTO records exact-final-blob approval of the final ADR blob before merge; fresh independent exact-head review binds to the final PR head; and HIGH/CRITICAL merge authorization and the guarded merge remain separate gates. The doctrine records why that approval-state commit is durable decision state rather than the lifecycle transcription `ADR-0005` prohibits. Material-correction eligibility, the stale-control list, `ADR-0013` exact-version reliance/drift rules and the repository-authority condition are unchanged. **Documentation/control only: no ADR is changed by this task, and no runtime, schema, migration, API, authorization, workflow, provider/IAM, deployment, release or production behaviour is authorized or affected.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index f85da63b8..307a46ed1 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -13,16 +13,16 @@ one-path report-only transient-state correction that followed the configured final-head automated review of that recovery head, the two-path factual clarification of ADR-0012 and durable-report correction that followed the configured final-head automated review of that correction head, and the -Route-B candidate stage of the revision-withdrawal/selection ordering -correction specified by Specification Amendment 8 after the configured +Route-B candidate and approval-state stages of the revision-withdrawal/selection +ordering correction specified by Specification Amendment 8 after the configured final-head automated review of that clarification head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-8 Route-B candidate as the -current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-8 Route-B approval-state commit +as the current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -44,11 +44,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-8 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the +Head commit: the Amendment-8 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-8 candidate +`42ae90a5932cf24ce90679196888cc3d83d88db8`; its SHA is deliberately not +self-pinned here (section 3). That candidate is the direct child of the historical factual-clarification head -`eb3129f13a9c858b3c012ee182315801f0b8563a`; its SHA is deliberately not -self-pinned here (section 3). That clarification head is the direct child of +`eb3129f13a9c858b3c012ee182315801f0b8563a`, which is the direct child of the historical report-only transient-state correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, which is the direct child of the historical report-only control-record recovery head @@ -62,7 +64,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -75,9 +77,9 @@ Implementing model: activation-epoch candidate and approval-state stages - Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded -implementing agent; transient-state correction, factual-clarification and -Amendment-8 candidate stages - Claude (Fable 5.1), bounded implementing agent. -Earlier rounds are recorded in Appendix A. +implementing agent; transient-state correction, factual-clarification, +Amendment-8 candidate and Amendment-8 approval-state stages - Claude (Fable +5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -181,14 +183,23 @@ decision-register blob b358acd5bbedf7482cfdc7b71866815c852773af CHANGELOG blob e2947275d19d3abb351a98854268d7db213370d7 implementation-report blob c47b874f1a48449e6c8f60a97f00ed1506d55fb9 (superseded by the Amendment-8 candidate) -Amendment-8 Route-B candidate (the commit that contains this report): -ADR-0012 blob cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 (PROPOSED; material architectural correction of 2db08058552ce901548ef3f71af4a294f5af7e61) +content-approved Amendment-8 candidate / approval-state parent: +42ae90a5932cf24ce90679196888cc3d83d88db8 +tree 45d8a0bdb05ca95b61c01cdc83dcecf3a3f60e98 +ADR-0012 blob cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 (PROPOSED; content-approved; material architectural correction of 2db08058552ce901548ef3f71af4a294f5af7e61) decision-register blob 45ce935149693d0e983fac1645f5363896e23a53 +implementation-report blob f85da63b89654d4774462de674eb02b4a1a0c38d CHANGELOG blob f3c2bc7976d5fb84e1a00cbfce05748197da900b +Amendment-8 Route-B approval-state commit (the commit that contains this report): +ADR-0012 blob 85937a4e1ff9a27d8f5b36ca82651821711472b2 (APPROVED; Javi, CTO; 2026-10-07; architecture-equivalent to cbb31a53f6f4ed6b96faafb79b273b55664f3ae0) +decision-register blob 246bd5e981d373447fccdd457c02645338bfee4b +CHANGELOG blob d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82 + feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual -clarification and the Amendment-8 candidate (unchanged): +clarification and the Amendment-8 candidate and approval-state commit +(unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -255,7 +266,15 @@ Approved specification: and Amendments 1-8, including five non-blocking candidate-inspection observations: #958 comment `6044203556`; - Amendment-8 Route-B candidate implementation authorization: #958 comment - `6044220221`. + `6044220221`; +- CTO exact candidate-content approval of the Amendment-8 candidate: recorded + on #958 on 2026-10-07 for candidate head + `42ae90a5932cf24ce90679196888cc3d83d88db8` and candidate ADR-0012 blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; +- Amendment-8 Route-B approval-state implementation authorization: #958 + comment `6045126538`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -276,11 +295,18 @@ approved ADR blob `a7d95ca918f418d596432071c8ba78a33c2630a9` and removed the report's remaining statements about which live PR #960 lifecycle gate was pending. The configured final-head automated review of that clarification head exposed a further architecture defect, `R-EB31-01`, specified by Specification -Amendment 8. The commit that contains this report is its Route-B candidate: one -ordinary single-parent commit on the clarification head that changes exactly -the four authorized correction paths, returns ADR-0012 to `Status: PROPOSED` -and requires revision withdrawal and every new durable selection of a -route-contract revision to share one database-atomic linearized order. +Amendment 8. Its `PROPOSED` Route-B candidate, one ordinary single-parent +commit on the clarification head that changed exactly the four authorized +correction paths and requires revision withdrawal and every new durable +selection of a route-contract revision to share one database-atomic linearized +order, received independent control inspection and CTO exact +candidate-content approval on 2026-10-07. The commit that contains this report +is the one bounded Route-B approval-state commit that records that approval as +durable decision state: ADR-0012 `PROPOSED -> APPROVED` with +`Approved by: Javi, CTO` and `Approval date: 2026-10-07`, the ADR-0012 register +row and the withdrawal-order correction changelog entry moved to the approved +state, and this report's approval-state evidence. No architecture content +changes. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -466,8 +492,9 @@ route-contract revision to share one database-atomic linearized order. commit and not a material architectural correction, and that stage authorized no PR-state or PR-metadata change. - **Final-head review of the factual-clarification head, Specification - Amendment 8 and the Amendment-8 Route-B candidate (the commit that contains - this report).** Authorized by #958 comment `6044220221` after the CTO + Amendment 8 and the Amendment-8 Route-B candidate (historical + content-approved candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`).** + Authorized by #958 comment `6044220221` after the CTO approved Specification Amendment 8 (#958 comment `6042399237`) and a fresh independent CRITICAL specification review of the base specification and Amendments 1-8 returned `APPROVED` (#958 comment `6044203556`). Control @@ -495,15 +522,39 @@ route-contract revision to share one database-atomic linearized order. semantics are otherwise unchanged. The independent review confirmed the finding, the classification and the sufficiency of the nine-case concurrency validation matrix, and recorded five non-blocking - candidate-inspection observations. The commit that contains this report is - one ordinary single-parent commit directly on the clarification head that - changes exactly the four authorized correction paths: ADR-0012 returns to + candidate-inspection observations. The candidate is one ordinary + single-parent commit directly on the clarification head that changed exactly + the four authorized correction paths: ADR-0012 returned to `Status: PROPOSED` with no current approver or approval date and implements Amendment 8 (section 4; section 5, decisions 49-60); the decision-register - ADR-0012 row, one new changelog entry and this report describe the - corrected version as `PROPOSED`; and the 2026-09-30, 2026-10-05 and - 2026-10-06 CTO approvals remain historical evidence bound to the earlier - exact versions. This stage authorizes no PR-state or PR-metadata change. + ADR-0012 row, one new changelog entry and the candidate-stage report + described the corrected version as `PROPOSED`; and the 2026-09-30, + 2026-10-05 and 2026-10-06 CTO approvals remain historical evidence bound to + the earlier exact versions. Control independently inspected the published + candidate against the actual GitHub source, found no blocking + exact-candidate finding, and the CTO approved its exact content - head + `42ae90a5932cf24ce90679196888cc3d83d88db8`, ADR-0012 blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0` - on 2026-10-07 in a durable + record on #958. That stage authorized no PR-state or PR-metadata change. +- **Amendment-8 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6045126538`. One ordinary + single-parent commit directly on the content-approved candidate records + that approval as durable decision state, changing only the four authorized + paths: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and + `Approval date: 2026-10-07` and the leading section-12 approval paragraphs, + which identify the approved content as the content represented by candidate + ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; the ADR-0012 + register row; the `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` + changelog entry; and this report. No architecture content changes (section + 9, "Amendment-8 approval-state validation"). The content approval is + recorded here through owning issue #958, approval date 2026-10-07, the + candidate head and the candidate ADR-0012 blob; its GitHub comment + identifier is not copied into repository source, as the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` requires and as the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + established for this task (section 5, decisions 36-37 and 64). PR #960 was + already a draft; this stage authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -517,6 +568,11 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-07) +of the content-approved Amendment-8 candidate blob cbb31a53f6f4ed6b96faafb79b273b55664f3ae0; +no architecture change + +historical Amendment-8 candidate at 42ae90a5932cf24ce90679196888cc3d83d88db8: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 2db08058552ce901548ef3f71af4a294f5af7e61 (Specification Amendment 8; Route-B candidate stage; candidate status PROPOSED) @@ -532,9 +588,9 @@ number, not to one pathname (Amendment 1 A1; validation time `b44303c214498baf76c9d9a0a7cab374377f9cbe` was simultaneously `develop` and the refreshed `feature/worker`, and `master` was `4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`. Those refs were unchanged at the -Amendment-6/7 candidate stage and at the Amendment-8 candidate stage, and the -same literal commands were run again at each stage against both commits with -identical results: +Amendment-6/7 candidate stage, at the Amendment-8 candidate stage and at the +Amendment-8 approval-state stage, and the same literal commands were run again +at each stage against both commits with identical results: ```text git log --format=%h -- ':(glob)**/*ADR-0012*' @@ -643,11 +699,13 @@ version as well: `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry that record the route-contract-revision version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while - the register row now records the candidate as `PROPOSED`. + the register row recorded the candidate as `PROPOSED` and now records the + approved version. -They remain historical evidence bound to their exact versions and heads. For -the candidate version, independent exact-candidate inspection, CTO exact -candidate-content approval, the approval-state commit, exact-final-blob +They remain historical evidence bound to their exact versions and heads. The +candidate version received its own independent control inspection and CTO +exact candidate-content approval on 2026-10-07, and the approval-state commit +that contains this report records it. For this version, exact-final-blob approval, independent exact-head review, CTO merge authorization, guarded merge, programme-local reliance and later migration, deployment, activation and observation are distinct controlled lifecycle categories; GitHub, through @@ -656,29 +714,45 @@ blocked for any head. ## 3. Commits -Amendment-8 Route-B candidate commit (the commit that contains this report): +Amendment-8 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose withdrawal-selection ordering correction` - - direct parent: the historical factual-clarification head - `eb3129f13a9c858b3c012ee182315801f0b8563a` - - changes only the four authorized correction paths (section 4); the - candidate ADR-0012 blob is `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, the - candidate decision-register blob `45ce935149693d0e983fac1645f5363896e23a53` - and the candidate changelog blob `f3c2bc7976d5fb84e1a00cbfce05748197da900b` + `THOTH-ASYNC-01-ADR-01: record withdrawal-selection correction approval` + - direct parent: the content-approved Amendment-8 candidate + `42ae90a5932cf24ce90679196888cc3d83d88db8` + - changes only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `85937a4e1ff9a27d8f5b36ca82651821711472b2`, the approval-state + decision-register blob `246bd5e981d373447fccdd457c02645338bfee4b` and the + approval-state changelog blob `d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the candidate ADR-0012, +here because this report is part of that tree; the approval-state ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its clarification-head blob (section -9). The commit identities are verified against the remote task-branch head -after push and recorded in the post-publication handoff; once published, the -PR #960 head and head tree are GitHub-owned lifecycle evidence under -`ADR-0005`. The authorized topology is a non-force fast-forward of the task -branch from the clarification head to the candidate commit; no merge commit, -rebase, squash, amend, force update or second candidate commit is authorized, -and no further source commit exists implicitly after it. +recorded above; every other file keeps its candidate blob (section 9). The +commit identities are verified against the remote task-branch head after push +and recorded in the post-publication handoff; once published, the PR #960 +head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The +authorized topology is a non-force fast-forward of the task branch from the +content-approved candidate to the approval-state commit; no merge commit, +rebase, squash, amend, force update or second approval-state commit is +authorized, and no further source commit exists implicitly after it. + +Amendment-8 Route-B candidate commit (content-approved; historical): + +- `42ae90a5932cf24ce90679196888cc3d83d88db8` - + `THOTH-ASYNC-01-ADR-01: propose withdrawal-selection ordering correction` + - ordinary single-parent additive commit; direct parent: the historical + factual-clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a` + - tree `45d8a0bdb05ca95b61c01cdc83dcecf3a3f60e98` + - changed only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0` + - published by a non-force fast-forward of `feature/async/adr-0012` from the + clarification head; the remote head, tree, parent and ADR-0012, + decision-register, changelog and report blobs matched the locally + validated object Factual-clarification commit (historical): @@ -817,11 +891,13 @@ first-parent order, newest first: ## 4. Files changed -Amendment-8 Route-B candidate write budget for the commit that contains this -report (authorization #958 comment `6044220221`; the Amendment 6 G10 / -Amendment 7 H12 budget, unchanged by Amendment 8); exactly four existing paths, -no new file, no deletion, and every other path byte-identical to the -clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`: +Amendment-8 Route-B approval-state write budget for the commit that contains +this report (authorization #958 comment `6045126538`), identical to the +Amendment-8 candidate write budget (authorization #958 comment `6044220221`; +the Amendment 6 G10 / Amendment 7 H12 budget, unchanged by Amendment 8); +exactly four existing paths, no new file, no deletion, and every other path +byte-identical to the content-approved candidate +`42ae90a5932cf24ce90679196888cc3d83d88db8`: - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -877,17 +953,83 @@ six paths at every stage): Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record -recovery, transient-state correction, factual-clarification and Amendment-8 -candidate stages; all four correction paths already existed. +recovery, transient-state correction, factual-clarification, Amendment-8 +candidate and Amendment-8 approval-state stages; all four correction paths +already existed. -Amendment-8 candidate changes in the commit that contains this report, -relative to the clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a` -(exactly the four authorized paths): +Amendment-8 approval-state changes in the commit that contains this report, +relative to the content-approved candidate +`42ae90a5932cf24ce90679196888cc3d83d88db8` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate-content approval of 2026-10-07. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-07`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + `This ADR is **APPROVED**.`, the same approver and date, and a paragraph + stating that the CTO approved this exact corrected content on 2026-10-07 + as the architecture content represented by candidate ADR-0012 blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that any ADR-0013 programme-local reliance on this exact version is a + separate state not effective until its own conditions are satisfied. + Every other line, including all architecture, the Amendment-8 ordering + rule, invariant 41, the validation requirements, the historical-approval + paragraph, the authority condition and the architecture-versus- + implementation separation, is byte-identical to the candidate (section + 9). Approval-state blob: `85937a4e1ff9a27d8f5b36ca82651821711472b2`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row only - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, and "its corrected content awaits CTO + approval" becomes "the CTO approved its exact corrected content on + 2026-10-07". The `Last updated: 2026-10-07` header already carries the + date of this change and is unchanged; every other row is unchanged. + Approval-state blob: `246bd5e981d373447fccdd457c02645338bfee4b`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the withdrawal-order correction entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry only - "propose" + becomes "record a CTO-approved", "`PROPOSED`: the corrected content is not + approved" becomes "`APPROVED`: the CTO approved the exact corrected content + on 2026-10-07", and "proposal only" becomes "decision only". Its + statements that the earlier entries and the 2026-09-30, 2026-10-05 and + 2026-10-06 approvals apply only to historical exact versions are + unchanged, and every other entry is byte-identical. Approval-state blob: + `d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-8 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit + the current stage; records the content-approved candidate, the CTO + candidate-content approval and the approval-state authorization; records + the approval-state validation and no-drift proof (section 9); and reframes + statements that would otherwise describe the candidate as the commit + containing this report, recording the candidate's identities as + historical facts. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the content-approved candidate through the approval-state +commit: every path other than the four authorized paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Amendment-8 candidate changes in the historical content-approved candidate +`42ae90a5932cf24ce90679196888cc3d83d88db8`, relative to the clarification +head `eb3129f13a9c858b3c012ee182315801f0b8563a` (exactly the four authorized +paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement Specification Amendment 8 and record the corrected version as `PROPOSED`. - - behavioural effect: architecture proposal only. The header carries + - behavioural effect: architecture proposal only. The header carried `Status: PROPOSED` and no current approver or approval date. In section 3.2, the per-`route_key` serialization paragraph states that revision withdrawal and every operation creating a new durable selection of a @@ -943,14 +1085,14 @@ relative to the clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a` - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-8 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate the current - stage; records the final-head review of the clarification head, - `R-EB31-01`, Amendment 8, its independent review and the candidate - authorization as governing evidence; records the candidate's validation - (section 9); and reframes statements that would otherwise describe the - clarification commit as the commit containing this report, recording that + - behavioural effect: documentation only. Made the candidate the + then-current stage; recorded the final-head review of the clarification + head, `R-EB31-01`, Amendment 8, its independent review and the candidate + authorization as governing evidence; recorded the candidate's validation + (section 9); and reframed statements that would otherwise describe the + clarification commit as the commit containing the report, recording that commit's identities as historical facts. All still-true earlier evidence - is preserved. + was preserved. - within authorized write budget: YES Byte-identical to the clarification head through the Amendment-8 candidate: @@ -1192,7 +1334,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state -correction, the factual clarification and the Amendment-8 candidate: +correction, the factual clarification, the Amendment-8 candidate and the +Amendment-8 approval-state commit: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -1371,7 +1514,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-8 candidate differs from the clarification head +The Amendment-8 approval-state commit differs from the content-approved +candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` in exactly the four +authorized paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-8 approval-state validation"). The +Amendment-8 candidate differed from the clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a` in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-8 candidate validation"). The @@ -1412,8 +1559,9 @@ Amendment-4/5 recovery stage (#958 comment `6013267369`), the Amendment-6/7 candidate stage (#958 comment `6021574322`), the Amendment-6/7 approval-state stage (#958 comment `6022486352`), the control-record recovery stage (#958 comment `6022850916`), the transient-state correction stage (#958 comment -`6036961945`), the factual-clarification stage (#958 comment `6041301223`) -and the Amendment-8 candidate stage (#958 comment `6044220221`). This matrix +`6036961945`), the factual-clarification stage (#958 comment `6041301223`), +the Amendment-8 candidate stage (#958 comment `6044220221`) and the +Amendment-8 approval-state stage (#958 comment `6045126538`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -1602,8 +1750,8 @@ candidate: review of the clarification head, and returned it to Draft before the Amendment-8 candidate (section 2) -Amendment-8 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-8 candidate stage - historical, completed before the approval-state +commit: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -1617,48 +1765,75 @@ commit that contains this report: the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (PR #960 was already a draft at preflight, so the one authorized Ready -> Draft mutation was not needed; no PR-state, - title or description change is authorized at this stage) + title or description change was authorized at that stage) - source edit: USED (the four authorized correction paths only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) - branch creation: NOT USED (not authorized; a detached local worktree at the clarification head was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent candidate commit that contains this report) +- commit: USED (the single-parent candidate commit + `42ae90a5932cf24ce90679196888cc3d83d88db8`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `eb3129f13a9c858b3c012ee182315801f0b8563a` to the candidate; the remote + head, tree, parent and ADR-0012, decision-register, changelog and report + blobs were verified) + +Amendment-8 approval-state stage - done up to and including the creation of +the commit that contains this report: -Authorized post-commit actions for the candidate commit - GitHub-owned +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `42ae90a5932cf24ce90679196888cc3d83d88db8` and target `feature/worker`, + the candidate's tree, single parent and ADR-0012, report, decision-register + and changelog blobs, the protected ADR-0008 and ADR-0010 blobs, the six-path + cumulative footprint, PR #960 as the only open pull request targeting + `feature/worker`, the presence of the content-approval and approval-state + authorization records on #958 with no later control record, and, as + stage-time evidence, the PR #960 review-thread state in GitHub; all matched + the authorization; current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, branch creation: NOT USED + (not authorized; the same detached local worktree, now at the + content-approved candidate, was used for construction, and the existing + task branch is the fast-forward target) +- commit: USED (the single-parent approval-state commit that contains this + report) + +Authorized post-commit actions for the approval-state commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `eb3129f13a9c858b3c012ee182315801f0b8563a` to the candidate commit; + `42ae90a5932cf24ce90679196888cc3d83d88db8` to the approval-state commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, changed path set, - cumulative footprint and PR #960 state, and that no review thread was - replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, ADR approval + state, changed path set, cumulative footprint and PR #960 state, and that + no review thread was replied to, resolved or dismissed; - the automatic PR CI that the push causes. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-8 candidate stage, -each a separate controlled lifecycle category where applicable (historical -stage-time evidence; GitHub, through #958 and PR #960, is authoritative for -the current state of each): +Not authorized for the implementing agent at the Amendment-8 approval-state +stage, each a separate controlled lifecycle category where applicable +(historical stage-time evidence; GitHub, through #958 and PR #960, is +authoritative for the current state of each): -- any edit to a path other than the four authorized correction paths: NOT USED - (not authorized) -- any ADR-0012 change outside Amendment 8 and the candidate `PROPOSED` state: - NOT USED (not authorized) -- independent exact-candidate inspection or exact-head review: NOT USED (never - on the implementing agent's authority) -- CTO candidate-content approval, approval-state commit, fresh `APPROVED` - status, approver or approval date, exact-final-blob approval: NOT USED (not +- any edit to a path other than the four authorized paths: NOT USED (not authorized) +- any ADR-0012 change outside the approval-state representation, including + any architecture change: NOT USED (not authorized) +- independent inspection or exact-head review: NOT USED (never on the + implementing agent's authority) +- exact-final-blob approval or confirmation: NOT USED (not authorized; never + on the implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second candidate commit, amend, rebase, squash, force push or history - rewrite: NOT USED (not authorized) +- any second approval-state commit, amend, rebase, squash, force push or + history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -1691,22 +1866,25 @@ are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments 4-5, Amendments 6-7, Amendment 8, the transient-state correction -authorization, the factual-clarification authorization and the Amendment-8 -candidate authorization, configured Codex/app review is not expected from a -push to a draft PR and is not relied upon at the Amendment-6/7 candidate, -approval-state, control-record recovery, transient-state correction, -factual-clarification or Amendment-8 candidate stage; configured Codex/app -review runs on separately authorized Ready transitions, which are GitHub-owned -lifecycle events. The actual job outcomes for the candidate commit exist only -after its publication; they are GitHub-owned lifecycle evidence recorded in the pull +authorization, the factual-clarification authorization, the Amendment-8 +candidate authorization and the Amendment-8 approval-state authorization, +configured Codex/app review is not expected from a push to a draft PR and is +not relied upon at the Amendment-6/7 candidate, approval-state, +control-record recovery, transient-state correction, factual-clarification, +Amendment-8 candidate or Amendment-8 approval-state stage; configured +Codex/app review runs on separately authorized Ready transitions, which are +GitHub-owned lifecycle events. The actual job outcomes for the approval-state +commit exist only after its publication; they are GitHub-owned lifecycle +evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, -factual-clarification or Amendment-8 candidate stage, followed the same -documentation-only classification, with no manual dispatch or rerun: +factual-clarification, Amendment-8 candidate or Amendment-8 approval-state +stage, followed the same documentation-only classification, with no manual +dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -1720,6 +1898,7 @@ b596536e7db0d6b8ace0a42fdd26bff42cabf104: 4 success (classify x3, check-changelo 22bd94dc959f11fe49c8bef8cd2a836140aadff7: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (control-record recovery head) 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (transient-state correction head) eb3129f13a9c858b3c012ee182315801f0b8563a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (factual-clarification head) +42ae90a5932cf24ce90679196888cc3d83d88db8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-8 candidate) ``` Manually initiated external actions (anything the implementing agent @@ -2038,9 +2217,11 @@ head `eb3129f13a9c858b3c012ee182315801f0b8563a`): records them here as historical facts because the clarification head is the Amendment-8 candidate's parent. -Amendment-8 Route-B candidate (the commit that contains this report). Items -53 and 57 are interpretations of the approved specification recorded for -explicit control inspection: +Amendment-8 Route-B candidate (historical content-approved candidate +`42ae90a5932cf24ce90679196888cc3d83d88db8`). Items 53 and 57 were +interpretations of the approved specification recorded for explicit control +inspection; control inspected the published candidate and the CTO approved its +exact content: 49. The Amendment-8 rule is stated once, in the "Revision withdrawal and route retirement" subsection of section 3.2, and the places that depend on it @@ -2126,20 +2307,78 @@ explicit control inspection: historical entry, including the record of the 2026-10-06 approval, stays byte-preserved; the new entry states which exact earlier versions those entries describe (Amendment 3 C8; decision 26). -60. This report does not self-pin the candidate commit's SHA, its tree or its - own final blob; it records only non-self-referential evidence and the - candidate ADR-0012, decision-register and changelog blobs, which do not - depend on its bytes. Because the report now lives in the candidate - commit, statements that described the clarification commit as the commit - containing this report are reframed as history, and that commit's - identities, now the candidate parent, are recorded in section 1 as - historical facts. `R-EB31-01` is recorded by its control-assigned name, +60. The candidate-stage report did not self-pin the candidate commit's SHA, + its tree or its own final blob; it recorded only non-self-referential + evidence and the candidate ADR-0012, decision-register and changelog + blobs, which did not depend on its bytes. Because the report then lived in + the candidate commit, statements that described the clarification commit + as the commit containing the report were reframed as history, and that + commit's identities, the candidate parent, were recorded in section 1 as + historical facts; section 1 now records the candidate's identities as + historical facts because the candidate is the approval-state parent. + `R-EB31-01` is recorded by its control-assigned name, and Amendment 8, its independent review receipt and the candidate authorization by their #958 comment identifiers, as the specification chain in section 2 already records every amendment, review receipt and authorization; no review, thread or review-comment identifier of the final-head automated review is copied. +Amendment-8 approval-state stage (the commit that contains this report): + +61. The approval-state header uses the same field order as the earlier approved + ADR-0012 versions (`Status`, `Date`, `Approved by`, `Approval date`, + `Decision owner`), and section 12 repeats the approver and date. +62. The approval date carried by the ADR is the date of the CTO exact + candidate-content approval record, 2026-10-07, as Route B requires; it is + neither back-dated nor anticipated. +63. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by the approval statement, the + approver/date and one paragraph stating that the CTO approved this exact + corrected content on 2026-10-07 as the architecture content represented by + candidate ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, + that this version differs from that candidate only in its approval-state + representation, that the approval does not by itself make the ADR + repository-authoritative and that ADR-0013 programme-local reliance on + this exact version is separate and not effective until its own conditions + are satisfied. The candidate blob is named because the approval-state + authorization requires the approved content to be identified by it; a Git + blob identity is content identity, not a GitHub lifecycle identifier of + the kind Route-B doctrine keeps out of repository files. The + historical-approval, architecture-only, authority-condition, + partial-supersession and amendment paragraphs were already true in the + approved state (decision 58) and are unchanged. +64. The approval-state authorization asked the report to record the GitHub + comment identifier of the CTO content-approval record. The + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` says the approval-state commit + must not copy content-approval, exact-final-blob approval, review or merge + identifiers into repository files, and the control adjudication on #958 + of the earlier Amendment-6/7 approval-state report found exactly that + transcription nonconforming and required a report-only recovery + (decisions 36-37). Repository doctrine outranks the task instruction, so + this report records the content approval only through owning issue #958, + approval date 2026-10-07, candidate head + `42ae90a5932cf24ce90679196888cc3d83d88db8` and candidate ADR-0012 blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, without the comment + identifier; the departure from the instruction is reported in the + post-publication handoff. +65. The decision-register `Last updated: 2026-10-07` header already carries the + date of this approval-state change, so it is unchanged; only the ADR-0012 + row changes. +66. Only the `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` changelog + entry changes, following the earlier approval-state wording pattern; every + other entry, including the historical approval entries, is byte-identical. +67. Architecture no-drift is proven by the exact normalized comparison in + section 9 ("Amendment-8 approval-state validation"), not by assertion. +68. This report does not self-pin the approval-state commit's SHA, its tree or + its own final blob; it records only non-self-referential evidence and the + approval-state ADR-0012, decision-register and changelog blobs, which do + not depend on its bytes. Because the report now lives in the + approval-state commit, statements that described the candidate as the + commit containing this report are reframed as history, and the + candidate's identities, now the approval-state parent, are recorded in + section 1 as historical facts. + List any deviation from the specification requiring authorization: - An earlier unpublished local activation-epoch candidate, @@ -2196,9 +2435,23 @@ List any deviation from the specification requiring authorization: - Amendment-8 candidate stage: NONE. No fifth path, new file, deletion, ADR change outside Amendment 8 and the candidate state, second commit, amend, rebase, squash, force push or history rewrite was needed or performed - before the commit that contains this report was created. Decisions 53 and - 57 are recorded for control inspection as interpretations within the - authorized scope, not as deviations. + before the candidate commit was created. Decisions 53 and 57 were recorded + for control inspection as interpretations within the authorized scope, not + as deviations, and control accepted them. +- Amendment-8 approval-state stage: the approval-state authorization asked the + report to record the GitHub identifier of the CTO content-approval comment. + The repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` says the approval-state commit must + not copy content-approval identifiers into repository files, and repository + records reference the owning issue and pull request instead; the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + applied that rule to this task (decision 36). This report therefore + identifies the content approval by its owning issue (#958), its date and + the exact candidate head and ADR blob it approved, without restating its + comment identifier (decision 64). No fifth path, new file, deletion, + architecture change, second commit, amend, rebase, squash, force push or + history rewrite was needed or performed before the commit that contains + this report was created. ## 6. Database and migration effects @@ -2257,8 +2510,10 @@ correction validation" records the validation of the historical correction commit `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`. "Factual-clarification validation" records the validation of the historical clarification commit `eb3129f13a9c858b3c012ee182315801f0b8563a`. "Amendment-8 candidate -validation" at the end of this section records the validation of the commit -that contains this report. +validation" records the validation of the historical content-approved +candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`. "Amendment-8 +approval-state validation" at the end of this section records the validation +of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -3857,15 +4112,18 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 ``` -The candidate ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the candidate tree and the candidate -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and recorded in the -post-publication handoff. +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate report's bytes, so they were +recorded above. The candidate report's own final blob, the candidate tree and +the candidate commit SHA depended on that report's bytes and were not +self-pinned in it; they were verified against the remote head after push and +recorded in the post-publication handoff, and section 1 now records them as +historical facts: commit `42ae90a5932cf24ce90679196888cc3d83d88db8`, tree +`45d8a0bdb05ca95b61c01cdc83dcecf3a3f60e98`, report blob +`f85da63b89654d4774462de674eb02b4a1a0c38d`. -Required topology of the candidate commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the candidate commit, checked after it was created and +before push; the published candidate satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -3884,6 +4142,281 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-8 approval-state validation + +Environment: the same full local Git worktree, detached at the content-approved +candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` (tree +`45d8a0bdb05ca95b61c01cdc83dcecf3a3f60e98`). Immediately before construction, +read-only GitHub inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `master` at +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`, `feature/async/adr-0012` at +`42ae90a5932cf24ce90679196888cc3d83d88db8` with single parent +`eb3129f13a9c858b3c012ee182315801f0b8563a`, ADR-0012 blob +`cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, report blob +`f85da63b89654d4774462de674eb02b4a1a0c38d`, decision-register blob +`45ce935149693d0e983fac1645f5363896e23a53` and changelog blob +`f3c2bc7976d5fb84e1a00cbfce05748197da900b` at that head, the protected +ADR-0008 and ADR-0010 blobs, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker` and the only open pull request targeting +`feature/worker`, the cumulative PR footprint exactly the six paths, the +content-approval and approval-state authorization records present on #958 with +no later control record, and PR #960 review-thread state that matched the +stage authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the approval-state commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 42ae90a5932cf24ce90679196888cc3d83d88db8 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths): + +```text +git diff --cached --name-only 42ae90a5932cf24ce90679196888cc3d83d88db8 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 42ae90a5932cf24ce90679196888cc3d83d88db8 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 42ae90a5932cf24ce90679196888cc3d83d88db8 +1 1 CHANGELOG.md +739 171 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +15 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 candidate -> approval state (every changed line, diff header lines +omitted): + +```text +git diff --cached -U0 42ae90a5932cf24ce90679196888cc3d83d88db8 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +@@ -3 +3 @@ +-Status: PROPOSED ++Status: APPROVED +@@ -4,0 +5,2 @@ Date: 2026-09-29 ++Approved by: Javi, CTO ++Approval date: 2026-10-07 +@@ -2613 +2615 @@ shared-engine tests. +-Current decision state: **PROPOSED**. ++This ADR is **APPROVED**. +@@ -2615,3 +2617,11 @@ Current decision state: **PROPOSED**. +-The corrected content of this version has not been approved. It may carry +-`APPROVED`, an approver and an approval date only after the CTO approves this +-exact corrected content under the repository decision process. ++Approved by: Javi, CTO ++Approval date: 2026-10-07 ++ ++The CTO approved this exact corrected content on 2026-10-07 under the ++repository decision process, as the architecture content represented by the ++Route-B candidate ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; ++this version differs from that candidate only in this approval-state ++representation. That approval does not by itself make this ADR ++repository-authoritative: the authority condition below still applies. Any ++ADR-0013 programme-local reliance on this exact version is a separate state ++that is not effective until its own conditions are satisfied for this version. +``` + +Architecture no-drift proof. The normalization `N` removes only the authorized +approval-state representation: the header `Status`, `Approved by` and +`Approval date` fields before the first `---` rule, and the section-12 +approval-state paragraphs between the `## 12. Approval and authority` heading +and the paragraph beginning "This version is a material architectural +correction". Everything else, including every other section-12 paragraph, is +kept: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 | awk "$N" | shasum -a 256 +1ecb63976863e91900cd35fcea5179ef46e4778932ece74c0edef4b2aa18d9c8 - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +1ecb63976863e91900cd35fcea5179ef46e4778932ece74c0edef4b2aa18d9c8 - +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display): + +```text +diff <(git cat-file -p cbb31a53f6f4ed6b96faafb79b273b55664f3ae0) <(git cat-file -p cbb31a53f6f4ed6b96faafb79b273b55664f3ae0 | awk "$N") | sed 's/[[:space:]]*$//' +3d2 +< Status: PROPOSED +2612,2618d2610 +< +< Current decision state: **PROPOSED**. +< +< The corrected content of this version has not been approved. It may carry +< `APPROVED`, an approver and an approval date only after the CTO approves this +< exact corrected content under the repository decision process. +< + +diff <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") | sed 's/[[:space:]]*$//' +3d2 +< Status: APPROVED +5,6d3 +< Approved by: Javi, CTO +< Approval date: 2026-10-07 +2614,2628d2610 +< +< This ADR is **APPROVED**. +< +< Approved by: Javi, CTO +< Approval date: 2026-10-07 +< +< The CTO approved this exact corrected content on 2026-10-07 under the +< repository decision process, as the architecture content represented by the +< Route-B candidate ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; +< this version differs from that candidate only in this approval-state +< representation. That approval does not by itself make this ADR +< repository-authoritative: the authority condition below still applies. Any +< ADR-0013 programme-local reliance on this exact version is a separate state +< that is not effective until its own conditions are satisfied for this version. +< +``` + +ADR-0012 approval state: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n 3,7p +Status: APPROVED +Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-07 +Decision owner: CTO +``` + +Decision-register candidate -> approval state (one line, the ADR-0012 row; +every other line byte-identical; word-level changes, condensed to one pair per +line): + +```text +git diff --cached -U0 42ae90a5932cf24ce90679196888cc3d83d88db8 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-07 + +diff <(git show 42ae90a5932cf24ce90679196888cc3d83d88db8:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 42ae90a5932cf24ce90679196888cc3d83d88db8 -- docs/engineering/decisions/decision-register.md +[-PROPOSED-] {+APPROVED+} +[-Pending-] {+Satisfied+} +{+the CTO approved+} {+exact+} +[-awaits CTO approval-] {+on 2026-10-07+} +``` + +Changelog candidate -> approval state (one line, the +`THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry; every other line +byte-identical; word-level changes, one pair per line): + +```text +git diff --cached -U0 42ae90a5932cf24ce90679196888cc3d83d88db8 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 42ae90a5932cf24ce90679196888cc3d83d88db8:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 42ae90a5932cf24ce90679196888cc3d83d88db8 -- CHANGELOG.md +[-propose-] {+record+} {+CTO-approved+} +[-`PROPOSED`:-] {+`APPROVED`:+} +{+CTO approved the exact+} +[-is not approved.**-] {+on 2026-10-07.**+} +[-proposal-] {+decision+} +``` + +Content-approval identifier (the command is shown with the identifier replaced +by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) 85937a4e1ff9a27d8f5b36ca82651821711472b2 +decision-register.md 246bd5e981d373447fccdd457c02645338bfee4b +CHANGELOG.md d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +85937a4e1ff9a27d8f5b36ca82651821711472b2 +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above; the ADR-0012 blob `85937a4e1ff9a27d8f5b36ca82651821711472b2` is the exact +final blob presented for CTO exact-final-blob confirmation at this stage. This +report's own final blob, the approval-state tree and the approval-state commit +SHA depend on this report's bytes and are not self-pinned; they are verified +against the remote head after push and recorded in the post-publication +handoff. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 42ae90a5932cf24ce90679196888cc3d83d88db8 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 42ae90a5932cf24ce90679196888cc3d83d88db8 HEAD +required: exit 0; no output + +git diff --name-only 42ae90a5932cf24ce90679196888cc3d83d88db8 HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -3982,6 +4515,16 @@ and the ADR-0012 row, and only one added entry); confirmed the protected decision and control blobs are unchanged; and read the report for statements that would become false once it moves into the candidate commit and reframed them. +Amendment-8 approval-state stage: compared the staged ADR-0012 with the +content-approved candidate blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0` by +the exact normalized comparison in section 9 (no difference), and read the raw +delta line by line; compared the staged register and changelog with the +candidate blobs (only the ADR-0012 row and the withdrawal-order correction +entry change); confirmed the protected decision and control blobs are +unchanged; confirmed by fixed-string search that the content-approval comment +identifier does not occur in this report; and read the report for statements +that would become false once it moves into the approval-state commit and +reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -3991,10 +4534,11 @@ Amendment-6/7 candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104`, the Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865` the control-record recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, the transient-state correction -head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a` and the factual-clarification -head `eb3129f13a9c858b3c012ee182315801f0b8563a`, are historical evidence; the -live PR #960 diff of the candidate commit exists only after its publication -and is GitHub-owned lifecycle evidence. +head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, the factual-clarification +head `eb3129f13a9c858b3c012ee182315801f0b8563a` and the content-approved +Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, are +historical evidence; the live PR #960 diff of the approval-state commit exists +only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -4006,9 +4550,9 @@ Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 recovery authorization, the Amendment-6/7 candidate and approval-state authorizations, the control-record recovery authorization, the transient-state correction authorization, the factual-clarification -authorization and the Amendment-8 candidate authorization, an automatic CI -failure, or any migration execution or Docker publication, is a HOLD -condition. +authorization, the Amendment-8 candidate authorization and the Amendment-8 +approval-state authorization, an automatic CI failure, or any migration +execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -4034,20 +4578,21 @@ permitted. The Amendment-6/7 authorizations permitted exactly one candidate commit and exactly one approval-state commit, the control-record recovery and transient-state correction authorizations each permitted exactly one report-only commit, the factual-clarification authorization permitted exactly -one two-path clarification commit, and the Amendment-8 candidate -authorization permits exactly one four-path candidate commit; any further -source correction exhausts those authorizations and requires a fresh CTO -authorization (Amendment 3 C10; Amendments 6-8; +one two-path clarification commit, the Amendment-8 candidate authorization +permitted exactly one four-path candidate commit, and the Amendment-8 +approval-state authorization permits exactly one direct-child approval-state +commit; any further source correction exhausts those authorizations and +requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-8; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-8 candidate stage the +- Historical stage-time evidence: at the Amendment-8 approval-state stage the implementing agent performed no independent inspection or review, - candidate-content approval, PR #960 title/description reconciliation, - Draft/Ready change, automated-review request, thread adjudication, - exact-head review or merge (section 4.2). + exact-final-blob approval or confirmation, PR #960 title/description + reconciliation, Draft/Ready change, automated-review request, thread + adjudication, exact-head review or merge (section 4.2). Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -4079,7 +4624,9 @@ Monitoring required: NONE two-path clarification commit, and the withdrawal/selection ordering defect found by the final-head review of that clarification head only under Specification Amendment 8, its independent review and a separate - authorization for this one four-path candidate commit. + authorization for one four-path candidate commit, and its approval state + only under a separate authorization for this one direct-child approval-state + commit. - The strand-check timing question left outside this correction (section 5, decision 53): Amendments 1-8 do not determine whether a withdrawal's strand-prevention evaluation may be stale relative to events or anomalies @@ -4173,11 +4720,15 @@ Monitoring required: NONE CRITICAL specification review of the base specification and Amendments 1-8 returned `APPROVED`, confirming the finding, the classification and the validation matrix and recording five non-blocking candidate-inspection - observations; and the Route-B candidate that contains this report - implements the correction in source as `PROPOSED` (section 4; section 5, - decisions 49-60). `R-EB31-01` is closed at specification level by Amendment - 8; whether the candidate closes it in source is decided by the independent - inspection and review of the candidate, which GitHub records. + observations; the content-approved Route-B candidate + `42ae90a5932cf24ce90679196888cc3d83d88db8` implements the correction in + source (section 4; section 5, decisions 49-60); control's independent + inspection of that candidate found no blocking exact-candidate finding; the + CTO approved its exact content on 2026-10-07; and the approval-state commit + that contains this report records that approval. `R-EB31-01` is closed at + specification level by Amendment 8; its source closure is adjudicated by the + independent exact-head review controls that precede any merge, which GitHub + records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -4191,17 +4742,32 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-8 candidate also implemented -the factual clarification, the transient-state correction, the control-record -recovery, the Amendment-6/7 approval-state commit and candidate and earlier -Route-B source stages of this task and prepared the earlier uncommitted +work. The implementing agent of this Amendment-8 approval-state commit also +implemented the Amendment-8 candidate, the factual clarification, the +transient-state correction, the control-record recovery, the Amendment-6/7 +approval-state commit and candidate and earlier Route-B source stages of this +task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent inspector or reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Suggested review focus: -- that the candidate changes exactly the four authorized paths from the +- that the approval-state delta from the content-approved Amendment-8 + candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` is limited to the ADR + header approval fields, the two leading section-12 paragraphs, the ADR-0012 + register row and the withdrawal-order correction changelog entry, that the + normalized comparison in section 9 shows no architecture drift from + candidate blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, and that the + approval-state ADR-0012 blob is `85937a4e1ff9a27d8f5b36ca82651821711472b2`; +- that the content approval is recorded only through owning issue #958, the + approval date 2026-10-07, candidate head + `42ae90a5932cf24ce90679196888cc3d83d88db8` and candidate ADR-0012 blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, that the approval date matches + the CTO candidate-content approval record, and that no content-approval, + exact-final-blob approval, review or merge identifier is copied into this + report (section 5, decision 64); +- that the candidate changed exactly the four authorized paths from the clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`, that the candidate ADR-0012 blob is `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, and that ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; @@ -4218,9 +4784,10 @@ Suggested review focus: rule, invents no withdrawal reversibility, equivalent-signature re-enablement, second lifecycle order or route abstraction, and leaves the strand-check timing question outside the correction as decision 53 records; -- that every ADR-0012 hunk outside those concerns is limited to the candidate - `PROPOSED` state (header and section 12) and that all other architecture is - byte-identical to the clarification head; +- that every ADR-0012 hunk outside those concerns is limited to the approval + state (header and section 12) and that all other architecture is + byte-identical to the clarification head and to the content-approved + candidate; - that the approval-state delta from the content-approved candidate `b596536e7db0d6b8ace0a42fdd26bff42cabf104` is limited to the ADR header approval fields, the two leading section-12 paragraphs, the ADR-0012 @@ -4269,15 +4836,16 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `PROPOSED` with no current approver or - approval date, bind the 2026-09-30, 2026-10-05 and 2026-10-06 approvals to - the earlier exact versions and keep the repository authority condition and - the ADR-0013 reliance distinction; -- that the register row, the new withdrawal-order correction changelog entry - and this report describe the corrected version as `PROPOSED`, that the - route-contract correction entry and every other historical changelog entry - are byte-preserved, and that the register header `Last updated` is - `2026-10-07`; +- that the header and section 12 carry `APPROVED` with Javi, CTO and + 2026-10-07, identify the approved content by candidate ADR-0012 blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, bind the 2026-09-30, 2026-10-05 + and 2026-10-06 approvals to the earlier exact versions and keep the + repository authority condition and the ADR-0013 reliance distinction; +- that the register row, the withdrawal-order correction changelog entry and + this report describe the corrected version as `APPROVED` on 2026-10-07, + that the route-contract correction entry and every other historical + changelog entry are byte-preserved, and that the register header + `Last updated` is `2026-10-07`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the clarification head, the recovery head and the content-approved candidates. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index cbb31a53f..85937a4e1 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-07 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -2610,11 +2612,19 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +This ADR is **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-07 + +The CTO approved this exact corrected content on 2026-10-07 under the +repository decision process, as the architecture content represented by the +Route-B candidate ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; +this version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 45ce93514..246bd5e98 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-07 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, and the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and the CTO approved its exact corrected content on 2026-10-07 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, and the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 8699af5635b8f18691ceaeccd3b24707a71ef4f2 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 7 Oct 2026 22:28:09 +0100 Subject: [PATCH 36/48] THOTH-ASYNC-01-ADR-01: propose coalescing-seal ordering correction --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1060 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 211 +++- .../decisions/decision-register.md | 2 +- 4 files changed, 1054 insertions(+), 220 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1fbe1bdc..4d0e17ca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 307a46ed1..ffbb44f9f 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -12,17 +12,20 @@ report-only control-record recovery of that approval-state report, the one-path report-only transient-state correction that followed the configured final-head automated review of that recovery head, the two-path factual clarification of ADR-0012 and durable-report correction that followed the -configured final-head automated review of that correction head, and the +configured final-head automated review of that correction head, the Route-B candidate and approval-state stages of the revision-withdrawal/selection ordering correction specified by Specification Amendment 8 after the configured -final-head automated review of that clarification head. It records +final-head automated review of that clarification head, and the Route-B +candidate stage of the coalescing-attachment/first-claim ordering correction +specified by Specification Amendment 9 after the configured final-head +automated review of that approval-state head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-8 Route-B approval-state commit -as the current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-9 Route-B candidate as the +current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -44,11 +47,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-8 Route-B approval-state commit that contains -this report, an ordinary single-parent additive commit whose direct parent is +Head commit: the Amendment-9 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical Amendment-8 approval-state head +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`; its SHA is deliberately not +self-pinned here (section 3). That approval-state head is the direct child of the content-approved Amendment-8 candidate -`42ae90a5932cf24ce90679196888cc3d83d88db8`; its SHA is deliberately not -self-pinned here (section 3). That candidate is the direct child of the +`42ae90a5932cf24ce90679196888cc3d83d88db8`, which is the direct child of the historical factual-clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`, which is the direct child of the historical report-only transient-state correction head @@ -64,7 +69,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -78,8 +83,9 @@ Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, -Amendment-8 candidate and Amendment-8 approval-state stages - Claude (Fable -5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. +Amendment-8 candidate, Amendment-8 approval-state and Amendment-9 candidate +stages - Claude (Fable 5.1), bounded implementing agent. Earlier rounds are +recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -191,15 +197,23 @@ decision-register blob 45ce935149693d0e983fac1645f5363896e23a53 implementation-report blob f85da63b89654d4774462de674eb02b4a1a0c38d CHANGELOG blob f3c2bc7976d5fb84e1a00cbfce05748197da900b -Amendment-8 Route-B approval-state commit (the commit that contains this report): -ADR-0012 blob 85937a4e1ff9a27d8f5b36ca82651821711472b2 (APPROVED; Javi, CTO; 2026-10-07; architecture-equivalent to cbb31a53f6f4ed6b96faafb79b273b55664f3ae0) +historical Amendment-8 approval-state head / Amendment-9 candidate parent: +e2588f7b7e63a5c71968d87a3409f0ff48635e6d +tree bec3637f333a12ed1895b2aae6f1078510067f5e +ADR-0012 blob 85937a4e1ff9a27d8f5b36ca82651821711472b2 (APPROVED; Javi, CTO; 2026-10-07; architecture-equivalent to cbb31a53f6f4ed6b96faafb79b273b55664f3ae0; CTO exact-final-blob approval recorded on #958 on 2026-10-07; the exact prior approved blob that the Amendment-9 correction corrects) decision-register blob 246bd5e981d373447fccdd457c02645338bfee4b CHANGELOG blob d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82 +implementation-report blob 307a46ed153cd1287b24d921e7286c62328e8cc1 (superseded by the Amendment-9 candidate) + +Amendment-9 Route-B candidate (the commit that contains this report): +ADR-0012 blob 0fc33df9aca61a2c82af452426f5574bfd3493cf (PROPOSED; material architectural correction of 85937a4e1ff9a27d8f5b36ca82651821711472b2) +decision-register blob 8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be +CHANGELOG blob 4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4 feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual -clarification and the Amendment-8 candidate and approval-state commit -(unchanged): +clarification, the Amendment-8 candidate and approval-state commit and the +Amendment-9 candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -274,7 +288,21 @@ Approved specification: is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and is not copied here; - Amendment-8 Route-B approval-state implementation authorization: #958 - comment `6045126538`. + comment `6045126538`; +- CTO exact-final-blob approval of the Amendment-8 approval-state ADR-0012 + blob `85937a4e1ff9a27d8f5b36ca82651821711472b2` at head + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`: recorded on #958 on 2026-10-07; + its GitHub comment identifier is GitHub-owned lifecycle evidence under + `ADR-0005` and Route-B doctrine and is not copied here; +- Specification Amendment 9 / coalescing attachment versus job-start + linearization, which accepts the final-head finding `R-E258-01` as valid and + blocking and classifies its correction as a material architectural + correction: #958 comment `6046180884`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-9, including six non-blocking candidate-inspection + observations: #958 comment `6046871411`; +- Amendment-9 Route-B candidate implementation authorization: #958 comment + `6046895895`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -300,13 +328,19 @@ commit on the clarification head that changed exactly the four authorized correction paths and requires revision withdrawal and every new durable selection of a route-contract revision to share one database-atomic linearized order, received independent control inspection and CTO exact -candidate-content approval on 2026-10-07. The commit that contains this report -is the one bounded Route-B approval-state commit that records that approval as -durable decision state: ADR-0012 `PROPOSED -> APPROVED` with -`Approved by: Javi, CTO` and `Approval date: 2026-10-07`, the ADR-0012 register -row and the withdrawal-order correction changelog entry moved to the approved -state, and this report's approval-state evidence. No architecture content -changes. +candidate-content approval on 2026-10-07. The one bounded Route-B +approval-state commit then recorded that approval as durable decision state +(ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +`Approval date: 2026-10-07`, the ADR-0012 register row and the withdrawal-order +correction changelog entry moved to the approved state), and the CTO recorded +exact-final-blob approval of that ADR blob. The configured final-head automated +review of that approval-state head exposed a further architecture defect, +`R-E258-01`, specified by Specification Amendment 9. The commit that contains +this report is its Route-B candidate: one ordinary single-parent commit on the +approval-state head that changes exactly the four authorized correction paths, +returns ADR-0012 to `Status: PROPOSED` and requires a coalescing attachment to +an existing logical job and that job's first successful claim/start to share +one database-atomic linearized order. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -536,16 +570,17 @@ changes. `42ae90a5932cf24ce90679196888cc3d83d88db8`, ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0` - on 2026-10-07 in a durable record on #958. That stage authorized no PR-state or PR-metadata change. -- **Amendment-8 approval-state stage (the commit that contains this - report).** Authorized by #958 comment `6045126538`. One ordinary - single-parent commit directly on the content-approved candidate records - that approval as durable decision state, changing only the four authorized +- **Amendment-8 approval-state stage (historical approval-state head + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`).** Authorized by #958 comment + `6045126538`. One ordinary single-parent commit directly on the + content-approved candidate recorded that approval as durable decision state, + changing only the four authorized paths: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and `Approval date: 2026-10-07` and the leading section-12 approval paragraphs, which identify the approved content as the content represented by candidate ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; the ADR-0012 register row; the `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` - changelog entry; and this report. No architecture content changes (section + changelog entry; and this report. No architecture content changed (section 9, "Amendment-8 approval-state validation"). The content approval is recorded here through owning issue #958, approval date 2026-10-07, the candidate head and the candidate ADR-0012 blob; its GitHub comment @@ -554,7 +589,52 @@ changes. `docs/engineering/decisions/README.md` requires and as the control adjudication on #958 of the earlier Amendment-6/7 approval-state report established for this task (section 5, decisions 36-37 and 64). PR #960 was - already a draft; this stage authorizes no PR-state or PR-metadata change. + already a draft; that stage authorized no PR-state or PR-metadata change. +- **Exact-final-blob approval, final-head review of the Amendment-8 + approval-state head, Specification Amendment 9 and the Amendment-9 Route-B + candidate (the commit that contains this report).** Authorized by #958 + comment `6046895895` after the CTO approved Specification Amendment 9 (#958 + comment `6046180884`) and a fresh independent CRITICAL specification review + of the base specification and Amendments 1-9 returned `APPROVED` (#958 + comment `6046871411`). After the approval-state head was published, control + independently inspected it and the CTO recorded exact-final-blob approval of + ADR-0012 blob `85937a4e1ff9a27d8f5b36ca82651821711472b2` at head + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` on 2026-10-07; control then + separately marked PR #960 Ready, which triggered the configured final-head + automated review of that head, and PR #960 had been returned to Draft before + any candidate source was staged. That review raised one new finding, + recorded by control as `R-E258-01` (HIGH, valid and blocking) and classified + as a `MATERIAL ARCHITECTURAL CORRECTION`: ADR-0012 allowed several + materialized route dispositions to point to one not-yet-started job under an + explicit coalescing rule and allowed an idempotency conflict to attach a + route to such a job, and it required database-enforced worker claims, but it + did not require the transaction that attaches a new disposition to an + existing coalesced job to share one database-atomic order with the + transaction that first claims/starts that job. A router could therefore + observe the job as not started, a worker could claim/start it and commit the + first execution-owned transition, and the router could then commit a + disposition recording the event as represented by a job whose inputs may + already have been fixed or whose effect may already have completed without + that trigger. Amendment 9 selects one coalescing-open/coalescing-sealed + boundary per logical job, sealed permanently by the first successful + claim/start; one database-atomic order between attachment and that + transition, with the attachment write itself proving the job is still open; + both commit orders; the aborted-claim rule; mechanism-neutral input/effect + completeness; the same boundary for every attachment path the ADR already + permits; and composition with the existing claim, idempotency, disposition + and target-concurrency rules, with the seal earlier than and distinct from + `EFFECT_STARTED`. The independent review confirmed the finding, the + classification, the seal boundary and the sufficiency of the nine-case + concurrency matrix, and recorded six non-blocking candidate-inspection + observations. The commit that contains this report is one ordinary + single-parent commit directly on the approval-state head that changes + exactly the four authorized correction paths: ADR-0012 returns to + `Status: PROPOSED` with no current approver or approval date and implements + Amendment 9 (section 4; section 5, decisions 69-80); the decision-register + ADR-0012 row, one new changelog entry and this report describe the + corrected version as `PROPOSED`; and the 2026-09-30, 2026-10-05, 2026-10-06 + and 2026-10-07 CTO approvals remain historical evidence bound to the earlier + exact versions. This stage authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -568,9 +648,14 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 85937a4e1ff9a27d8f5b36ca82651821711472b2 +(Specification Amendment 9; Route-B candidate stage; candidate status PROPOSED) + +historical Amendment-8 approval-state at e2588f7b7e63a5c71968d87a3409f0ff48635e6d: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-07) of the content-approved Amendment-8 candidate blob cbb31a53f6f4ed6b96faafb79b273b55664f3ae0; -no architecture change +no architecture change; CTO exact-final-blob approval of 85937a4e1ff9a27d8f5b36ca82651821711472b2 +recorded on #958 on 2026-10-07 historical Amendment-8 candidate at 42ae90a5932cf24ce90679196888cc3d83d88db8: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 2db08058552ce901548ef3f71af4a294f5af7e61 @@ -588,9 +673,10 @@ number, not to one pathname (Amendment 1 A1; validation time `b44303c214498baf76c9d9a0a7cab374377f9cbe` was simultaneously `develop` and the refreshed `feature/worker`, and `master` was `4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`. Those refs were unchanged at the -Amendment-6/7 candidate stage, at the Amendment-8 candidate stage and at the -Amendment-8 approval-state stage, and the same literal commands were run again -at each stage against both commits with identical results: +Amendment-6/7 candidate stage, at the Amendment-8 candidate stage, at the +Amendment-8 approval-state stage and at the Amendment-9 candidate stage, and +the same literal commands were run again at each stage against both commits +with identical results: ```text git log --format=%h -- ':(glob)**/*ADR-0012*' @@ -699,13 +785,37 @@ version as well: `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` changelog entry that record the route-contract-revision version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while - the register row recorded the candidate as `PROPOSED` and now records the - approved version. + the register row recorded the Amendment-8 candidate as `PROPOSED` and then + the approved version. They remain historical evidence bound to their exact versions and heads. The -candidate version received its own independent control inspection and CTO -exact candidate-content approval on 2026-10-07, and the approval-state commit -that contains this report records it. For this version, exact-final-blob +Amendment-8 candidate version received its own independent control inspection +and CTO exact candidate-content approval on 2026-10-07, the historical +approval-state commit `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` recorded it in +ADR-0012 blob `85937a4e1ff9a27d8f5b36ca82651821711472b2`, and that blob received +CTO exact-final-blob approval at that head on 2026-10-07. + +The Amendment-9 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every record bound to the withdrawal/selection +ordering version as well: + +- the CTO content approval of candidate + `42ae90a5932cf24ce90679196888cc3d83d88db8` / ADR blob + `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; +- the 2026-10-07 approval state carried by ADR blob + `85937a4e1ff9a27d8f5b36ca82651821711472b2` and its exact-final-blob approval at + head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the approval-state head; +- the approval-state-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` changelog entry that + record the withdrawal/selection ordering version as `APPROVED`; the + changelog entry is preserved and remains true only of that historical + version, while the register row now records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. For +the candidate version, independent exact-candidate inspection, CTO exact +candidate-content approval, the approval-state commit, exact-final-blob approval, independent exact-head review, CTO merge authorization, guarded merge, programme-local reliance and later migration, deployment, activation and observation are distinct controlled lifecycle categories; GitHub, through @@ -714,31 +824,45 @@ blocked for any head. ## 3. Commits -Amendment-8 Route-B approval-state commit (the commit that contains this -report): +Amendment-9 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: record withdrawal-selection correction approval` - - direct parent: the content-approved Amendment-8 candidate - `42ae90a5932cf24ce90679196888cc3d83d88db8` - - changes only the four authorized paths, and only to record the approval - state (section 4); the approval-state ADR-0012 blob is - `85937a4e1ff9a27d8f5b36ca82651821711472b2`, the approval-state - decision-register blob `246bd5e981d373447fccdd457c02645338bfee4b` and the - approval-state changelog blob `d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82` + `THOTH-ASYNC-01-ADR-01: propose coalescing-seal ordering correction` + - direct parent: the historical Amendment-8 approval-state head + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` + - changes only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `0fc33df9aca61a2c82af452426f5574bfd3493cf`, the + candidate decision-register blob `8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be` + and the candidate changelog blob `4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the approval-state ADR-0012, +here because this report is part of that tree; the candidate ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its candidate blob (section 9). The -commit identities are verified against the remote task-branch head after push -and recorded in the post-publication handoff; once published, the PR #960 -head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The -authorized topology is a non-force fast-forward of the task branch from the -content-approved candidate to the approval-state commit; no merge commit, -rebase, squash, amend, force update or second approval-state commit is -authorized, and no further source commit exists implicitly after it. +recorded above; every other file keeps its approval-state-head blob (section +9). The commit identities are verified against the remote task-branch head +after push and recorded in the post-publication handoff; once published, the +PR #960 head and head tree are GitHub-owned lifecycle evidence under +`ADR-0005`. The authorized topology is a non-force fast-forward of the task +branch from the approval-state head to the candidate commit; no merge commit, +rebase, squash, amend, force update or second candidate commit is authorized, +and no further source commit exists implicitly after it. + +Amendment-8 Route-B approval-state commit (historical): + +- `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` - + `THOTH-ASYNC-01-ADR-01: record withdrawal-selection correction approval` + - ordinary single-parent additive commit; direct parent: the content-approved + Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` + - tree `bec3637f333a12ed1895b2aae6f1078510067f5e` + - changed only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `85937a4e1ff9a27d8f5b36ca82651821711472b2`, which received CTO + exact-final-blob approval at that head on 2026-10-07 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + candidate; the remote head, tree, parent, ADR-0012, decision-register, + changelog and report blobs and the ADR approval state matched the locally + validated object Amendment-8 Route-B candidate commit (content-approved; historical): @@ -891,13 +1015,24 @@ first-parent order, newest first: ## 4. Files changed -Amendment-8 Route-B approval-state write budget for the commit that contains -this report (authorization #958 comment `6045126538`), identical to the -Amendment-8 candidate write budget (authorization #958 comment `6044220221`; -the Amendment 6 G10 / Amendment 7 H12 budget, unchanged by Amendment 8); -exactly four existing paths, no new file, no deletion, and every other path -byte-identical to the content-approved candidate -`42ae90a5932cf24ce90679196888cc3d83d88db8`: +Amendment-9 Route-B candidate write budget for the commit that contains this +report (authorization #958 comment `6046895895`; the Amendment 6 G10 / +Amendment 7 H12 budget, unchanged by Amendments 8-9); exactly four existing +paths, no new file, no deletion, and every other path byte-identical to the +historical Amendment-8 approval-state head +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-8 Route-B approval-state write budget (authorization #958 +comment `6045126538`), identical to the Amendment-8 candidate write budget +(authorization #958 comment `6044220221`; the Amendment 6 G10 / Amendment 7 +H12 budget, unchanged by Amendment 8); exactly four existing paths, no new +file, no deletion, and every other path byte-identical to the content-approved +candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`: - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -954,11 +1089,107 @@ six paths at every stage): Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 -candidate and Amendment-8 approval-state stages; all four correction paths -already existed. +candidate, Amendment-8 approval-state and Amendment-9 candidate stages; all +four correction paths already existed. + +Amendment-9 candidate changes in the commit that contains this report, +relative to the approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` +(exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement Specification Amendment 9 and record the corrected + version as `PROPOSED`. + - behavioural effect: architecture proposal only. The header carries + `Status: PROPOSED` and no current approver or approval date. Section 3.5 + states that a job is not yet started, and so coalescing-open, only until + its first successful execution claim/start commits; binds the + idempotency-conflict attachment to the database-atomic coalescing + boundary; and gains the unnumbered subsection "Coalescing-open and + coalescing-sealed jobs": one durable boundary per logical job of a + coalescing kind, sealed permanently by the first successful committed + claim/start, the seal belonging to the logical job and never reopened by + retry, lease reclaim, WAITING/resume, reconciliation, cancellation, + recovery or later claims, with the persisted encoding left to the + implementation specification; one database-atomic serialization, lock or + compare-and-set order between attachment and first claim/start, the + attachment write itself proving the job is still open and an + application-level pre-check insufficient, with the SQL, lock or + constraint left to the implementation specification; both commit orders; + the aborted-claim rule; mechanism-neutral input/effect completeness + (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other + kinds through a retained input set or equivalent, no handler enumeration + of dispositions); the same boundary for every attachment path the ADR + already permits, without deciding whether any path may coalesce; and the + seal's distinction from `EFFECT_STARTED` with the composition list. + Section 3.15 states that a surviving job accepts attachments only while + open and represents every pre-seal attachment. Invariant 8 names the + coalescing-open condition and invariant 42 is appended; section 8 item 1 + and section 9 rollout proof 5 name the boundary; section 11 adds the nine + Amendment-9 concurrency validation cases, with explicit `CURRENT_STATE`, + retained-input, idempotency-conflict, multi-instance and crash/restart + evidence, after the existing coalescing tests. Section 12 records the + `PROPOSED` state, adds the boundary to the description of this material + correction and binds the 2026-09-30, 2026-10-05, 2026-10-06 and + 2026-10-07 approvals to the historical earlier versions. + `(event_id, route_key)` uniqueness, route eligibility, activation epochs, + route-contract revisions, the Amendment-8 withdrawal/selection ordering, + idempotency-key identity, which kinds may coalesce, the explicit, durable + and audited coalescing requirement, `CURRENT_STATE` and `REVISION_BOUND` + semantics, target-scoped serialization, applied-revision evidence, the + attempt phases, claim-token fencing, WAITING and RECONCILIATION_REQUIRED, + retry and reconciliation semantics, worker trust boundaries and the BE-04 + transition are unchanged. Candidate blob: + `0fc33df9aca61a2c82af452426f5574bfd3493cf`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the material correction makes the ADR-0012 row's `APPROVED` + assertion untrue for the new version. + - behavioural effect: the ADR-0012 row records `PROPOSED` / `Pending`, + binds the 2026-09-30, 2026-10-05, 2026-10-06 and 2026-10-07 approvals to + the historical earlier exact versions and adds one summary of the + coalescing-open/coalescing-sealed boundary and the attachment-versus- + first-claim order; its partial-supersession, authority-condition, ADR-0013 + and implementation-authorization sentences are unchanged. The header + `Last updated: 2026-10-07` already carries the date of this row change and + is unchanged (section 5, decision 78). No other row changes. Candidate + blob: `8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: required changelog entry. + - behavioural effect: exactly one new + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry at the top of + `[Unreleased] -> Changed`, describing the corrected version as `PROPOSED` + and stating that the earlier + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-CORRECTION`, `THOTH-ASYNC-01-ADR-01-APPROVAL` and + `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries and the approvals they + record apply only to the historical earlier exact versions. No heading is + added and every existing entry is byte-preserved. Candidate blob: + `4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-9 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate the current + stage; records the exact-final-blob approval of the Amendment-8 + approval-state blob, the final-head review of that head, `R-E258-01`, + Amendment 9, its independent review and the candidate authorization as + governing evidence; records the candidate's validation (section 9); and + reframes statements that would otherwise describe the approval-state + commit as the commit containing this report, recording that commit's + identities as historical facts. All still-true earlier evidence is + preserved. + - within authorized write budget: YES + +Byte-identical to the approval-state head through the Amendment-9 candidate: +every path other than the four correction paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. -Amendment-8 approval-state changes in the commit that contains this report, -relative to the content-approved candidate +Historical Amendment-8 approval-state changes in the approval-state commit +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, relative to the content-approved +candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` (exactly the four authorized paths, approval state only): @@ -1006,13 +1237,13 @@ approval state only): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-8 approval-state stage truthfully. - - behavioural effect: documentation only. Makes the approval-state commit - the current stage; records the content-approved candidate, the CTO - candidate-content approval and the approval-state authorization; records - the approval-state validation and no-drift proof (section 9); and reframes - statements that would otherwise describe the candidate as the commit - containing this report, recording the candidate's identities as - historical facts. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the approval-state commit + the then-current stage; recorded the content-approved candidate, the CTO + candidate-content approval and the approval-state authorization; recorded + the approval-state validation and no-drift proof (section 9); and + reframed statements that would otherwise describe the candidate as the + commit containing the report, recording the candidate's identities as + historical facts. All still-true earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the content-approved candidate through the approval-state @@ -1334,8 +1565,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state -correction, the factual clarification, the Amendment-8 candidate and the -Amendment-8 approval-state commit: +correction, the factual clarification, the Amendment-8 candidate, the +Amendment-8 approval-state commit and the Amendment-9 candidate: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -1514,7 +1745,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-8 approval-state commit differs from the content-approved +The Amendment-9 candidate differs from the approval-state head +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d` in exactly the four authorized +correction paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-9 candidate validation"). The +Amendment-8 approval-state commit differed from the content-approved candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-8 approval-state validation"). The @@ -1560,8 +1795,9 @@ candidate stage (#958 comment `6021574322`), the Amendment-6/7 approval-state stage (#958 comment `6022486352`), the control-record recovery stage (#958 comment `6022850916`), the transient-state correction stage (#958 comment `6036961945`), the factual-clarification stage (#958 comment `6041301223`), -the Amendment-8 candidate stage (#958 comment `6044220221`) and the -Amendment-8 approval-state stage (#958 comment `6045126538`). This matrix +the Amendment-8 candidate stage (#958 comment `6044220221`), the Amendment-8 +approval-state stage (#958 comment `6045126538`) and the Amendment-9 candidate +stage (#958 comment `6046895895`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -1779,8 +2015,8 @@ commit: head, tree, parent and ADR-0012, decision-register, changelog and report blobs were verified) -Amendment-8 approval-state stage - done up to and including the creation of -the commit that contains this report: +Amendment-8 approval-state stage - historical, completed before the +Amendment-9 candidate: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -1793,47 +2029,82 @@ the commit that contains this report: stage-time evidence, the PR #960 review-thread state in GitHub; all matched the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, - title or description change is authorized at this stage) + title or description change was authorized at that stage) - source edit: USED (the four authorized paths, approval state only) - new file creation, file deletion/move/rename, branch creation: NOT USED (not authorized; the same detached local worktree, now at the content-approved candidate, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent approval-state commit that contains this - report) +- commit: USED (the single-parent approval-state commit + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `42ae90a5932cf24ce90679196888cc3d83d88db8` to the approval-state commit; the + remote head, tree, parent, ADR-0012, decision-register, changelog and report + blobs and the ADR approval state were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the approval-state head, and returned it to Draft before the + Amendment-9 candidate (section 2) + +Amendment-9 candidate stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` and target `feature/worker`, + the approval-state head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008 + and ADR-0010 blobs, the six-path cumulative footprint, PR #960 as the only + open pull request targeting `feature/worker`, the presence of the Amendment + 9, independent-review and authorization records on #958 with no later + control record, and, as stage-time evidence, the PR #960 review-thread + state in GitHub; all matched the authorization; current review-thread state + remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 had been returned to draft by control + before this stage, so no PR-state mutation was needed; no PR-state, title + or description change is authorized at this stage) +- source edit: USED (the four authorized correction paths only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; the same detached local + worktree, now at the approval-state head, was used for construction, and + the existing task branch is the fast-forward target) +- commit: USED (the single-parent candidate commit that contains this report) -Authorized post-commit actions for the approval-state commit - GitHub-owned +Authorized post-commit actions for the candidate commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `42ae90a5932cf24ce90679196888cc3d83d88db8` to the approval-state commit; + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` to the candidate commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, ADR approval - state, changed path set, cumulative footprint and PR #960 state, and that - no review thread was replied to, resolved or dismissed; -- the automatic PR CI that the push causes. + ADR-0012, decision-register, changelog and report blobs, changed path set, + cumulative footprint and PR #960 state, and that no review thread was + replied to, resolved or dismissed; +- the automatic PR CI that the push causes, and read-only inspection of any + automated review that runs on the draft head. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-8 approval-state -stage, each a separate controlled lifecycle category where applicable -(historical stage-time evidence; GitHub, through #958 and PR #960, is -authoritative for the current state of each): +Not authorized for the implementing agent at the Amendment-9 candidate stage, +each a separate controlled lifecycle category where applicable (historical +stage-time evidence; GitHub, through #958 and PR #960, is authoritative for +the current state of each): -- any edit to a path other than the four authorized paths: NOT USED (not - authorized) -- any ADR-0012 change outside the approval-state representation, including - any architecture change: NOT USED (not authorized) -- independent inspection or exact-head review: NOT USED (never on the - implementing agent's authority) -- exact-final-blob approval or confirmation: NOT USED (not authorized; never +- any edit to a path other than the four authorized correction paths: NOT USED + (not authorized) +- any ADR-0012 change outside Amendment 9 and the candidate `PROPOSED` state: + NOT USED (not authorized) +- independent exact-candidate inspection or exact-head review: NOT USED (never on the implementing agent's authority) +- CTO candidate-content approval, approval-state commit, fresh `APPROVED` + status, approver or approval date, exact-final-blob approval: NOT USED (not + authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second approval-state commit, amend, rebase, squash, force push or - history rewrite: NOT USED (not authorized) +- any second candidate commit, amend, rebase, squash, force push or history + rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -1865,26 +2136,27 @@ classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments -4-5, Amendments 6-7, Amendment 8, the transient-state correction +4-5, Amendments 6-7, Amendments 8-9, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 -candidate authorization and the Amendment-8 approval-state authorization, -configured Codex/app review is not expected from a push to a draft PR and is -not relied upon at the Amendment-6/7 candidate, approval-state, -control-record recovery, transient-state correction, factual-clarification, -Amendment-8 candidate or Amendment-8 approval-state stage; configured -Codex/app review runs on separately authorized Ready transitions, which are -GitHub-owned lifecycle events. The actual job outcomes for the approval-state -commit exist only after its publication; they are GitHub-owned lifecycle -evidence recorded in the pull +candidate and approval-state authorizations and the Amendment-9 candidate +authorization, configured Codex/app review is not expected from a push to a +draft PR and is not relied upon at the Amendment-6/7 candidate, +approval-state, control-record recovery, transient-state correction, +factual-clarification, Amendment-8 candidate, Amendment-8 approval-state or +Amendment-9 candidate stage; configured Codex/app review runs on separately +authorized Ready transitions, which are GitHub-owned lifecycle events, and any +automated review that nevertheless runs on a draft head is inspected read-only. +The actual job outcomes for the candidate commit exist only after its +publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, -factual-clarification, Amendment-8 candidate or Amendment-8 approval-state -stage, followed the same documentation-only classification, with no manual -dispatch or rerun: +factual-clarification, Amendment-8 candidate, Amendment-8 approval-state or +Amendment-9 candidate stage, followed the same documentation-only +classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -1899,6 +2171,7 @@ b596536e7db0d6b8ace0a42fdd26bff42cabf104: 4 success (classify x3, check-changelo 9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (transient-state correction head) eb3129f13a9c858b3c012ee182315801f0b8563a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (factual-clarification head) 42ae90a5932cf24ce90679196888cc3d83d88db8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-8 candidate) +e2588f7b7e63a5c71968d87a3409f0ff48635e6d: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-8 approval-state head) ``` Manually initiated external actions (anything the implementing agent @@ -2323,7 +2596,8 @@ exact content: authorization; no review, thread or review-comment identifier of the final-head automated review is copied. -Amendment-8 approval-state stage (the commit that contains this report): +Amendment-8 approval-state stage (historical approval-state head +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`): 61. The approval-state header uses the same field order as the earlier approved ADR-0012 versions (`Status`, `Date`, `Approved by`, `Approval date`, @@ -2370,14 +2644,111 @@ Amendment-8 approval-state stage (the commit that contains this report): other entry, including the historical approval entries, is byte-identical. 67. Architecture no-drift is proven by the exact normalized comparison in section 9 ("Amendment-8 approval-state validation"), not by assertion. -68. This report does not self-pin the approval-state commit's SHA, its tree or - its own final blob; it records only non-self-referential evidence and the - approval-state ADR-0012, decision-register and changelog blobs, which do - not depend on its bytes. Because the report now lives in the - approval-state commit, statements that described the candidate as the - commit containing this report are reframed as history, and the - candidate's identities, now the approval-state parent, are recorded in - section 1 as historical facts. +68. The approval-state report did not self-pin the approval-state commit's + SHA, its tree or its own final blob; it recorded only non-self-referential + evidence and the approval-state ADR-0012, decision-register and changelog + blobs, which did not depend on its bytes. Because the report then lived in + the approval-state commit, statements that described the candidate as the + commit containing the report were reframed as history, and the + candidate's identities, the approval-state parent, were recorded in + section 1 as historical facts; section 1 now records the approval-state + commit's identities as historical facts because that head is the + Amendment-9 candidate's parent. + +Amendment-9 Route-B candidate (the commit that contains this report). Item 75 +is an interpretation of the approved specification recorded for explicit +control inspection: + +69. The Amendment-9 rule is stated once, in a new unnumbered subsection + "Coalescing-open and coalescing-sealed jobs" at the end of section 3.5, + following the unnumbered-subsection style of sections 3.2 and 3.8, and + the places that depend on it point to it: the section-3.5 sentence that + permits several materialized dispositions to point to one not-yet-started + job now defines "not yet started" as coalescing-open until the first + successful claim/start commits; the idempotency-conflict attachment is + bound to the boundary; and section 3.15's coalescing paragraph and + invariant 8 reference it. The heading outline gains exactly that one + heading. +70. One semantic anchor is used: the subsection states that the + "not-yet-started job" that coalescing may attach to and the "first + successful claim/start" that seals it refer to the same durable + transition, and that the seal is a property of the logical job, not of an + attempt or a disposition (independent-review observations 1 and 4). The + persisted encoding is left to the implementation specification, with the + statement that a separate sealed flag is not required where the canonical + job state, attempt or claim transition already enforces the property. +71. The database-atomic order is stated as Amendment 9 J5 states it: one + serialization, lock or compare-and-set boundary or database-enforced + equivalent; the forbidden observe-then-attach sequence; the attachment + write itself proving the job is still open; the first claim/start + participating so that no concurrent attachment commits after it; an + application-level pre-check insufficient; no SQL, lock or constraint + selected. +72. Both commit orders are written as J6 defines them, including that the + claim/start-first refusal commits no disposition, leaves the + `(event_id, route_key)` slot unused, leaves the obligation owed and + visible and follows the existing idempotency-conflict error and + attention/recovery path, and that the losing router re-reads durable + state before any retry under existing semantics, with no new logical-job + identity and no new replay or current-state rule. +73. The aborted-claim rule (J7) binds sealing to the durable committed + transition and states that an attachment may still succeed after rollback + when every ordinary condition holds. +74. Input/effect completeness (J8) is stated mechanism-neutrally: the kind's + coalescing contract defines representation; `CURRENT_STATE` may use + current-state/effect-fingerprint semantics; another kind may use a + retained input set or equivalent; handlers are not required to enumerate + dispositions; `CURRENT_STATE`/`REVISION_BOUND` semantics, effect + fingerprints, target-scoped concurrency and applied-revision evidence are + unchanged. +75. The attachment-scope sentence (authorization section 5; independent-review + observation 5) is written conditionally: every attachment path "where this + ADR already permits that attachment", including historical + enrollment/backfill/replay and directly created logical jobs, uses the + boundary, and the boundary "does not itself decide whether any path may + coalesce". No path is newly permitted or newly forbidden to coalesce; + whether a path may attach remains governed by the existing explicit + durable coalescing rule and the idempotency-conflict rule. This + interpretation is recorded for control inspection because the + authorization requires HOLD if the sentence would have needed a new + coalescing-permission decision; it did not. +76. The seal is stated to be earlier than and distinct from `EFFECT_STARTED` + (J9), with the composition list of J9 and the statement that no second + job identity, job lifecycle, route lifecycle, claim-lock mode or second + order is introduced. +77. Invariant 42 is appended after invariant 41, and invariant 8 gains the + coalescing-open qualifier and a reference to invariant 42; invariants 1-41 + are otherwise unrenumbered and unchanged (decision 23 pattern). +78. The decision-register header `Last updated: 2026-10-07` already carries + the date on which the candidate changes the ADR-0012 row, so it is + unchanged (decisions 25 and 57 pattern); only the ADR-0012 row changes. +79. The nine Amendment 9 J10 validation cases are added to section 11 + immediately after the existing coalescing tests as eight bullets: cases + 1-2 (both orders, with separate router and worker instances), case 3 + (with explicit `CURRENT_STATE` and retained-input evidence), case 4 (with + the idempotency-conflict path), case 5, case 6 (several router instances), + case 7, case 8 (with seal persistence across crash and restart) and case + 9; these carry the independent review's six evidence refinements + (observations 1-6) without adding architecture. All Amendment 1-8 + validation requirements are retained. +80. The header drops `Approved by` / `Approval date` and section 12 restores + the two candidate-state paragraphs used by the earlier Route-B candidates + (decisions 8, 9 and 58), adds the coalescing boundary to the description + of this material correction and binds the 2026-10-07 approval, like the + 2026-09-30, 2026-10-05 and 2026-10-06 approvals, to the historical earlier + version without embedding its identities. The changelog gains one new + entry and every historical entry, including the record of the 2026-10-07 + approval, stays byte-preserved (decisions 26 and 59). This report does not + self-pin the candidate commit's SHA, its tree or its own final blob; + because the report now lives in the candidate commit, statements that + described the approval-state commit as the commit containing this report + are reframed as history, and that commit's identities, now the candidate + parent, are recorded in section 1 as historical facts. The exact-final-blob + approval of the approval-state ADR blob is recorded through owning issue + #958, its date and the blob and head it approved; its GitHub comment + identifier is not copied, consistent with decisions 37 and 64, no review, + thread or review-comment identifier of the final-head automated review is + copied, and `R-E258-01` is recorded by its control-assigned name. List any deviation from the specification requiring authorization: @@ -2450,8 +2821,14 @@ List any deviation from the specification requiring authorization: the exact candidate head and ADR blob it approved, without restating its comment identifier (decision 64). No fifth path, new file, deletion, architecture change, second commit, amend, rebase, squash, force push or - history rewrite was needed or performed before the commit that contains - this report was created. + history rewrite was needed or performed before the approval-state commit was + created. +- Amendment-9 candidate stage: NONE. No fifth path, new file, deletion, ADR + change outside Amendment 9 and the candidate state, second commit, amend, + rebase, squash, force push or history rewrite was needed or performed + before the commit that contains this report was created. Decision 75 is + recorded for control inspection as an interpretation within the authorized + scope, not as a deviation. ## 6. Database and migration effects @@ -2460,8 +2837,9 @@ Migration added: NO No schema, data, index, constraint or `thoth-api/src/schema.rs` effect. The activation-epoch and route-contract-revision model, including its required database-level revision-equivalence property and its required database-atomic -ordering of revision withdrawal against new revision selection, is -architecture for a future +ordering of revision withdrawal against new revision selection and of +coalescing attachment against a job's first claim/start, is architecture for a +future separately authorized shared-engine implementation; it creates no migration obligation by itself and names no table, column or index. @@ -2475,8 +2853,9 @@ Cross-repository dependencies: NONE for the corrections, clarification and candidate recorded here. The verified BE-04 per-consumer impact record for the ADR's later legacy-contract retirement is preserved unchanged in Appendix A; neither the activation-epoch correction, -the route-contract-revision correction nor the withdrawal/selection ordering -correction changes a released contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts +the route-contract-revision correction, the withdrawal/selection ordering +correction nor the coalescing-seal ordering correction changes a released +contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts are internal to the future shared engine; any later external read or executor contract derived from them needs its own specification, merged upstream contract and downstream task. @@ -2512,8 +2891,10 @@ validation" records the validation of the historical clarification commit `eb3129f13a9c858b3c012ee182315801f0b8563a`. "Amendment-8 candidate validation" records the validation of the historical content-approved candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`. "Amendment-8 -approval-state validation" at the end of this section records the validation -of the commit that contains this report. +approval-state validation" records the validation of the historical +approval-state commit `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`. "Amendment-9 +candidate validation" at the end of this section records the validation of +the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -4388,17 +4769,22 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 85937a4e1ff9a27d8f5b36ca82651821711472b2 ``` -The approval-state ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above; the ADR-0012 blob `85937a4e1ff9a27d8f5b36ca82651821711472b2` is the exact -final blob presented for CTO exact-final-blob confirmation at this stage. This -report's own final blob, the approval-state tree and the approval-state commit -SHA depend on this report's bytes and are not self-pinned; they are verified -against the remote head after push and recorded in the post-publication -handoff. +The approval-state ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the approval-state report's bytes, so they +were recorded above; the ADR-0012 blob `85937a4e1ff9a27d8f5b36ca82651821711472b2` +was the exact final blob presented for CTO exact-final-blob approval at that +stage and received it on 2026-10-07. The approval-state report's own final +blob, the approval-state tree and the approval-state commit SHA depended on +that report's bytes and were not self-pinned in it; they were verified against +the remote head after push and recorded in the post-publication handoff, and +section 1 now records them as historical facts: commit +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, tree +`bec3637f333a12ed1895b2aae6f1078510067f5e`, report blob +`307a46ed153cd1287b24d921e7286c62328e8cc1`. -Required topology of the approval-state commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the approval-state commit, checked after it was created +and before push; the published approval-state commit satisfied every +requirement: ```text git rev-list --parents -n 1 HEAD @@ -4417,6 +4803,221 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-9 candidate validation + +Environment: the same full local Git worktree, detached at the historical +Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` (tree +`bec3637f333a12ed1895b2aae6f1078510067f5e`). Immediately before construction, +read-only GitHub inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `master` at +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`, `feature/async/adr-0012` at +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d` with single parent +`42ae90a5932cf24ce90679196888cc3d83d88db8`, ADR-0012 blob +`85937a4e1ff9a27d8f5b36ca82651821711472b2`, report blob +`307a46ed153cd1287b24d921e7286c62328e8cc1`, decision-register blob +`246bd5e981d373447fccdd457c02645338bfee4b` and changelog blob +`d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82` at that head, the protected +ADR-0008 and ADR-0010 blobs, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker` and the only open pull request targeting +`feature/worker`, the cumulative PR footprint exactly the six paths, the +Amendment 9, independent-review and authorization records present on #958 with +no later control record, and PR #960 review-thread state that matched the +stage authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the candidate commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check e2588f7b7e63a5c71968d87a3409f0ff48635e6d +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the approval-state head (exactly the four authorized +paths): + +```text +git diff --cached --name-only e2588f7b7e63a5c71968d87a3409f0ff48635e6d +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status e2588f7b7e63a5c71968d87a3409f0ff48635e6d +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat e2588f7b7e63a5c71968d87a3409f0ff48635e6d +1 0 CHANGELOG.md +869 191 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +183 28 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (only the ADR-0012 row; every other line, including +the `Last updated: 2026-10-07` header, byte-identical): + +```text +git diff --cached -U0 e2588f7b7e63a5c71968d87a3409f0ff48635e6d -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-07 + +diff <(git show e2588f7b7e63a5c71968d87a3409f0ff48635e6d:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n 20p | grep -o '^| `ADR-0012` | [^|]* | [A-Z]* |' +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 e2588f7b7e63a5c71968d87a3409f0ff48635e6d -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show e2588f7b7e63a5c71968d87a3409f0ff48635e6d:CHANGELOG.md) <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state, outline and hunks: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +2771:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'APPROVED' +every match is in section 12 (lines 2774-2820): the sentence that the version +may carry APPROVED only after CTO approval, the historical-approval paragraph +and the authority condition; no sentence asserts that this version is approved + +diff <(git show e2588f7b7e63a5c71968d87a3409f0ff48635e6d:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +17a18 +> ### Coalescing-open and coalescing-sealed jobs +exit 1 (the one added heading is the complete outline change) + +git diff --cached -U0 e2588f7b7e63a5c71968d87a3409f0ff48635e6d -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -5,2 +4,0 @@ Date: 2026-09-29 +@@ -1061 +1059,3 @@ materialized route dispositions may point to one **not-yet-started** job only +@@ -1073 +1073,2 @@ An idempotency conflict never silently means "route satisfied". It either: +@@ -1096,0 +1098,97 @@ kind must not consume all worker capacity indefinitely. +@@ -1577 +1675,4 @@ domain rule and must retain enough correlation to explain which events or +@@ -1905,2 +2006,2 @@ execution until a separate approved architecture decision says otherwise. +@@ -2024,0 +2126,17 @@ execution until a separate approved architecture decision says otherwise. +@@ -2040 +2158,3 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: +@@ -2251 +2371,2 @@ Rollout should prove in order: +@@ -2519,0 +2641,35 @@ Before shared implementation can be approved, evidence must include: +@@ -2615,4 +2771 @@ shared-engine tests. +@@ -2620,8 +2773,3 @@ Approval date: 2026-10-07 +@@ -2641 +2789,5 @@ database-atomic linearized order, so that a stale availability observation can +@@ -2650,5 +2802,8 @@ carried `Status: APPROVED` with CTO approval dated 2026-10-06, retained through + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^### Coalescing-open\|^42\. ' +1098:### Coalescing-open and coalescing-sealed jobs +2126:42. For every logical job of a kind that permits many-route-to-one-job + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'coalescing-open' +8 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'coalescing-sealed' +5 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'compare-and-set' +3 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'pre-claim observation' +2 +``` + +The fifteen hunks are: the header status and approval fields (two hunks); the +section-3.5 not-yet-started sentence; the idempotency-conflict attachment +item; the new "Coalescing-open and coalescing-sealed jobs" subsection; the +section-3.15 coalescing paragraph; invariant 8; invariant 42; section 8 item +1; section 9 rollout proof 5; the nine section-11 validation cases; and the +four section-12 hunks that record the candidate state, extend the description +of this material correction and bind the 2026-10-07 approval to the +historical earlier version. No other line of the ADR changes; in particular +the Amendment-8 withdrawal/selection text, the claim, lease and attempt-phase +text of sections 3.4 and 3.6, the idempotency-key, target-identity and +target-concurrency text of sections 3.3 and 3.5 and the disposition-uniqueness +text of section 3.2 are byte-identical to the approval-state head. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) 0fc33df9aca61a2c82af452426f5574bfd3493cf +decision-register.md 8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be +CHANGELOG.md 4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +0fc33df9aca61a2c82af452426f5574bfd3493cf +``` + +The candidate ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the candidate tree and the candidate +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the candidate commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: e2588f7b7e63a5c71968d87a3409f0ff48635e6d (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check e2588f7b7e63a5c71968d87a3409f0ff48635e6d HEAD +required: exit 0; no output + +git diff --name-only e2588f7b7e63a5c71968d87a3409f0ff48635e6d HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -4525,6 +5126,22 @@ unchanged; confirmed by fixed-string search that the content-approval comment identifier does not occur in this report; and read the report for statements that would become false once it moves into the approval-state commit and reframed them. +Amendment-9 candidate stage: compared the staged ADR-0012 with the +approval-state head's ADR blob `85937a4e1ff9a27d8f5b36ca82651821711472b2` hunk +by hunk against every requirement of Amendment 9 J4-J11 and the six +candidate-inspection observations; confirmed each is expressed in section 3.5 +(the not-yet-started sentence, the idempotency-conflict item and the new +subsection), section 3.15, invariants 8 and 42, sections 8, 9 and 11, and the +candidate state in the header and section 12; confirmed that every changed +hunk concerns coalescing attachment versus first claim/start, its validation +or the candidate state, and that the ADR-0008/ADR-0010 partial-supersession +header text, the Amendment-8 ordering text, the claim, attempt-phase, +idempotency, target-concurrency and disposition-uniqueness text and all other +architecture are byte-identical to the approval-state head; compared the +staged register and changelog with the approval-state-head blobs (only the +ADR-0012 row, and only one added entry); confirmed the protected decision and +control blobs are unchanged; and read the report for statements that would +become false once it moves into the candidate commit and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -4535,10 +5152,11 @@ Amendment-6/7 approval-state head `609a01427847233b7280557baa61825ac2079865` the control-record recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, the transient-state correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, the factual-clarification -head `eb3129f13a9c858b3c012ee182315801f0b8563a` and the content-approved -Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, are -historical evidence; the live PR #960 diff of the approval-state commit exists -only after its publication and is GitHub-owned lifecycle evidence. +head `eb3129f13a9c858b3c012ee182315801f0b8563a`, the content-approved +Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` and the +Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, are +historical evidence; the live PR #960 diff of the candidate commit exists only +after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -4550,9 +5168,10 @@ Failures or warnings: NONE EXPECTED. Under Amendment 3 C10, the Amendment-4/5 recovery authorization, the Amendment-6/7 candidate and approval-state authorizations, the control-record recovery authorization, the transient-state correction authorization, the factual-clarification -authorization, the Amendment-8 candidate authorization and the Amendment-8 -approval-state authorization, an automatic CI failure, or any migration -execution or Docker publication, is a HOLD condition. +authorization, the Amendment-8 candidate authorization, the Amendment-8 +approval-state authorization and the Amendment-9 candidate authorization, an +automatic CI failure, or any migration execution or Docker publication, is a +HOLD condition. ## 12. Rollout and rollback @@ -4579,20 +5198,21 @@ commit and exactly one approval-state commit, the control-record recovery and transient-state correction authorizations each permitted exactly one report-only commit, the factual-clarification authorization permitted exactly one two-path clarification commit, the Amendment-8 candidate authorization -permitted exactly one four-path candidate commit, and the Amendment-8 -approval-state authorization permits exactly one direct-child approval-state -commit; any further source correction exhausts those authorizations and -requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-8; -`docs/engineering/decisions/README.md`). +permitted exactly one four-path candidate commit, the Amendment-8 +approval-state authorization permitted exactly one direct-child approval-state +commit, and the Amendment-9 candidate authorization permits exactly one +four-path candidate commit; any further source correction exhausts those +authorizations and requires a fresh CTO authorization (Amendment 3 C10; +Amendments 6-9; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-8 approval-state stage the +- Historical stage-time evidence: at the Amendment-9 candidate stage the implementing agent performed no independent inspection or review, - exact-final-blob approval or confirmation, PR #960 title/description - reconciliation, Draft/Ready change, automated-review request, thread - adjudication, exact-head review or merge (section 4.2). + candidate-content approval, PR #960 title/description reconciliation, + Draft/Ready change, automated-review request, thread adjudication, + exact-head review or merge (section 4.2). Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -4624,9 +5244,12 @@ Monitoring required: NONE two-path clarification commit, and the withdrawal/selection ordering defect found by the final-head review of that clarification head only under Specification Amendment 8, its independent review and a separate - authorization for one four-path candidate commit, and its approval state - only under a separate authorization for this one direct-child approval-state - commit. + authorization for one four-path candidate commit, its approval state only + under a separate authorization for one direct-child approval-state commit, + and the coalescing-attachment/first-claim ordering defect found by the + final-head review of that approval-state head only under Specification + Amendment 9, its independent review and a separate authorization for this + one four-path candidate commit. - The strand-check timing question left outside this correction (section 5, decision 53): Amendments 1-8 do not determine whether a withdrawal's strand-prevention evaluation may be stale relative to events or anomalies @@ -4643,7 +5266,8 @@ Monitoring required: NONE section 9 is exact-worktree evidence for the activation-epoch candidate, its approval-state commit, the recovery commit, the Amendment-6/7 candidate, its approval-state commit, the control-record recovery, the transient-state - correction and this factual clarification. + correction, the factual clarification, the Amendment-8 candidate and + approval-state commit and this Amendment-9 candidate. ## 14. Unresolved issues @@ -4724,11 +5348,33 @@ Monitoring required: NONE `42ae90a5932cf24ce90679196888cc3d83d88db8` implements the correction in source (section 4; section 5, decisions 49-60); control's independent inspection of that candidate found no blocking exact-candidate finding; the - CTO approved its exact content on 2026-10-07; and the approval-state commit - that contains this report records that approval. `R-EB31-01` is closed at - specification level by Amendment 8; its source closure is adjudicated by the - independent exact-head review controls that precede any merge, which GitHub - records. + CTO approved its exact content on 2026-10-07; the historical approval-state + commit `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` recorded that approval; and + the CTO recorded exact-final-blob approval of the resulting ADR-0012 blob + `85937a4e1ff9a27d8f5b36ca82651821711472b2`. `R-EB31-01` is closed at + specification level by Amendment 8 and, per the CTO's Amendment-9 record, + closed in the source of that approval-state head; its thread adjudication + belongs to the review controls that GitHub records. +- The configured final-head automated review of the Amendment-8 + approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` opened one further + thread, identified here substantively and by its control-assigned finding + name `R-E258-01`: a router could observe a coalescible job as not yet + started, a worker could claim/start it, and the router could then attach a + new route disposition to the started job from its stale observation, so + that the event was recorded as represented by a job whose inputs or effect + may already have been fixed without it; the ADR required database-enforced + claims but not that attachment and first claim/start share one + database-atomic order. Control accepted it as valid, HIGH and blocking and + classified the correction as a material architectural correction; the CTO + approved Specification Amendment 9; a fresh independent CRITICAL + specification review of the base specification and Amendments 1-9 returned + `APPROVED`, confirming the finding, the classification, the seal boundary + and the validation matrix and recording six non-blocking + candidate-inspection observations; and the Route-B candidate that contains + this report implements the correction in source as `PROPOSED` (section 4; + section 5, decisions 69-80). `R-E258-01` is closed at specification level + by Amendment 9; whether the candidate closes it in source is decided by the + independent inspection and review of the candidate, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -4742,17 +5388,42 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-8 approval-state commit also -implemented the Amendment-8 candidate, the factual clarification, the -transient-state correction, the control-record recovery, the Amendment-6/7 -approval-state commit and candidate and earlier Route-B source stages of this -task and prepared the earlier uncommitted +work. The implementing agent of this Amendment-9 candidate also implemented +the Amendment-8 approval-state commit and candidate, the factual +clarification, the transient-state correction, the control-record recovery, +the Amendment-6/7 approval-state commit and candidate and earlier Route-B +source stages of this task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent inspector or reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Suggested review focus: +- that the candidate changes exactly the four authorized paths from the + approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, that the + candidate ADR-0012 blob is `0fc33df9aca61a2c82af452426f5574bfd3493cf`, and that + ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that every Amendment 9 J4-J11 requirement is expressed in ADR-0012: the + coalescing-open/coalescing-sealed boundary anchored to the first successful + claim/start as the same transition as "not yet started", the permanence of + the seal through retry, lease reclaim, WAITING/resume, reconciliation, + cancellation, recovery and later claims, the database-atomic + attachment-versus-first-claim order with the attachment write proving the + job open and pre-checks insufficient, both commit orders, the aborted-claim + rule, mechanism-neutral input/effect completeness, the same boundary for + every already-permitted attachment path, the seal's distinction from + `EFFECT_STARTED`, the composition list, invariant 42 and the nine section-11 + cases with their evidence refinements, and that the six independent-review + observations are reflected as recorded in section 5, decisions 70, 75 and + 79; +- that the candidate changes no coalescing permission, idempotency-key + identity, `CURRENT_STATE`/`REVISION_BOUND`, target-concurrency, + applied-revision, attempt-phase, claim-token, WAITING/RECONCILIATION_REQUIRED + or Amendment-8 ordering semantics, and that decision 75 decides no new + coalescing path; +- that every ADR-0012 hunk outside those concerns is limited to the candidate + `PROPOSED` state (header and section 12) and that all other architecture is + byte-identical to the approval-state head; - that the approval-state delta from the content-approved Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` is limited to the ADR header approval fields, the two leading section-12 paragraphs, the ADR-0012 @@ -4836,19 +5507,18 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `APPROVED` with Javi, CTO and - 2026-10-07, identify the approved content by candidate ADR-0012 blob - `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`, bind the 2026-09-30, 2026-10-05 - and 2026-10-06 approvals to the earlier exact versions and keep the - repository authority condition and the ADR-0013 reliance distinction; -- that the register row, the withdrawal-order correction changelog entry and - this report describe the corrected version as `APPROVED` on 2026-10-07, - that the route-contract correction entry and every other historical - changelog entry are byte-preserved, and that the register header - `Last updated` is `2026-10-07`; +- that the header and section 12 carry `PROPOSED` with no current approver or + approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06 and 2026-10-07 + approvals to the earlier exact versions and keep the repository authority + condition and the ADR-0013 reliance distinction; +- that the register row, the new coalescing-seal correction changelog entry + and this report describe the corrected version as `PROPOSED`, that the + withdrawal-order correction entry and every other historical changelog + entry are byte-preserved, and that the register header `Last updated` is + `2026-10-07`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other - protected control files are byte-identical to the clarification head, the - recovery head and the content-approved candidates. + protected control files are byte-identical to the approval-state head, the + clarification head, the recovery head and the content-approved candidates. ## Appendix A - Historical authoring and correction record @@ -4861,9 +5531,9 @@ boundary and one optional deactivation boundary per route is superseded by the append-only activation-epoch model, and the route-level event kind/version contract and "emits K" emission floor described in earlier rounds are superseded by immutable route-contract revisions and producer-version coverage -in the current section 3.2. The 2026-09-30, 2026-10-05 and 2026-10-06 -approvals and every review recorded here are bound to the exact historical -versions and heads they name. +in the current section 3.2. The 2026-09-30, 2026-10-05, 2026-10-06 and +2026-10-07 approvals and every review recorded here are bound to the exact +historical versions and heads they name. ### Initial candidate and review round 1 @@ -5661,8 +6331,8 @@ stage: deduplication boundary. 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO approval of that exact corrected content; earlier approvals and reviews, - including the 2026-09-30, 2026-10-05 and 2026-10-06 approvals, remain - bound to the earlier exact versions and heads they name. + including the 2026-09-30, 2026-10-05, 2026-10-06 and 2026-10-07 + approvals, remain bound to the earlier exact versions and heads they name. 11. The route-contract-revision model does not permit silent reinterpretation or loss either: every epoch is bound to one immutable revision, an event outside its epoch revision's accepted versions becomes a visible anomaly @@ -5675,5 +6345,13 @@ stage: one durable linearized order, a selection that linearized first remains valid historical state, and a withdrawal that linearized first refuses later selection before any epoch, disposition or job exists. +13. The coalescing-seal rule does not permit a route disposition to be + attached from a stale observation to a job that has already started: a + logical job of a coalescing kind is coalescing-open only until its first + successful claim/start commits, attachment and that transition have one + durable linearized order, an attachment that linearized first is genuinely + represented by the started job, and a claim/start that linearized first + refuses the later attachment with no disposition committed, the + `(event_id, route_key)` slot unused and the obligation still owed. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 85937a4e1..0fc33df9a 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-07 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -1058,7 +1056,9 @@ one durable disposition may exist for a given `(event_id, route_key)`, and an eligible route obligation eventually receives exactly one. A successfully materialized disposition points to exactly one materialized job. Several materialized route dispositions may point to one **not-yet-started** job only -when the kind defines an explicit durable coalescing rule. An explicit, +when the kind defines an explicit durable coalescing rule; a job is not yet +started, and so coalescing-open, only until its first successful execution +claim/start commits (below). An explicit, authorized and audited terminal non-materialized disposition points to no job and records the actor, authority, reason and any covering reconciliation/backfill. Materialized and terminal non-materialized @@ -1070,7 +1070,8 @@ never erases the per-event route records needed for audit/completeness. An idempotency conflict never silently means "route satisfied". It either: 1. attaches the route to an existing not-yet-started job when the kind's - coalescing rule explicitly permits it; or + coalescing rule explicitly permits it, inside the database-atomic + coalescing boundary below; or 2. produces a deterministic routing/materialization error surfaced for attention/recovery. @@ -1094,6 +1095,103 @@ The shared scheduler supports per-kind concurrency limits. Provider-specific rate limits remain owned by handlers, but a single failing or rate-limited job kind must not consume all worker capacity indefinitely. +### Coalescing-open and coalescing-sealed jobs + +For every job kind that permits many-route-to-one-job coalescing, each logical +job has one durable semantic boundary. The logical job is **coalescing-open** +only before its first successful execution claim/start transition linearizes; +while it is open, a new materialized route disposition may still be attached to +it under the kind's explicit coalescing rule. The first successful committed +claim/start transition makes the logical job **coalescing-sealed**, +permanently: no new route disposition may be attached to it afterwards. The +"not-yet-started job" that coalescing may attach to and the "first successful +claim/start" that seals it refer to the same durable transition. The seal is a +property of the logical job, not of one attempt or one route disposition: a +later retry after a pre-write failure, lease expiry and reclaim, WAITING and a +later resume, RECONCILIATION_REQUIRED and its reconciliation attempts, +cancellation or recovery transitions, and every later claim or attempt on the +same logical job never reopen coalescing. The exact persisted column or state +encoding is an implementation-specification detail; a separate sealed flag is +not required where the canonical job state, attempt or claim transition already +enforces the same property. + +A coalescing attachment and the logical job's first successful claim/start +transition participate in one database-atomic serialization, lock or +compare-and-set boundary, or another database-enforced mechanism with +equivalent atomic ordering, so that the two operations have one durable +linearized order. A router or materializer may not observe a job as not yet +started, give up that protection and later commit a disposition attachment from +that observation: the attachment write itself must atomically prove that the +logical job is still coalescing-open. Likewise, the first claim/start +transition participates in the boundary, so that no concurrent attachment can +commit after it. An application-level pre-check without database-enforced +ordering between the check and the attachment does not satisfy this rule. The +exact SQL, lock or constraint is an implementation-specification detail. + +Both commit orders are defined: + +- attachment first: when a valid attachment linearizes before the first + claim/start, the new materialized disposition commits and points to the + existing job; the job remains a valid representative of that route and event + under the kind's explicit coalescing contract; the later first claim/start + may proceed; the job's execution represents every disposition and durable + input contribution that committed before the seal, under the kind's approved + coalescing semantics; and starting the job never rewrites or detaches a + previously attached valid disposition; +- claim/start first: when the first successful claim/start linearizes first, + the logical job is sealed before execution proceeds; a later attempted + attachment is refused; no materialized route disposition is committed for + the losing route and event; the `(event_id, route_key)` uniqueness slot + remains unused; the eligible route obligation remains owed and visible in + the route backlog; and the idempotency-conflict rule above follows its + deterministic routing/materialization error and attention/recovery path + rather than treating the route as represented by the started job. The losing + router or materializer may retry only after re-reading current durable state + and under the kind's existing authorized materialization and recovery + semantics; this rule creates no new logical-job identity and no new replay + or current-state rule. + +Sealing is bound to the durable committed transition, not to an attempted +claim. A worker attempt whose first claim/start transaction acquires transient +locks but rolls back before its sealing transition commits has not sealed the +job; after that rollback an attachment may still succeed when every ordinary +coalescing, idempotency and materialization requirement remains satisfied. + +A kind that permits coalescing defines, in its approved coalescing contract, +how the one surviving job represents every attachment that committed before +the seal, so that execution never uses an input or membership snapshot that +omits a disposition whose attachment linearized first. The contract stays +mechanism-neutral: a `CURRENT_STATE` kind may represent accepted pre-seal +attachments through its canonical current-state and effect-fingerprint +semantics (section 3.3); another approved kind may require an explicit durable +retained input set or an equivalent durable representation. A worker handler +is not required to enumerate route dispositions. This boundary does not change +`CURRENT_STATE` or `REVISION_BOUND` semantics, effect fingerprints, +target-scoped concurrency or applied-revision evidence. + +Every operation that durably attaches a route disposition to an existing +coalescible logical job uses this same boundary, including an attachment made +through historical enrollment, backfill or replay, or to a directly created +logical job, where this ADR already permits that attachment; none of those +paths bypasses it. The boundary does not itself decide whether any path may +coalesce: the kind's explicit durable coalescing rule and the rules above +decide that. + +The coalescing seal is earlier than, and distinct from, `EFFECT_STARTED`: +attachment closes at the first successful execution claim/start because +coalescing is permitted only into a not-yet-started job, while +`EFFECT_STARTED` fences the later external-write boundary (sections 3.4 and +3.6). The boundary composes with database-enforced claims, leases and current +claim tokens, `FOR UPDATE SKIP LOCKED` or the equivalent claim mechanism the +implementation selects, `(event_id, route_key)` disposition uniqueness, +idempotency-key uniqueness, explicit durable coalescing rules, `CURRENT_STATE` +and `REVISION_BOUND` semantics, target-scoped concurrency and +applied-revision/fingerprint evidence, the attempt phases, WAITING and +RECONCILIATION_REQUIRED, retry and reconciliation transitions and the +scheduler's deadlock and lock-ordering controls. It introduces no second job +identity, job lifecycle, route lifecycle or claim-lock mode, and no second +order for claim/start against attachment. + ### 3.6 Delivery semantics, checkpoints and ambiguous writes The engine guarantees durable at-least-once execution opportunities. It does @@ -1574,7 +1672,10 @@ silently delete historical attempts. Coalescing/debouncing may be defined by individual job kinds, but it must be durable and auditable: it may reduce executable jobs only under an explicit domain rule and must retain enough correlation to explain which events or -requests were represented by the surviving job. +requests were represented by the surviving job. A surviving job accepts +attachments only while it is coalescing-open and represents every attachment +that committed before its first successful claim/start sealed it (section +3.5). --- @@ -1902,8 +2003,8 @@ execution until a separate approved architecture decision says otherwise. `(event_id, route_key)`; an eligible route obligation remains owed until it has received exactly one. A successfully materialized disposition maps to exactly one job; many materialized dispositions may map to one - not-yet-started job only under an explicit durable coalescing rule. An - explicit authorized/audited terminal + not-yet-started, coalescing-open job only under an explicit durable + coalescing rule (invariant 42). An explicit authorized/audited terminal non-materialized disposition maps to no job, records actor, authority, reason and any covering reconciliation/backfill, and shares the same uniqueness boundary so it cannot coexist with a materialized disposition for @@ -2022,6 +2123,23 @@ execution until a separate approved architecture decision says otherwise. without database-enforced ordering does not satisfy this. The mechanism composes with the per-`route_key` lifecycle serialization and introduces no second contradictory lifecycle order. +42. For every logical job of a kind that permits many-route-to-one-job + coalescing, the job is coalescing-open only before its first successful + execution claim/start transition commits and is permanently + coalescing-sealed by that transition; the seal belongs to the logical job, + and no retry, lease reclaim, WAITING resume, reconciliation, cancellation, + recovery or later claim reopens it. A coalescing attachment and that first + claim/start share one database-atomic serialization, lock or + compare-and-set boundary with one durable linearized order: an attachment + that linearizes first commits a disposition that the started job genuinely + represents and that is never rewritten or detached; a claim/start that + linearizes first seals the job before execution, and the later attachment + is refused with no disposition committed, the `(event_id, route_key)` slot + unused, the obligation still owed and visible and the deterministic + idempotency-conflict error path taken. A stale pre-claim observation never + commits an attachment after sealing; an application-level pre-check does + not satisfy this; a rolled-back first claim does not seal. The seal is + earlier than and distinct from `EFFECT_STARTED`. ## 8. Implementation impact and decomposition @@ -2037,7 +2155,9 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: revision selection and deterministic materializer compatibility; - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - - effect-scoped idempotency and explicit coalescing; + - effect-scoped idempotency and explicit coalescing, with the + database-atomic coalescing-open/coalescing-sealed boundary between + attachment and first claim/start; - revision semantics plus domain-owned canonical effect-target identity and target-scoped ordering/evidence contract; - new kind-scoped executor API; @@ -2248,7 +2368,8 @@ Rollout should prove in order: behaviour, including in-epoch contract-mismatch detection and the ordering of revision withdrawal against epoch opening and first enrollment; 4. crash/lease renewal/WAITING/reconciliation recovery; -5. effect-scoped idempotency, revision ordering and coalescing; +5. effect-scoped idempotency, revision ordering and coalescing, including the + ordering of coalescing attachment against first claim/start; 6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; 7. one or more non-destructive representative handlers; 8. protected operator controls and reconciliation exit path; @@ -2517,6 +2638,41 @@ Before shared implementation can be approved, evidence must include: per-target revision evidence; - allowed many-route-to-one-job coalescing tests and deterministic conflict attention when coalescing is not permitted; +- deterministic concurrency tests of a coalescing attachment racing the first + successful claim/start of the same logical job, in the attachment-first + order and in the claim/start-first order, with the router/materializer and + the worker running as separate concurrent instances; +- proof that after an attachment-first order the worker's execution genuinely + represents the newly attached disposition and input under the kind's + coalescing contract, shown for a `CURRENT_STATE` kind through its + current-state and effect-fingerprint semantics and, for a kind that uses a + retained input set or another durable representation, through that + representation; +- proof that after a claim/start-first order no new route disposition is + committed, the `(event_id, route_key)` slot remains unused, the eligible + route obligation remains owed and visible in the route backlog, and the + deterministic routing/materialization error and attention/recovery path is + taken, including when the attachment attempt arrives through the + idempotency-conflict path; +- proof that a stale pre-claim observation cannot commit an attachment after + the sealing transition linearizes; +- proof that several concurrent attachments from several router or + materializer instances racing the first claim/start produce one total + order, every attachment that linearized before the seal being accepted and + represented and every attachment after it refused; +- proof that a first claim/start transaction that rolls back before its + sealing transition commits does not durably seal the job, and that a later + attachment may still succeed when every ordinary coalescing, idempotency + and materialization condition holds; +- proof that once a first claim/start has committed, retry scheduling, lease + expiry and reclaim, WAITING and resume, RECONCILIATION_REQUIRED and + reconciliation, cancellation and recovery transitions and later claims never + reopen coalescing, and that the seal persists across worker crash and + restart; +- proof that the selected locking or compare-and-set order for attachment and + first claim/start composes with `(event_id, route_key)` disposition + uniqueness, idempotency-key uniqueness, worker claim locking and + target-scoped concurrency without deadlock or split ordering; - current-token lease renewal and refusal to renew superseded/expired claims; - stale-token refusal for completion, failure, cancellation, checkpointing and reconciliation; @@ -2612,19 +2768,11 @@ shared-engine tests. ## 12. Approval and authority -This ADR is **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-07 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-07 under the -repository decision process, as the architecture content represented by the -Route-B candidate ADR-0012 blob `cbb31a53f6f4ed6b96faafb79b273b55664f3ae0`; -this version differs from that candidate only in this approval-state -representation. That approval does not by itself make this ADR -repository-authoritative: the authority condition below still applies. Any -ADR-0013 programme-local reliance on this exact version is a separate state -that is not effective until its own conditions are satisfied for this version. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the @@ -2638,7 +2786,11 @@ atomic revision equivalence, deterministic materializer compatibility, per-revision route health and safe revision withdrawal, and it requires revision withdrawal and every new durable selection of a revision to share one database-atomic linearized order, so that a stale availability observation can -never commit a new selection of a withdrawn revision. +never commit a new selection of a withdrawn revision. It further defines the +coalescing-open/coalescing-sealed boundary of a logical job in section 3.5 and +requires a coalescing attachment and the job's first successful claim/start to +share one database-atomic linearized order, so that no route disposition can be +attached from a stale observation to a job that has already started. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO approval dated 2026-09-30. A later corrected version, which introduced the @@ -2647,11 +2799,14 @@ activation-epoch model without immutable route-contract revisions, carried version, which introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, carried `Status: APPROVED` with CTO approval dated 2026-10-06, retained through -a factual clarification of that version. Those approvals, and every review of -those earlier versions, remain historical evidence bound to those exact earlier -versions and source heads only; they do not approve this version. The exact -historical identities are recorded in the task implementation report and the -owning issue. +a factual clarification of that version. A further corrected version, which +introduced that withdrawal/selection order without a database-atomic order +between coalescing attachment and a job's first claim/start, carried +`Status: APPROVED` with CTO approval dated 2026-10-07. Those approvals, and +every review of those earlier versions, remain historical evidence bound to +those exact earlier versions and source heads only; they do not approve this +version. The exact historical identities are recorded in the task +implementation report and the owning issue. CTO approval of this ADR adopts the architecture decision and invariants recorded here. It is architecture approval only: it does not authorize diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 246bd5e98..8b5ef6b5b 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-07 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and the CTO approved its exact corrected content on 2026-10-07 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, and the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, and the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From e41f377202379238b91eb43d60f5115aa8adc20f Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 8 Oct 2026 19:06:41 +0100 Subject: [PATCH 37/48] THOTH-ASYNC-01-ADR-01: record coalescing-seal correction approval --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 953 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 20 +- .../decisions/decision-register.md | 4 +- 4 files changed, 780 insertions(+), 199 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0e17ca0..2731521f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-CORRECTION`: record a CTO-approved material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version replaces the single activation interval per route with append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals `[activation_generation, deactivation_generation)` totally ordered by activation generation; opening, write-once closing and permanent retirement serialized through one durable database mechanism, so a closed epoch never changes or reopens and a retired route never opens another epoch; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized historical backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, with any epoch identifier subordinate to `route_key` and excluded from that key. Section 3.2 now states the disposition model consistently with section 3.5 and invariant 8 (exactly one disposition per `(event_id, route_key)`; a materialized disposition points to exactly one job; a terminal non-materialized disposition points to no job), the validation requirements add repeated-cycle, boundary, concurrency, retirement and historical-eligibility tests, and section 12 aligns the ADR's amendment clause with the repository amendment process. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-05.** The earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions of ADR-0012 and do not approve this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index ffbb44f9f..5c3b5a096 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -16,16 +16,16 @@ configured final-head automated review of that correction head, the Route-B candidate and approval-state stages of the revision-withdrawal/selection ordering correction specified by Specification Amendment 8 after the configured final-head automated review of that clarification head, and the Route-B -candidate stage of the coalescing-attachment/first-claim ordering correction -specified by Specification Amendment 9 after the configured final-head -automated review of that approval-state head. It records +candidate and approval-state stages of the coalescing-attachment/first-claim +ordering correction specified by Specification Amendment 9 after the configured +final-head automated review of that Amendment-8 approval-state head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-9 Route-B candidate as the -current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-9 Route-B approval-state commit +as the current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -47,11 +47,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-9 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the +Head commit: the Amendment-9 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-9 candidate +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`; its SHA is deliberately not +self-pinned here (section 3). That candidate is the direct child of the historical Amendment-8 approval-state head -`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`; its SHA is deliberately not -self-pinned here (section 3). That approval-state head is the direct child of +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, which is the direct child of the content-approved Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, which is the direct child of the historical factual-clarification head @@ -69,7 +71,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -83,9 +85,9 @@ Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, -Amendment-8 candidate, Amendment-8 approval-state and Amendment-9 candidate -stages - Claude (Fable 5.1), bounded implementing agent. Earlier rounds are -recorded in Appendix A. +Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate and +Amendment-9 approval-state stages - Claude (Fable 5.1), bounded implementing +agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -205,15 +207,23 @@ decision-register blob 246bd5e981d373447fccdd457c02645338bfee4b CHANGELOG blob d1fbe1bdc7699bae18970cdcc9c61d9cbdcefa82 implementation-report blob 307a46ed153cd1287b24d921e7286c62328e8cc1 (superseded by the Amendment-9 candidate) -Amendment-9 Route-B candidate (the commit that contains this report): -ADR-0012 blob 0fc33df9aca61a2c82af452426f5574bfd3493cf (PROPOSED; material architectural correction of 85937a4e1ff9a27d8f5b36ca82651821711472b2) +content-approved Amendment-9 candidate / approval-state parent: +8699af5635b8f18691ceaeccd3b24707a71ef4f2 +tree ebff7219611e7855c62856019fc051fcf41cb2b5 +ADR-0012 blob 0fc33df9aca61a2c82af452426f5574bfd3493cf (PROPOSED; content-approved; material architectural correction of 85937a4e1ff9a27d8f5b36ca82651821711472b2) decision-register blob 8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be +implementation-report blob ffbb44f9f250d325da6846796dfae8051a3a839e CHANGELOG blob 4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4 +Amendment-9 Route-B approval-state commit (the commit that contains this report): +ADR-0012 blob cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c (APPROVED; Javi, CTO; 2026-10-08; architecture-equivalent to 0fc33df9aca61a2c82af452426f5574bfd3493cf) +decision-register blob c618d9d2eec622660ee7c1868111f747efa27629 +CHANGELOG blob 2731521f363189063b9ab09068df7852889121a2 + feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit and the -Amendment-9 candidate (unchanged): +Amendment-9 candidate and approval-state commit (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -302,7 +312,15 @@ Approved specification: and Amendments 1-9, including six non-blocking candidate-inspection observations: #958 comment `6046871411`; - Amendment-9 Route-B candidate implementation authorization: #958 comment - `6046895895`. + `6046895895`; +- CTO exact candidate-content approval of the Amendment-9 candidate: recorded + on #958 on 2026-10-08 by Javi, CTO for candidate head + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` and candidate ADR-0012 blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; +- Amendment-9 Route-B approval-state implementation authorization: #958 + comment `6065879000`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -335,12 +353,18 @@ approval-state commit then recorded that approval as durable decision state correction changelog entry moved to the approved state), and the CTO recorded exact-final-blob approval of that ADR blob. The configured final-head automated review of that approval-state head exposed a further architecture defect, -`R-E258-01`, specified by Specification Amendment 9. The commit that contains -this report is its Route-B candidate: one ordinary single-parent commit on the -approval-state head that changes exactly the four authorized correction paths, -returns ADR-0012 to `Status: PROPOSED` and requires a coalescing attachment to -an existing logical job and that job's first successful claim/start to share -one database-atomic linearized order. +`R-E258-01`, specified by Specification Amendment 9. Its `PROPOSED` Route-B +candidate, one ordinary single-parent commit on the Amendment-8 approval-state +head that changed exactly the four authorized correction paths and requires a +coalescing attachment to an existing logical job and that job's first +successful claim/start to share one database-atomic linearized order, received +independent control inspection and CTO exact candidate-content approval on +2026-10-08. The commit that contains this report is the one bounded Route-B +approval-state commit that records that approval as durable decision state: +ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +`Approval date: 2026-10-08`, the ADR-0012 register row and the coalescing-seal +correction changelog entry moved to the approved state, and this report's +approval-state evidence. No architecture content changes. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -592,7 +616,8 @@ one database-atomic linearized order. already a draft; that stage authorized no PR-state or PR-metadata change. - **Exact-final-blob approval, final-head review of the Amendment-8 approval-state head, Specification Amendment 9 and the Amendment-9 Route-B - candidate (the commit that contains this report).** Authorized by #958 + candidate (historical content-approved candidate + `8699af5635b8f18691ceaeccd3b24707a71ef4f2`).** Authorized by #958 comment `6046895895` after the CTO approved Specification Amendment 9 (#958 comment `6046180884`) and a fresh independent CRITICAL specification review of the base specification and Amendments 1-9 returned `APPROVED` (#958 @@ -626,15 +651,39 @@ one database-atomic linearized order. `EFFECT_STARTED`. The independent review confirmed the finding, the classification, the seal boundary and the sufficiency of the nine-case concurrency matrix, and recorded six non-blocking candidate-inspection - observations. The commit that contains this report is one ordinary - single-parent commit directly on the approval-state head that changes - exactly the four authorized correction paths: ADR-0012 returns to - `Status: PROPOSED` with no current approver or approval date and implements - Amendment 9 (section 4; section 5, decisions 69-80); the decision-register - ADR-0012 row, one new changelog entry and this report describe the - corrected version as `PROPOSED`; and the 2026-09-30, 2026-10-05, 2026-10-06 - and 2026-10-07 CTO approvals remain historical evidence bound to the earlier - exact versions. This stage authorizes no PR-state or PR-metadata change. + observations. The candidate is one ordinary single-parent commit directly + on the Amendment-8 approval-state head that changed exactly the four + authorized correction paths: ADR-0012 returned to `Status: PROPOSED` with no + current approver or approval date and implements Amendment 9 (section 4; + section 5, decisions 69-80); the decision-register ADR-0012 row, one new + changelog entry and the candidate-stage report described the corrected + version as `PROPOSED`; and the 2026-09-30, 2026-10-05, 2026-10-06 and + 2026-10-07 CTO approvals remain historical evidence bound to the earlier + exact versions. Control independently inspected the published candidate + against the actual GitHub source, found no blocking exact-candidate + finding, and Javi, CTO approved its exact content - head + `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, ADR-0012 blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf` - on 2026-10-08 in a durable + record on #958. That stage authorized no PR-state or PR-metadata change. +- **Amendment-9 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6065879000`. One ordinary + single-parent commit directly on the content-approved candidate records + that approval as durable decision state, changing only the four authorized + paths: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and + `Approval date: 2026-10-08` and the leading section-12 approval paragraphs, + which identify the approved content as the content represented by candidate + ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`; the ADR-0012 + register row and the register's `Last updated` header; the + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` changelog entry; and this + report. No architecture content changes (section 9, "Amendment-9 + approval-state validation"). The content approval is recorded here through + owning issue #958, approver Javi, CTO, approval date 2026-10-08, the + candidate head and the candidate ADR-0012 blob; its GitHub comment + identifier is not copied into repository source, as the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` and the approval-state authorization + itself require (section 5, decision 84). PR #960 was already a draft; this + stage authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -648,6 +697,11 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-08) +of the content-approved Amendment-9 candidate blob 0fc33df9aca61a2c82af452426f5574bfd3493cf; +no architecture change + +historical Amendment-9 candidate at 8699af5635b8f18691ceaeccd3b24707a71ef4f2: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 85937a4e1ff9a27d8f5b36ca82651821711472b2 (Specification Amendment 9; Route-B candidate stage; candidate status PROPOSED) @@ -674,9 +728,9 @@ validation time `b44303c214498baf76c9d9a0a7cab374377f9cbe` was simultaneously `develop` and the refreshed `feature/worker`, and `master` was `4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`. Those refs were unchanged at the Amendment-6/7 candidate stage, at the Amendment-8 candidate stage, at the -Amendment-8 approval-state stage and at the Amendment-9 candidate stage, and -the same literal commands were run again at each stage against both commits -with identical results: +Amendment-8 approval-state stage, at the Amendment-9 candidate stage and at +the Amendment-9 approval-state stage, and the same literal commands were run +again at each stage against both commits with identical results: ```text git log --format=%h -- ':(glob)**/*ADR-0012*' @@ -811,42 +865,60 @@ ordering version as well: `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` changelog entry that record the withdrawal/selection ordering version as `APPROVED`; the changelog entry is preserved and remains true only of that historical - version, while the register row now records the candidate as `PROPOSED`. + version, while the register row recorded the Amendment-9 candidate as + `PROPOSED` and now records the approved version. -They remain historical evidence bound to their exact versions and heads. For -the candidate version, independent exact-candidate inspection, CTO exact -candidate-content approval, the approval-state commit, exact-final-blob -approval, independent exact-head review, CTO merge authorization, guarded -merge, programme-local reliance and later migration, deployment, activation -and observation are distinct controlled lifecycle categories; GitHub, through -#958 and PR #960, is authoritative for which of them is pending, satisfied or -blocked for any head. +They remain historical evidence bound to their exact versions and heads. The +Amendment-9 candidate version received its own independent control inspection +and CTO exact candidate-content approval on 2026-10-08, and the approval-state +commit that contains this report records it. For this version, +exact-final-blob approval, independent exact-head review, CTO merge +authorization, guarded merge, programme-local reliance and later migration, +deployment, activation and observation are distinct controlled lifecycle +categories; GitHub, through #958 and PR #960, is authoritative for which of +them is pending, satisfied or blocked for any head. ## 3. Commits -Amendment-9 Route-B candidate commit (the commit that contains this report): +Amendment-9 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose coalescing-seal ordering correction` - - direct parent: the historical Amendment-8 approval-state head - `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` - - changes only the four authorized correction paths (section 4); the - candidate ADR-0012 blob is `0fc33df9aca61a2c82af452426f5574bfd3493cf`, the - candidate decision-register blob `8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be` - and the candidate changelog blob `4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4` + `THOTH-ASYNC-01-ADR-01: record coalescing-seal correction approval` + - direct parent: the content-approved Amendment-9 candidate + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` + - changes only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`, the approval-state + decision-register blob `c618d9d2eec622660ee7c1868111f747efa27629` and the + approval-state changelog blob `2731521f363189063b9ab09068df7852889121a2` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the candidate ADR-0012, +here because this report is part of that tree; the approval-state ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its approval-state-head blob (section -9). The commit identities are verified against the remote task-branch head -after push and recorded in the post-publication handoff; once published, the -PR #960 head and head tree are GitHub-owned lifecycle evidence under -`ADR-0005`. The authorized topology is a non-force fast-forward of the task -branch from the approval-state head to the candidate commit; no merge commit, -rebase, squash, amend, force update or second candidate commit is authorized, -and no further source commit exists implicitly after it. +recorded above; every other file keeps its candidate blob (section 9). The +commit identities are verified against the remote task-branch head after push +and recorded in the post-publication handoff; once published, the PR #960 +head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The +authorized topology is a non-force fast-forward of the task branch from the +content-approved candidate to the approval-state commit; no merge commit, +rebase, squash, amend, force update or second approval-state commit is +authorized, and no further source commit exists implicitly after it. + +Amendment-9 Route-B candidate commit (content-approved; historical): + +- `8699af5635b8f18691ceaeccd3b24707a71ef4f2` - + `THOTH-ASYNC-01-ADR-01: propose coalescing-seal ordering correction` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` + - tree `ebff7219611e7855c62856019fc051fcf41cb2b5` + - changed only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `0fc33df9aca61a2c82af452426f5574bfd3493cf` + - published by a non-force fast-forward of `feature/async/adr-0012` from the + Amendment-8 approval-state head; the remote head, tree, parent and + ADR-0012, decision-register, changelog and report blobs matched the + locally validated object Amendment-8 Route-B approval-state commit (historical): @@ -1015,12 +1087,24 @@ first-parent order, newest first: ## 4. Files changed -Amendment-9 Route-B candidate write budget for the commit that contains this -report (authorization #958 comment `6046895895`; the Amendment 6 G10 / -Amendment 7 H12 budget, unchanged by Amendments 8-9); exactly four existing -paths, no new file, no deletion, and every other path byte-identical to the -historical Amendment-8 approval-state head -`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`: +Amendment-9 Route-B approval-state write budget for the commit that contains +this report (authorization #958 comment `6065879000`), identical to the +Amendment-9 candidate write budget (authorization #958 comment `6046895895`; +the Amendment 6 G10 / Amendment 7 H12 budget, unchanged by Amendments 8-9); +exactly four existing paths, no new file, no deletion, and every other path +byte-identical to the content-approved candidate +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-9 Route-B candidate write budget (authorization #958 +comment `6046895895`; the Amendment 6 G10 / Amendment 7 H12 budget, unchanged +by Amendments 8-9); exactly four existing paths, no new file, no deletion, and +every other path byte-identical to the historical Amendment-8 approval-state +head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`: - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -1089,17 +1173,85 @@ six paths at every stage): Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 -candidate, Amendment-8 approval-state and Amendment-9 candidate stages; all -four correction paths already existed. +candidate, Amendment-8 approval-state, Amendment-9 candidate and Amendment-9 +approval-state stages; all four correction paths already existed. -Amendment-9 candidate changes in the commit that contains this report, -relative to the approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` -(exactly the four authorized paths): +Amendment-9 approval-state changes in the commit that contains this report, +relative to the content-approved candidate +`8699af5635b8f18691ceaeccd3b24707a71ef4f2` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate-content approval of 2026-10-08. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-08`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + "Current decision state: **APPROVED**.", the same approver and date, and + a paragraph stating that the CTO approved this exact corrected content on + 2026-10-08 as the architecture content represented by candidate ADR-0012 + blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that any ADR-0013 programme-local reliance on this exact version is a + separate state not effective until its own conditions are satisfied. + Every other line, including all architecture, the Amendment-9 coalescing + boundary, the Amendment-8 ordering rule, invariants 41 and 42, the + validation requirements, the historical-approval paragraph, the authority + condition and the architecture-versus-implementation separation, is + byte-identical to the candidate (section 9). Approval-state blob: + `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, and "its corrected content awaits CTO + approval" becomes "Javi, CTO approved its exact corrected content on + 2026-10-08"; and the header `Last updated` line advances from + `2026-10-07` to `2026-10-08`, the date of this row change, as the + approval-state authorization requires (section 5, decision 85). Every + other row is unchanged. Approval-state blob: + `c618d9d2eec622660ee7c1868111f747efa27629`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the coalescing-seal correction entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry only - "propose" + becomes "record a CTO-approved", "`PROPOSED`: the corrected content is not + approved" becomes "`APPROVED`: Javi, CTO approved the exact corrected + content on 2026-10-08", and "proposal only" becomes "decision only". Its + statements that the earlier entries and the 2026-09-30, 2026-10-05, + 2026-10-06 and 2026-10-07 approvals apply only to historical exact + versions are unchanged, and every other entry is byte-identical. + Approval-state blob: `2731521f363189063b9ab09068df7852889121a2`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-9 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit + the current stage; records the content-approved candidate, the CTO + candidate-content approval and the approval-state authorization; records + the approval-state validation and no-drift proof (section 9); and reframes + statements that would otherwise describe the candidate as the commit + containing this report, recording the candidate's identities as + historical facts. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the content-approved candidate through the approval-state +commit: every path other than the four authorized paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical Amendment-9 candidate changes in the content-approved candidate +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`, relative to the Amendment-8 +approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` (exactly the four +authorized paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement Specification Amendment 9 and record the corrected version as `PROPOSED`. - - behavioural effect: architecture proposal only. The header carries + - behavioural effect: architecture proposal only. The header carried `Status: PROPOSED` and no current approver or approval date. Section 3.5 states that a job is not yet started, and so coalescing-open, only until its first successful execution claim/start commits; binds the @@ -1170,15 +1322,15 @@ relative to the approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-9 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate the current - stage; records the exact-final-blob approval of the Amendment-8 - approval-state blob, the final-head review of that head, `R-E258-01`, - Amendment 9, its independent review and the candidate authorization as - governing evidence; records the candidate's validation (section 9); and - reframes statements that would otherwise describe the approval-state - commit as the commit containing this report, recording that commit's - identities as historical facts. All still-true earlier evidence is - preserved. + - behavioural effect: documentation only. Made the candidate the + then-current stage; recorded the exact-final-blob approval of the + Amendment-8 approval-state blob, the final-head review of that head, + `R-E258-01`, Amendment 9, its independent review and the candidate + authorization as governing evidence; recorded the candidate's validation + (section 9); and reframed statements that would otherwise describe the + Amendment-8 approval-state commit as the commit containing the report, + recording that commit's identities as historical facts. All still-true + earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the approval-state head through the Amendment-9 candidate: @@ -1566,7 +1718,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate, the -Amendment-8 approval-state commit and the Amendment-9 candidate: +Amendment-8 approval-state commit, the Amendment-9 candidate and the +Amendment-9 approval-state commit: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -1745,7 +1898,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-9 candidate differs from the approval-state head +The Amendment-9 approval-state commit differs from the content-approved +candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` in exactly the four +authorized paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-9 approval-state validation"). The +Amendment-9 candidate differed from the Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-9 candidate validation"). The @@ -1796,8 +1953,9 @@ stage (#958 comment `6022486352`), the control-record recovery stage (#958 comment `6022850916`), the transient-state correction stage (#958 comment `6036961945`), the factual-clarification stage (#958 comment `6041301223`), the Amendment-8 candidate stage (#958 comment `6044220221`), the Amendment-8 -approval-state stage (#958 comment `6045126538`) and the Amendment-9 candidate -stage (#958 comment `6046895895`). This matrix +approval-state stage (#958 comment `6045126538`), the Amendment-9 candidate +stage (#958 comment `6046895895`) and the Amendment-9 approval-state stage +(#958 comment `6065879000`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -2046,8 +2204,8 @@ Amendment-9 candidate: review of the approval-state head, and returned it to Draft before the Amendment-9 candidate (section 2) -Amendment-9 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-9 candidate stage - historical, completed before the Amendment-9 +approval-state commit: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -2061,25 +2219,53 @@ commit that contains this report: state in GitHub; all matched the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (PR #960 had been returned to draft by control - before this stage, so no PR-state mutation was needed; no PR-state, title - or description change is authorized at this stage) + before that stage, so no PR-state mutation was needed; no PR-state, title + or description change was authorized at that stage) - source edit: USED (the four authorized correction paths only) - new file creation: NOT USED (not authorized) - file deletion/move/rename: NOT USED (not authorized) - branch creation: NOT USED (not authorized; the same detached local worktree, now at the approval-state head, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent candidate commit that contains this report) +- commit: USED (the single-parent candidate commit + `8699af5635b8f18691ceaeccd3b24707a71ef4f2`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` to the candidate; the remote head, + tree, parent and ADR-0012, decision-register, changelog and report blobs + were verified; no automated review ran on the draft candidate head) + +Amendment-9 approval-state stage - done up to and including the creation of +the commit that contains this report: -Authorized post-commit actions for the candidate commit - GitHub-owned +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` and target `feature/worker`, + the candidate's tree, single parent and ADR-0012, report, decision-register + and changelog blobs, the protected ADR-0008 and ADR-0010 blobs, the six-path + cumulative footprint, PR #960 as the only open pull request targeting + `feature/worker`, the presence of the content-approval and approval-state + authorization records on #958 with no later control record, and, as + stage-time evidence, the PR #960 review-thread state in GitHub; all matched + the authorization; current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, branch creation: NOT USED + (not authorized; the same detached local worktree, now at the + content-approved candidate, was used for construction, and the existing + task branch is the fast-forward target) +- commit: USED (the single-parent approval-state commit that contains this + report) + +Authorized post-commit actions for the approval-state commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` to the candidate commit; + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` to the approval-state commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, changed path set, - cumulative footprint and PR #960 state, and that no review thread was - replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, ADR approval + state, changed path set, cumulative footprint and PR #960 state, and that + no review thread was replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -2087,24 +2273,23 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-9 candidate stage, -each a separate controlled lifecycle category where applicable (historical -stage-time evidence; GitHub, through #958 and PR #960, is authoritative for -the current state of each): +Not authorized for the implementing agent at the Amendment-9 approval-state +stage, each a separate controlled lifecycle category where applicable +(historical stage-time evidence; GitHub, through #958 and PR #960, is +authoritative for the current state of each): -- any edit to a path other than the four authorized correction paths: NOT USED - (not authorized) -- any ADR-0012 change outside Amendment 9 and the candidate `PROPOSED` state: - NOT USED (not authorized) -- independent exact-candidate inspection or exact-head review: NOT USED (never - on the implementing agent's authority) -- CTO candidate-content approval, approval-state commit, fresh `APPROVED` - status, approver or approval date, exact-final-blob approval: NOT USED (not +- any edit to a path other than the four authorized paths: NOT USED (not authorized) +- any ADR-0012 change outside the approval-state representation, including + any architecture change: NOT USED (not authorized) +- independent inspection or exact-head review: NOT USED (never on the + implementing agent's authority) +- exact-final-blob approval or confirmation: NOT USED (not authorized; never + on the implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second candidate commit, amend, rebase, squash, force push or history - rewrite: NOT USED (not authorized) +- any second approval-state commit, amend, rebase, squash, force push or + history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -2138,25 +2323,26 @@ Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments 4-5, Amendments 6-7, Amendments 8-9, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 -candidate and approval-state authorizations and the Amendment-9 candidate -authorization, configured Codex/app review is not expected from a push to a -draft PR and is not relied upon at the Amendment-6/7 candidate, -approval-state, control-record recovery, transient-state correction, -factual-clarification, Amendment-8 candidate, Amendment-8 approval-state or -Amendment-9 candidate stage; configured Codex/app review runs on separately -authorized Ready transitions, which are GitHub-owned lifecycle events, and any -automated review that nevertheless runs on a draft head is inspected read-only. -The actual job outcomes for the candidate commit exist only after its -publication; they are GitHub-owned lifecycle evidence recorded in the pull +candidate and approval-state authorizations and the Amendment-9 candidate and +approval-state authorizations, configured Codex/app review is not expected +from a push to a draft PR and is not relied upon at the Amendment-6/7 +candidate, approval-state, control-record recovery, transient-state +correction, factual-clarification, Amendment-8 candidate, Amendment-8 +approval-state, Amendment-9 candidate or Amendment-9 approval-state stage; +configured Codex/app review runs on separately authorized Ready transitions, +which are GitHub-owned lifecycle events, and any automated review that +nevertheless runs on a draft head is inspected read-only. The actual job +outcomes for the approval-state commit exist only after its publication; they +are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, -factual-clarification, Amendment-8 candidate, Amendment-8 approval-state or -Amendment-9 candidate stage, followed the same documentation-only -classification, with no manual dispatch or rerun: +factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, +Amendment-9 candidate or Amendment-9 approval-state stage, followed the same +documentation-only classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -2172,6 +2358,7 @@ b596536e7db0d6b8ace0a42fdd26bff42cabf104: 4 success (classify x3, check-changelo eb3129f13a9c858b3c012ee182315801f0b8563a: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (factual-clarification head) 42ae90a5932cf24ce90679196888cc3d83d88db8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-8 candidate) e2588f7b7e63a5c71968d87a3409f0ff48635e6d: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-8 approval-state head) +8699af5635b8f18691ceaeccd3b24707a71ef4f2: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-9 candidate) ``` Manually initiated external actions (anything the implementing agent @@ -2655,9 +2842,11 @@ Amendment-8 approval-state stage (historical approval-state head commit's identities as historical facts because that head is the Amendment-9 candidate's parent. -Amendment-9 Route-B candidate (the commit that contains this report). Item 75 -is an interpretation of the approved specification recorded for explicit -control inspection: +Amendment-9 Route-B candidate (historical content-approved candidate +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`). Item 75 was an interpretation of the +approved specification recorded for explicit control inspection; control +inspected the published candidate, confirmed that it created no new coalescing +permission, and the CTO approved its exact content: 69. The Amendment-9 rule is stated once, in a new unnumbered subsection "Coalescing-open and coalescing-sealed jobs" at the end of section 3.5, @@ -2738,18 +2927,72 @@ control inspection: 2026-09-30, 2026-10-05 and 2026-10-06 approvals, to the historical earlier version without embedding its identities. The changelog gains one new entry and every historical entry, including the record of the 2026-10-07 - approval, stays byte-preserved (decisions 26 and 59). This report does not - self-pin the candidate commit's SHA, its tree or its own final blob; - because the report now lives in the candidate commit, statements that - described the approval-state commit as the commit containing this report - are reframed as history, and that commit's identities, now the candidate - parent, are recorded in section 1 as historical facts. The exact-final-blob + approval, stays byte-preserved (decisions 26 and 59). The candidate-stage + report did not self-pin the candidate commit's SHA, its tree or its own + final blob; because the report then lived in the candidate commit, + statements that described the Amendment-8 approval-state commit as the + commit containing the report were reframed as history, and that commit's + identities, the candidate parent, were recorded in section 1 as + historical facts; section 1 now records the candidate's identities as + historical facts because the candidate is the approval-state parent. The + exact-final-blob approval of the approval-state ADR blob is recorded through owning issue #958, its date and the blob and head it approved; its GitHub comment identifier is not copied, consistent with decisions 37 and 64, no review, thread or review-comment identifier of the final-head automated review is copied, and `R-E258-01` is recorded by its control-assigned name. +Amendment-9 approval-state stage (the commit that contains this report): + +81. The approval-state header uses the same field order as the earlier + approved ADR-0012 versions (`Status`, `Date`, `Approved by`, + `Approval date`, `Decision owner`), and section 12 repeats the approver + and date. +82. The approval date carried by the ADR is the date of the CTO exact + candidate-content approval record, 2026-10-08, as Route B requires; it is + neither back-dated nor anticipated. +83. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by "Current decision state: + **APPROVED**.", the approver/date and one paragraph stating that the CTO + approved this exact corrected content on 2026-10-08 as the architecture + content represented by candidate ADR-0012 blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that ADR-0013 programme-local reliance on this exact version is separate + and not effective until its own conditions are satisfied (decision 63 + pattern; the authorization requires the approved content to be identified + by the candidate blob). The historical-approval, architecture-only, + authority-condition, partial-supersession and amendment paragraphs were + already true in the approved state (decision 80) and are unchanged. +84. The content approval is recorded only through owning issue #958, approver + Javi, CTO, approval date 2026-10-08, candidate head + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` and candidate ADR-0012 blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`. Its GitHub comment identifier + is not copied into repository source: the repository-authoritative Route-B + rule in `docs/engineering/decisions/README.md` forbids it, the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + applied that rule to this task (decisions 36-37 and 64), and the + approval-state authorization for this stage requires the same. +85. The decision-register header `Last updated` advances to `2026-10-08`, + the date on which the approval-state commit changes the ADR-0012 row, as + the approval-state authorization requires and for the reason recorded in + decision 25; otherwise only the ADR-0012 row changes. +86. Only the `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` changelog + entry changes, following the earlier approval-state wording pattern and + naming the approver as the authorization requires; every other entry, + including the historical approval entries, is byte-identical. +87. Architecture no-drift is proven by the exact normalized comparison in + section 9 ("Amendment-9 approval-state validation"), not by assertion. +88. This report does not self-pin the approval-state commit's SHA, its tree or + its own final blob; it records only non-self-referential evidence and the + approval-state ADR-0012, decision-register and changelog blobs, which do + not depend on its bytes. Because the report now lives in the + approval-state commit, statements that described the candidate as the + commit containing this report are reframed as history, and the + candidate's identities, now the approval-state parent, are recorded in + section 1 as historical facts. + List any deviation from the specification requiring authorization: - An earlier unpublished local activation-epoch candidate, @@ -2826,9 +3069,14 @@ List any deviation from the specification requiring authorization: - Amendment-9 candidate stage: NONE. No fifth path, new file, deletion, ADR change outside Amendment 9 and the candidate state, second commit, amend, rebase, squash, force push or history rewrite was needed or performed - before the commit that contains this report was created. Decision 75 is - recorded for control inspection as an interpretation within the authorized - scope, not as a deviation. + before the candidate commit was created. Decision 75 was recorded for + control inspection as an interpretation within the authorized scope, not as + a deviation, and control confirmed it. +- Amendment-9 approval-state stage: NONE. The approval-state transformation + followed its separate authorization exactly; no fifth path, new file, + deletion, architecture change, second commit, amend, rebase, squash, force + push or history rewrite was needed or performed before the commit that + contains this report was created. ## 6. Database and migration effects @@ -2893,8 +3141,10 @@ validation" records the validation of the historical content-approved candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`. "Amendment-8 approval-state validation" records the validation of the historical approval-state commit `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`. "Amendment-9 -candidate validation" at the end of this section records the validation of -the commit that contains this report. +candidate validation" records the validation of the historical +content-approved candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`. "Amendment-9 +approval-state validation" at the end of this section records the validation +of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -4991,15 +5241,18 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 0fc33df9aca61a2c82af452426f5574bfd3493cf ``` -The candidate ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the candidate tree and the candidate -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and recorded in the -post-publication handoff. +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate report's bytes, so they were +recorded above. The candidate report's own final blob, the candidate tree and +the candidate commit SHA depended on that report's bytes and were not +self-pinned in it; they were verified against the remote head after push and +recorded in the post-publication handoff, and section 1 now records them as +historical facts: commit `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, tree +`ebff7219611e7855c62856019fc051fcf41cb2b5`, report blob +`ffbb44f9f250d325da6846796dfae8051a3a839e`. -Required topology of the candidate commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the candidate commit, checked after it was created and +before push; the published candidate satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -5018,6 +5271,286 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-9 approval-state validation + +Environment: the same full local Git worktree, detached at the content-approved +candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` (tree +`ebff7219611e7855c62856019fc051fcf41cb2b5`). Immediately before construction, +read-only GitHub inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `master` at +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`, `feature/async/adr-0012` at +`8699af5635b8f18691ceaeccd3b24707a71ef4f2` with single parent +`e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, ADR-0012 blob +`0fc33df9aca61a2c82af452426f5574bfd3493cf`, report blob +`ffbb44f9f250d325da6846796dfae8051a3a839e`, decision-register blob +`8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be` and changelog blob +`4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4` at that head, the protected +ADR-0008 and ADR-0010 blobs, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker` and the only open pull request targeting +`feature/worker`, the cumulative PR footprint exactly the six paths, the +content-approval and approval-state authorization records present on #958 with +no later control record, and PR #960 review-thread state that matched the +stage authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the approval-state commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 8699af5635b8f18691ceaeccd3b24707a71ef4f2 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths): + +```text +git diff --cached --name-only 8699af5635b8f18691ceaeccd3b24707a71ef4f2 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 8699af5635b8f18691ceaeccd3b24707a71ef4f2 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 8699af5635b8f18691ceaeccd3b24707a71ef4f2 +1 1 CHANGELOG.md +762 191 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +15 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 candidate -> approval state (every changed line, diff header lines +omitted): + +```text +git diff --cached -U0 8699af5635b8f18691ceaeccd3b24707a71ef4f2 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +@@ -3 +3 @@ +-Status: PROPOSED ++Status: APPROVED +@@ -4,0 +5,2 @@ Date: 2026-09-29 ++Approved by: Javi, CTO ++Approval date: 2026-10-08 +@@ -2771 +2773 @@ shared-engine tests. +-Current decision state: **PROPOSED**. ++Current decision state: **APPROVED**. +@@ -2773,3 +2775,11 @@ Current decision state: **PROPOSED**. +-The corrected content of this version has not been approved. It may carry +-`APPROVED`, an approver and an approval date only after the CTO approves this +-exact corrected content under the repository decision process. ++Approved by: Javi, CTO ++Approval date: 2026-10-08 ++ ++The CTO approved this exact corrected content on 2026-10-08 under the ++repository decision process, as the architecture content represented by the ++Route-B candidate ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`; ++this version differs from that candidate only in this approval-state ++representation. That approval does not by itself make this ADR ++repository-authoritative: the authority condition below still applies. Any ++ADR-0013 programme-local reliance on this exact version is a separate state ++that is not effective until its own conditions are satisfied for this version. +``` + +Architecture no-drift proof. The normalization `N` removes only the authorized +approval-state representation: the header `Status`, `Approved by` and +`Approval date` fields before the first `---` rule, and the section-12 +approval-state paragraphs between the `## 12. Approval and authority` heading +and the paragraph beginning "This version is a material architectural +correction". Everything else, including every other section-12 paragraph, is +kept: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p 0fc33df9aca61a2c82af452426f5574bfd3493cf | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p 0fc33df9aca61a2c82af452426f5574bfd3493cf | awk "$N" | shasum -a 256 +33bb6972129c88c537608b740d8f284e8c4a5c86927bbb3e693a1f2c86036a02 - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +33bb6972129c88c537608b740d8f284e8c4a5c86927bbb3e693a1f2c86036a02 - +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display): + +```text +diff <(git cat-file -p 0fc33df9aca61a2c82af452426f5574bfd3493cf) <(git cat-file -p 0fc33df9aca61a2c82af452426f5574bfd3493cf | awk "$N") | sed 's/[[:space:]]*$//' +3d2 +< Status: PROPOSED +2770,2776d2768 +< +< Current decision state: **PROPOSED**. +< +< The corrected content of this version has not been approved. It may carry +< `APPROVED`, an approver and an approval date only after the CTO approves this +< exact corrected content under the repository decision process. +< + +diff <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") | sed 's/[[:space:]]*$//' +3d2 +< Status: APPROVED +5,6d3 +< Approved by: Javi, CTO +< Approval date: 2026-10-08 +2772,2786d2768 +< +< Current decision state: **APPROVED**. +< +< Approved by: Javi, CTO +< Approval date: 2026-10-08 +< +< The CTO approved this exact corrected content on 2026-10-08 under the +< repository decision process, as the architecture content represented by the +< Route-B candidate ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`; +< this version differs from that candidate only in this approval-state +< representation. That approval does not by itself make this ADR +< repository-authoritative: the authority condition below still applies. Any +< ADR-0013 programme-local reliance on this exact version is a separate state +< that is not effective until its own conditions are satisfied for this version. +< +``` + +ADR-0012 approval state: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n 3,7p +Status: APPROVED +Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-08 +Decision owner: CTO +``` + +Decision-register candidate -> approval state (the `Last updated` header line +and the ADR-0012 row; every other line byte-identical; row changes as +word-level pairs): + +```text +git diff --cached -U0 8699af5635b8f18691ceaeccd3b24707a71ef4f2 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-07 + +git diff --cached -U0 8699af5635b8f18691ceaeccd3b24707a71ef4f2 -- docs/engineering/decisions/decision-register.md | grep '^[-+]Last updated' +-Last updated: 2026-10-07 ++Last updated: 2026-10-08 + +diff <(git show 8699af5635b8f18691ceaeccd3b24707a71ef4f2:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 8699af5635b8f18691ceaeccd3b24707a71ef4f2 -- docs/engineering/decisions/decision-register.md +[-2026-10-07-] {+2026-10-08+} +[-PROPOSED-] {+APPROVED+} +[-Pending-] {+Satisfied+} +{+Javi, CTO approved+} {+exact+} +[-awaits CTO approval-] {+on 2026-10-08+} +``` + +Changelog candidate -> approval state (one line, the +`THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry; every other line +byte-identical; word-level changes, one pair per line): + +```text +git diff --cached -U0 8699af5635b8f18691ceaeccd3b24707a71ef4f2 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 8699af5635b8f18691ceaeccd3b24707a71ef4f2:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 8699af5635b8f18691ceaeccd3b24707a71ef4f2 -- CHANGELOG.md +[-propose-] {+record+} {+CTO-approved+} +[-`PROPOSED`:-] {+`APPROVED`: Javi, CTO approved+} {+exact+} +[-is not approved.**-] {+on 2026-10-08.**+} +[-proposal-] {+decision+} +``` + +Content-approval identifier (the command is shown with the identifier replaced +by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c +decision-register.md c618d9d2eec622660ee7c1868111f747efa27629 +CHANGELOG.md 2731521f363189063b9ab09068df7852889121a2 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above; the ADR-0012 blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` is the exact +final blob presented for CTO exact-final-blob approval at this stage. This +report's own final blob, the approval-state tree and the approval-state commit +SHA depend on this report's bytes and are not self-pinned; they are verified +against the remote head after push and recorded in the post-publication +handoff. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 8699af5635b8f18691ceaeccd3b24707a71ef4f2 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 8699af5635b8f18691ceaeccd3b24707a71ef4f2 HEAD +required: exit 0; no output + +git diff --name-only 8699af5635b8f18691ceaeccd3b24707a71ef4f2 HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -5142,6 +5675,16 @@ staged register and changelog with the approval-state-head blobs (only the ADR-0012 row, and only one added entry); confirmed the protected decision and control blobs are unchanged; and read the report for statements that would become false once it moves into the candidate commit and reframed them. +Amendment-9 approval-state stage: compared the staged ADR-0012 with the +content-approved candidate blob `0fc33df9aca61a2c82af452426f5574bfd3493cf` by +the exact normalized comparison in section 9 (no difference), and read the raw +delta line by line; compared the staged register and changelog with the +candidate blobs (only the `Last updated` header and the ADR-0012 row, and only +the coalescing-seal correction entry, change); confirmed the protected +decision and control blobs are unchanged; confirmed by fixed-string search +that the content-approval comment identifier does not occur in this report; +and read the report for statements that would become false once it moves into +the approval-state commit and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -5153,10 +5696,12 @@ the control-record recovery head `22bd94dc959f11fe49c8bef8cd2a836140aadff7`, the transient-state correction head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, the factual-clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`, the content-approved -Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` and the -Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, are -historical evidence; the live PR #960 diff of the candidate commit exists only -after its publication and is GitHub-owned lifecycle evidence. +Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, the +Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` and +the content-approved Amendment-9 candidate +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`, are historical evidence; the live +PR #960 diff of the approval-state commit exists only after its publication +and is GitHub-owned lifecycle evidence. ## 11. CI @@ -5169,9 +5714,9 @@ recovery authorization, the Amendment-6/7 candidate and approval-state authorizations, the control-record recovery authorization, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate authorization, the Amendment-8 -approval-state authorization and the Amendment-9 candidate authorization, an -automatic CI failure, or any migration execution or Docker publication, is a -HOLD condition. +approval-state authorization, the Amendment-9 candidate authorization and the +Amendment-9 approval-state authorization, an automatic CI failure, or any +migration execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -5200,19 +5745,21 @@ report-only commit, the factual-clarification authorization permitted exactly one two-path clarification commit, the Amendment-8 candidate authorization permitted exactly one four-path candidate commit, the Amendment-8 approval-state authorization permitted exactly one direct-child approval-state -commit, and the Amendment-9 candidate authorization permits exactly one -four-path candidate commit; any further source correction exhausts those -authorizations and requires a fresh CTO authorization (Amendment 3 C10; -Amendments 6-9; `docs/engineering/decisions/README.md`). +commit, the Amendment-9 candidate authorization permitted exactly one +four-path candidate commit, and the Amendment-9 approval-state authorization +permits exactly one direct-child approval-state commit; any further source +correction exhausts those authorizations and requires a fresh CTO +authorization (Amendment 3 C10; Amendments 6-9; +`docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-9 candidate stage the +- Historical stage-time evidence: at the Amendment-9 approval-state stage the implementing agent performed no independent inspection or review, - candidate-content approval, PR #960 title/description reconciliation, - Draft/Ready change, automated-review request, thread adjudication, - exact-head review or merge (section 4.2). + exact-final-blob approval or confirmation, PR #960 title/description + reconciliation, Draft/Ready change, automated-review request, thread + adjudication, exact-head review or merge (section 4.2). Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -5248,8 +5795,9 @@ Monitoring required: NONE under a separate authorization for one direct-child approval-state commit, and the coalescing-attachment/first-claim ordering defect found by the final-head review of that approval-state head only under Specification - Amendment 9, its independent review and a separate authorization for this - one four-path candidate commit. + Amendment 9, its independent review and a separate authorization for one + four-path candidate commit, and its approval state only under a separate + authorization for this one direct-child approval-state commit. - The strand-check timing question left outside this correction (section 5, decision 53): Amendments 1-8 do not determine whether a withdrawal's strand-prevention evaluation may be stale relative to events or anomalies @@ -5267,7 +5815,8 @@ Monitoring required: NONE approval-state commit, the recovery commit, the Amendment-6/7 candidate, its approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and - approval-state commit and this Amendment-9 candidate. + approval-state commit, the Amendment-9 candidate and this Amendment-9 + approval-state commit. ## 14. Unresolved issues @@ -5370,11 +5919,15 @@ Monitoring required: NONE specification review of the base specification and Amendments 1-9 returned `APPROVED`, confirming the finding, the classification, the seal boundary and the validation matrix and recording six non-blocking - candidate-inspection observations; and the Route-B candidate that contains - this report implements the correction in source as `PROPOSED` (section 4; - section 5, decisions 69-80). `R-E258-01` is closed at specification level - by Amendment 9; whether the candidate closes it in source is decided by the - independent inspection and review of the candidate, which GitHub records. + candidate-inspection observations; the content-approved Route-B candidate + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` implements the correction in + source (section 4; section 5, decisions 69-80); control's independent + inspection of that candidate found no blocking exact-candidate finding; + Javi, CTO approved its exact content on 2026-10-08; and the approval-state + commit that contains this report records that approval. `R-E258-01` is + closed at specification level by Amendment 9; its source closure is + adjudicated by the independent exact-head review controls that precede any + merge, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -5388,21 +5941,37 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-9 candidate also implemented -the Amendment-8 approval-state commit and candidate, the factual -clarification, the transient-state correction, the control-record recovery, -the Amendment-6/7 approval-state commit and candidate and earlier Route-B -source stages of this task and prepared the earlier uncommitted +work. The implementing agent of this Amendment-9 approval-state commit also +implemented the Amendment-9 candidate, the Amendment-8 approval-state commit +and candidate, the factual clarification, the transient-state correction, the +control-record recovery, the Amendment-6/7 approval-state commit and candidate +and earlier Route-B source stages of this task and prepared the earlier +uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent inspector or reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Suggested review focus: -- that the candidate changes exactly the four authorized paths from the - approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, that the - candidate ADR-0012 blob is `0fc33df9aca61a2c82af452426f5574bfd3493cf`, and that - ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that the approval-state delta from the content-approved Amendment-9 + candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` is limited to the ADR + header approval fields, the two leading section-12 paragraphs, the ADR-0012 + register row and `Last updated` header and the coalescing-seal correction + changelog entry, that the normalized comparison in section 9 shows no + architecture drift from candidate blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`, and that the approval-state + ADR-0012 blob is `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`; +- that the content approval is recorded only through owning issue #958, + approver Javi, CTO, approval date 2026-10-08, candidate head + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` and candidate ADR-0012 blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`, that the approval date matches + the CTO candidate-content approval record, and that no content-approval, + exact-final-blob approval, review or merge identifier is copied into this + report (section 5, decision 84); +- that the candidate changed exactly the four authorized paths from the + Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, + that the candidate ADR-0012 blob is `0fc33df9aca61a2c82af452426f5574bfd3493cf`, + and that ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; - that every Amendment 9 J4-J11 requirement is expressed in ADR-0012: the coalescing-open/coalescing-sealed boundary anchored to the first successful claim/start as the same transition as "not yet started", the permanence of @@ -5421,9 +5990,10 @@ Suggested review focus: applied-revision, attempt-phase, claim-token, WAITING/RECONCILIATION_REQUIRED or Amendment-8 ordering semantics, and that decision 75 decides no new coalescing path; -- that every ADR-0012 hunk outside those concerns is limited to the candidate - `PROPOSED` state (header and section 12) and that all other architecture is - byte-identical to the approval-state head; +- that every ADR-0012 hunk outside those concerns is limited to the approval + state (header and section 12) and that all other architecture is + byte-identical to the Amendment-8 approval-state head and to the + content-approved Amendment-9 candidate; - that the approval-state delta from the content-approved Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` is limited to the ADR header approval fields, the two leading section-12 paragraphs, the ADR-0012 @@ -5507,17 +6077,18 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `PROPOSED` with no current approver or - approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06 and 2026-10-07 - approvals to the earlier exact versions and keep the repository authority - condition and the ADR-0013 reliance distinction; -- that the register row, the new coalescing-seal correction changelog entry - and this report describe the corrected version as `PROPOSED`, that the - withdrawal-order correction entry and every other historical changelog - entry are byte-preserved, and that the register header `Last updated` is - `2026-10-07`; +- that the header and section 12 carry `APPROVED` with Javi, CTO and + 2026-10-08, identify the approved content by candidate ADR-0012 blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`, bind the 2026-09-30, 2026-10-05, + 2026-10-06 and 2026-10-07 approvals to the earlier exact versions and keep + the repository authority condition and the ADR-0013 reliance distinction; +- that the register row, the coalescing-seal correction changelog entry and + this report describe the corrected version as `APPROVED` on 2026-10-08, + that the withdrawal-order correction entry and every other historical + changelog entry are byte-preserved, and that the register header + `Last updated` is `2026-10-08`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other - protected control files are byte-identical to the approval-state head, the + protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. ## Appendix A - Historical authoring and correction record diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 0fc33df9a..cbe8322d9 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-08 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -2768,11 +2770,19 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +Current decision state: **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-08 + +The CTO approved this exact corrected content on 2026-10-08 under the +repository decision process, as the architecture content represented by the +Route-B candidate ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`; +this version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 8b5ef6b5b..c618d9d2e 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-07 +Last updated: 2026-10-08 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-07 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, and the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-08 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, and the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 6e521f00b039769b28a26ab069c7e97d22de4e08 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 8 Oct 2026 21:40:11 +0100 Subject: [PATCH 38/48] THOTH-ASYNC-01-ADR-01: propose Amendment-10 strand-safety correction (#958) --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1130 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 206 ++- .../decisions/decision-register.md | 2 +- 4 files changed, 1120 insertions(+), 219 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2731521f3..653cc699c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed, records the anomaly durably and visibly, evaluates availability from post-withdrawal state, never treats the withdrawn revision as selectable and creates no disposition, job or route slot); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 5c3b5a096..f4c0bd0ae 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -18,14 +18,17 @@ ordering correction specified by Specification Amendment 8 after the configured final-head automated review of that clarification head, and the Route-B candidate and approval-state stages of the coalescing-attachment/first-claim ordering correction specified by Specification Amendment 9 after the configured -final-head automated review of that Amendment-8 approval-state head. It records +final-head automated review of that Amendment-8 approval-state head, and the +Route-B candidate stage of the withdrawal strand-safety decision-stability +correction specified by Specification Amendment 10 after the configured +final-head automated review of that Amendment-9 approval-state head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-9 Route-B approval-state commit -as the current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-10 Route-B candidate as the +current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -47,11 +50,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-9 Route-B approval-state commit that contains -this report, an ordinary single-parent additive commit whose direct parent is +Head commit: the Amendment-10 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical Amendment-9 approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f`; its SHA is deliberately not +self-pinned here (section 3). That approval-state head is the direct child of the content-approved Amendment-9 candidate -`8699af5635b8f18691ceaeccd3b24707a71ef4f2`; its SHA is deliberately not -self-pinned here (section 3). That candidate is the direct child of the +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`, which is the direct child of the historical Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, which is the direct child of the content-approved Amendment-8 candidate @@ -71,7 +76,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -85,9 +90,9 @@ Claude (Opus 5.5), bounded implementing agent; Amendment-4/5 recovery stage - Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, -Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate and -Amendment-9 approval-state stages - Claude (Fable 5.1), bounded implementing -agent. Earlier rounds are recorded in Appendix A. +Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, +Amendment-9 approval-state and Amendment-10 candidate stages - Claude (Fable +5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -215,15 +220,24 @@ decision-register blob 8b5ef6b5b6ca7f6d49ed5066aba5e49575de06be implementation-report blob ffbb44f9f250d325da6846796dfae8051a3a839e CHANGELOG blob 4d0e17ca0b11dea0ed4905ef9776693ee2c87bc4 -Amendment-9 Route-B approval-state commit (the commit that contains this report): -ADR-0012 blob cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c (APPROVED; Javi, CTO; 2026-10-08; architecture-equivalent to 0fc33df9aca61a2c82af452426f5574bfd3493cf) +historical Amendment-9 approval-state head / Amendment-10 candidate parent: +e41f377202379238b91eb43d60f5115aa8adc20f +tree 38f4153d961f771f177ba354a717845115b2c497 +ADR-0012 blob cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c (APPROVED; Javi, CTO; 2026-10-08; architecture-equivalent to 0fc33df9aca61a2c82af452426f5574bfd3493cf; CTO exact-final-blob approval recorded on #958 on 2026-10-08; the exact prior approved blob that the Amendment-10 correction corrects) decision-register blob c618d9d2eec622660ee7c1868111f747efa27629 CHANGELOG blob 2731521f363189063b9ab09068df7852889121a2 +implementation-report blob 5c3b5a096acd2b3c43161aca03249212a9a7c453 (superseded by the Amendment-10 candidate) + +Amendment-10 Route-B candidate (the commit that contains this report): +ADR-0012 blob d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 (PROPOSED; material architectural correction of cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c) +decision-register blob 73acf0e7a83116d3ffee47cd6ff40c14075e8643 +CHANGELOG blob 653cc699c972f0457bd1cdbc032374e3090396fd feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual -clarification, the Amendment-8 candidate and approval-state commit and the -Amendment-9 candidate and approval-state commit (unchanged): +clarification, the Amendment-8 candidate and approval-state commit, the +Amendment-9 candidate and approval-state commit and the Amendment-10 candidate +(unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -320,7 +334,23 @@ Approved specification: is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and is not copied here; - Amendment-9 Route-B approval-state implementation authorization: #958 - comment `6065879000`. + comment `6065879000`; +- CTO exact-final-blob approval of the Amendment-9 approval-state ADR-0012 + blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` at head + `e41f377202379238b91eb43d60f5115aa8adc20f`: recorded on #958 on 2026-10-08; + its GitHub comment identifier is GitHub-owned lifecycle evidence under + `ADR-0005` and Route-B doctrine and is not copied here; +- Specification Amendment 10 / withdrawal strand-safety linearization, which + accepts the final-head finding `R-E41F-01` as valid and blocking and + classifies its correction as a material architectural correction: #958 + comment `6066621583`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-10, including five non-blocking candidate-inspection + observations: #958 comment `6067729400`; +- PR #960 Ready -> Draft authorization for Amendment-10 Route-B preparation, + a control action that preceded this stage: #958 comment `6067780272`; +- Amendment-10 Route-B candidate implementation authorization: #958 comment + `6067886387`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -359,12 +389,19 @@ head that changed exactly the four authorized correction paths and requires a coalescing attachment to an existing logical job and that job's first successful claim/start to share one database-atomic linearized order, received independent control inspection and CTO exact candidate-content approval on -2026-10-08. The commit that contains this report is the one bounded Route-B -approval-state commit that records that approval as durable decision state: -ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and -`Approval date: 2026-10-08`, the ADR-0012 register row and the coalescing-seal -correction changelog entry moved to the approved state, and this report's -approval-state evidence. No architecture content changes. +2026-10-08. The one bounded Route-B approval-state commit then recorded that +approval as durable decision state (ADR-0012 `PROPOSED -> APPROVED` with +`Approved by: Javi, CTO` and `Approval date: 2026-10-08`, the ADR-0012 +register row and the coalescing-seal correction changelog entry moved to the +approved state), and the CTO recorded exact-final-blob approval of that ADR +blob. The configured final-head automated review of that approval-state head +exposed a further architecture defect, `R-E41F-01`, specified by +Specification Amendment 10. The commit that contains this report is its +Route-B candidate: one ordinary single-parent commit on the approval-state +head that changes exactly the four authorized correction paths, returns +ADR-0012 to `Status: PROPOSED` and requires a revision withdrawal to commit +only from a database-consistent strand-safety decision that remains valid +through the withdrawal's linearization/commit point. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -665,25 +702,74 @@ approval-state evidence. No architecture content changes. `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf` - on 2026-10-08 in a durable record on #958. That stage authorized no PR-state or PR-metadata change. -- **Amendment-9 approval-state stage (the commit that contains this - report).** Authorized by #958 comment `6065879000`. One ordinary - single-parent commit directly on the content-approved candidate records - that approval as durable decision state, changing only the four authorized +- **Amendment-9 approval-state stage (historical approval-state head + `e41f377202379238b91eb43d60f5115aa8adc20f`).** Authorized by #958 comment + `6065879000`. One ordinary single-parent commit directly on the + content-approved candidate recorded that approval as durable decision + state, changing only the four authorized paths: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and `Approval date: 2026-10-08` and the leading section-12 approval paragraphs, which identify the approved content as the content represented by candidate ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`; the ADR-0012 register row and the register's `Last updated` header; the `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` changelog entry; and this - report. No architecture content changes (section 9, "Amendment-9 + report. No architecture content changed (section 9, "Amendment-9 approval-state validation"). The content approval is recorded here through owning issue #958, approver Javi, CTO, approval date 2026-10-08, the candidate head and the candidate ADR-0012 blob; its GitHub comment identifier is not copied into repository source, as the repository-authoritative Route-B rule in `docs/engineering/decisions/README.md` and the approval-state authorization - itself require (section 5, decision 84). PR #960 was already a draft; this - stage authorizes no PR-state or PR-metadata change. + itself require (section 5, decision 84). PR #960 was already a draft; that + stage authorized no PR-state or PR-metadata change. +- **Exact-final-blob approval, final-head review of the Amendment-9 + approval-state head, Specification Amendment 10 and the Amendment-10 Route-B + candidate (the commit that contains this report).** Authorized by #958 + comment `6067886387` after the CTO approved Specification Amendment 10 (#958 + comment `6066621583`) and a fresh independent CRITICAL specification review + of the base specification and Amendments 1-10 returned `APPROVED` (#958 + comment `6067729400`). After the approval-state head was published, control + independently inspected it and the CTO recorded exact-final-blob approval of + ADR-0012 blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` at head + `e41f377202379238b91eb43d60f5115aa8adc20f` on 2026-10-08; control then + separately marked PR #960 Ready, which triggered the configured final-head + automated review of that head, and returned PR #960 to Draft under #958 + comment `6067780272` before any candidate source was staged. That review + raised one new finding, recorded by control as `R-E41F-01` (HIGH, valid and + blocking) and classified as a `MATERIAL ARCHITECTURAL CORRECTION`: ADR-0012 + refused or held a withdrawal that would strand an unresolved in-epoch + contract-mismatch anomaly, and Amendment 8 gave withdrawal one + database-atomic order with every operation that creates a new durable + selection of or reference to the withdrawn revision, but creation of such an + anomaly references the epoch's expected revision and need not select or + reference the separate compatible revision being withdrawn, so a withdrawal + could evaluate its strand predicate, observe no anomaly, and commit after a + concurrently committed anomaly had made that decision unsafe; this report + had recorded that timing question as outside the Amendment-8 correction + (section 5, decision 53). Amendment 10 selects one database-consistent + strand-safety decision that remains valid through the withdrawal's + linearization/commit point; coverage of every already-defined durable + safe-to-unsafe predicate mutation, at minimum unresolved + `IN_EPOCH_CONTRACT_MISMATCH` anomaly creation and removal of the last + approved resolution/materialization path; both anomaly-first and + withdrawal-first semantics; no write skew across resolution-path removal; + composition with the Amendment-8 withdrawal/selection order as one coherent + database order without a second withdrawal lifecycle; unchanged mismatch, + enrollment, revision, retirement, health, evidence and Amendment-9 + semantics; and nine validation cases, with the concrete mechanism left to + the implementation specification. The independent review confirmed the + finding, the classification, the decision-stability rule and the + sufficiency of the nine-case matrix, and recorded five non-blocking + candidate-inspection observations. The commit that contains this report is + one ordinary single-parent commit directly on the approval-state head that + changes exactly the four authorized correction paths: ADR-0012 returns to + `Status: PROPOSED` with no current approver or approval date and implements + Amendment 10 (section 4; section 5, decisions 89-100); the decision-register + ADR-0012 row, one new changelog entry and this report describe the + corrected version as `PROPOSED`; and the 2026-09-30, 2026-10-05, + 2026-10-06, 2026-10-07 and 2026-10-08 CTO approvals remain historical + evidence bound to the earlier exact versions. This stage authorizes no + PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -697,9 +783,14 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +MATERIAL ARCHITECTURAL CORRECTION of prior approved blob cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c +(Specification Amendment 10; Route-B candidate stage; candidate status PROPOSED) + +historical Amendment-9 approval-state at e41f377202379238b91eb43d60f5115aa8adc20f: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-08) of the content-approved Amendment-9 candidate blob 0fc33df9aca61a2c82af452426f5574bfd3493cf; -no architecture change +no architecture change; CTO exact-final-blob approval of cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c +recorded on #958 on 2026-10-08 historical Amendment-9 candidate at 8699af5635b8f18691ceaeccd3b24707a71ef4f2: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 85937a4e1ff9a27d8f5b36ca82651821711472b2 @@ -866,45 +957,83 @@ ordering version as well: record the withdrawal/selection ordering version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while the register row recorded the Amendment-9 candidate as - `PROPOSED` and now records the approved version. + `PROPOSED` and then the approved version. They remain historical evidence bound to their exact versions and heads. The Amendment-9 candidate version received its own independent control inspection -and CTO exact candidate-content approval on 2026-10-08, and the approval-state -commit that contains this report records it. For this version, -exact-final-blob approval, independent exact-head review, CTO merge -authorization, guarded merge, programme-local reliance and later migration, -deployment, activation and observation are distinct controlled lifecycle -categories; GitHub, through #958 and PR #960, is authoritative for which of -them is pending, satisfied or blocked for any head. +and CTO exact candidate-content approval on 2026-10-08, the historical +approval-state commit `e41f377202379238b91eb43d60f5115aa8adc20f` recorded it in +ADR-0012 blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`, and that blob received +CTO exact-final-blob approval at that head on 2026-10-08. + +The Amendment-10 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every record bound to the coalescing-seal ordering +version as well: + +- the CTO content approval of candidate + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` / ADR blob + `0fc33df9aca61a2c82af452426f5574bfd3493cf`; +- the 2026-10-08 approval state carried by ADR blob + `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` and its exact-final-blob approval at + head `e41f377202379238b91eb43d60f5115aa8adc20f`; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the approval-state head; +- the approval-state-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` changelog entry that + record the coalescing-seal ordering version as `APPROVED`; the changelog + entry is preserved and remains true only of that historical version, while + the register row now records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. For +the candidate version, independent exact-candidate inspection, CTO exact +candidate-content approval, the approval-state commit, exact-final-blob +approval, independent exact-head review, CTO merge authorization, guarded +merge, programme-local reliance and later migration, deployment, activation +and observation are distinct controlled lifecycle categories; GitHub, through +#958 and PR #960, is authoritative for which of them is pending, satisfied or +blocked for any head. ## 3. Commits -Amendment-9 Route-B approval-state commit (the commit that contains this -report): +Amendment-10 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: record coalescing-seal correction approval` - - direct parent: the content-approved Amendment-9 candidate - `8699af5635b8f18691ceaeccd3b24707a71ef4f2` - - changes only the four authorized paths, and only to record the approval - state (section 4); the approval-state ADR-0012 blob is - `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`, the approval-state - decision-register blob `c618d9d2eec622660ee7c1868111f747efa27629` and the - approval-state changelog blob `2731521f363189063b9ab09068df7852889121a2` + `THOTH-ASYNC-01-ADR-01: propose Amendment-10 strand-safety correction (#958)` + - direct parent: the historical Amendment-9 approval-state head + `e41f377202379238b91eb43d60f5115aa8adc20f` + - changes only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`, the + candidate decision-register blob `73acf0e7a83116d3ffee47cd6ff40c14075e8643` + and the candidate changelog blob `653cc699c972f0457bd1cdbc032374e3090396fd` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the approval-state ADR-0012, +here because this report is part of that tree; the candidate ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its candidate blob (section 9). The -commit identities are verified against the remote task-branch head after push -and recorded in the post-publication handoff; once published, the PR #960 -head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The -authorized topology is a non-force fast-forward of the task branch from the -content-approved candidate to the approval-state commit; no merge commit, -rebase, squash, amend, force update or second approval-state commit is -authorized, and no further source commit exists implicitly after it. +recorded above; every other file keeps its approval-state-head blob (section +9). The commit identities are verified against the remote task-branch head +after push and recorded in the post-publication handoff; once published, the +PR #960 head and head tree are GitHub-owned lifecycle evidence under +`ADR-0005`. The authorized topology is a non-force fast-forward of the task +branch from the approval-state head to the candidate commit; no merge commit, +rebase, squash, amend, force update or second candidate commit is authorized, +and no further source commit exists implicitly after it. + +Amendment-9 Route-B approval-state commit (historical): + +- `e41f377202379238b91eb43d60f5115aa8adc20f` - + `THOTH-ASYNC-01-ADR-01: record coalescing-seal correction approval` + - ordinary single-parent additive commit; direct parent: the content-approved + Amendment-9 candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` + - tree `38f4153d961f771f177ba354a717845115b2c497` + - changed only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`, which received CTO + exact-final-blob approval at that head on 2026-10-08 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + candidate; the remote head, tree, parent, ADR-0012, decision-register, + changelog and report blobs and the ADR approval state matched the locally + validated object Amendment-9 Route-B candidate commit (content-approved; historical): @@ -1087,13 +1216,24 @@ first-parent order, newest first: ## 4. Files changed -Amendment-9 Route-B approval-state write budget for the commit that contains -this report (authorization #958 comment `6065879000`), identical to the -Amendment-9 candidate write budget (authorization #958 comment `6046895895`; -the Amendment 6 G10 / Amendment 7 H12 budget, unchanged by Amendments 8-9); -exactly four existing paths, no new file, no deletion, and every other path -byte-identical to the content-approved candidate -`8699af5635b8f18691ceaeccd3b24707a71ef4f2`: +Amendment-10 Route-B candidate write budget for the commit that contains this +report (authorization #958 comment `6067886387`; the Amendment 6 G10 / +Amendment 7 H12 budget, unchanged by Amendments 8-10); exactly four existing +paths, no new file, no deletion, and every other path byte-identical to the +historical Amendment-9 approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-9 Route-B approval-state write budget (authorization #958 +comment `6065879000`), identical to the Amendment-9 candidate write budget +(authorization #958 comment `6046895895`; the Amendment 6 G10 / Amendment 7 +H12 budget, unchanged by Amendments 8-9); exactly four existing paths, no new +file, no deletion, and every other path byte-identical to the content-approved +candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`: - `CHANGELOG.md` - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` @@ -1173,11 +1313,115 @@ six paths at every stage): Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 -candidate, Amendment-8 approval-state, Amendment-9 candidate and Amendment-9 -approval-state stages; all four correction paths already existed. +candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 +approval-state and Amendment-10 candidate stages; all four correction paths +already existed. + +Amendment-10 candidate changes in the commit that contains this report, +relative to the approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` +(exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement Specification Amendment 10 and record the corrected + version as `PROPOSED`. + - behavioural effect: architecture proposal only. The header carries + `Status: PROPOSED` and no current approver or approval date. In section + 3.2, "Revision withdrawal and route retirement" gains, immediately after + the existing strand-prevention paragraph, the decision-stability rule: a + withdrawal commits only from a database-consistent strand-safety decision + that remains valid through its linearization/commit point, an in-transaction + scan being insufficient by itself; every already-defined durable mutation + that can turn the predicate from safe to unsafe shares the withdrawal's + database-atomic serialization/constraint boundary or forces a + database-enforced re-evaluate/abort, at minimum creation of an unresolved + `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the + withdrawal affects (stated to reference the epoch's expected revision and + not necessarily the compatible revision being withdrawn, so that the + Amendment-8 order does not by itself cover it) and removal of the last + approved resolution/materialization path; the predicate-improving + exemption; the mechanism-neutral example list with the + scan-decide-commit sequence excluded; the HOLD-for-specification rule for + undefined transitions; the anomaly-first and withdrawal-first orders as + Amendment 10 defines them, including "strand safety, not anomaly + existence" and the withdrawal-first visible-unresolved-anomaly outcome + with no automatic success, divergence or deletion; the no-write-skew rule + for alternative resolution paths; and the statement that the rule is a + property of the strand-prevention predicate composing with the + Amendment-8 order as one coherent database ordering, with no second + withdrawal lifecycle. The section-3.2 lifecycle pointer paragraph and the + withdrawal composition paragraph name the rule; "In-epoch contract + mismatch" gains one paragraph stating that detection records facts and + raises attention, need not calculate or persist resolution-path + availability, is never suppressed by a withdrawal, and defers the ordering + to the withdrawal section. Invariant 43 is appended; section 8 item 1 and + section 9 rollout proof 3 name the rule; section 11 adds the nine + Amendment-10 validation cases immediately after the existing + withdrawal/selection composition bullet. Section 12 records the + `PROPOSED` state, adds the rule to the description of this material + correction and binds the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 + and 2026-10-08 approvals to the historical earlier versions. The mismatch + definition and recorded facts, resolution options and recording, + historical-enrollment field-by-field validation, revision immutability + and equivalence, producer-version coverage, permanent retirement, route + health, append-only evidence, withdrawal's future-selection-only effects, + the Amendment-8 withdrawal/selection paragraphs and both commit orders, + the Amendment-9 coalescing boundary, `CURRENT_STATE` and `REVISION_BOUND` + semantics, claim fencing, external-write ambiguity and reconciliation, + worker trust boundaries and the BE-04 transition are unchanged. + Candidate blob: `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the material correction makes the ADR-0012 row's `APPROVED` + assertion untrue for the new version. + - behavioural effect: the ADR-0012 row records `PROPOSED` / `Pending`, + binds the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 and 2026-10-08 + approvals to the historical earlier exact versions and adds one summary of + the database-consistent strand-safety decision, the anomaly/withdrawal + orders and the no-write-skew rule; its partial-supersession, + authority-condition, ADR-0013 and implementation-authorization sentences + are unchanged. The header `Last updated: 2026-10-08` already carries the + date of this row change and is unchanged (section 5, decision 97). No + other row changes. Candidate blob: + `73acf0e7a83116d3ffee47cd6ff40c14075e8643`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: required changelog entry. + - behavioural effect: exactly one new + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry at the top of + `[Unreleased] -> Changed`, describing the corrected version as `PROPOSED` + and stating that the earlier + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-CORRECTION`, `THOTH-ASYNC-01-ADR-01-APPROVAL` and + `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries and the approvals they + record apply only to the historical earlier exact versions. No heading is + added and every existing entry is byte-preserved. Candidate blob: + `653cc699c972f0457bd1cdbc032374e3090396fd`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-10 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate the current + stage; records the exact-final-blob approval of the Amendment-9 + approval-state blob, the final-head review of that head, `R-E41F-01`, + Amendment 10, its independent review, the Ready -> Draft control action + and the candidate authorization as governing evidence; records the + candidate's validation (section 9); replaces the deferred strand-check + timing limitation with the implemented rule; and reframes statements that + would otherwise describe the approval-state commit as the commit + containing this report, recording that commit's identities as historical + facts. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the approval-state head through the Amendment-10 candidate: +every path other than the four correction paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. -Amendment-9 approval-state changes in the commit that contains this report, -relative to the content-approved candidate +Historical Amendment-9 approval-state changes in the approval-state commit +`e41f377202379238b91eb43d60f5115aa8adc20f`, relative to the content-approved +candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` (exactly the four authorized paths, approval state only): @@ -1228,13 +1472,13 @@ approval state only): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-9 approval-state stage truthfully. - - behavioural effect: documentation only. Makes the approval-state commit - the current stage; records the content-approved candidate, the CTO - candidate-content approval and the approval-state authorization; records - the approval-state validation and no-drift proof (section 9); and reframes - statements that would otherwise describe the candidate as the commit - containing this report, recording the candidate's identities as - historical facts. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the approval-state commit + the then-current stage; recorded the content-approved candidate, the CTO + candidate-content approval and the approval-state authorization; recorded + the approval-state validation and no-drift proof (section 9); and + reframed statements that would otherwise describe the candidate as the + commit containing the report, recording the candidate's identities as + historical facts. All still-true earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the content-approved candidate through the approval-state @@ -1718,8 +1962,8 @@ Amendment-6/7 candidate changes in the historical content-approved candidate Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate, the -Amendment-8 approval-state commit, the Amendment-9 candidate and the -Amendment-9 approval-state commit: +Amendment-8 approval-state commit, the Amendment-9 candidate, the Amendment-9 +approval-state commit and the Amendment-10 candidate: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -1898,7 +2142,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-9 approval-state commit differs from the content-approved +The Amendment-10 candidate differs from the approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f` in exactly the four authorized +correction paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-10 candidate validation"). The +Amendment-9 approval-state commit differed from the content-approved candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-9 approval-state validation"). The @@ -1954,8 +2202,9 @@ comment `6022850916`), the transient-state correction stage (#958 comment `6036961945`), the factual-clarification stage (#958 comment `6041301223`), the Amendment-8 candidate stage (#958 comment `6044220221`), the Amendment-8 approval-state stage (#958 comment `6045126538`), the Amendment-9 candidate -stage (#958 comment `6046895895`) and the Amendment-9 approval-state stage -(#958 comment `6065879000`). This matrix +stage (#958 comment `6046895895`), the Amendment-9 approval-state stage +(#958 comment `6065879000`) and the Amendment-10 candidate stage (#958 +comment `6067886387`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -2234,8 +2483,8 @@ approval-state commit: tree, parent and ADR-0012, decision-register, changelog and report blobs were verified; no automated review ran on the draft candidate head) -Amendment-9 approval-state stage - done up to and including the creation of -the commit that contains this report: +Amendment-9 approval-state stage - historical, completed before the +Amendment-10 candidate: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -2248,24 +2497,57 @@ the commit that contains this report: stage-time evidence, the PR #960 review-thread state in GitHub; all matched the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, - title or description change is authorized at this stage) + title or description change was authorized at that stage) - source edit: USED (the four authorized paths, approval state only) - new file creation, file deletion/move/rename, branch creation: NOT USED - (not authorized; the same detached local worktree, now at the + (not authorized; the same detached local worktree, then at the content-approved candidate, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent approval-state commit that contains this - report) +- commit: USED (the single-parent approval-state commit + `e41f377202379238b91eb43d60f5115aa8adc20f`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `8699af5635b8f18691ceaeccd3b24707a71ef4f2` to the approval-state commit; the + remote head, tree, parent, ADR-0012, decision-register, changelog and report + blobs and the ADR approval state were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the approval-state head, and returned it to Draft before the + Amendment-10 candidate (section 2) -Authorized post-commit actions for the approval-state commit - GitHub-owned +Amendment-10 candidate stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `e41f377202379238b91eb43d60f5115aa8adc20f` and target `feature/worker`, + the approval-state head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008 + and ADR-0010 blobs, the six-path cumulative footprint, the presence of the + Amendment 10, independent-review, Ready -> Draft and authorization records + on #958 with no later control record, and, as stage-time evidence, the PR + #960 review-thread state in GitHub; all matched the authorization; current + review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 had been returned to draft by control + before this stage, so no PR-state mutation was needed; no PR-state, title + or description change is authorized at this stage) +- source edit: USED (the four authorized correction paths only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; a fresh detached local worktree + at the approval-state head, registered in the local repository and + touching no other worktree, branch or ref, was used for construction, and + the existing task branch is the fast-forward target) +- commit: USED (the single-parent candidate commit that contains this report) + +Authorized post-commit actions for the candidate commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `8699af5635b8f18691ceaeccd3b24707a71ef4f2` to the approval-state commit; + `e41f377202379238b91eb43d60f5115aa8adc20f` to the candidate commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, ADR approval - state, changed path set, cumulative footprint and PR #960 state, and that - no review thread was replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, changed path set, + cumulative footprint and PR #960 state, and that no review thread was + replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -2273,23 +2555,24 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-9 approval-state +Not authorized for the implementing agent at the Amendment-10 candidate stage, each a separate controlled lifecycle category where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): -- any edit to a path other than the four authorized paths: NOT USED (not - authorized) -- any ADR-0012 change outside the approval-state representation, including - any architecture change: NOT USED (not authorized) -- independent inspection or exact-head review: NOT USED (never on the - implementing agent's authority) -- exact-final-blob approval or confirmation: NOT USED (not authorized; never +- any edit to a path other than the four authorized correction paths: NOT USED + (not authorized) +- any ADR-0012 change outside Amendment 10 and the candidate `PROPOSED` state: + NOT USED (not authorized) +- independent exact-candidate inspection or exact-head review: NOT USED (never on the implementing agent's authority) +- CTO candidate-content approval, approval-state commit, fresh `APPROVED` + status, approver or approval date, exact-final-blob approval: NOT USED (not + authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second approval-state commit, amend, rebase, squash, force push or - history rewrite: NOT USED (not authorized) +- any second candidate commit, amend, rebase, squash, force push or history + rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -2321,18 +2604,19 @@ classifies as documentation-only, so the `classify` and `check-changelog` jobs are expected to run and pass while the build, test, lint, format, migration and Docker build/push jobs are expected to be skipped. No container-registry push and no migration execution is expected. Under Amendment 3 C2/C11, Amendments -4-5, Amendments 6-7, Amendments 8-9, the transient-state correction +4-5, Amendments 6-7, Amendments 8-10, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 -candidate and approval-state authorizations and the Amendment-9 candidate and -approval-state authorizations, configured Codex/app review is not expected -from a push to a draft PR and is not relied upon at the Amendment-6/7 -candidate, approval-state, control-record recovery, transient-state -correction, factual-clarification, Amendment-8 candidate, Amendment-8 -approval-state, Amendment-9 candidate or Amendment-9 approval-state stage; -configured Codex/app review runs on separately authorized Ready transitions, -which are GitHub-owned lifecycle events, and any automated review that -nevertheless runs on a draft head is inspected read-only. The actual job -outcomes for the approval-state commit exist only after its publication; they +candidate and approval-state authorizations, the Amendment-9 candidate and +approval-state authorizations and the Amendment-10 candidate authorization, +configured Codex/app review is not expected from a push to a draft PR and is +not relied upon at the Amendment-6/7 candidate, approval-state, +control-record recovery, transient-state correction, factual-clarification, +Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, +Amendment-9 approval-state or Amendment-10 candidate stage; configured +Codex/app review runs on separately authorized Ready transitions, which are +GitHub-owned lifecycle events, and any automated review that nevertheless +runs on a draft head is inspected read-only. The actual job outcomes for the +candidate commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -2341,8 +2625,9 @@ publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, -Amendment-9 candidate or Amendment-9 approval-state stage, followed the same -documentation-only classification, with no manual dispatch or rerun: +Amendment-9 candidate, Amendment-9 approval-state or Amendment-10 candidate +stage, followed the same documentation-only classification, with no manual +dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -2359,6 +2644,7 @@ eb3129f13a9c858b3c012ee182315801f0b8563a: 4 success (classify x3, check-changelo 42ae90a5932cf24ce90679196888cc3d83d88db8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-8 candidate) e2588f7b7e63a5c71968d87a3409f0ff48635e6d: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-8 approval-state head) 8699af5635b8f18691ceaeccd3b24707a71ef4f2: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-9 candidate) +e41f377202379238b91eb43d60f5115aa8adc20f: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-9 approval-state head) ``` Manually initiated external actions (anything the implementing agent @@ -2942,7 +3228,8 @@ permission, and the CTO approved its exact content: thread or review-comment identifier of the final-head automated review is copied, and `R-E258-01` is recorded by its control-assigned name. -Amendment-9 approval-state stage (the commit that contains this report): +Amendment-9 approval-state stage (historical approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f`): 81. The approval-state header uses the same field order as the earlier approved ADR-0012 versions (`Status`, `Date`, `Approved by`, @@ -2984,14 +3271,150 @@ Amendment-9 approval-state stage (the commit that contains this report): including the historical approval entries, is byte-identical. 87. Architecture no-drift is proven by the exact normalized comparison in section 9 ("Amendment-9 approval-state validation"), not by assertion. -88. This report does not self-pin the approval-state commit's SHA, its tree or - its own final blob; it records only non-self-referential evidence and the - approval-state ADR-0012, decision-register and changelog blobs, which do - not depend on its bytes. Because the report now lives in the - approval-state commit, statements that described the candidate as the - commit containing this report are reframed as history, and the - candidate's identities, now the approval-state parent, are recorded in - section 1 as historical facts. +88. The approval-state report did not self-pin the approval-state commit's + SHA, its tree or its own final blob; it recorded only non-self-referential + evidence and the approval-state ADR-0012, decision-register and changelog + blobs, which did not depend on its bytes. Because the report then lived in + the approval-state commit, statements that described the candidate as the + commit containing the report were reframed as history, and the + candidate's identities, the approval-state parent, were recorded in + section 1 as historical facts; section 1 now records the approval-state + commit's identities as historical facts because that head is the + Amendment-10 candidate's parent. + +Amendment-10 Route-B candidate (the commit that contains this report). Items +91, 92 and 95 record interpretations of the approved specification for +explicit control inspection: + +89. The Amendment-10 rule is stated once, inside "Revision withdrawal and + route retirement" in section 3.2, immediately after the existing + strand-prevention paragraph and before the Amendment-8 + withdrawal/selection paragraphs, as prose without a new heading, so that + the withdrawal section itself carries the normative rule and the heading + outline is unchanged; the section-3.2 lifecycle pointer paragraph, the + "In-epoch contract mismatch" section and the withdrawal composition + paragraph point to it, and invariant 43 binds it (authorization section + 4: traceable in the withdrawal section and the invariants, no validation + prose substituted for a binding invariant). +90. The decision-stability requirement (Amendment 10 K3) is stated as the + Amendment states it: a withdrawal commits only from a database-consistent + strand-safety decision that remains valid through its + linearization/commit point; evaluating the predicate inside the + withdrawal transaction is not by itself sufficient; the forbidden + scan-decide-concurrent-mutation-commit sequence is named; the two + permitted families (the same database-atomic serialization/constraint + boundary, or a database-enforced detect-and-re-evaluate/abort before + commit) are named; and the mechanism list is the Amendment's example + list, with no table, lock, isolation level, trigger or constraint + selected (authorization section 4, item 2). +91. The covered mutations (K4) are the Amendment's minimum: creation of an + unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution + options the withdrawal affects, with the explicit statement that such an + anomaly identifies the epoch's expected revision and need not select or + reference the compatible revision being withdrawn, so that the + Amendment-8 order does not by itself cover it; and any other + already-defined operation that removes the last approved + resolution/materialization path on which the decision relies. The + predicate-improving exemption and the HOLD-for-specification rule for + transitions the ADR does not already define are stated. Interpretation: + the "approved resolution or materialization path" is enumerated as the + three path kinds the existing strand-prevention paragraph already names + (a selectable compatible revision, an approved reconciliation/divergence + procedure, an approved migration/reconciliation/disposition plan); no new + path kind, revocability, compatibility or availability semantics is + introduced (authorization section 4, items 8-9). +92. Both orders (K5) are written as the Amendment defines them. Anomaly-first + includes the "strand safety, not anomaly existence" statement. + Withdrawal-first includes never-suppressed detection, evaluation of any + availability the anomaly transaction depends on from post-withdrawal + durable state, no treatment of the withdrawn revision as selectable, no + disposition/job/slot from detection, continued visibility until a + then-valid enrollment or approved reconciliation/divergence, and the + statement that this order may leave a visible unresolved anomaly without + an immediately selectable compatible revision, with no automatic success, + automatic divergence or deletion. Interpretation (independent-review + observation 1): mismatch detection is described as fact recording that + "need not calculate or persist" resolution-path availability, a + permissive statement placed in the "In-epoch contract mismatch" section; + availability-dependent decisions are taken from the durable state current + when they are made. No detection-time availability computation is + mandated or forbidden. +93. The no-write-skew rule (K6) is stated as the Amendment states it: where + the decision relies on the continued existence of one or more selectable + compatible revisions or another already-approved durable + resolution/materialization path, concurrent withdrawal or control + transactions cannot remove those paths through write skew while each + observes the other as available; when two would together remove the last + approved path, at most the safety-preserving one commits without + re-evaluation and the other detects the conflict and re-evaluates or + aborts. +94. Amendment-8 interaction (K7; independent-review observation 3): the rule + is declared a property of the strand-prevention predicate that + introduces no second withdrawal lifecycle; the withdrawal/selection order + remains the one durable order between withdrawal and new selection; the + two rules "share one coherent database ordering where practical"; the + Amendment-8 paragraphs, both of their commit orders and the existing + selection-first sentence that every strand-prevention rule remains + binding on a later withdrawal are byte-identical; and database-order + composition remains mandatory through the composition paragraph, which + now names the decision-stability rule, invariant 43 and section-11 case + 8. +95. Unchanged semantics (K8; independent-review observation 2): the mismatch + definition and recorded facts, the resolution options and append-only + resolution recording, historical-enrollment field-by-field validation, + revision immutability and equivalence, producer-version coverage, + permanent retirement, per-revision route health, withdrawal's + future-selection-only effects, the Amendment-9 coalescing boundary, + `CURRENT_STATE`/`REVISION_BOUND`, claim fencing, external-write + ambiguity/reconciliation, worker trust boundaries and the BE-04 + transition are byte-identical; the only change to the mismatch section is + the appended detection paragraph. Interpretation: no mechanism linking + mismatch detection to historical-enrollment resolution is specified; the + new text requires only that availability-dependent decisions use current + durable state, so the observation's HOLD condition (an additional + architecture decision being required) did not arise. +96. Invariant 43 is appended after invariant 42; invariants 1-42 are + byte-identical and unrenumbered (decision 23 pattern). +97. The decision-register header `Last updated: 2026-10-08` already carries + the date on which the candidate changes the ADR-0012 row, so it is + unchanged (decisions 25, 57, 78 and 85 pattern); only the ADR-0012 row + changes. +98. The nine Amendment 10 K9 validation cases are added to section 11 as nine + bullets, one-to-one with K9 items 1-9, immediately after the existing + Amendment-8 withdrawal/selection composition bullet so that they sit with + the existing withdrawal tests; every Amendment 1-9 validation requirement + is retained (independent-review observation 4). The candidate is + documentation-only: it specifies future deterministic runtime validation + and executes no SQL, migration, test harness or concurrency test. +99. The header drops `Approved by` / `Approval date` and section 12 restores + the two candidate-state paragraphs used by the earlier Route-B candidates + (decisions 8, 9, 58 and 80), adds the strand-safety rule to the + description of this material correction and binds the 2026-10-08 + approval, like the 2026-09-30, 2026-10-05, 2026-10-06 and 2026-10-07 + approvals, to the historical earlier version without embedding its + identities. The changelog gains one new entry and every historical entry, + including the record of the 2026-10-08 approval, stays byte-preserved + (decisions 26 and 59). This report does not self-pin the candidate + commit's SHA, its tree or its own final blob; because the report now lives + in the candidate commit, statements that described the approval-state + commit as the commit containing this report are reframed as history, and + that commit's identities, now the candidate parent, are recorded in + section 1 as historical facts. The exact-final-blob approval of the + approval-state ADR blob is recorded through owning issue #958, its date + and the blob and head it approved; its GitHub comment identifier is not + copied, consistent with decisions 37 and 64, no review, thread or + review-comment identifier of the final-head automated review is copied, + and `R-E41F-01` is recorded by its control-assigned name. The Amendment + 10, independent-review receipt, Ready -> Draft and candidate authorization + records are cited by their comment identifiers as specification and + authorization evidence, consistent with section 2. +100. Local isolation (independent-review observation 5): the candidate was + constructed in a fresh detached local worktree at the approval-state + head, registered in the local repository; no branch was created, checked + out or moved, and no other worktree, branch, ref, stash or uncommitted + change was read for construction or modified. The deferred strand-check + timing limitation recorded by decision 53 and section 13 is replaced by + the implemented rule. List any deviation from the specification requiring authorization: @@ -3075,8 +3498,14 @@ List any deviation from the specification requiring authorization: - Amendment-9 approval-state stage: NONE. The approval-state transformation followed its separate authorization exactly; no fifth path, new file, deletion, architecture change, second commit, amend, rebase, squash, force - push or history rewrite was needed or performed before the commit that - contains this report was created. + push or history rewrite was needed or performed before the approval-state + commit was created. +- Amendment-10 candidate stage: NONE. No fifth path, new file, deletion, ADR + change outside Amendment 10 and the candidate state, second commit, amend, + rebase, squash, force push or history rewrite was needed or performed + before the commit that contains this report was created. Decisions 91, 92 + and 95 are recorded for control inspection as interpretations within the + authorized scope, not as deviations. ## 6. Database and migration effects @@ -3086,8 +3515,9 @@ No schema, data, index, constraint or `thoth-api/src/schema.rs` effect. The activation-epoch and route-contract-revision model, including its required database-level revision-equivalence property and its required database-atomic ordering of revision withdrawal against new revision selection and of -coalescing attachment against a job's first claim/start, is architecture for a -future +coalescing attachment against a job's first claim/start and its required +database-consistent strand-safety decision for revision withdrawal, is +architecture for a future separately authorized shared-engine implementation; it creates no migration obligation by itself and names no table, column or index. @@ -3102,8 +3532,8 @@ candidate recorded here. The verified BE-04 per-consumer impact record for the ADR's later legacy-contract retirement is preserved unchanged in Appendix A; neither the activation-epoch correction, the route-contract-revision correction, the withdrawal/selection ordering -correction nor the coalescing-seal ordering correction changes a released -contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts +correction, the coalescing-seal ordering correction nor the withdrawal +strand-safety correction changes a released contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts are internal to the future shared engine; any later external read or executor contract derived from them needs its own specification, merged upstream contract and downstream task. @@ -3143,8 +3573,10 @@ approval-state validation" records the validation of the historical approval-state commit `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`. "Amendment-9 candidate validation" records the validation of the historical content-approved candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`. "Amendment-9 -approval-state validation" at the end of this section records the validation -of the commit that contains this report. +approval-state validation" records the validation of the historical +approval-state commit `e41f377202379238b91eb43d60f5115aa8adc20f`. +"Amendment-10 candidate validation" at the end of this section records the +validation of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -5522,17 +5954,22 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c ``` -The approval-state ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above; the ADR-0012 blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` is the exact -final blob presented for CTO exact-final-blob approval at this stage. This -report's own final blob, the approval-state tree and the approval-state commit -SHA depend on this report's bytes and are not self-pinned; they are verified -against the remote head after push and recorded in the post-publication -handoff. +The approval-state ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the approval-state report's bytes, so they +were recorded above; the ADR-0012 blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` +was the exact final blob presented for CTO exact-final-blob approval at that +stage and received it on 2026-10-08. The approval-state report's own final +blob, the approval-state tree and the approval-state commit SHA depended on +that report's bytes and were not self-pinned in it; they were verified against +the remote head after push and recorded in the post-publication handoff, and +section 1 now records them as historical facts: commit +`e41f377202379238b91eb43d60f5115aa8adc20f`, tree +`38f4153d961f771f177ba354a717845115b2c497`, report blob +`5c3b5a096acd2b3c43161aca03249212a9a7c453`. -Required topology of the approval-state commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the approval-state commit, checked after it was created +and before push; the published approval-state commit satisfied every +requirement: ```text git rev-list --parents -n 1 HEAD @@ -5551,6 +5988,223 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-10 candidate validation + +Environment: a fresh full local Git worktree of `thoth-pub/thoth`, detached at +the historical Amendment-9 approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f` (tree +`38f4153d961f771f177ba354a717845115b2c497`), registered in the local repository +and touching no other worktree, branch or ref. Immediately before +construction, read-only GitHub inspection showed `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`e41f377202379238b91eb43d60f5115aa8adc20f` with single parent +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`, ADR-0012 blob +`cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`, report blob +`5c3b5a096acd2b3c43161aca03249212a9a7c453`, decision-register blob +`c618d9d2eec622660ee7c1868111f747efa27629` and changelog blob +`2731521f363189063b9ab09068df7852889121a2` at that head, the protected +ADR-0008 and ADR-0010 blobs, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker`, the cumulative PR footprint exactly +the six paths, the Amendment 10, independent-review, Ready -> Draft and +authorization records present on #958 with no later control record, and PR +#960 review-thread state that matched the stage authorization (stage-time +evidence; current review-thread state remains GitHub-owned). ADR-0012, the +decision register and the changelog were edited first and their deterministic +blobs recorded; then only this report was edited; all four were staged, and +every check below ran against that exact final staged index before the +candidate commit was created from it; the report's numeric line counts below +were filled in and the complete check set rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check e41f377202379238b91eb43d60f5115aa8adc20f +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the approval-state head (exactly the four authorized +paths): + +```text +git diff --cached --name-only e41f377202379238b91eb43d60f5115aa8adc20f +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status e41f377202379238b91eb43d60f5115aa8adc20f +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat e41f377202379238b91eb43d60f5115aa8adc20f +1 0 CHANGELOG.md +935 195 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +183 23 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (only the ADR-0012 row; every other line, including +the `Last updated: 2026-10-08` header, byte-identical): + +```text +git diff --cached -U0 e41f377202379238b91eb43d60f5115aa8adc20f -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-08 + +diff <(git show e41f377202379238b91eb43d60f5115aa8adc20f:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n 20p | grep -o '^| `ADR-0012` | [^|]* | [A-Z]* |' +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 e41f377202379238b91eb43d60f5115aa8adc20f -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show e41f377202379238b91eb43d60f5115aa8adc20f:CHANGELOG.md) <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state, outline and hunks: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +2930:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'APPROVED' +every match is in section 12 (lines 2933-2990): the sentence that the version +may carry APPROVED only after CTO approval, the historical-approval paragraph +and the authority condition; no sentence asserts that this version is approved + +diff <(git show e41f377202379238b91eb43d60f5115aa8adc20f:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +exit 0; no output (no heading added or changed) + +git diff --cached -U0 e41f377202379238b91eb43d60f5115aa8adc20f -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -5,2 +4,0 @@ Date: 2026-09-29 +@@ -302 +300,3 @@ composes with this per-`route_key` lifecycle serialization: one route never has +@@ -558,0 +559,8 @@ record. Resolving an anomaly never deletes it or its detection evidence. +@@ -648,0 +657,87 @@ disposition plan closes or transfers that work safely. +@@ -693,2 +788,3 @@ historical-enrollment validation and `(event_id, route_key)` disposition +@@ -2144,0 +2241,25 @@ execution until a separate approved architecture decision says otherwise. +@@ -2157 +2278,2 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: +@@ -2370,2 +2492,4 @@ Rollout should prove in order: +@@ -2585,0 +2710,33 @@ Before shared implementation can be approved, evidence must include: +@@ -2773,4 +2930 @@ shared-engine tests. +@@ -2778,8 +2932,3 @@ Approval date: 2026-10-08 +@@ -2803 +2952,9 @@ share one database-atomic linearized order, so that no route disposition can be +@@ -2815 +2972,4 @@ between coalescing attachment and a job's first claim/start, carried + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^43\. ' +2241:43. A revision withdrawal commits only from a database-consistent + +diff <(git show e41f377202379238b91eb43d60f5115aa8adc20f:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^42\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^42\. /p') +exit 0; no output (invariants 1-42 and the section heading byte-identical up to the first line of invariant 42; the remainder of invariant 42 is inside the unchanged region between hunks) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'strand-safety' +12 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'IN_EPOCH_CONTRACT_MISMATCH' +4 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'write skew\|write-skew' +3 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'post-withdrawal' +3 +``` + +The fourteen hunks are: the header status and approval fields (two hunks); the +section-3.2 lifecycle pointer sentence; the "In-epoch contract mismatch" +detection paragraph; the decision-stability rule, covered mutations, +mechanism neutrality, both anomaly-creation/withdrawal orders, the +no-write-skew rule and the no-second-lifecycle statement inside "Revision +withdrawal and route retirement"; the withdrawal composition paragraph; +invariant 43; section 8 item 1; section 9 rollout proof 3; the nine +section-11 validation cases; and the four section-12 hunks that record the +candidate state, extend the description of this material correction and bind +the 2026-10-08 approval to the historical earlier version. No other line of +the ADR changes; in particular the Amendment-8 withdrawal/selection +paragraphs and both of their commit orders, the Amendment-9 coalescing +subsection, the mismatch definition and its recorded-facts list, the +historical-enrollment validation list and the disposition-uniqueness text of +section 3.2 are byte-identical to the approval-state head. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 +decision-register.md 73acf0e7a83116d3ffee47cd6ff40c14075e8643 +CHANGELOG.md 653cc699c972f0457bd1cdbc032374e3090396fd +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 +``` + +The candidate ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the candidate tree and the candidate +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the candidate commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: e41f377202379238b91eb43d60f5115aa8adc20f (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check e41f377202379238b91eb43d60f5115aa8adc20f HEAD +required: exit 0; no output + +git diff --name-only e41f377202379238b91eb43d60f5115aa8adc20f HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -5685,6 +6339,24 @@ decision and control blobs are unchanged; confirmed by fixed-string search that the content-approval comment identifier does not occur in this report; and read the report for statements that would become false once it moves into the approval-state commit and reframed them. +Amendment-10 candidate stage: compared the staged ADR-0012 with the +approval-state head's ADR blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c` hunk +by hunk against every requirement of Amendment 10 K3-K9 and the five +candidate-inspection observations; confirmed each is expressed in section 3.2 +(the lifecycle pointer sentence, the mismatch detection paragraph and the +decision-stability text and both orders inside "Revision withdrawal and route +retirement"), invariant 43, sections 8, 9 and 11, and the candidate state in +the header and section 12; confirmed that every changed hunk concerns +withdrawal strand-safety decision stability, its validation or the candidate +state, and that the ADR-0008/ADR-0010 partial-supersession header text, the +Amendment-8 withdrawal/selection paragraphs, the Amendment-9 coalescing +subsection, the mismatch definition, the enrollment validation list and all +other architecture are byte-identical to the approval-state head; compared +the staged register and changelog with the approval-state-head blobs (only +the ADR-0012 row, and only one added entry); confirmed the protected decision +and control blobs are unchanged; and read the report for statements that +would become false once it moves into the candidate commit and reframed them, +including the deferred strand-check timing limitation. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -5697,10 +6369,11 @@ the control-record recovery head head `9b4bb1b02204aa2267a4c0b75016fa96c5e22f4a`, the factual-clarification head `eb3129f13a9c858b3c012ee182315801f0b8563a`, the content-approved Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, the -Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d` and +Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, the content-approved Amendment-9 candidate -`8699af5635b8f18691ceaeccd3b24707a71ef4f2`, are historical evidence; the live -PR #960 diff of the approval-state commit exists only after its publication +`8699af5635b8f18691ceaeccd3b24707a71ef4f2` and the Amendment-9 approval-state +head `e41f377202379238b91eb43d60f5115aa8adc20f`, are historical evidence; the +live PR #960 diff of the candidate commit exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -5714,9 +6387,10 @@ recovery authorization, the Amendment-6/7 candidate and approval-state authorizations, the control-record recovery authorization, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate authorization, the Amendment-8 -approval-state authorization, the Amendment-9 candidate authorization and the -Amendment-9 approval-state authorization, an automatic CI failure, or any -migration execution or Docker publication, is a HOLD condition. +approval-state authorization, the Amendment-9 candidate authorization, the +Amendment-9 approval-state authorization and the Amendment-10 candidate +authorization, an automatic CI failure, or any migration execution or Docker +publication, is a HOLD condition. ## 12. Rollout and rollback @@ -5746,20 +6420,21 @@ one two-path clarification commit, the Amendment-8 candidate authorization permitted exactly one four-path candidate commit, the Amendment-8 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-9 candidate authorization permitted exactly one -four-path candidate commit, and the Amendment-9 approval-state authorization -permits exactly one direct-child approval-state commit; any further source -correction exhausts those authorizations and requires a fresh CTO -authorization (Amendment 3 C10; Amendments 6-9; +four-path candidate commit, the Amendment-9 approval-state authorization +permitted exactly one direct-child approval-state commit, and the +Amendment-10 candidate authorization permits exactly one four-path candidate +commit; any further source correction exhausts those authorizations and +requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-10; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-9 approval-state stage the +- Historical stage-time evidence: at the Amendment-10 candidate stage the implementing agent performed no independent inspection or review, - exact-final-blob approval or confirmation, PR #960 title/description - reconciliation, Draft/Ready change, automated-review request, thread - adjudication, exact-head review or merge (section 4.2). + candidate-content approval, PR #960 title/description reconciliation, + Draft/Ready change, automated-review request, thread adjudication, + exact-head review or merge (section 4.2). Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -5796,15 +6471,21 @@ Monitoring required: NONE and the coalescing-attachment/first-claim ordering defect found by the final-head review of that approval-state head only under Specification Amendment 9, its independent review and a separate authorization for one - four-path candidate commit, and its approval state only under a separate - authorization for this one direct-child approval-state commit. -- The strand-check timing question left outside this correction (section 5, - decision 53): Amendments 1-8 do not determine whether a withdrawal's - strand-prevention evaluation may be stale relative to events or anomalies - that commit concurrently with the withdrawal and are not revision - selections. The candidate neither decides it nor implies a mechanism for - it; it belongs to a later specification or to the implementation - specification. + four-path candidate commit, its approval state only under a separate + authorization for one direct-child approval-state commit, and the + withdrawal strand-safety defect found by the final-head review of that + approval-state head only under Specification Amendment 10, its independent + review and a separate authorization for this one four-path candidate + commit. +- The strand-check timing question that decision 53 left outside the + Amendment-8 correction is now determined by Specification Amendment 10 and + implemented by this candidate (section 5, decisions 89-100): a withdrawal's + strand-prevention decision must be database-consistent and stable through + its commit against concurrently committing anomalies and resolution-path + removals. The concrete lock, constraint or isolation mechanism remains an + implementation-specification detail, and whether a transition the ADR does + not already define is strand-relevant returns to specification rather than + being guessed. - Exact table, column, index and encoding names for route-contract revisions, anomalies and capability declarations are deliberately left to the future shared-engine implementation specification. @@ -5815,8 +6496,8 @@ Monitoring required: NONE approval-state commit, the recovery commit, the Amendment-6/7 candidate, its approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and - approval-state commit, the Amendment-9 candidate and this Amendment-9 - approval-state commit. + approval-state commit, the Amendment-9 candidate and approval-state commit + and this Amendment-10 candidate. ## 14. Unresolved issues @@ -5923,11 +6604,34 @@ Monitoring required: NONE `8699af5635b8f18691ceaeccd3b24707a71ef4f2` implements the correction in source (section 4; section 5, decisions 69-80); control's independent inspection of that candidate found no blocking exact-candidate finding; - Javi, CTO approved its exact content on 2026-10-08; and the approval-state - commit that contains this report records that approval. `R-E258-01` is - closed at specification level by Amendment 9; its source closure is - adjudicated by the independent exact-head review controls that precede any - merge, which GitHub records. + Javi, CTO approved its exact content on 2026-10-08; the historical + approval-state commit `e41f377202379238b91eb43d60f5115aa8adc20f` recorded that + approval; and the CTO recorded exact-final-blob approval of the resulting + ADR-0012 blob `cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c`. `R-E258-01` is + closed at specification level by Amendment 9 and, per the CTO's + Amendment-10 record, closed in the source of that approval-state head; its + thread adjudication belongs to the review controls that GitHub records. +- The configured final-head automated review of the Amendment-9 + approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` opened one further + thread, identified here substantively and by its control-assigned finding + name `R-E41F-01`: a withdrawal's strand-prevention decision could be made + from a stale view, because the ADR ordered withdrawal only against new + durable selections of or references to the withdrawn revision, while + creation of an unresolved in-epoch contract-mismatch anomaly references the + epoch's expected revision and need not select the compatible revision being + withdrawn; a withdrawal could scan, observe no anomaly, and commit after a + concurrently committed anomaly had made the withdrawal strand that anomaly. + Control accepted it as valid, HIGH and blocking and classified the + correction as a material architectural correction; the CTO approved + Specification Amendment 10; a fresh independent CRITICAL specification + review of the base specification and Amendments 1-10 returned `APPROVED`, + confirming the finding, the classification, the decision-stability rule and + the validation matrix and recording five non-blocking candidate-inspection + observations; and the Route-B candidate that contains this report + implements the correction in source as `PROPOSED` (section 4; section 5, + decisions 89-100). `R-E41F-01` is closed at specification level by + Amendment 10; whether the candidate closes it in source is decided by the + independent inspection and review of the candidate, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -5941,18 +6645,43 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-9 approval-state commit also -implemented the Amendment-9 candidate, the Amendment-8 approval-state commit -and candidate, the factual clarification, the transient-state correction, the -control-record recovery, the Amendment-6/7 approval-state commit and candidate -and earlier Route-B source stages of this task and prepared the earlier -uncommitted +work. The implementing agent of this Amendment-10 candidate also implemented +the Amendment-9 approval-state commit and candidate, the Amendment-8 +approval-state commit and candidate, the factual clarification, the +transient-state correction, the control-record recovery, the Amendment-6/7 +approval-state commit and candidate and earlier Route-B source stages of this +task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent inspector or reviewer of this task (Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Suggested review focus: +- that the candidate changes exactly the four authorized paths from the + approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`, that the + candidate ADR-0012 blob is `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`, and that + ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that every Amendment 10 K3-K9 requirement is expressed in ADR-0012: the + database-consistent strand-safety decision valid through the withdrawal's + linearization/commit point with an in-transaction scan insufficient, the + coverage of every already-defined safe-to-unsafe mutation including + anomaly creation that references only the epoch's expected revision and + last-resolution-path removal, the predicate-improving exemption and the + HOLD-for-specification rule, mechanism neutrality, both anomaly-first and + withdrawal-first orders with their fail-safe semantics, the no-write-skew + rule, composition with the Amendment-8 order as one coherent database order + without a second withdrawal lifecycle, the unchanged-semantics list, + invariant 43 and the nine section-11 cases, and that the five + independent-review observations are reflected as recorded in section 5, + decisions 92, 95, 94, 98 and 100; +- that the candidate invents no revocability, reconciliation, compatibility + or availability semantics, specifies no table, lock, isolation level, + trigger or constraint, changes no mismatch, enrollment, revision, + retirement, health, evidence, Amendment-8 or Amendment-9 semantics, and + that decisions 91, 92 and 95 decide no new architecture; +- that every ADR-0012 hunk outside those concerns is limited to the candidate + `PROPOSED` state (header and section 12) and that all other architecture is + byte-identical to the approval-state head; - that the approval-state delta from the content-approved Amendment-9 candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2` is limited to the ADR header approval fields, the two leading section-12 paragraphs, the ADR-0012 @@ -5990,9 +6719,9 @@ Suggested review focus: applied-revision, attempt-phase, claim-token, WAITING/RECONCILIATION_REQUIRED or Amendment-8 ordering semantics, and that decision 75 decides no new coalescing path; -- that every ADR-0012 hunk outside those concerns is limited to the approval - state (header and section 12) and that all other architecture is - byte-identical to the Amendment-8 approval-state head and to the +- that every Amendment-9 ADR-0012 hunk outside those concerns was limited to + the approval state (header and section 12) and that all other architecture + was byte-identical to the Amendment-8 approval-state head and to the content-approved Amendment-9 candidate; - that the approval-state delta from the content-approved Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8` is limited to the ADR @@ -6077,16 +6806,15 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `APPROVED` with Javi, CTO and - 2026-10-08, identify the approved content by candidate ADR-0012 blob - `0fc33df9aca61a2c82af452426f5574bfd3493cf`, bind the 2026-09-30, 2026-10-05, - 2026-10-06 and 2026-10-07 approvals to the earlier exact versions and keep - the repository authority condition and the ADR-0013 reliance distinction; -- that the register row, the coalescing-seal correction changelog entry and - this report describe the corrected version as `APPROVED` on 2026-10-08, - that the withdrawal-order correction entry and every other historical - changelog entry are byte-preserved, and that the register header - `Last updated` is `2026-10-08`; +- that the header and section 12 carry `PROPOSED` with no current approver or + approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 and + 2026-10-08 approvals to the earlier exact versions and keep the repository + authority condition and the ADR-0013 reliance distinction; +- that the register row, the new strand-safety correction changelog entry and + this report describe the corrected version as `PROPOSED`, that the + coalescing-seal correction entry and every other historical changelog entry + are byte-preserved, and that the register header `Last updated` is + `2026-10-08`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. @@ -6102,9 +6830,9 @@ boundary and one optional deactivation boundary per route is superseded by the append-only activation-epoch model, and the route-level event kind/version contract and "emits K" emission floor described in earlier rounds are superseded by immutable route-contract revisions and producer-version coverage -in the current section 3.2. The 2026-09-30, 2026-10-05, 2026-10-06 and -2026-10-07 approvals and every review recorded here are bound to the exact -historical versions and heads they name. +in the current section 3.2. The 2026-09-30, 2026-10-05, 2026-10-06, +2026-10-07 and 2026-10-08 approvals and every review recorded here are bound +to the exact historical versions and heads they name. ### Initial candidate and review round 1 @@ -6902,8 +7630,9 @@ stage: deduplication boundary. 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO approval of that exact corrected content; earlier approvals and reviews, - including the 2026-09-30, 2026-10-05, 2026-10-06 and 2026-10-07 - approvals, remain bound to the earlier exact versions and heads they name. + including the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 and + 2026-10-08 approvals, remain bound to the earlier exact versions and heads + they name. 11. The route-contract-revision model does not permit silent reinterpretation or loss either: every epoch is bound to one immutable revision, an event outside its epoch revision's accepted versions becomes a visible anomaly @@ -6924,5 +7653,16 @@ stage: represented by the started job, and a claim/start that linearized first refuses the later attachment with no disposition committed, the `(event_id, route_key)` slot unused and the obligation still owed. +14. The withdrawal strand-safety rule does not permit a stale strand-prevention + scan to commit a withdrawal that strands existing work: a withdrawal + commits only from a database-consistent decision valid through its + commit, every already-defined durable mutation that can make that + decision unsafe - at minimum concurrent anomaly creation and + last-resolution-path removal - shares its database-atomic boundary or + forces re-evaluation, an anomaly-first withdrawal that would strand the + anomaly is refused or held, a withdrawal-first detection still records + the anomaly durably with no disposition, job or slot and from + post-withdrawal availability, and concurrent removals of alternative + resolution paths cannot write-skew into stranded work. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index cbe8322d9..d81d56a05 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-08 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -299,7 +297,9 @@ an epoch that references it, or enrolling an event's first route disposition under it) share one database-atomic order for the same route; see "Revision withdrawal and route retirement" below. Whatever mechanism provides that order composes with this per-`route_key` lifecycle serialization: one route never has -two contradictory lifecycle orders. +two contradictory lifecycle orders. The strand-safety decision on which a +withdrawal relies is likewise database-consistent and stable through the +withdrawal's commit; see the same section. A route's lifecycle state is one of: @@ -556,6 +556,14 @@ Resolution is recorded as append-only, audited evidence linked to the anomaly, including actor, authority, reason and the resulting disposition or divergence record. Resolving an anomaly never deletes it or its detection evidence. +Detection records the facts above and raises attention; it need not calculate +or persist which resolution paths are available, and any +availability-dependent decision about the anomaly is taken from the durable +state current when that decision is made. Detection is never suppressed +because a revision has been withdrawn. The ordering of anomaly creation +against a concurrent revision withdrawal is defined in "Revision withdrawal +and route retirement" below. + Route health exposes open anomalies separately from, and alongside, the undispositioned eligible-event backlog. An in-epoch contract mismatch is therefore never an invisible fourth eligibility state. @@ -646,6 +654,93 @@ selectable compatible revision or an approved reconciliation/divergence procedure), unless an explicitly approved migration, reconciliation or disposition plan closes or transfers that work safely. +That strand-prevention decision must be **database-consistent and stable +through the withdrawal's linearization/commit point**: a withdrawal may commit +only from a strand-safety evaluation that is still true of durable state when +the withdrawal commits. Evaluating the predicate inside the withdrawal's own +transaction is not by itself sufficient; a scan that decided "safe to +withdraw", followed by a concurrent commit that changes the durable facts the +decision relied on, followed by the withdrawal's commit, does not satisfy this +rule. Every already-defined durable mutation that can change the +strand-prevention predicate for a proposed withdrawal from "safe to withdraw" +to "withdrawal would strand existing work" must therefore either participate +in the same database-atomic serialization or constraint boundary as the +withdrawal, or cause the withdrawal transaction to detect the concurrent +change and re-evaluate or abort before it commits, through a database-enforced +mechanism with equivalent effect. The covered mutations include, at minimum: + +- durable creation of a new unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly + whose approved resolution options the proposed withdrawal affects. Such an + anomaly identifies the epoch's expected revision; it need not select or + reference the separate compatible revision whose availability for historical + enrollment makes it resolvable, so the withdrawal/selection order below does + not by itself order it against the withdrawal, and this rule does; +- any other already-defined durable operation that removes the last approved + resolution or materialization path - a selectable compatible revision, an + explicitly approved reconciliation/divergence procedure or an explicitly + approved migration, reconciliation or disposition plan - on which that + withdrawal's strand-safety decision relies. + +A durable change that can only make the predicate safer, such as completing +or dispositioning existing work or adding a resolution path, need not be +forced into that boundary for strand prevention, although an implementation +may serialize it there to keep one coherent lock order. The mechanism is an +implementation-specification detail: the per-route serialization boundary +below extended to withdrawal, a row or advisory lock, a serializable or +predicate-conflict mechanism, a commit-time generation or version check, or +another database-enforced compare-and-recheck scheme with equivalent semantics +all qualify; an application-level "scan, decide safe, commit" sequence without +such enforcement does not. Whether a state transition that this ADR does not +already define is strand-relevant is an architecture decision; implementation +does not guess it and returns it for specification. + +Both orders of anomaly creation against withdrawal are defined: + +- anomaly first: when creation of the unresolved mismatch anomaly linearizes + before the withdrawal, the withdrawal's strand-safety decision includes + that anomaly; the withdrawal is refused or held if, after it, the anomaly + would have no selectable compatible revision and no approved + reconciliation/divergence path; the anomaly remains durable, visible and + attention-raising; detection creates no route disposition and no job; and + the `(event_id, route_key)` slot remains unused. A withdrawal transaction + whose scan preceded the anomaly's creation may not commit from that stale + scan. Where another valid approved resolution path remains after the + withdrawal, the anomaly's existence alone does not refuse it: the rule is + strand safety, not anomaly existence; +- withdrawal first: when the withdrawal linearizes before the mismatch anomaly + exists, the later anomaly is still recorded durably under the fail-safe + mismatch semantics above; detection is never suppressed because a revision + has already been withdrawn; the anomaly transaction evaluates any + resolution availability it depends on from post-withdrawal durable state + and never treats the withdrawn revision as still selectable; no route + disposition or job is created and the `(event_id, route_key)` slot remains + unused merely because the anomaly was detected; and the anomaly remains + visible and attention-raising until a then-valid compatible historical + enrollment or an explicitly approved reconciliation/divergence procedure + resolves it. This order may leave a visible unresolved anomaly with no + immediately selectable compatible revision; the engine does not resolve it + automatically, does not record automatic divergence and does not delete it. + +Where the strand-safety decision relies on the continued existence of one or +more selectable compatible revisions or another already-approved durable +resolution or materialization path, concurrent withdrawal or control +transactions must not be able to remove those paths through write skew while +each independently observes the other path as still available. When two such +transactions would together remove the last approved path for an existing +unresolved anomaly or outstanding obligation, at most the safety-preserving +one may commit without re-evaluation; the other detects the conflict and +re-evaluates or aborts under the same database-enforced rule. + +This decision-stability rule is a property of the strand-prevention predicate +and introduces no second withdrawal lifecycle: withdrawal remains append-only, +audited and future-selection-only, the withdrawal/selection order below +remains the one durable order between withdrawal and new selection, and the +two rules share one coherent database ordering where practical. The rule +closes the stale concurrent-withdrawal decision race only; it does not claim +that a producer-contract violation occurring strictly after a completed +withdrawal can never create a new unresolved anomaly, and such an anomaly is +handled under the withdrawal-first order above. + Withdrawal shares one database-atomic serialization or constraint boundary with every operation that creates a new durable selection of, or reference to, the revision, so that for any revision, withdrawal and new future selection have one @@ -690,8 +785,9 @@ exclusion constraint is an implementation-specification detail. Whichever mechanism is selected must compose with the serialized open, close, reactivate and retire lifecycle, with database-atomic equivalent-revision convergence, with historical-enrollment validation and `(event_id, route_key)` disposition -uniqueness, with the strand-prevention rule above, with permanent route -retirement and with the engine's normal deadlock and lock-ordering controls. No +uniqueness, with the strand-prevention rule above and its decision-stability +rule, with permanent route retirement and with the engine's normal deadlock +and lock-ordering controls. No implementation may introduce a second route lifecycle order that contradicts the per-`route_key` order. @@ -2142,6 +2238,31 @@ execution until a separate approved architecture decision says otherwise. commits an attachment after sealing; an application-level pre-check does not satisfy this; a rolled-back first claim does not seal. The seal is earlier than and distinct from `EFFECT_STARTED`. +43. A revision withdrawal commits only from a database-consistent + strand-safety decision that remains valid through the withdrawal's + linearization/commit point. Every already-defined durable mutation that + can change that strand-prevention predicate from safe to unsafe - at + minimum, creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly + whose approved resolution options the withdrawal affects, even where the + anomaly references only the epoch's expected revision and not the + compatible revision being withdrawn, and removal of the last approved + resolution or materialization path on which the decision relies - shares + one database-atomic serialization or constraint boundary with the + withdrawal or forces the withdrawal to detect the change and re-evaluate + or abort before commit; a stale application-level scan, even inside the + withdrawal transaction, does not satisfy this. Anomaly-first, the + withdrawal includes the anomaly and is refused or held where it would + leave no selectable compatible revision and no approved + reconciliation/divergence path; withdrawal-first, mismatch detection is + never suppressed, records the anomaly durably and visibly, evaluates + availability from post-withdrawal state, never treats the withdrawn + revision as selectable and creates no disposition, job or route slot. + Concurrent removals of alternative resolution paths cannot write-skew into + stranded work. The rule composes with invariant 41's withdrawal/selection + order as one coherent database order, introduces no second withdrawal + lifecycle, invents no new revocability, reconciliation, compatibility or + availability semantics, and leaves the mechanism to the implementation + specification. ## 8. Implementation impact and decomposition @@ -2154,7 +2275,8 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: including producer-version coverage evidence, in-epoch contract-mismatch anomalies, revision-validated historical enrollment, atomic revision equivalence, database-atomic ordering of revision withdrawal against new - revision selection and deterministic materializer compatibility; + revision selection, database-consistent strand-safety decisions for + revision withdrawal and deterministic materializer compatibility; - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - effect-scoped idempotency and explicit coalescing, with the @@ -2367,8 +2489,10 @@ Rollout should prove in order: 2. mixed-version route completeness and out-of-order transaction commits; 3. activation, deactivation, reactivation-epoch, route-contract-revision transition, retirement, event-emission-floor and producer-version-coverage - behaviour, including in-epoch contract-mismatch detection and the ordering - of revision withdrawal against epoch opening and first enrollment; + behaviour, including in-epoch contract-mismatch detection, the ordering + of revision withdrawal against epoch opening and first enrollment, and the + stability of withdrawal's strand-safety decision against concurrent + anomaly creation and resolution-path removal; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing, including the ordering of coalescing attachment against first claim/start; @@ -2583,6 +2707,39 @@ Before shared implementation can be approved, evidence must include: shares that mechanism, with enrollment validation and disposition uniqueness and with permanent route retirement, without deadlock and without a split or contradictory lifecycle order; +- deterministic concurrency tests of unresolved in-epoch contract-mismatch + anomaly creation racing withdrawal of the compatible revision on which that + anomaly's approved resolution depends, in the anomaly-first order, with the + anomaly referencing only the epoch's expected revision; +- the same anomaly-creation/withdrawal race in the withdrawal-first order; +- proof that a withdrawal transaction whose strand-safety scan ran before the + anomaly's creation cannot commit from that stale view after the anomaly + linearizes, including when the scan ran inside the withdrawal transaction; +- proof that, anomaly-first, a withdrawal that would leave the anomaly with + no selectable compatible revision and no approved reconciliation/divergence + path is refused or held, with the anomaly still durable, visible and + attention-raising and no disposition, job or `(event_id, route_key)` slot + created; +- proof that, anomaly-first, a withdrawal may be permitted where another + valid approved resolution path remains for the anomaly, showing that the + rule is strand safety rather than anomaly existence alone; +- proof that, withdrawal-first, mismatch detection still records the anomaly + durably, creates no route disposition or job, consumes no + `(event_id, route_key)` slot, and evaluates any resolution availability it + depends on from post-withdrawal durable state rather than from a stale + pre-withdrawal observation, never treating the withdrawn revision as + selectable; +- proof that concurrent withdrawals or removals of alternative resolution + paths cannot write-skew into a state in which an existing unresolved + anomaly or outstanding obligation has no approved path while each + transaction independently passed the strand check; +- proof that the selected strand-safety boundary composes with the + withdrawal/selection order above, the per-`route_key` lifecycle + serialization, equivalent-revision convergence, enrollment validation, + `(event_id, route_key)` disposition uniqueness and permanent route + retirement without deadlock and without a split or contradictory order; +- proof that existing mismatch/anomaly health, count, age and attention + evidence remains visible through both anomaly-creation/withdrawal orders; - a backward-compatible materializer handling an older revision only where explicitly declared, and a forward-compatible materializer handling a newer revision only where explicitly declared and semantically valid; @@ -2770,19 +2927,11 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-08 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-08 under the -repository decision process, as the architecture content represented by the -Route-B candidate ADR-0012 blob `0fc33df9aca61a2c82af452426f5574bfd3493cf`; -this version differs from that candidate only in this approval-state -representation. That approval does not by itself make this ADR -repository-authoritative: the authority condition below still applies. Any -ADR-0013 programme-local reliance on this exact version is a separate state -that is not effective until its own conditions are satisfied for this version. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the @@ -2800,7 +2949,15 @@ never commit a new selection of a withdrawn revision. It further defines the coalescing-open/coalescing-sealed boundary of a logical job in section 3.5 and requires a coalescing attachment and the job's first successful claim/start to share one database-atomic linearized order, so that no route disposition can be -attached from a stale observation to a job that has already started. +attached from a stale observation to a job that has already started. It +further requires a revision withdrawal to commit only from a +database-consistent strand-safety decision that remains valid through the +withdrawal's commit, with every already-defined durable mutation that can make that decision +unsafe - at minimum concurrent creation of an unresolved in-epoch +contract-mismatch anomaly and removal of the last approved resolution path - +sharing one database-atomic boundary with the withdrawal or forcing its +re-evaluation, so that a stale strand-prevention scan can never commit a +withdrawal that strands existing work. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO approval dated 2026-09-30. A later corrected version, which introduced the @@ -2812,7 +2969,10 @@ carried `Status: APPROVED` with CTO approval dated 2026-10-06, retained through a factual clarification of that version. A further corrected version, which introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, carried -`Status: APPROVED` with CTO approval dated 2026-10-07. Those approvals, and +`Status: APPROVED` with CTO approval dated 2026-10-07. A further corrected +version, which introduced that coalescing boundary without a +database-consistent strand-safety decision for revision withdrawal, carried +`Status: APPROVED` with CTO approval dated 2026-10-08. Those approvals, and every review of those earlier versions, remain historical evidence bound to those exact earlier versions and source heads only; they do not approve this version. The exact historical identities are recorded in the task diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index c618d9d2e..73acf0e7a 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-08 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-08 under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, and the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, and the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection still records the anomaly durably with no disposition, job or slot and from post-withdrawal availability, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From e0fa590e06a00d08df883bc4ddeae40b7660fcb8 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 8 Oct 2026 22:30:23 +0100 Subject: [PATCH 39/48] THOTH-ASYNC-01-ADR-01: align withdrawal-first availability wording (#958) --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 808 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 9 +- .../decisions/decision-register.md | 2 +- 4 files changed, 664 insertions(+), 157 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 653cc699c..e78f8e3e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed, records the anomaly durably and visibly, evaluates availability from post-withdrawal state, never treats the withdrawn revision as selectable and creates no disposition, job or route slot); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index f4c0bd0ae..17773fd66 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -21,14 +21,17 @@ ordering correction specified by Specification Amendment 9 after the configured final-head automated review of that Amendment-8 approval-state head, and the Route-B candidate stage of the withdrawal strand-safety decision-stability correction specified by Specification Amendment 10 after the configured -final-head automated review of that Amendment-9 approval-state head. It records +final-head automated review of that Amendment-9 approval-state head, and the +four-path candidate-consistency correction of that candidate after its +independent exact-candidate inspection. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-10 Route-B candidate as the -current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-10 Route-B replacement +candidate (the `F-A10-CAND-01` consistency correction) as the current stage +and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -50,11 +53,14 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-10 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the -historical Amendment-9 approval-state head -`e41f377202379238b91eb43d60f5115aa8adc20f`; its SHA is deliberately not -self-pinned here (section 3). That approval-state head is the direct child of +Head commit: the Amendment-10 Route-B replacement candidate commit that +contains this report, an ordinary single-parent additive commit whose direct +parent is the historical first Amendment-10 candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08`, inspected `CHANGES REQUIRED / +F-A10-CAND-01`; its SHA is deliberately not self-pinned here (section 3). That +first candidate is the direct child of the historical Amendment-9 +approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f`, which is the direct child of the content-approved Amendment-9 candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, which is the direct child of the historical Amendment-8 approval-state head @@ -91,8 +97,9 @@ Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, -Amendment-9 approval-state and Amendment-10 candidate stages - Claude (Fable -5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. +Amendment-9 approval-state, Amendment-10 candidate and Amendment-10 +consistency-correction stages - Claude (Fable 5.1), bounded implementing +agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -228,16 +235,24 @@ decision-register blob c618d9d2eec622660ee7c1868111f747efa27629 CHANGELOG blob 2731521f363189063b9ab09068df7852889121a2 implementation-report blob 5c3b5a096acd2b3c43161aca03249212a9a7c453 (superseded by the Amendment-10 candidate) -Amendment-10 Route-B candidate (the commit that contains this report): -ADR-0012 blob d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 (PROPOSED; material architectural correction of cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c) +historical first Amendment-10 candidate / consistency-correction parent: +6e521f00b039769b28a26ab069c7e97d22de4e08 +tree 7523efb689fc875b92e9e3fec2d6305277e1f940 +ADR-0012 blob d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 (PROPOSED; material architectural correction of cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c; independent exact-candidate inspection CHANGES REQUIRED / F-A10-CAND-01; never content-approved) decision-register blob 73acf0e7a83116d3ffee47cd6ff40c14075e8643 CHANGELOG blob 653cc699c972f0457bd1cdbc032374e3090396fd +implementation-report blob f4c0bd0ae0b2bfd091d5602463ec4de24140bfb4 (superseded by the consistency correction) + +Amendment-10 Route-B replacement candidate (the commit that contains this report): +ADR-0012 blob b41e674dc5e3f1ba03715538ff2396e4d2f50c6c (PROPOSED; F-A10-CAND-01 consistency correction of d81d56a05bf65a28ea54d07dee58b0e75ef0aff6; invariant-43 withdrawal-first clause only) +decision-register blob 53a1a5fa5a75c15b5d24c71849988bdadbdb57e9 +CHANGELOG blob e78f8e3e94965e1381cddd941b015017e5f90c10 feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the -Amendment-9 candidate and approval-state commit and the Amendment-10 candidate -(unchanged): +Amendment-9 candidate and approval-state commit, the Amendment-10 candidate +and its consistency correction (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -350,7 +365,11 @@ Approved specification: - PR #960 Ready -> Draft authorization for Amendment-10 Route-B preparation, a control action that preceded this stage: #958 comment `6067780272`; - Amendment-10 Route-B candidate implementation authorization: #958 comment - `6067886387`. + `6067886387` (consumed by the first candidate); +- independent exact-candidate inspection of the first Amendment-10 candidate, + `CHANGES REQUIRED / F-A10-CAND-01`: #958 comment `6069039607`; +- F-A10-CAND-01 consistency-correction authorization: #958 comment + `6069074613`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -396,12 +415,19 @@ register row and the coalescing-seal correction changelog entry moved to the approved state), and the CTO recorded exact-final-blob approval of that ADR blob. The configured final-head automated review of that approval-state head exposed a further architecture defect, `R-E41F-01`, specified by -Specification Amendment 10. The commit that contains this report is its -Route-B candidate: one ordinary single-parent commit on the approval-state -head that changes exactly the four authorized correction paths, returns -ADR-0012 to `Status: PROPOSED` and requires a revision withdrawal to commit -only from a database-consistent strand-safety decision that remains valid -through the withdrawal's linearization/commit point. +Specification Amendment 10. Its first `PROPOSED` Route-B candidate, one +ordinary single-parent commit on the approval-state head that changed exactly +the four authorized correction paths, returned ADR-0012 to +`Status: PROPOSED` and requires a revision withdrawal to commit only from a +database-consistent strand-safety decision that remains valid through the +withdrawal's linearization/commit point, was independently inspected and +returned `CHANGES REQUIRED` on one candidate-content inconsistency, +`F-A10-CAND-01`. The commit that contains this report is the authorized +replacement candidate: one ordinary single-parent commit on that first +candidate that changes exactly the same four paths and aligns the +withdrawal-first availability wording of invariant 43 and its companion +summaries with the fact-only mismatch-detection rule. The Amendment-10 +architecture is otherwise unchanged and the version remains `PROPOSED`. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -724,7 +750,8 @@ through the withdrawal's linearization/commit point. stage authorized no PR-state or PR-metadata change. - **Exact-final-blob approval, final-head review of the Amendment-9 approval-state head, Specification Amendment 10 and the Amendment-10 Route-B - candidate (the commit that contains this report).** Authorized by #958 + candidate (historical first candidate + `6e521f00b039769b28a26ab069c7e97d22de4e08`).** Authorized by #958 comment `6067886387` after the CTO approved Specification Amendment 10 (#958 comment `6066621583`) and a fresh independent CRITICAL specification review of the base specification and Amendments 1-10 returned `APPROVED` (#958 @@ -760,16 +787,46 @@ through the withdrawal's linearization/commit point. the implementation specification. The independent review confirmed the finding, the classification, the decision-stability rule and the sufficiency of the nine-case matrix, and recorded five non-blocking - candidate-inspection observations. The commit that contains this report is - one ordinary single-parent commit directly on the approval-state head that - changes exactly the four authorized correction paths: ADR-0012 returns to + candidate-inspection observations. The first candidate was one ordinary + single-parent commit directly on the approval-state head that changed + exactly the four authorized correction paths: ADR-0012 returned to `Status: PROPOSED` with no current approver or approval date and implements Amendment 10 (section 4; section 5, decisions 89-100); the decision-register - ADR-0012 row, one new changelog entry and this report describe the - corrected version as `PROPOSED`; and the 2026-09-30, 2026-10-05, - 2026-10-06, 2026-10-07 and 2026-10-08 CTO approvals remain historical - evidence bound to the earlier exact versions. This stage authorizes no - PR-state or PR-metadata change. + ADR-0012 row, one new changelog entry and the candidate-stage report + described the corrected version as `PROPOSED`; and the 2026-09-30, + 2026-10-05, 2026-10-06, 2026-10-07 and 2026-10-08 CTO approvals remain + historical evidence bound to the earlier exact versions. That stage + authorized no PR-state or PR-metadata change. +- **Independent exact-candidate inspection and the F-A10-CAND-01 + consistency correction (the commit that contains this report).** + Authorized by #958 comment `6069074613` after control independently + inspected the actual committed source of the first candidate and returned + `CHANGES REQUIRED` (#958 comment `6069039607`) with one HIGH blocking + candidate-content inconsistency, `F-A10-CAND-01`: invariant 43 said, + unconditionally, that withdrawal-first mismatch detection "evaluates + availability from post-withdrawal state", which read in isolation imposes + a detection-time resolution-path computation, while the section-3.2 + mismatch paragraph makes detection fact-only (it need not calculate or + persist resolution-path availability) and the section-3.2 withdrawal-first + paragraph restricts post-withdrawal evaluation to any availability the + anomaly transaction depends on; the strand-safety changelog entry, the + register row and Appendix B item 14 repeated the same shorthand. The + inspection confirmed everything else about the first candidate: the + single-parent topology, the four-path delta, the six-path footprint, the + protected blobs, the `PROPOSED` state, invariants 1-42 preserved, + invariant 43 and the nine validation cases present, and the + strand-predicate rule, no-write-skew rule, both orders and Amendment-8/9 + composition present without an invented mechanism. The correction is a + candidate-consistency correction inside Amendment 10, not a new + architecture decision and not a factual clarification of an approved + version. The commit that contains this report is one ordinary + single-parent commit directly on the first candidate that changes exactly + the same four paths: in ADR-0012 only the withdrawal-first clause of + invariant 43 (section 4; section 5, decisions 101-104); the ADR-0012 + register row; the existing strand-safety changelog entry; and this report. + ADR-0012 remains `PROPOSED`; the first candidate is historical + `CHANGES REQUIRED` evidence and was never content-approved. This stage + authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -783,8 +840,14 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +F-A10-CAND-01 consistency correction of first-candidate blob d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 +(invariant-43 withdrawal-first availability clause only; Specification Amendment 10; +Route-B replacement-candidate stage; candidate status PROPOSED) + +historical first Amendment-10 candidate at 6e521f00b039769b28a26ab069c7e97d22de4e08: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c -(Specification Amendment 10; Route-B candidate stage; candidate status PROPOSED) +(Specification Amendment 10; Route-B candidate stage; candidate status PROPOSED; +independent exact-candidate inspection CHANGES REQUIRED / F-A10-CAND-01; never content-approved) historical Amendment-9 approval-state at e41f377202379238b91eb43d60f5115aa8adc20f: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-08) @@ -993,31 +1056,56 @@ and observation are distinct controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for which of them is pending, satisfied or blocked for any head. +The first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` +received independent exact-candidate inspection, which returned +`CHANGES REQUIRED / F-A10-CAND-01`; it was never content-approved. The +consistency correction creates a new exact ADR-0012 version, so that +inspection outcome is bound to the first candidate's ADR blob +`d81d56a05bf65a28ea54d07dee58b0e75ef0aff6` and head, and the replacement +candidate requires its own fresh independent exact-candidate inspection +before any content approval. + ## 3. Commits -Amendment-10 Route-B candidate commit (the commit that contains this report): +Amendment-10 Route-B replacement candidate commit, the F-A10-CAND-01 +consistency correction (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose Amendment-10 strand-safety correction (#958)` - - direct parent: the historical Amendment-9 approval-state head - `e41f377202379238b91eb43d60f5115aa8adc20f` - - changes only the four authorized correction paths (section 4); the - candidate ADR-0012 blob is `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`, the - candidate decision-register blob `73acf0e7a83116d3ffee47cd6ff40c14075e8643` - and the candidate changelog blob `653cc699c972f0457bd1cdbc032374e3090396fd` + `THOTH-ASYNC-01-ADR-01: align withdrawal-first availability wording (#958)` + - direct parent: the historical first Amendment-10 candidate + `6e521f00b039769b28a26ab069c7e97d22de4e08` + - changes only the four authorized paths (section 4); the corrected + ADR-0012 blob is `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, the corrected + decision-register blob `53a1a5fa5a75c15b5d24c71849988bdadbdb57e9` and the + corrected changelog blob `e78f8e3e94965e1381cddd941b015017e5f90c10` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the candidate ADR-0012, +here because this report is part of that tree; the corrected ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its approval-state-head blob (section -9). The commit identities are verified against the remote task-branch head -after push and recorded in the post-publication handoff; once published, the -PR #960 head and head tree are GitHub-owned lifecycle evidence under -`ADR-0005`. The authorized topology is a non-force fast-forward of the task -branch from the approval-state head to the candidate commit; no merge commit, -rebase, squash, amend, force update or second candidate commit is authorized, -and no further source commit exists implicitly after it. +recorded above; every other file keeps its first-candidate blob (section 9). +The commit identities are verified against the remote task-branch head after +push and recorded in the post-publication handoff; once published, the PR +#960 head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. +The authorized topology is a non-force fast-forward of the task branch from +the first candidate to the correction commit; no merge commit, rebase, +squash, amend, force update or second correction commit is authorized, and no +further source commit exists implicitly after it. + +Amendment-10 Route-B first candidate commit (historical; inspected +`CHANGES REQUIRED / F-A10-CAND-01`; never content-approved): + +- `6e521f00b039769b28a26ab069c7e97d22de4e08` - + `THOTH-ASYNC-01-ADR-01: propose Amendment-10 strand-safety correction (#958)` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-9 approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` + - tree `7523efb689fc875b92e9e3fec2d6305277e1f940` + - changed only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6` + - published by a non-force fast-forward of `feature/async/adr-0012` from the + approval-state head; the remote head, tree, parent and ADR-0012, + decision-register, changelog and report blobs matched the locally + validated object Amendment-9 Route-B approval-state commit (historical): @@ -1216,11 +1304,23 @@ first-parent order, newest first: ## 4. Files changed -Amendment-10 Route-B candidate write budget for the commit that contains this -report (authorization #958 comment `6067886387`; the Amendment 6 G10 / -Amendment 7 H12 budget, unchanged by Amendments 8-10); exactly four existing +F-A10-CAND-01 consistency-correction write budget for the commit that +contains this report (authorization #958 comment `6069074613`; the same four +existing paths as the Amendment-10 candidate budget); exactly four existing paths, no new file, no deletion, and every other path byte-identical to the -historical Amendment-9 approval-state head +historical first Amendment-10 candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-10 Route-B candidate write budget (authorization #958 +comment `6067886387`; the Amendment 6 G10 / Amendment 7 H12 budget, unchanged +by Amendments 8-10); exactly four existing paths, no new file, no deletion, +and every other path byte-identical to the historical Amendment-9 +approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`: - `CHANGELOG.md` @@ -1314,12 +1414,77 @@ Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 -approval-state and Amendment-10 candidate stages; all four correction paths -already existed. +approval-state, Amendment-10 candidate and Amendment-10 consistency-correction +stages; all four correction paths already existed. -Amendment-10 candidate changes in the commit that contains this report, -relative to the approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` -(exactly the four authorized paths): +Consistency-correction changes in the commit that contains this report, +relative to the first Amendment-10 candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08` (exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: correct `F-A10-CAND-01`, the unconditional withdrawal-first + availability-evaluation clause of invariant 43. + - behavioural effect: architecture proposal only; one hunk. Invariant 43's + withdrawal-first clause now states that mismatch detection is never + suppressed and always records the anomaly durably and visibly, creating + no disposition, job or route slot and without being conditioned on + computing or persisting resolution-path availability, and that any + availability-dependent computation or decision, if performed during or + after detection, uses current post-withdrawal durable state and never + treats the withdrawn revision as selectable. Every other line of + ADR-0012 - the header and section 12 (`PROPOSED`, no current approver or + approval date), the section-3.2 mismatch and withdrawal paragraphs, + invariants 1-42, the rest of invariant 43, the nine Amendment-10 + validation cases and all earlier validation - is byte-identical to the + first candidate (section 9). Corrected blob: + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the ADR-0012 row repeated the same unconditional shorthand. + - behavioural effect: in the ADR-0012 row only, "a withdrawal-first + detection still records the anomaly durably with no disposition, job or + slot and from post-withdrawal availability" becomes the conditional rule + above; `PROPOSED`, `Pending`, every historical approval binding, the + partial-supersession, authority-condition, ADR-0013 and + implementation-authorization sentences and every other row are + unchanged, and the header `Last updated: 2026-10-08` is unchanged. + Corrected blob: `53a1a5fa5a75c15b5d24c71849988bdadbdb57e9`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: the existing strand-safety entry repeated the same shorthand. + - behavioural effect: only the existing + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry changes, replacing + "records the anomaly durably and visibly, evaluates availability from + post-withdrawal state, never treats the withdrawn revision as selectable + and creates no disposition, job or route slot" with the conditional + rule; no entry is added and every other entry and heading is + byte-preserved. Corrected blob: `e78f8e3e94965e1381cddd941b015017e5f90c10`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: correct the same shorthand in Appendix B item 14 and record the + consistency-correction stage truthfully. + - behavioural effect: documentation only. Appendix B item 14 carries the + conditional rule; the first candidate becomes historical + `CHANGES REQUIRED / F-A10-CAND-01` evidence with its now-known SHA, tree + and report blob recorded in section 1; the correction becomes the + current stage with the first candidate as its direct parent; the + inspection and authorization records are added to section 2; the + correction's validation is recorded in section 9 (section 5, decisions + 101-104). Decision 92 and the first candidate's section-4 description, + which already carried the qualified rule, are unchanged apart from + lineage reframing. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the first candidate through the consistency correction: +every path other than the four authorized paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical Amendment-10 candidate changes in the first candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08`, relative to the approval-state head +`e41f377202379238b91eb43d60f5115aa8adc20f` (exactly the four authorized +paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement Specification Amendment 10 and record the corrected @@ -1401,16 +1566,16 @@ relative to the approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-10 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate the current - stage; records the exact-final-blob approval of the Amendment-9 - approval-state blob, the final-head review of that head, `R-E41F-01`, - Amendment 10, its independent review, the Ready -> Draft control action - and the candidate authorization as governing evidence; records the - candidate's validation (section 9); replaces the deferred strand-check - timing limitation with the implemented rule; and reframes statements that - would otherwise describe the approval-state commit as the commit - containing this report, recording that commit's identities as historical - facts. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the candidate the + then-current stage; recorded the exact-final-blob approval of the + Amendment-9 approval-state blob, the final-head review of that head, + `R-E41F-01`, Amendment 10, its independent review, the Ready -> Draft + control action and the candidate authorization as governing evidence; + recorded the candidate's validation (section 9); replaced the deferred + strand-check timing limitation with the implemented rule; and reframed + statements that would otherwise describe the approval-state commit as + the commit containing the report, recording that commit's identities as + historical facts. All still-true earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the approval-state head through the Amendment-10 candidate: @@ -1963,7 +2128,8 @@ Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate, the Amendment-8 approval-state commit, the Amendment-9 candidate, the Amendment-9 -approval-state commit and the Amendment-10 candidate: +approval-state commit, the Amendment-10 candidate and its consistency +correction: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -2142,7 +2308,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-10 candidate differs from the approval-state head +The consistency correction differs from the first Amendment-10 candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08` in exactly the four authorized paths +and from refreshed `feature/worker` in exactly the six cumulative PR paths +(section 9, "Consistency-correction validation"). The Amendment-10 candidate +differed from the approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-10 candidate validation"). The @@ -2203,8 +2373,9 @@ comment `6022850916`), the transient-state correction stage (#958 comment the Amendment-8 candidate stage (#958 comment `6044220221`), the Amendment-8 approval-state stage (#958 comment `6045126538`), the Amendment-9 candidate stage (#958 comment `6046895895`), the Amendment-9 approval-state stage -(#958 comment `6065879000`) and the Amendment-10 candidate stage (#958 -comment `6067886387`). This matrix +(#958 comment `6065879000`), the Amendment-10 candidate stage (#958 comment +`6067886387`) and the F-A10-CAND-01 consistency-correction stage (#958 +comment `6069074613`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -2514,8 +2685,8 @@ Amendment-10 candidate: review of the approval-state head, and returned it to Draft before the Amendment-10 candidate (section 2) -Amendment-10 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-10 candidate stage - historical, completed before the consistency +correction: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -2537,13 +2708,47 @@ commit that contains this report: at the approval-state head, registered in the local repository and touching no other worktree, branch or ref, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent candidate commit that contains this report) +- commit: USED (the single-parent candidate commit + `6e521f00b039769b28a26ab069c7e97d22de4e08`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `e41f377202379238b91eb43d60f5115aa8adc20f` to the candidate; the remote head, + tree, parent and ADR-0012, decision-register, changelog and report blobs + were verified; no automated review ran on the draft candidate head) +- independent exact-candidate inspection: NOT USED by the implementing agent; + control inspected the published candidate and returned + `CHANGES REQUIRED / F-A10-CAND-01` (section 2) + +F-A10-CAND-01 consistency-correction stage - done up to and including the +creation of the commit that contains this report: -Authorized post-commit actions for the candidate commit - GitHub-owned +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `6e521f00b039769b28a26ab069c7e97d22de4e08` and target `feature/worker`, + the first candidate's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008 + and ADR-0010 blobs, the six-path cumulative footprint, the presence of the + inspection and authorization records on #958 with no later control + record, and, as stage-time evidence, the PR #960 review-thread state in + GitHub; all matched the authorization; current review-thread state remains + GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths only; in ADR-0012 only the + withdrawal-first clause of invariant 43) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; the same detached local + worktree, verified clean and exact at the first candidate and touching no + other worktree, branch or ref, was reused for construction, and the + existing task branch is the fast-forward target) +- commit: USED (the single-parent correction commit that contains this + report) + +Authorized post-commit actions for the correction commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `e41f377202379238b91eb43d60f5115aa8adc20f` to the candidate commit; + `6e521f00b039769b28a26ab069c7e97d22de4e08` to the correction commit; - post-push read-only verification of the remote head, tree, direct parent, ADR-0012, decision-register, changelog and report blobs, changed path set, cumulative footprint and PR #960 state, and that no review thread was @@ -2555,15 +2760,15 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-10 candidate -stage, each a separate controlled lifecycle category where applicable -(historical stage-time evidence; GitHub, through #958 and PR #960, is -authoritative for the current state of each): +Not authorized for the implementing agent at the F-A10-CAND-01 +consistency-correction stage, each a separate controlled lifecycle category +where applicable (historical stage-time evidence; GitHub, through #958 and PR +#960, is authoritative for the current state of each): -- any edit to a path other than the four authorized correction paths: NOT USED - (not authorized) -- any ADR-0012 change outside Amendment 10 and the candidate `PROPOSED` state: - NOT USED (not authorized) +- any edit to a path other than the four authorized paths: NOT USED (not + authorized) +- any ADR-0012 change outside the withdrawal-first availability clause of + invariant 43: NOT USED (not authorized) - independent exact-candidate inspection or exact-head review: NOT USED (never on the implementing agent's authority) - CTO candidate-content approval, approval-state commit, fresh `APPROVED` @@ -2571,7 +2776,7 @@ authoritative for the current state of each): authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second candidate commit, amend, rebase, squash, force push or history +- any second correction commit, amend, rebase, squash, force push or history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) @@ -2607,16 +2812,17 @@ and no migration execution is expected. Under Amendment 3 C2/C11, Amendments 4-5, Amendments 6-7, Amendments 8-10, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate and approval-state authorizations, the Amendment-9 candidate and -approval-state authorizations and the Amendment-10 candidate authorization, -configured Codex/app review is not expected from a push to a draft PR and is -not relied upon at the Amendment-6/7 candidate, approval-state, -control-record recovery, transient-state correction, factual-clarification, -Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, -Amendment-9 approval-state or Amendment-10 candidate stage; configured -Codex/app review runs on separately authorized Ready transitions, which are -GitHub-owned lifecycle events, and any automated review that nevertheless -runs on a draft head is inspected read-only. The actual job outcomes for the -candidate commit exist only after its publication; they +approval-state authorizations, the Amendment-10 candidate authorization and +the F-A10-CAND-01 consistency-correction authorization, configured Codex/app +review is not expected from a push to a draft PR and is not relied upon at +the Amendment-6/7 candidate, approval-state, control-record recovery, +transient-state correction, factual-clarification, Amendment-8 candidate, +Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 +approval-state, Amendment-10 candidate or consistency-correction stage; +configured Codex/app review runs on separately authorized Ready transitions, +which are GitHub-owned lifecycle events, and any automated review that +nevertheless runs on a draft head is inspected read-only. The actual job +outcomes for the correction commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -2625,9 +2831,9 @@ publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, -Amendment-9 candidate, Amendment-9 approval-state or Amendment-10 candidate -stage, followed the same documentation-only classification, with no manual -dispatch or rerun: +Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate or +consistency-correction stage, followed the same documentation-only +classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -2645,6 +2851,7 @@ eb3129f13a9c858b3c012ee182315801f0b8563a: 4 success (classify x3, check-changelo e2588f7b7e63a5c71968d87a3409f0ff48635e6d: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-8 approval-state head) 8699af5635b8f18691ceaeccd3b24707a71ef4f2: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-9 candidate) e41f377202379238b91eb43d60f5115aa8adc20f: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-9 approval-state head) +6e521f00b039769b28a26ab069c7e97d22de4e08: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-10 candidate) ``` Manually initiated external actions (anything the implementing agent @@ -3282,9 +3489,10 @@ Amendment-9 approval-state stage (historical approval-state head commit's identities as historical facts because that head is the Amendment-10 candidate's parent. -Amendment-10 Route-B candidate (the commit that contains this report). Items -91, 92 and 95 record interpretations of the approved specification for -explicit control inspection: +Amendment-10 Route-B candidate (historical first candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08`, inspected `CHANGES REQUIRED / +F-A10-CAND-01`). Items 91, 92 and 95 recorded interpretations of the approved +specification for explicit control inspection: 89. The Amendment-10 rule is stated once, inside "Revision withdrawal and route retirement" in section 3.2, immediately after the existing @@ -3394,12 +3602,14 @@ explicit control inspection: approvals, to the historical earlier version without embedding its identities. The changelog gains one new entry and every historical entry, including the record of the 2026-10-08 approval, stays byte-preserved - (decisions 26 and 59). This report does not self-pin the candidate - commit's SHA, its tree or its own final blob; because the report now lives - in the candidate commit, statements that described the approval-state - commit as the commit containing this report are reframed as history, and - that commit's identities, now the candidate parent, are recorded in - section 1 as historical facts. The exact-final-blob approval of the + (decisions 26 and 59). The first-candidate report did not self-pin the + candidate commit's SHA, its tree or its own final blob; because the report + then lived in the candidate commit, statements that described the + approval-state commit as the commit containing the report were reframed + as history, and that commit's identities, the candidate parent, were + recorded in section 1 as historical facts; section 1 now records the + first candidate's identities as historical facts because that head is + the consistency correction's parent. The exact-final-blob approval of the approval-state ADR blob is recorded through owning issue #958, its date and the blob and head it approved; its GitHub comment identifier is not copied, consistent with decisions 37 and 64, no review, thread or @@ -3416,6 +3626,52 @@ explicit control inspection: timing limitation recorded by decision 53 and section 13 is replaced by the implemented rule. +F-A10-CAND-01 consistency correction (the commit that contains this report): + +101. The correction changes ADR-0012 in exactly one hunk, the withdrawal-first + clause of invariant 43. Old wording: "withdrawal-first, mismatch detection + is never suppressed, records the anomaly durably and visibly, evaluates + availability from post-withdrawal state, never treats the withdrawn + revision as selectable and creates no disposition, job or route slot." + New wording: "withdrawal-first, mismatch detection is never suppressed + and always records the anomaly durably and visibly, creating no + disposition, job or route slot and without being conditioned on computing + or persisting resolution-path availability; any availability-dependent + computation or decision, if performed during or after detection, uses + current post-withdrawal durable state and never treats the withdrawn + revision as selectable." The new wording matches the section-3.2 mismatch + paragraph (detection need not calculate or persist availability), the + section-3.2 withdrawal-first paragraph (post-withdrawal evaluation of any + availability the anomaly transaction depends on) and validation case 6, + none of which changes; neither anomaly creation nor detection is + conditioned on computing an available resolution path. +102. The same conditional rule replaces the unconditional shorthand in the + ADR-0012 register row, in the existing strand-safety changelog entry (no + entry is added) and in Appendix B item 14. Decision 92 and the + first-candidate section-4 description, which already carried the + qualified rule, are unchanged apart from lineage reframing. No other + ADR-0012 line, register row or changelog entry changes. +103. The correction is recorded as a candidate-consistency correction inside + the approved Specification Amendment 10, not as a new architecture + decision and not as a factual clarification of an approved version; it + adds no automatic success, automatic divergence, revocability, + compatibility or reconciliation semantics and selects no mechanism. + ADR-0012 remains `PROPOSED` with no current approver or approval date; + the first candidate is historical `CHANGES REQUIRED` evidence and is not + presented as content-approved. The inspection finding is recorded by its + control-assigned name `F-A10-CAND-01`, and the inspection and + authorization records by their comment identifiers as evidence, + consistent with section 2. +104. This report does not self-pin the correction commit's SHA, its tree or + its own final blob; because the report now lives in the correction + commit, statements that described the first candidate as the commit + containing this report are reframed as history, and the first candidate's + identities, now the correction parent, are recorded in section 1 as + historical facts. Construction reused the same detached scratch worktree, + verified clean and exact at the first candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` + before editing, touching no other worktree, branch, ref, stash or + uncommitted change. + List any deviation from the specification requiring authorization: - An earlier unpublished local activation-epoch candidate, @@ -3503,9 +3759,16 @@ List any deviation from the specification requiring authorization: - Amendment-10 candidate stage: NONE. No fifth path, new file, deletion, ADR change outside Amendment 10 and the candidate state, second commit, amend, rebase, squash, force push or history rewrite was needed or performed - before the commit that contains this report was created. Decisions 91, 92 - and 95 are recorded for control inspection as interpretations within the - authorized scope, not as deviations. + before the candidate commit was created. Decisions 91, 92 and 95 were + recorded for control inspection as interpretations within the authorized + scope, not as deviations; the independent inspection raised no finding + against them and one finding, `F-A10-CAND-01`, against invariant 43's + wording. +- F-A10-CAND-01 consistency-correction stage: NONE. The correction followed + its separate authorization exactly; no fifth path, new file, deletion, ADR + change outside the invariant-43 clause, second commit, amend, rebase, + squash, force push or history rewrite was needed or performed before the + commit that contains this report was created. ## 6. Database and migration effects @@ -3575,7 +3838,9 @@ candidate validation" records the validation of the historical content-approved candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`. "Amendment-9 approval-state validation" records the validation of the historical approval-state commit `e41f377202379238b91eb43d60f5115aa8adc20f`. -"Amendment-10 candidate validation" at the end of this section records the +"Amendment-10 candidate validation" records the validation of the historical +first candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`. +"Consistency-correction validation" at the end of this section records the validation of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git @@ -6178,28 +6443,220 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 ``` -The candidate ADR-0012, decision-register and changelog blobs are +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate report's bytes, so they +were recorded above. The candidate report's own final blob, the candidate +tree and the candidate commit SHA depended on that report's bytes and were +not self-pinned in it; they were verified against the remote head after push +and recorded in the post-publication handoff, and section 1 now records them +as historical facts: commit `6e521f00b039769b28a26ab069c7e97d22de4e08`, tree +`7523efb689fc875b92e9e3fec2d6305277e1f940`, report blob +`f4c0bd0ae0b2bfd091d5602463ec4de24140bfb4`. + +Required topology of the candidate commit, checked after it was created and +before push; the published candidate satisfied every requirement: + +```text +git rev-list --parents -n 1 HEAD +required: e41f377202379238b91eb43d60f5115aa8adc20f (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check e41f377202379238b91eb43d60f5115aa8adc20f HEAD +required: exit 0; no output + +git diff --name-only e41f377202379238b91eb43d60f5115aa8adc20f HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + +### Consistency-correction validation + +Environment: the same detached local Git worktree, verified clean (no tracked +or untracked change) and exact at the historical first Amendment-10 candidate +`6e521f00b039769b28a26ab069c7e97d22de4e08` (tree +`7523efb689fc875b92e9e3fec2d6305277e1f940`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `feature/async/adr-0012` at +`6e521f00b039769b28a26ab069c7e97d22de4e08` with single parent +`e41f377202379238b91eb43d60f5115aa8adc20f` and no descendant, ADR-0012 blob +`d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`, report blob +`f4c0bd0ae0b2bfd091d5602463ec4de24140bfb4`, decision-register blob +`73acf0e7a83116d3ffee47cd6ff40c14075e8643` and changelog blob +`653cc699c972f0457bd1cdbc032374e3090396fd` at that head, the protected +ADR-0008 and ADR-0010 blobs, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker`, the cumulative PR footprint exactly +the six paths, the inspection and authorization records present on #958 as +the two latest comments with no later control record, and PR #960 +review-thread state that matched the stage authorization (stage-time +evidence; current review-thread state remains GitHub-owned). ADR-0012, the +decision register and the changelog were edited first and their deterministic +blobs recorded; then only this report was edited; all four were staged, and +every check below ran against that exact final staged index before the +correction commit was created from it; the report's numeric line counts below +were filled in and the complete check set rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 6e521f00b039769b28a26ab069c7e97d22de4e08 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Correction delta against the first candidate (exactly the four authorized +paths): + +```text +git diff --cached --name-only 6e521f00b039769b28a26ab069c7e97d22de4e08 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 6e521f00b039769b28a26ab069c7e97d22de4e08 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 6e521f00b039769b28a26ab069c7e97d22de4e08 +1 1 CHANGELOG.md +656 152 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +6 3 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 delta (exactly one hunk, the withdrawal-first clause of invariant +43; header, section 3.2, invariants 1-42, section 11 and section 12 +byte-identical): + +```text +git diff --cached -U0 6e521f00b039769b28a26ab069c7e97d22de4e08 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -2257,3 +2257,6 @@ execution until a separate approved architecture decision says otherwise. + +git diff --cached --word-diff=plain -U0 6e521f00b039769b28a26ab069c7e97d22de4e08 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^\[-\|^{+\|^ ' + never [-suppressed,-]{+suppressed and always+} records the anomaly durably and visibly, + [-evaluates-] +[- availability from-]{+creating no disposition, job or route slot and without being conditioned+} +{+ on computing or persisting resolution-path availability; any+} +{+ availability-dependent computation or decision, if performed during or+} +{+ after detection, uses current+} post-withdrawal [-state,-]{+durable state and+} never + treats the withdrawn revision as [-selectable and creates no disposition, job or route slot.-]{+selectable.+} + +diff <(git show 6e521f00b039769b28a26ab069c7e97d22de4e08:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed '2257,2259d') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed '2257,2262d') +exit 0; no output (every line outside the replaced three-line clause, now six lines, byte-identical) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +2933:Current decision state: **PROPOSED**. + +diff <(git show 6e521f00b039769b28a26ab069c7e97d22de4e08:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +exit 0; no output + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'post-withdrawal' +3 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'need not calculate\|without being conditioned' +2 +``` + +Decision-register delta (only the ADR-0012 row; every other line, including +the `Last updated: 2026-10-08` header, byte-identical): + +```text +git diff --cached -U0 6e521f00b039769b28a26ab069c7e97d22de4e08 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-08 + +diff <(git show 6e521f00b039769b28a26ab069c7e97d22de4e08:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n 20p | grep -o '^| `ADR-0012` | [^|]* | [A-Z]* |' +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | +``` + +Changelog delta (only the existing +`THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry; no entry added; every +other entry and heading byte-preserved): + +```text +git diff --cached -U0 6e521f00b039769b28a26ab069c7e97d22de4e08 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 6e521f00b039769b28a26ab069c7e97d22de4e08:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git show :CHANGELOG.md | sed -n 22p | grep -c 'THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION' +1 +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED replacement candidate) b41e674dc5e3f1ba03715538ff2396e4d2f50c6c +decision-register.md 53a1a5fa5a75c15b5d24c71849988bdadbdb57e9 +CHANGELOG.md e78f8e3e94965e1381cddd941b015017e5f90c10 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +b41e674dc5e3f1ba03715538ff2396e4d2f50c6c +``` + +The corrected ADR-0012, decision-register and changelog blobs are deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the candidate tree and the candidate +above. This report's own final blob, the correction tree and the correction commit SHA depend on this report's bytes and are not self-pinned; they are verified against the remote head after push and recorded in the post-publication handoff. -Required topology of the candidate commit, checked after it is created and +Required topology of the correction commit, checked after it is created and before any push, with results recorded in the post-publication handoff: ```text git rev-list --parents -n 1 HEAD -required: e41f377202379238b91eb43d60f5115aa8adc20f (exactly one parent) +required: 6e521f00b039769b28a26ab069c7e97d22de4e08 (exactly one parent) git rev-parse HEAD^{tree} required: equal to the git write-tree output of the validated index -git diff --check e41f377202379238b91eb43d60f5115aa8adc20f HEAD +git diff --check 6e521f00b039769b28a26ab069c7e97d22de4e08 HEAD required: exit 0; no output -git diff --name-only e41f377202379238b91eb43d60f5115aa8adc20f HEAD -required: exactly the four candidate paths +git diff --name-only 6e521f00b039769b28a26ab069c7e97d22de4e08 HEAD +required: exactly the four authorized paths git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths @@ -6357,6 +6814,22 @@ the ADR-0012 row, and only one added entry); confirmed the protected decision and control blobs are unchanged; and read the report for statements that would become false once it moves into the candidate commit and reframed them, including the deferred strand-check timing limitation. +Consistency-correction stage: compared the staged ADR-0012 with the first +candidate's ADR blob `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`: the only delta +is the withdrawal-first clause of invariant 43 (one hunk), and the new clause +was read against the section-3.2 mismatch paragraph, the section-3.2 +withdrawal-first paragraph, validation case 6 and decision 92 for the same +conditional semantics: unconditional fact-based detection, no disposition, +job or slot, no obligation to compute or persist availability, and any +availability-dependent computation or decision using current post-withdrawal +durable state and never treating the withdrawn revision as selectable; +compared the staged register and changelog with the first-candidate blobs +(only the ADR-0012 row, and only the existing strand-safety entry); confirmed +the protected decision and control blobs are unchanged; and read the report +for statements that would become false once it moves into the correction +commit, in particular any statement naming the approval-state head as the +direct parent of the commit containing this report or presenting the first +candidate as the current or content-approved candidate, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -6371,10 +6844,11 @@ head `eb3129f13a9c858b3c012ee182315801f0b8563a`, the content-approved Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, the Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, the content-approved Amendment-9 candidate -`8699af5635b8f18691ceaeccd3b24707a71ef4f2` and the Amendment-9 approval-state -head `e41f377202379238b91eb43d60f5115aa8adc20f`, are historical evidence; the -live PR #960 diff of the candidate commit exists only after its publication -and is GitHub-owned lifecycle evidence. +`8699af5635b8f18691ceaeccd3b24707a71ef4f2`, the Amendment-9 approval-state +head `e41f377202379238b91eb43d60f5115aa8adc20f` and the first Amendment-10 +candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`, are historical evidence; +the live PR #960 diff of the correction commit exists only after its +publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -6388,9 +6862,10 @@ authorizations, the control-record recovery authorization, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate authorization, the Amendment-8 approval-state authorization, the Amendment-9 candidate authorization, the -Amendment-9 approval-state authorization and the Amendment-10 candidate -authorization, an automatic CI failure, or any migration execution or Docker -publication, is a HOLD condition. +Amendment-9 approval-state authorization, the Amendment-10 candidate +authorization and the F-A10-CAND-01 consistency-correction authorization, an +automatic CI failure, or any migration execution or Docker publication, is a +HOLD condition. ## 12. Rollout and rollback @@ -6421,17 +6896,18 @@ permitted exactly one four-path candidate commit, the Amendment-8 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-9 candidate authorization permitted exactly one four-path candidate commit, the Amendment-9 approval-state authorization -permitted exactly one direct-child approval-state commit, and the -Amendment-10 candidate authorization permits exactly one four-path candidate -commit; any further source correction exhausts those authorizations and -requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-10; -`docs/engineering/decisions/README.md`). +permitted exactly one direct-child approval-state commit, the Amendment-10 +candidate authorization permitted exactly one four-path candidate commit, and +the F-A10-CAND-01 consistency-correction authorization permits exactly one +direct-child four-path correction commit; any further source correction +exhausts those authorizations and requires a fresh CTO authorization +(Amendment 3 C10; Amendments 6-10; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-10 candidate stage the - implementing agent performed no independent inspection or review, +- Historical stage-time evidence: at the F-A10-CAND-01 consistency-correction + stage the implementing agent performed no independent inspection or review, candidate-content approval, PR #960 title/description reconciliation, Draft/Ready change, automated-review request, thread adjudication, exact-head review or merge (section 4.2). @@ -6475,11 +6951,14 @@ Monitoring required: NONE authorization for one direct-child approval-state commit, and the withdrawal strand-safety defect found by the final-head review of that approval-state head only under Specification Amendment 10, its independent - review and a separate authorization for this one four-path candidate - commit. + review and a separate authorization for one four-path candidate commit, + and the invariant-43 wording inconsistency found by the independent + exact-candidate inspection of that candidate only under a separate + authorization for this one four-path correction commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and - implemented by this candidate (section 5, decisions 89-100): a withdrawal's + implemented by the Amendment-10 candidate as corrected by this consistency + correction (section 5, decisions 89-104): a withdrawal's strand-prevention decision must be database-consistent and stable through its commit against concurrently committing anomalies and resolution-path removals. The concrete lock, constraint or isolation mechanism remains an @@ -6496,8 +6975,8 @@ Monitoring required: NONE approval-state commit, the recovery commit, the Amendment-6/7 candidate, its approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and - approval-state commit, the Amendment-9 candidate and approval-state commit - and this Amendment-10 candidate. + approval-state commit, the Amendment-9 candidate and approval-state commit, + the Amendment-10 candidate and this consistency correction. ## 14. Unresolved issues @@ -6627,11 +7106,17 @@ Monitoring required: NONE review of the base specification and Amendments 1-10 returned `APPROVED`, confirming the finding, the classification, the decision-stability rule and the validation matrix and recording five non-blocking candidate-inspection - observations; and the Route-B candidate that contains this report - implements the correction in source as `PROPOSED` (section 4; section 5, - decisions 89-100). `R-E41F-01` is closed at specification level by - Amendment 10; whether the candidate closes it in source is decided by the - independent inspection and review of the candidate, which GitHub records. + observations; the first Route-B candidate + `6e521f00b039769b28a26ab069c7e97d22de4e08` implemented the correction in + source as `PROPOSED` (section 4; section 5, decisions 89-100); control's + independent inspection of that candidate returned `CHANGES REQUIRED` on + one candidate-content inconsistency, `F-A10-CAND-01` (invariant 43's + unconditional withdrawal-first availability clause versus the fact-only + detection rule), and the replacement candidate that contains this report + aligns that wording (section 5, decisions 101-104). `R-E41F-01` is closed + at specification level by Amendment 10; whether the replacement candidate + closes it in source is decided by the independent inspection and review of + that candidate, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -6645,8 +7130,9 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-10 candidate also implemented -the Amendment-9 approval-state commit and candidate, the Amendment-8 +work. The implementing agent of this consistency correction also implemented +the first Amendment-10 candidate, the Amendment-9 approval-state commit and +candidate, the Amendment-8 approval-state commit and candidate, the factual clarification, the transient-state correction, the control-record recovery, the Amendment-6/7 approval-state commit and candidate and earlier Route-B source stages of this @@ -6657,10 +7143,25 @@ eligible to act as an independent inspector or reviewer of this task Suggested review focus: -- that the candidate changes exactly the four authorized paths from the - approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`, that the - candidate ADR-0012 blob is `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`, and that - ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that the correction changes exactly the four authorized paths from the + first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`, that + the corrected ADR-0012 blob is `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, that + its only ADR-0012 delta is the withdrawal-first clause of invariant 43, and + that ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that invariant 43 now states that withdrawal-first mismatch detection is + never suppressed and always records the anomaly durably and visibly, + creates no disposition, job or route slot, is not conditioned on computing + or persisting resolution-path availability, and that any + availability-dependent computation or decision, if performed, uses current + post-withdrawal durable state and never treats the withdrawn revision as + selectable, consistently with the section-3.2 mismatch paragraph, the + section-3.2 withdrawal-first paragraph, validation case 6 and decision 92, + and that the register row, the existing strand-safety changelog entry and + Appendix B item 14 say the same (section 5, decisions 101-104); +- that the first candidate changed exactly the four authorized paths from the + approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`, that its + ADR-0012 blob was `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6`, and that + ADR-0008, ADR-0010 and ADR-0013 kept their protected blobs; - that every Amendment 10 K3-K9 requirement is expressed in ADR-0012: the database-consistent strand-safety decision valid through the withdrawal's linearization/commit point with an in-transaction scan insufficient, the @@ -7660,9 +8161,12 @@ stage: decision unsafe - at minimum concurrent anomaly creation and last-resolution-path removal - shares its database-atomic boundary or forces re-evaluation, an anomaly-first withdrawal that would strand the - anomaly is refused or held, a withdrawal-first detection still records - the anomaly durably with no disposition, job or slot and from - post-withdrawal availability, and concurrent removals of alternative - resolution paths cannot write-skew into stranded work. + anomaly is refused or held, a withdrawal-first detection always records + the anomaly durably with no disposition, job or slot and without being + conditioned on computing resolution-path availability, any + availability-dependent computation or decision, if performed, uses + current post-withdrawal durable state and never treats the withdrawn + revision as selectable, and concurrent removals of alternative resolution + paths cannot write-skew into stranded work. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index d81d56a05..b41e674dc 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -2254,9 +2254,12 @@ execution until a separate approved architecture decision says otherwise. withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is - never suppressed, records the anomaly durably and visibly, evaluates - availability from post-withdrawal state, never treats the withdrawn - revision as selectable and creates no disposition, job or route slot. + never suppressed and always records the anomaly durably and visibly, + creating no disposition, job or route slot and without being conditioned + on computing or persisting resolution-path availability; any + availability-dependent computation or decision, if performed during or + after detection, uses current post-withdrawal durable state and never + treats the withdrawn revision as selectable. Concurrent removals of alternative resolution paths cannot write-skew into stranded work. The rule composes with invariant 41's withdrawal/selection order as one coherent database order, introduces no second withdrawal diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 73acf0e7a..53a1a5fa5 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-08 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, and the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection still records the anomaly durably with no disposition, job or slot and from post-withdrawal availability, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, and the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 576a860be47aa1ebe39968f4d3297b26ee1d17df Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 9 Oct 2026 16:01:11 +0100 Subject: [PATCH 40/48] THOTH-ASYNC-01-ADR-01: record Amendment-10 strand-safety content approval (#958) --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 941 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 21 +- .../decisions/decision-register.md | 4 +- 4 files changed, 781 insertions(+), 187 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e78f8e3e9..b9596d44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs beneath one stable logical `route_key` and binds every epoch to exactly one immutable route-contract revision, so that no mutable route-level event kind/version contract can reinterpret historical obligations: each revision's canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, and equivalent revisions converge atomically at the database; eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with `{v1} -> {v1, v2} -> {v2}` expand/contract transitions and cross-route fan-out coverage; an in-epoch contract mismatch becomes a durable, queryable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition and leaves that slot unused when refused; materializer compatibility derives deterministically from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal and route retirement never strand obligations or anomalies; and a genuinely second independent effect requires a new `route_key`, not a revision. The invariants, rollback and validation requirements are reconciled with that model. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-06.** The earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 17773fd66..65fdd7677 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -21,17 +21,18 @@ ordering correction specified by Specification Amendment 9 after the configured final-head automated review of that Amendment-8 approval-state head, and the Route-B candidate stage of the withdrawal strand-safety decision-stability correction specified by Specification Amendment 10 after the configured -final-head automated review of that Amendment-9 approval-state head, and the +final-head automated review of that Amendment-9 approval-state head, the four-path candidate-consistency correction of that candidate after its -independent exact-candidate inspection. It records +independent exact-candidate inspection, and the Route-B approval-state stage +of that corrected candidate. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-10 Route-B replacement -candidate (the `F-A10-CAND-01` consistency correction) as the current stage -and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-10 Route-B approval-state +commit as the current stage and every earlier candidate, approval-state, +recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -53,12 +54,14 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-10 Route-B replacement candidate commit that -contains this report, an ordinary single-parent additive commit whose direct -parent is the historical first Amendment-10 candidate +Head commit: the Amendment-10 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-10 replacement candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8`; its SHA is deliberately not +self-pinned here (section 3). That replacement candidate is the direct child +of the historical first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`, inspected `CHANGES REQUIRED / -F-A10-CAND-01`; its SHA is deliberately not self-pinned here (section 3). That -first candidate is the direct child of the historical Amendment-9 +F-A10-CAND-01`, which is the direct child of the historical Amendment-9 approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`, which is the direct child of the content-approved Amendment-9 candidate @@ -82,7 +85,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -97,9 +100,9 @@ Claude (Fable 5.1), bounded implementing agent; Amendment-6/7 candidate, approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, -Amendment-9 approval-state, Amendment-10 candidate and Amendment-10 -consistency-correction stages - Claude (Fable 5.1), bounded implementing -agent. Earlier rounds are recorded in Appendix A. +Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 +consistency-correction and Amendment-10 approval-state stages - Claude (Fable +5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -243,16 +246,25 @@ decision-register blob 73acf0e7a83116d3ffee47cd6ff40c14075e8643 CHANGELOG blob 653cc699c972f0457bd1cdbc032374e3090396fd implementation-report blob f4c0bd0ae0b2bfd091d5602463ec4de24140bfb4 (superseded by the consistency correction) -Amendment-10 Route-B replacement candidate (the commit that contains this report): -ADR-0012 blob b41e674dc5e3f1ba03715538ff2396e4d2f50c6c (PROPOSED; F-A10-CAND-01 consistency correction of d81d56a05bf65a28ea54d07dee58b0e75ef0aff6; invariant-43 withdrawal-first clause only) +content-approved Amendment-10 replacement candidate / approval-state parent: +e0fa590e06a00d08df883bc4ddeae40b7660fcb8 +tree 54c7738524328fcc67f9c82c943ec638c803062b +ADR-0012 blob b41e674dc5e3f1ba03715538ff2396e4d2f50c6c (PROPOSED; content-approved; F-A10-CAND-01 consistency correction of d81d56a05bf65a28ea54d07dee58b0e75ef0aff6; invariant-43 withdrawal-first clause only; independent exact-candidate reinspection APPROVED / SATISFIED) decision-register blob 53a1a5fa5a75c15b5d24c71849988bdadbdb57e9 +implementation-report blob 17773fd661dd4d89f1166a09c17ecd0830ac4eba CHANGELOG blob e78f8e3e94965e1381cddd941b015017e5f90c10 +Amendment-10 Route-B approval-state commit (the commit that contains this report): +ADR-0012 blob f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 (APPROVED; Javi, CTO; 2026-10-09; architecture-equivalent to b41e674dc5e3f1ba03715538ff2396e4d2f50c6c) +decision-register blob 2ed1499172780c6db82bb5f8808f2368fd8b96d1 +CHANGELOG blob b9596d44ff260d6b818ad9c8174e843cf4d0c5b1 + feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the -Amendment-9 candidate and approval-state commit, the Amendment-10 candidate -and its consistency correction (unchanged): +Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, +its consistency correction and the Amendment-10 approval-state commit +(unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -369,7 +381,20 @@ Approved specification: - independent exact-candidate inspection of the first Amendment-10 candidate, `CHANGES REQUIRED / F-A10-CAND-01`: #958 comment `6069039607`; - F-A10-CAND-01 consistency-correction authorization: #958 comment - `6069074613`. + `6069074613`; +- independent exact-candidate reinspection of the replacement candidate, + `APPROVED / SATISFIED` with `F-A10-CAND-01` resolved in source: recorded + on #958 on 2026-10-09; its GitHub comment identifier is GitHub-owned + lifecycle evidence under `ADR-0005` and Route-B doctrine and is not copied + here; +- CTO exact candidate-content approval of the Amendment-10 replacement + candidate: recorded on #958 on 2026-10-09 by Javi, CTO for candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; +- Amendment-10 Route-B approval-state implementation authorization: #958 + comment `6083303288`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -422,12 +447,18 @@ the four authorized correction paths, returned ADR-0012 to database-consistent strand-safety decision that remains valid through the withdrawal's linearization/commit point, was independently inspected and returned `CHANGES REQUIRED` on one candidate-content inconsistency, -`F-A10-CAND-01`. The commit that contains this report is the authorized -replacement candidate: one ordinary single-parent commit on that first -candidate that changes exactly the same four paths and aligns the -withdrawal-first availability wording of invariant 43 and its companion -summaries with the fact-only mismatch-detection rule. The Amendment-10 -architecture is otherwise unchanged and the version remains `PROPOSED`. +`F-A10-CAND-01`. The authorized replacement candidate, one ordinary +single-parent commit on that first candidate that changed exactly the same +four paths and aligned the withdrawal-first availability wording of +invariant 43 and its companion summaries with the fact-only +mismatch-detection rule, received independent exact-candidate reinspection +(`APPROVED / SATISFIED`) and CTO exact candidate-content approval on +2026-10-09. The commit that contains this report is the one bounded Route-B +approval-state commit that records that approval as durable decision state: +ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +`Approval date: 2026-10-09`, the ADR-0012 register row and the strand-safety +correction changelog entry moved to the approved state, and this report's +approval-state evidence. No architecture content changes. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -798,7 +829,8 @@ architecture is otherwise unchanged and the version remains `PROPOSED`. historical evidence bound to the earlier exact versions. That stage authorized no PR-state or PR-metadata change. - **Independent exact-candidate inspection and the F-A10-CAND-01 - consistency correction (the commit that contains this report).** + consistency correction (historical content-approved replacement candidate + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`).** Authorized by #958 comment `6069074613` after control independently inspected the actual committed source of the first candidate and returned `CHANGES REQUIRED` (#958 comment `6069039607`) with one HIGH blocking @@ -819,14 +851,40 @@ architecture is otherwise unchanged and the version remains `PROPOSED`. composition present without an invented mechanism. The correction is a candidate-consistency correction inside Amendment 10, not a new architecture decision and not a factual clarification of an approved - version. The commit that contains this report is one ordinary - single-parent commit directly on the first candidate that changes exactly - the same four paths: in ADR-0012 only the withdrawal-first clause of - invariant 43 (section 4; section 5, decisions 101-104); the ADR-0012 - register row; the existing strand-safety changelog entry; and this report. - ADR-0012 remains `PROPOSED`; the first candidate is historical - `CHANGES REQUIRED` evidence and was never content-approved. This stage - authorizes no PR-state or PR-metadata change. + version. The replacement candidate was one ordinary single-parent commit + directly on the first candidate that changed exactly the same four paths: + in ADR-0012 only the withdrawal-first clause of invariant 43 (section 4; + section 5, decisions 101-104); the ADR-0012 register row; the existing + strand-safety changelog entry; and the candidate-stage report. ADR-0012 + remained `PROPOSED`; the first candidate is historical `CHANGES REQUIRED` + evidence and was never content-approved. Control independently reinspected + the published replacement candidate against the actual GitHub source, + found `F-A10-CAND-01` resolved in source and no blocking exact-candidate + finding, and Javi, CTO approved its exact architecture content - head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c` - on 2026-10-09 in a durable + record on #958. That stage authorized no PR-state or PR-metadata change. +- **Amendment-10 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6083303288`. One ordinary + single-parent commit directly on the content-approved replacement + candidate records that approval as durable decision state, changing only + the four authorized paths: ADR-0012 `PROPOSED -> APPROVED` with + `Approved by: Javi, CTO` and `Approval date: 2026-10-09` and the leading + section-12 approval paragraphs, which identify the approved content as the + content represented by candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; the ADR-0012 register row and + the register's `Last updated` header; the + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` changelog entry; and this + report. No architecture content changes (section 9, "Amendment-10 + approval-state validation"). The content approval is recorded here through + owning issue #958, approver Javi, CTO, approval date 2026-10-09, the + candidate head and the candidate ADR-0012 blob; its GitHub comment + identifier is not copied into repository source, as the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` and the approval-state authorization + itself require (section 5, decision 108). PR #960 was already a draft; this + stage authorizes no PR-state or PR-metadata change. Out-of-scope changes made: NONE @@ -840,9 +898,17 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-09) +of the content-approved Amendment-10 replacement candidate blob b41e674dc5e3f1ba03715538ff2396e4d2f50c6c +at candidate head e0fa590e06a00d08df883bc4ddeae40b7660fcb8; +no architecture change + +historical Amendment-10 replacement candidate at e0fa590e06a00d08df883bc4ddeae40b7660fcb8: F-A10-CAND-01 consistency correction of first-candidate blob d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 (invariant-43 withdrawal-first availability clause only; Specification Amendment 10; -Route-B replacement-candidate stage; candidate status PROPOSED) +Route-B replacement-candidate stage; candidate status PROPOSED; +independent exact-candidate reinspection APPROVED / SATISFIED; CTO exact +candidate-content approval on 2026-10-09) historical first Amendment-10 candidate at 6e521f00b039769b28a26ab069c7e97d22de4e08: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob cbe8322d95fe8d5e9911d421a5f10f1ae4afd55c @@ -1059,38 +1125,58 @@ blocked for any head. The first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` received independent exact-candidate inspection, which returned `CHANGES REQUIRED / F-A10-CAND-01`; it was never content-approved. The -consistency correction creates a new exact ADR-0012 version, so that +consistency correction created a new exact ADR-0012 version, so that inspection outcome is bound to the first candidate's ADR blob -`d81d56a05bf65a28ea54d07dee58b0e75ef0aff6` and head, and the replacement -candidate requires its own fresh independent exact-candidate inspection -before any content approval. +`d81d56a05bf65a28ea54d07dee58b0e75ef0aff6` and head. The replacement +candidate received its own fresh independent exact-candidate reinspection +(`APPROVED / SATISFIED`, `F-A10-CAND-01` resolved in source) and CTO exact +candidate-content approval on 2026-10-09, and the approval-state commit that +contains this report records that approval. ## 3. Commits -Amendment-10 Route-B replacement candidate commit, the F-A10-CAND-01 -consistency correction (the commit that contains this report): +Amendment-10 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: align withdrawal-first availability wording (#958)` - - direct parent: the historical first Amendment-10 candidate - `6e521f00b039769b28a26ab069c7e97d22de4e08` - - changes only the four authorized paths (section 4); the corrected - ADR-0012 blob is `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, the corrected - decision-register blob `53a1a5fa5a75c15b5d24c71849988bdadbdb57e9` and the - corrected changelog blob `e78f8e3e94965e1381cddd941b015017e5f90c10` + `THOTH-ASYNC-01-ADR-01: record Amendment-10 strand-safety content approval (#958)` + - direct parent: the content-approved Amendment-10 replacement candidate + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` + - changes only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`, the approval-state + decision-register blob `2ed1499172780c6db82bb5f8808f2368fd8b96d1` and the + approval-state changelog blob `b9596d44ff260d6b818ad9c8174e843cf4d0c5b1` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the corrected ADR-0012, +here because this report is part of that tree; the approval-state ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its first-candidate blob (section 9). -The commit identities are verified against the remote task-branch head after -push and recorded in the post-publication handoff; once published, the PR -#960 head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. -The authorized topology is a non-force fast-forward of the task branch from -the first candidate to the correction commit; no merge commit, rebase, -squash, amend, force update or second correction commit is authorized, and no -further source commit exists implicitly after it. +recorded above; every other file keeps its candidate blob (section 9). The +commit identities are verified against the remote task-branch head after push +and recorded in the post-publication handoff; once published, the PR #960 +head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The +authorized topology is a non-force fast-forward of the task branch from the +content-approved candidate to the approval-state commit; no merge commit, +rebase, squash, amend, force update or second approval-state commit is +authorized, and no further source commit exists implicitly after it. + +Amendment-10 Route-B replacement candidate commit (content-approved; +historical): + +- `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` - + `THOTH-ASYNC-01-ADR-01: align withdrawal-first availability wording (#958)` + - ordinary single-parent additive commit; direct parent: the historical + first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` + - tree `54c7738524328fcc67f9c82c943ec638c803062b` + - changed only the four authorized paths (section 4); the candidate + ADR-0012 blob is `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, independently + reinspected `APPROVED / SATISFIED` and content-approved by Javi, CTO on + 2026-10-09 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + first candidate; the remote head, tree, parent and ADR-0012, + decision-register, changelog and report blobs matched the locally + validated object Amendment-10 Route-B first candidate commit (historical; inspected `CHANGES REQUIRED / F-A10-CAND-01`; never content-approved): @@ -1304,11 +1390,23 @@ first-parent order, newest first: ## 4. Files changed -F-A10-CAND-01 consistency-correction write budget for the commit that -contains this report (authorization #958 comment `6069074613`; the same four -existing paths as the Amendment-10 candidate budget); exactly four existing -paths, no new file, no deletion, and every other path byte-identical to the -historical first Amendment-10 candidate +Amendment-10 Route-B approval-state write budget for the commit that contains +this report (authorization #958 comment `6083303288`), identical to the +consistency-correction and Amendment-10 candidate write budgets; exactly four +existing paths, no new file, no deletion, and every other path byte-identical +to the content-approved replacement candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical F-A10-CAND-01 consistency-correction write budget (authorization +#958 comment `6069074613`; the same four existing paths as the Amendment-10 +candidate budget); exactly four existing paths, no new file, no deletion, and +every other path byte-identical to the historical first Amendment-10 +candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`: - `CHANGELOG.md` @@ -1414,12 +1512,89 @@ Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 -approval-state, Amendment-10 candidate and Amendment-10 consistency-correction -stages; all four correction paths already existed. +approval-state, Amendment-10 candidate, Amendment-10 consistency-correction +and Amendment-10 approval-state stages; all four correction paths already +existed. -Consistency-correction changes in the commit that contains this report, -relative to the first Amendment-10 candidate -`6e521f00b039769b28a26ab069c7e97d22de4e08` (exactly the four authorized paths): +Amendment-10 approval-state changes in the commit that contains this report, +relative to the content-approved replacement candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate-content approval of 2026-10-09. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-09`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + "Current decision state: **APPROVED**.", the same approver and date, and + a paragraph stating that the CTO approved this exact corrected content on + 2026-10-09 as the architecture content represented by candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that any ADR-0013 programme-local reliance on this exact version is a + separate state not effective until its own conditions are satisfied. + Every other line, including all architecture, the Amendment-10 + decision-stability rule and both orders, the corrected invariant 43, + invariants 1-42, the validation requirements, the historical-approval + paragraph, the authority condition and the + architecture-versus-implementation separation, is byte-identical to the + candidate (section 9). Approval-state blob: + `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, "its corrected content awaits CTO + approval" becomes "Javi, CTO approved its exact corrected content on + 2026-10-09", bound to candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c` as historical content-approval + evidence, with the statement that content approval is architecture-content + approval only and that exact-final-blob approval, independent final + source-head review, merge authorization and ADR-0013 reliance are separate + gates recorded in GitHub; and the header `Last updated` line advances + from `2026-10-08` to `2026-10-09`, the date of this row change, as the + approval-state authorization requires (section 5, decision 109). Every + other row is unchanged. Approval-state blob: + `2ed1499172780c6db82bb5f8808f2368fd8b96d1`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the strand-safety correction entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry only - "propose" + becomes "record a CTO-approved", "`PROPOSED`: the corrected content is not + approved" becomes "`APPROVED`: Javi, CTO approved the exact corrected + content on 2026-10-09", and "proposal only" becomes "decision only". Its + statements that the earlier entries and the 2026-09-30, 2026-10-05, + 2026-10-06, 2026-10-07 and 2026-10-08 approvals apply only to historical + exact versions are unchanged, and every other entry is byte-identical. + Approval-state blob: `b9596d44ff260d6b818ad9c8174e843cf4d0c5b1`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-10 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit + the current stage; records the reinspection, the CTO candidate-content + approval and the approval-state authorization; records the approval-state + validation and no-drift proof (section 9); and reframes statements that + would otherwise describe the replacement candidate as the commit + containing this report, recording that candidate's identities as + historical facts. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the content-approved replacement candidate through the +approval-state commit: every path other than the four authorized paths, +including ADR-0008 `622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical consistency-correction changes in the replacement candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, relative to the first Amendment-10 +candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` (exactly the four +authorized paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: correct `F-A10-CAND-01`, the unconditional withdrawal-first @@ -1463,16 +1638,16 @@ relative to the first Amendment-10 candidate - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: correct the same shorthand in Appendix B item 14 and record the consistency-correction stage truthfully. - - behavioural effect: documentation only. Appendix B item 14 carries the - conditional rule; the first candidate becomes historical + - behavioural effect: documentation only. Appendix B item 14 carried the + conditional rule; the first candidate became historical `CHANGES REQUIRED / F-A10-CAND-01` evidence with its now-known SHA, tree - and report blob recorded in section 1; the correction becomes the - current stage with the first candidate as its direct parent; the - inspection and authorization records are added to section 2; the - correction's validation is recorded in section 9 (section 5, decisions + and report blob recorded in section 1; the correction became the + then-current stage with the first candidate as its direct parent; the + inspection and authorization records were added to section 2; the + correction's validation was recorded in section 9 (section 5, decisions 101-104). Decision 92 and the first candidate's section-4 description, - which already carried the qualified rule, are unchanged apart from - lineage reframing. All still-true earlier evidence is preserved. + which already carried the qualified rule, were unchanged apart from + lineage reframing. All still-true earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the first candidate through the consistency correction: @@ -2128,8 +2303,8 @@ Byte-identical to the recovery head through the Amendment-6/7 candidate, the approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate, the Amendment-8 approval-state commit, the Amendment-9 candidate, the Amendment-9 -approval-state commit, the Amendment-10 candidate and its consistency -correction: +approval-state commit, the Amendment-10 candidate, its consistency +correction and the Amendment-10 approval-state commit: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -2308,10 +2483,15 @@ Files deleted, moved or renamed: NONE PASS -The consistency correction differs from the first Amendment-10 candidate -`6e521f00b039769b28a26ab069c7e97d22de4e08` in exactly the four authorized paths -and from refreshed `feature/worker` in exactly the six cumulative PR paths -(section 9, "Consistency-correction validation"). The Amendment-10 candidate +The Amendment-10 approval-state commit differs from the content-approved +replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` in exactly +the four authorized paths and from refreshed `feature/worker` in exactly the +six cumulative PR paths (section 9, "Amendment-10 approval-state +validation"). The consistency correction differed from the first Amendment-10 +candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` in exactly the four +authorized paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Consistency-correction validation"). The +Amendment-10 candidate differed from the approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f` in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six @@ -2374,8 +2554,9 @@ the Amendment-8 candidate stage (#958 comment `6044220221`), the Amendment-8 approval-state stage (#958 comment `6045126538`), the Amendment-9 candidate stage (#958 comment `6046895895`), the Amendment-9 approval-state stage (#958 comment `6065879000`), the Amendment-10 candidate stage (#958 comment -`6067886387`) and the F-A10-CAND-01 consistency-correction stage (#958 -comment `6069074613`). This matrix +`6067886387`), the F-A10-CAND-01 consistency-correction stage (#958 comment +`6069074613`) and the Amendment-10 approval-state stage (#958 comment +`6083303288`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -2718,8 +2899,8 @@ correction: control inspected the published candidate and returned `CHANGES REQUIRED / F-A10-CAND-01` (section 2) -F-A10-CAND-01 consistency-correction stage - done up to and including the -creation of the commit that contains this report: +F-A10-CAND-01 consistency-correction stage - historical, completed before +the Amendment-10 approval-state commit: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -2741,18 +2922,51 @@ creation of the commit that contains this report: worktree, verified clean and exact at the first candidate and touching no other worktree, branch or ref, was reused for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent correction commit that contains this +- commit: USED (the single-parent correction commit + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `6e521f00b039769b28a26ab069c7e97d22de4e08` to the replacement candidate; the + remote head, tree, parent and ADR-0012, decision-register, changelog and + report blobs were verified; no automated review ran on the draft candidate + head) +- independent exact-candidate reinspection: NOT USED by the implementing + agent; control reinspected the published replacement candidate and returned + `APPROVED / SATISFIED` (section 2) + +Amendment-10 approval-state stage - done up to and including the creation of +the commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + PR #960 open, draft, unmerged and mergeable with head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and target `feature/worker`, + the replacement candidate's tree, single parent and ADR-0012, report, + decision-register and changelog blobs, the protected ADR-0008 and ADR-0010 + blobs, the six-path cumulative footprint, the absence of any ADR-0012 + commit reachable from `develop` (Route-B eligibility), the presence of the + reinspection, content-approval and approval-state authorization records on + #958 with no later control record, and, as stage-time evidence, the PR + #960 review-thread state in GitHub; all matched the authorization; current + review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, branch creation: NOT USED + (not authorized; the same detached local worktree, verified clean and + exact at the content-approved replacement candidate and touching no other + worktree, branch or ref, was reused for construction, and the existing + task branch is the fast-forward target) +- commit: USED (the single-parent approval-state commit that contains this report) -Authorized post-commit actions for the correction commit - GitHub-owned +Authorized post-commit actions for the approval-state commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `6e521f00b039769b28a26ab069c7e97d22de4e08` to the correction commit; + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` to the approval-state commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, changed path set, - cumulative footprint and PR #960 state, and that no review thread was - replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, ADR approval + state, changed path set, cumulative footprint and PR #960 state, and that + no review thread was replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -2760,24 +2974,23 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the F-A10-CAND-01 -consistency-correction stage, each a separate controlled lifecycle category -where applicable (historical stage-time evidence; GitHub, through #958 and PR -#960, is authoritative for the current state of each): +Not authorized for the implementing agent at the Amendment-10 approval-state +stage, each a separate controlled lifecycle category where applicable +(historical stage-time evidence; GitHub, through #958 and PR #960, is +authoritative for the current state of each): - any edit to a path other than the four authorized paths: NOT USED (not authorized) -- any ADR-0012 change outside the withdrawal-first availability clause of - invariant 43: NOT USED (not authorized) -- independent exact-candidate inspection or exact-head review: NOT USED (never +- any ADR-0012 change outside the approval-state representation, including + any architecture change: NOT USED (not authorized) +- independent inspection or exact-head review: NOT USED (never on the + implementing agent's authority) +- exact-final-blob approval or confirmation: NOT USED (not authorized; never on the implementing agent's authority) -- CTO candidate-content approval, approval-state commit, fresh `APPROVED` - status, approver or approval date, exact-final-blob approval: NOT USED (not - authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second correction commit, amend, rebase, squash, force push or history - rewrite: NOT USED (not authorized) +- any second approval-state commit, amend, rebase, squash, force push or + history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -2812,17 +3025,19 @@ and no migration execution is expected. Under Amendment 3 C2/C11, Amendments 4-5, Amendments 6-7, Amendments 8-10, the transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate and approval-state authorizations, the Amendment-9 candidate and -approval-state authorizations, the Amendment-10 candidate authorization and -the F-A10-CAND-01 consistency-correction authorization, configured Codex/app -review is not expected from a push to a draft PR and is not relied upon at -the Amendment-6/7 candidate, approval-state, control-record recovery, -transient-state correction, factual-clarification, Amendment-8 candidate, -Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 -approval-state, Amendment-10 candidate or consistency-correction stage; -configured Codex/app review runs on separately authorized Ready transitions, -which are GitHub-owned lifecycle events, and any automated review that -nevertheless runs on a draft head is inspected read-only. The actual job -outcomes for the correction commit exist only after its publication; they +approval-state authorizations, the Amendment-10 candidate authorization, the +F-A10-CAND-01 consistency-correction authorization and the Amendment-10 +approval-state authorization, configured Codex/app review is not expected +from a push to a draft PR and is not relied upon at the Amendment-6/7 +candidate, approval-state, control-record recovery, transient-state +correction, factual-clarification, Amendment-8 candidate, Amendment-8 +approval-state, Amendment-9 candidate, Amendment-9 approval-state, +Amendment-10 candidate, consistency-correction or Amendment-10 approval-state +stage; configured Codex/app review runs on separately authorized Ready +transitions, which are GitHub-owned lifecycle events, and any automated +review that nevertheless runs on a draft head is inspected read-only. The +actual job outcomes for the approval-state commit exist only after its +publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -2831,9 +3046,9 @@ publish an image rather than skip, that is a HOLD condition. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, -Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate or -consistency-correction stage, followed the same documentation-only -classification, with no manual dispatch or rerun: +Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, +consistency-correction or Amendment-10 approval-state stage, followed the +same documentation-only classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -2852,6 +3067,7 @@ e2588f7b7e63a5c71968d87a3409f0ff48635e6d: 4 success (classify x3, check-changelo 8699af5635b8f18691ceaeccd3b24707a71ef4f2: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-9 candidate) e41f377202379238b91eb43d60f5115aa8adc20f: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-9 approval-state head) 6e521f00b039769b28a26ab069c7e97d22de4e08: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-10 candidate) +e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-10 replacement candidate) ``` Manually initiated external actions (anything the implementing agent @@ -3626,7 +3842,8 @@ specification for explicit control inspection: timing limitation recorded by decision 53 and section 13 is replaced by the implemented rule. -F-A10-CAND-01 consistency correction (the commit that contains this report): +F-A10-CAND-01 consistency correction (historical content-approved replacement +candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`): 101. The correction changes ADR-0012 in exactly one hunk, the withdrawal-first clause of invariant 43. Old wording: "withdrawal-first, mismatch detection @@ -3656,22 +3873,90 @@ F-A10-CAND-01 consistency correction (the commit that contains this report): decision and not as a factual clarification of an approved version; it adds no automatic success, automatic divergence, revocability, compatibility or reconciliation semantics and selects no mechanism. - ADR-0012 remains `PROPOSED` with no current approver or approval date; - the first candidate is historical `CHANGES REQUIRED` evidence and is not - presented as content-approved. The inspection finding is recorded by its + ADR-0012 remained `PROPOSED` with no current approver or approval date + at that stage; the first candidate is historical `CHANGES REQUIRED` + evidence and is not presented as content-approved. The inspection finding is recorded by its control-assigned name `F-A10-CAND-01`, and the inspection and authorization records by their comment identifiers as evidence, consistent with section 2. -104. This report does not self-pin the correction commit's SHA, its tree or - its own final blob; because the report now lives in the correction - commit, statements that described the first candidate as the commit - containing this report are reframed as history, and the first candidate's - identities, now the correction parent, are recorded in section 1 as - historical facts. Construction reused the same detached scratch worktree, +104. The correction report did not self-pin the correction commit's SHA, its + tree or its own final blob; because the report then lived in the + correction commit, statements that described the first candidate as the + commit containing the report were reframed as history, and the first + candidate's identities, the correction parent, were recorded in section 1 + as historical facts; section 1 now records the replacement candidate's + identities as historical facts because that head is the approval-state + commit's parent. Construction reused the same detached scratch worktree, verified clean and exact at the first candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` before editing, touching no other worktree, branch, ref, stash or uncommitted change. +Amendment-10 approval-state stage (the commit that contains this report): + +105. The approval-state header uses the same field order as the earlier + approved ADR-0012 versions (`Status`, `Date`, `Approved by`, + `Approval date`, `Decision owner`), and section 12 repeats the approver + and date; the original decision date `2026-09-29` is preserved. +106. The approval date carried by the ADR is the date of the CTO exact + candidate-content approval record, 2026-10-09, as Route B and the + approval-state authorization require; it is neither back-dated nor + anticipated. +107. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by "Current decision state: + **APPROVED**.", the approver/date and one paragraph stating that the CTO + approved this exact corrected content on 2026-10-09 as the architecture + content represented by candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that ADR-0013 programme-local reliance on this exact version is separate + and not effective until its own conditions are satisfied (decisions 63 + and 83 pattern; the authorization requires the approved content to be + identified by both the candidate head and the candidate blob). The + historical-approval, material-correction, architecture-only, + authority-condition, partial-supersession and amendment paragraphs were + already true in the approved state (decision 99) and are unchanged. +108. The content approval is recorded only through owning issue #958, approver + Javi, CTO, approval date 2026-10-09, candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`. Its GitHub comment identifier + is not copied into repository source: the repository-authoritative Route-B + rule in `docs/engineering/decisions/README.md` forbids it, the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + applied that rule to this task (decisions 36-37, 64 and 84), and the + approval-state authorization for this stage requires the same. +109. The decision-register header `Last updated` advances to `2026-10-09`, + the date on which the approval-state commit changes the ADR-0012 row, as + the approval-state authorization requires and for the reason recorded in + decision 25; otherwise only the ADR-0012 row changes. The row binds the + content approval to the candidate head and blob as historical + content-approval evidence and states, in the durable form that + `docs/engineering/AGENTS.md` section 1.1 requires, that exact-final-blob + approval, independent final source-head review, merge authorization and + ADR-0013 reliance are separate gates recorded in GitHub, rather than + wording that a later review or merge would falsify; the existing + authority-condition sentence is unchanged. +110. Only the `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` changelog + entry changes, following the earlier approval-state wording pattern + (decisions 66 and 86: "propose" to "record a CTO-approved", the + `PROPOSED` sentence to the `APPROVED` sentence naming the approver and + date, and "proposal only" to "decision only"); the candidate head and + blob are not named there because the established entry approach does not + name them and the owning issue is authoritative for lifecycle records; + every other entry, including the historical approval entries, is + byte-identical. +111. Architecture no-drift is proven by the exact normalized comparison in + section 9 ("Amendment-10 approval-state validation"), not by assertion. +112. This report does not self-pin the approval-state commit's SHA, its tree + or its own final blob; it records only non-self-referential evidence and + the approval-state ADR-0012, decision-register and changelog blobs, which + do not depend on its bytes. Because the report now lives in the + approval-state commit, statements that described the replacement + candidate as the commit containing this report are reframed as history, + and the replacement candidate's identities, now the approval-state + parent, are recorded in section 1 as historical facts. + List any deviation from the specification requiring authorization: - An earlier unpublished local activation-epoch candidate, @@ -3768,7 +4053,12 @@ List any deviation from the specification requiring authorization: its separate authorization exactly; no fifth path, new file, deletion, ADR change outside the invariant-43 clause, second commit, amend, rebase, squash, force push or history rewrite was needed or performed before the - commit that contains this report was created. + correction commit was created. +- Amendment-10 approval-state stage: NONE. The approval-state transformation + followed its separate authorization exactly; no fifth path, new file, + deletion, architecture change, second commit, amend, rebase, squash, force + push or history rewrite was needed or performed before the commit that + contains this report was created. ## 6. Database and migration effects @@ -3840,8 +4130,11 @@ approval-state validation" records the validation of the historical approval-state commit `e41f377202379238b91eb43d60f5115aa8adc20f`. "Amendment-10 candidate validation" records the validation of the historical first candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`. -"Consistency-correction validation" at the end of this section records the -validation of the commit that contains this report. +"Consistency-correction validation" records the validation of the historical +content-approved replacement candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8`. "Amendment-10 approval-state +validation" at the end of this section records the validation of the commit +that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -6635,15 +6928,18 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro b41e674dc5e3f1ba03715538ff2396e4d2f50c6c ``` -The corrected ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the correction tree and the correction -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and recorded in the -post-publication handoff. +The corrected ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the correction report's bytes, so they +were recorded above. The correction report's own final blob, the correction +tree and the correction commit SHA depended on that report's bytes and were +not self-pinned in it; they were verified against the remote head after push +and recorded in the post-publication handoff, and section 1 now records them +as historical facts: commit `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, tree +`54c7738524328fcc67f9c82c943ec638c803062b`, report blob +`17773fd661dd4d89f1166a09c17ecd0830ac4eba`. -Required topology of the correction commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the correction commit, checked after it was created and +before push; the published correction commit satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -6662,6 +6958,252 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-10 approval-state validation + +Environment: the same detached local Git worktree, verified clean (no tracked +or untracked change) and exact at the content-approved replacement candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8` (tree +`54c7738524328fcc67f9c82c943ec638c803062b`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, no ADR-0012 commit reachable from +`develop` (Route-B eligibility), `feature/async/adr-0012` at +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8` with single parent +`6e521f00b039769b28a26ab069c7e97d22de4e08` and no descendant, ADR-0012 blob +`b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, report blob +`17773fd661dd4d89f1166a09c17ecd0830ac4eba`, decision-register blob +`53a1a5fa5a75c15b5d24c71849988bdadbdb57e9` and changelog blob +`e78f8e3e94965e1381cddd941b015017e5f90c10` at that head, the protected +ADR-0008 and ADR-0010 blobs, PR #960 open, draft, unmerged and mergeable with +that head and target `feature/worker`, the cumulative PR footprint exactly +the six paths, the reinspection, content-approval and approval-state +authorization records present on #958 as the three latest comments with no +later control record, and PR #960 review-thread state that matched the stage +authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the approval-state commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check e0fa590e06a00d08df883bc4ddeae40b7660fcb8 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths): + +```text +git diff --cached --name-only e0fa590e06a00d08df883bc4ddeae40b7660fcb8 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status e0fa590e06a00d08df883bc4ddeae40b7660fcb8 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat e0fa590e06a00d08df883bc4ddeae40b7660fcb8 +1 1 CHANGELOG.md +762 179 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +16 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 approval-state delta (four hunks: the header status line, the two +inserted header approval lines, the section-12 decision-state line and the +section-12 approval paragraphs; no other hunk): + +```text +git diff --cached -U0 e0fa590e06a00d08df883bc4ddeae40b7660fcb8 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -4,0 +5,2 @@ Date: 2026-09-29 +@@ -2933 +2935 @@ shared-engine tests. +@@ -2935,3 +2937,12 @@ Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: APPROVED +5:Approved by: Javi, CTO +6:Approval date: 2026-10-09 +2935:Current decision state: **APPROVED**. +2937:Approved by: Javi, CTO +2938:Approval date: 2026-10-09 +``` + +Architecture no-drift proof. The normalization `N` drops the header +`Status`, `Approved by` and `Approval date` lines before the first `---` +separator and the section-12 lines from the heading up to, but not +including, the line beginning "This version is a material architectural +correction"; it keeps every other byte. The content-approved candidate blob +and the staged approval-state ADR normalize to identical content: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p b41e674dc5e3f1ba03715538ff2396e4d2f50c6c | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p b41e674dc5e3f1ba03715538ff2396e4d2f50c6c | awk "$N" | shasum -a 256 +d2ebd6501107fcdec5f8b4125d2e7e746d58c77a6fa793550e5dd92842e8efa9 - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +d2ebd6501107fcdec5f8b4125d2e7e746d58c77a6fa793550e5dd92842e8efa9 - +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display), so that the complete raw delta is visible: + +```text +candidate b41e674dc5e3f1ba03715538ff2396e4d2f50c6c: +Status: PROPOSED +Current decision state: **PROPOSED**. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. + +approval-state ADR: +Status: APPROVED +Approved by: Javi, CTO +Approval date: 2026-10-09 +Current decision state: **APPROVED**. +Approved by: Javi, CTO +Approval date: 2026-10-09 +The CTO approved this exact corrected content on 2026-10-09 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and +candidate ADR-0012 blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; this +version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. +``` + +Decision-register delta (the `Last updated` header line and the ADR-0012 row +only): + +```text +git diff --cached -U0 e0fa590e06a00d08df883bc4ddeae40b7660fcb8 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-08 + +diff <(git show e0fa590e06a00d08df883bc4ddeae40b7660fcb8:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-09 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and +``` + +Changelog delta (the existing +`THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry only; every other +line byte-identical): + +```text +git diff --cached -U0 e0fa590e06a00d08df883bc4ddeae40b7660fcb8 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show e0fa590e06a00d08df883bc4ddeae40b7660fcb8:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 e0fa590e06a00d08df883bc4ddeae40b7660fcb8 -- CHANGELOG.md | grep -o '\[-[^]]*-\]\|{+[^}]*+}' +[-propose-] +{+record+} +{+CTO-approved+} +[-`PROPOSED`:-] +{+`APPROVED`: Javi, CTO approved+} +{+exact+} +[-is not approved.**-] +{+on 2026-10-09.**+} +[-proposal-] +{+decision+} +``` + +Content-approval identifier (the command is shown with the identifier replaced +by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 +decision-register.md 2ed1499172780c6db82bb5f8808f2368fd8b96d1 +CHANGELOG.md b9596d44ff260d6b818ad9c8174e843cf4d0c5b1 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above; the ADR-0012 blob `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` is the exact +final blob presented for CTO exact-final-blob approval at this stage. This +report's own final blob, the approval-state tree and the approval-state commit +SHA depend on this report's bytes and are not self-pinned; they are verified +against the remote head after push and recorded in the post-publication +handoff. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: e0fa590e06a00d08df883bc4ddeae40b7660fcb8 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check e0fa590e06a00d08df883bc4ddeae40b7660fcb8 HEAD +required: exit 0; no output + +git diff --name-only e0fa590e06a00d08df883bc4ddeae40b7660fcb8 HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -6830,6 +7372,19 @@ for statements that would become false once it moves into the correction commit, in particular any statement naming the approval-state head as the direct parent of the commit containing this report or presenting the first candidate as the current or content-approved candidate, and reframed them. +Amendment-10 approval-state stage: compared the staged ADR-0012 with the +content-approved candidate blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c` by +the exact normalized comparison in section 9 (no difference), and read the +raw delta line by line; compared the staged register and changelog with the +candidate blobs (only the `Last updated` header and the ADR-0012 row, and +only the strand-safety correction entry, change); confirmed the protected +decision and control blobs are unchanged; confirmed by fixed-string search +that the content-approval comment identifier does not occur in this report; +and read the report for statements that would become false once it moves into +the approval-state commit, in particular any statement naming the first +candidate as the direct parent of the commit containing this report or +describing the replacement candidate as still the current `PROPOSED` +candidate, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -6845,10 +7400,11 @@ Amendment-8 candidate `42ae90a5932cf24ce90679196888cc3d83d88db8`, the Amendment-8 approval-state head `e2588f7b7e63a5c71968d87a3409f0ff48635e6d`, the content-approved Amendment-9 candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, the Amendment-9 approval-state -head `e41f377202379238b91eb43d60f5115aa8adc20f` and the first Amendment-10 -candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`, are historical evidence; -the live PR #960 diff of the correction commit exists only after its -publication and is GitHub-owned lifecycle evidence. +head `e41f377202379238b91eb43d60f5115aa8adc20f`, the first Amendment-10 +candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` and the content-approved +replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, are +historical evidence; the live PR #960 diff of the approval-state commit +exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -6863,9 +7419,9 @@ transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate authorization, the Amendment-8 approval-state authorization, the Amendment-9 candidate authorization, the Amendment-9 approval-state authorization, the Amendment-10 candidate -authorization and the F-A10-CAND-01 consistency-correction authorization, an -automatic CI failure, or any migration execution or Docker publication, is a -HOLD condition. +authorization, the F-A10-CAND-01 consistency-correction authorization and the +Amendment-10 approval-state authorization, an automatic CI failure, or any +migration execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -6897,20 +7453,22 @@ approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-9 candidate authorization permitted exactly one four-path candidate commit, the Amendment-9 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-10 -candidate authorization permitted exactly one four-path candidate commit, and -the F-A10-CAND-01 consistency-correction authorization permits exactly one -direct-child four-path correction commit; any further source correction -exhausts those authorizations and requires a fresh CTO authorization -(Amendment 3 C10; Amendments 6-10; `docs/engineering/decisions/README.md`). +candidate authorization permitted exactly one four-path candidate commit, the +F-A10-CAND-01 consistency-correction authorization permitted exactly one +direct-child four-path correction commit, and the Amendment-10 approval-state +authorization permits exactly one direct-child approval-state commit; any +further source correction exhausts those authorizations and requires a fresh +CTO authorization (Amendment 3 C10; Amendments 6-10; +`docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the F-A10-CAND-01 consistency-correction - stage the implementing agent performed no independent inspection or review, - candidate-content approval, PR #960 title/description reconciliation, - Draft/Ready change, automated-review request, thread adjudication, - exact-head review or merge (section 4.2). +- Historical stage-time evidence: at the Amendment-10 approval-state stage + the implementing agent performed no independent inspection or review, + exact-final-blob approval or confirmation, PR #960 title/description + reconciliation, Draft/Ready change, automated-review request, thread + adjudication, exact-head review or merge (section 4.2). Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -6952,9 +7510,11 @@ Monitoring required: NONE withdrawal strand-safety defect found by the final-head review of that approval-state head only under Specification Amendment 10, its independent review and a separate authorization for one four-path candidate commit, - and the invariant-43 wording inconsistency found by the independent + the invariant-43 wording inconsistency found by the independent exact-candidate inspection of that candidate only under a separate - authorization for this one four-path correction commit. + authorization for one four-path correction commit, and its approval state + only under a separate authorization for this one direct-child + approval-state commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -6976,7 +7536,8 @@ Monitoring required: NONE approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, - the Amendment-10 candidate and this consistency correction. + the Amendment-10 candidate, its consistency correction and this + Amendment-10 approval-state commit. ## 14. Unresolved issues @@ -7112,11 +7673,15 @@ Monitoring required: NONE independent inspection of that candidate returned `CHANGES REQUIRED` on one candidate-content inconsistency, `F-A10-CAND-01` (invariant 43's unconditional withdrawal-first availability clause versus the fact-only - detection rule), and the replacement candidate that contains this report - aligns that wording (section 5, decisions 101-104). `R-E41F-01` is closed - at specification level by Amendment 10; whether the replacement candidate - closes it in source is decided by the independent inspection and review of - that candidate, which GitHub records. + detection rule); the replacement candidate + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` aligned that wording (section 5, + decisions 101-104); control's independent reinspection of that candidate + found `F-A10-CAND-01` resolved in source and no blocking exact-candidate + finding; Javi, CTO approved its exact architecture content on 2026-10-09; + and the approval-state commit that contains this report records that + approval. `R-E41F-01` is closed at specification level by Amendment 10; its + source closure is adjudicated by the independent exact-head review controls + that precede any merge, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -7130,9 +7695,9 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this consistency correction also implemented -the first Amendment-10 candidate, the Amendment-9 approval-state commit and -candidate, the Amendment-8 +work. The implementing agent of this Amendment-10 approval-state commit also +implemented the Amendment-10 replacement candidate and first candidate, the +Amendment-9 approval-state commit and candidate, the Amendment-8 approval-state commit and candidate, the factual clarification, the transient-state correction, the control-record recovery, the Amendment-6/7 approval-state commit and candidate and earlier Route-B source stages of this @@ -7143,11 +7708,26 @@ eligible to act as an independent inspector or reviewer of this task Suggested review focus: -- that the correction changes exactly the four authorized paths from the +- that the approval-state delta from the content-approved replacement + candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` is limited to the ADR + header approval fields, the two leading section-12 paragraphs, the ADR-0012 + register row and `Last updated` header and the strand-safety correction + changelog entry, that the normalized comparison in section 9 shows no + architecture drift from candidate blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, and that the approval-state + ADR-0012 blob is `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`; +- that the content approval is recorded only through owning issue #958, + approver Javi, CTO, approval date 2026-10-09, candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, that the approval date matches + the CTO candidate-content approval record, and that no content-approval, + exact-final-blob approval, review or merge identifier is copied into this + report (section 5, decision 108); +- that the correction changed exactly the four authorized paths from the first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`, that - the corrected ADR-0012 blob is `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, that - its only ADR-0012 delta is the withdrawal-first clause of invariant 43, and - that ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; + the corrected ADR-0012 blob was `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, that + its only ADR-0012 delta was the withdrawal-first clause of invariant 43, and + that ADR-0008, ADR-0010 and ADR-0013 kept their protected blobs; - that invariant 43 now states that withdrawal-first mismatch detection is never suppressed and always records the anomaly durably and visibly, creates no disposition, job or route slot, is not conditioned on computing @@ -7307,15 +7887,18 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `PROPOSED` with no current approver or - approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 and - 2026-10-08 approvals to the earlier exact versions and keep the repository - authority condition and the ADR-0013 reliance distinction; -- that the register row, the new strand-safety correction changelog entry and - this report describe the corrected version as `PROPOSED`, that the - coalescing-seal correction entry and every other historical changelog entry - are byte-preserved, and that the register header `Last updated` is - `2026-10-08`; +- that the header and section 12 carry `APPROVED` with Javi, CTO and + 2026-10-09, identify the approved content by candidate head + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, bind the 2026-09-30, + 2026-10-05, 2026-10-06, 2026-10-07 and 2026-10-08 approvals to the earlier + exact versions and keep the repository authority condition and the ADR-0013 + reliance distinction; +- that the register row, the strand-safety correction changelog entry and + this report describe the corrected version as `APPROVED` on 2026-10-09, + that the coalescing-seal correction entry and every other historical + changelog entry are byte-preserved, and that the register header + `Last updated` is `2026-10-09`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index b41e674dc..f8c258b0f 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-09 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -2930,11 +2932,20 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +Current decision state: **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-09 + +The CTO approved this exact corrected content on 2026-10-09 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and +candidate ADR-0012 blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; this +version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 53a1a5fa5..2ed149917 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-08 +Last updated: 2026-10-09 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-08 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, and the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-09 under the repository decision process, as the architecture content represented by Route-B candidate head `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only: exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, and the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 528c2db04af5c53b9820b946ddac2ebb936eb4cd Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Mon, 12 Oct 2026 14:58:23 +0100 Subject: [PATCH 41/48] THOTH-ASYNC-01-ADR-01: propose Amendment-11 routed-job atomicity correction (#958) --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1174 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 305 ++++- .../decisions/decision-register.md | 4 +- 4 files changed, 1275 insertions(+), 209 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9596d44f..91218d7ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 65fdd7677..6919cf8f5 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -23,16 +23,18 @@ Route-B candidate stage of the withdrawal strand-safety decision-stability correction specified by Specification Amendment 10 after the configured final-head automated review of that Amendment-9 approval-state head, the four-path candidate-consistency correction of that candidate after its -independent exact-candidate inspection, and the Route-B approval-state stage -of that corrected candidate. It records +independent exact-candidate inspection, the Route-B approval-state stage of +that corrected candidate, and the Route-B candidate stage of the atomic +routed-job creation and mutually exclusive anomaly-resolution correction +specified by Specification Amendment 11 after the configured final-head +automated review of that Amendment-10 approval-state head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-10 Route-B approval-state -commit as the current stage and every earlier candidate, approval-state, -recovery, +Route-B correction stages, with the Amendment-11 Route-B candidate as the +current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -54,12 +56,14 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-10 Route-B approval-state commit that contains -this report, an ordinary single-parent additive commit whose direct parent is +Head commit: the Amendment-11 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical Amendment-10 approval-state head +`576a860be47aa1ebe39968f4d3297b26ee1d17df`; its SHA is deliberately not +self-pinned here (section 3). That approval-state head is the direct child of the content-approved Amendment-10 replacement candidate -`e0fa590e06a00d08df883bc4ddeae40b7660fcb8`; its SHA is deliberately not -self-pinned here (section 3). That replacement candidate is the direct child -of the historical first Amendment-10 candidate +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, which is the direct child of +the historical first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`, inspected `CHANGES REQUIRED / F-A10-CAND-01`, which is the direct child of the historical Amendment-9 approval-state head @@ -85,7 +89,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -101,8 +105,9 @@ approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 -consistency-correction and Amendment-10 approval-state stages - Claude (Fable -5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. +consistency-correction, Amendment-10 approval-state and Amendment-11 +candidate stages - Claude (Fable 5.1), bounded implementing agent. Earlier +rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -254,17 +259,25 @@ decision-register blob 53a1a5fa5a75c15b5d24c71849988bdadbdb57e9 implementation-report blob 17773fd661dd4d89f1166a09c17ecd0830ac4eba CHANGELOG blob e78f8e3e94965e1381cddd941b015017e5f90c10 -Amendment-10 Route-B approval-state commit (the commit that contains this report): -ADR-0012 blob f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 (APPROVED; Javi, CTO; 2026-10-09; architecture-equivalent to b41e674dc5e3f1ba03715538ff2396e4d2f50c6c) +historical Amendment-10 approval-state head / Amendment-11 candidate parent: +576a860be47aa1ebe39968f4d3297b26ee1d17df +tree f3c43ab726f4ffbf273e6fe1103b0c7229991e4f +ADR-0012 blob f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 (APPROVED; Javi, CTO; 2026-10-09; architecture-equivalent to b41e674dc5e3f1ba03715538ff2396e4d2f50c6c; CTO exact-final-blob approval recorded on #958 on 2026-10-12; the exact prior approved blob that the Amendment-11 correction corrects) decision-register blob 2ed1499172780c6db82bb5f8808f2368fd8b96d1 CHANGELOG blob b9596d44ff260d6b818ad9c8174e843cf4d0c5b1 +implementation-report blob 65fdd7677b12a10dcf8e53f1f173531e9c088d6b (superseded by the Amendment-11 candidate) + +Amendment-11 Route-B candidate (the commit that contains this report): +ADR-0012 blob 7e461106986fe6efad2021d1ad6f52573e6e9d66 (PROPOSED; material architectural correction of f8c258b0f1b0d19b401c4184ca4dc03a15c865d3) +decision-register blob 98cf125665a3f0011a6ca9c7f90b2b9562931fe7 +CHANGELOG blob 91218d7aee40053dfb84c83414f50434e1398695 feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, -its consistency correction and the Amendment-10 approval-state commit -(unchanged): +its consistency correction, the Amendment-10 approval-state commit and the +Amendment-11 candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -394,7 +407,27 @@ Approved specification: is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and is not copied here; - Amendment-10 Route-B approval-state implementation authorization: #958 - comment `6083303288`. + comment `6083303288`; +- independent exact approval-state source inspection of the Amendment-10 + approval-state head: recorded on #958 on 2026-10-10; its GitHub comment + identifier is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B + doctrine and is not copied here; +- CTO exact-final-blob approval of the Amendment-10 approval-state ADR-0012 + blob `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` at head + `576a860be47aa1ebe39968f4d3297b26ee1d17df`: recorded on #958 on 2026-10-12; + its GitHub comment identifier is GitHub-owned lifecycle evidence under + `ADR-0005` and Route-B doctrine and is not copied here; +- Specification Amendment 11 / atomic routed-job creation and mutually + exclusive anomaly resolution, which accepts the final-head findings + `R-576A-01` and `R-576A-02` as valid and blocking and classifies their + correction as a material architectural correction: #958 comment + `6123767275`; +- CTO specification approval of Amendment 11: #958 comment `6124032150`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-11: #958 comment `6125170650`; +- Amendment-11 Route-B candidate implementation authorization, which also + authorized engineering control to return PR #960 to Draft once before the + candidate was written: #958 comment `6125285446`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -453,12 +486,22 @@ four paths and aligned the withdrawal-first availability wording of invariant 43 and its companion summaries with the fact-only mismatch-detection rule, received independent exact-candidate reinspection (`APPROVED / SATISFIED`) and CTO exact candidate-content approval on -2026-10-09. The commit that contains this report is the one bounded Route-B -approval-state commit that records that approval as durable decision state: -ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and -`Approval date: 2026-10-09`, the ADR-0012 register row and the strand-safety -correction changelog entry moved to the approved state, and this report's -approval-state evidence. No architecture content changes. +2026-10-09. The one bounded Route-B approval-state commit then recorded that +approval as durable decision state (ADR-0012 `PROPOSED -> APPROVED` with +`Approved by: Javi, CTO` and `Approval date: 2026-10-09`, the ADR-0012 +register row and the strand-safety correction changelog entry moved to the +approved state), and the CTO recorded exact-final-blob approval of that ADR +blob. The configured final-head automated review of that approval-state head +exposed two further architecture defects, `R-576A-01` and `R-576A-02`, +specified by Specification Amendment 11. The commit that contains this report +is its Route-B candidate: one ordinary single-parent commit on the +approval-state head that changes exactly the four authorized correction +paths, returns ADR-0012 to `Status: PROPOSED`, requires a new routed job to +become durable only together with its materialized disposition and +indispensable input facts in one transaction, and requires compatible +historical enrollment and approved reconciliation/divergence of one mismatch +anomaly to compete through one database-enforced terminal-resolution +decision. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -864,11 +907,12 @@ approval-state evidence. No architecture content changes. `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, ADR-0012 blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c` - on 2026-10-09 in a durable record on #958. That stage authorized no PR-state or PR-metadata change. -- **Amendment-10 approval-state stage (the commit that contains this - report).** Authorized by #958 comment `6083303288`. One ordinary - single-parent commit directly on the content-approved replacement - candidate records that approval as durable decision state, changing only - the four authorized paths: ADR-0012 `PROPOSED -> APPROVED` with +- **Amendment-10 approval-state stage (historical approval-state head + `576a860be47aa1ebe39968f4d3297b26ee1d17df`).** Authorized by #958 comment + `6083303288`. One ordinary single-parent commit directly on the + content-approved replacement candidate recorded that approval as durable + decision state, changing only the four authorized paths: ADR-0012 + `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and `Approval date: 2026-10-09` and the leading section-12 approval paragraphs, which identify the approved content as the content represented by candidate head @@ -876,15 +920,72 @@ approval-state evidence. No architecture content changes. `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; the ADR-0012 register row and the register's `Last updated` header; the `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` changelog entry; and this - report. No architecture content changes (section 9, "Amendment-10 + report. No architecture content changed (section 9, "Amendment-10 approval-state validation"). The content approval is recorded here through owning issue #958, approver Javi, CTO, approval date 2026-10-09, the candidate head and the candidate ADR-0012 blob; its GitHub comment identifier is not copied into repository source, as the repository-authoritative Route-B rule in `docs/engineering/decisions/README.md` and the approval-state authorization - itself require (section 5, decision 108). PR #960 was already a draft; this - stage authorizes no PR-state or PR-metadata change. + itself require (section 5, decision 108). PR #960 was already a draft; that + stage authorized no PR-state or PR-metadata change. +- **Exact-final-blob approval, final-head review of the Amendment-10 + approval-state head, Specification Amendment 11 and the Amendment-11 Route-B + candidate (the commit that contains this report).** Authorized by #958 + comment `6125285446` after the CTO approved Specification Amendment 11 (#958 + comments `6123767275` and `6124032150`) and a fresh independent CRITICAL + specification review of the base specification and Amendments 1-11 returned + `APPROVED` (#958 comment `6125170650`). After the approval-state head was + published, control independently inspected it on 2026-10-10 and the CTO + recorded exact-final-blob approval of ADR-0012 blob + `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` at head + `576a860be47aa1ebe39968f4d3297b26ee1d17df` on 2026-10-12; control then + separately marked PR #960 Ready, which triggered the configured final-head + automated review of that head, and returned PR #960 to Draft under the + candidate authorization before any candidate source was staged. That review + raised two new findings, recorded by control as `R-576A-01` and `R-576A-02` + (each HIGH, valid and blocking) and classified as one + `MATERIAL ARCHITECTURAL CORRECTION`. `R-576A-01`: section 3.2 and invariant + 8 required a materialized disposition to reference exactly one job and + Amendment 9 ordered attachment to an existing job against its first claim, + but nothing required a newly created routed job and the disposition that + gives it meaning to commit in one transaction, so a router could commit the + job, crash before the disposition, let a worker claim and start it, and + leave recovery owing an obligation that the now-sealed job could no longer + represent. `R-576A-02`: an unresolved mismatch anomaly could be resolved by + compatible historical enrollment or by approved reconciliation/divergence, + but nothing arbitrated the two paths, and because divergence creates no + disposition the `(event_id, route_key)` uniqueness key could not exclude a + concurrent divergence, so contradictory terminal outcomes could both commit. + Amendment 11 selects A11-1, one all-or-nothing transaction for a new routed + job, its materialized disposition and its indispensable input or membership + facts, with no precommit claim or effect, rollback on failure, uniqueness + race or crash, and lost-acknowledgment recovery without duplicates, while + attachment to an existing job keeps the Amendment-9 boundary and + independent direct jobs keep their own semantics; A11-2, one canonical + logical anomaly identity and one database-enforced terminal-resolution + decision shared by enrollment and divergence, with both commit orders, + commit-time authority and compatibility checks, append-only evidence, no + bypass by generic backfill and separately fenced external compensation; + A11-3, composition with the Amendment-8, Amendment-9 and Amendment-10 + rules without write skew or split ordering; and fourteen deterministic + validation cases, with the concrete tables, constraints, locks and SQL left + to the implementation specification. The independent review confirmed both + findings, the classification, both rules, the composition and the + sufficiency of the fourteen-case matrix, and recommended four non-blocking + negative vectors for the later implementation specification. The commit + that contains this report is one ordinary single-parent commit directly on + the approval-state head that changes exactly the four authorized correction + paths: ADR-0012 returns to `Status: PROPOSED` with no current approver or + approval date and implements Amendment 11 (section 4; section 5, decisions + 113-124); the decision-register ADR-0012 row, one new changelog entry and + this report describe the corrected version as `PROPOSED`; and the + 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and 2026-10-09 + CTO approvals remain historical evidence bound to the earlier exact + versions. This stage authorizes no PR-state or PR-metadata change; the PR + #960 title and description, which describe the historical Amendment-10 + source, are GitHub-owned and their reconciliation is a separate control + action. Out-of-scope changes made: NONE @@ -898,10 +999,15 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +MATERIAL ARCHITECTURAL CORRECTION of prior approved blob f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 +(Specification Amendment 11; Route-B candidate stage; candidate status PROPOSED) + +historical Amendment-10 approval-state at 576a860be47aa1ebe39968f4d3297b26ee1d17df: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-09) of the content-approved Amendment-10 replacement candidate blob b41e674dc5e3f1ba03715538ff2396e4d2f50c6c at candidate head e0fa590e06a00d08df883bc4ddeae40b7660fcb8; -no architecture change +no architecture change; CTO exact-final-blob approval of f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 +recorded on #958 on 2026-10-12 historical Amendment-10 replacement candidate at e0fa590e06a00d08df883bc4ddeae40b7660fcb8: F-A10-CAND-01 consistency correction of first-candidate blob d81d56a05bf65a28ea54d07dee58b0e75ef0aff6 @@ -1130,36 +1236,82 @@ inspection outcome is bound to the first candidate's ADR blob `d81d56a05bf65a28ea54d07dee58b0e75ef0aff6` and head. The replacement candidate received its own fresh independent exact-candidate reinspection (`APPROVED / SATISFIED`, `F-A10-CAND-01` resolved in source) and CTO exact -candidate-content approval on 2026-10-09, and the approval-state commit that -contains this report records that approval. +candidate-content approval on 2026-10-09; the historical approval-state +commit `576a860be47aa1ebe39968f4d3297b26ee1d17df` recorded that approval in +ADR-0012 blob `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`, and that blob received +independent approval-state inspection on 2026-10-10 and CTO exact-final-blob +approval at that head on 2026-10-12. + +The Amendment-11 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every record bound to the strand-safety version as +well: + +- the CTO content approval of candidate + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` / ADR blob + `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; +- the 2026-10-09 approval state carried by ADR blob + `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`, its independent approval-state + inspection and its exact-final-blob approval at head + `576a860be47aa1ebe39968f4d3297b26ee1d17df`; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the approval-state head; +- the approval-state-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` changelog entry that record + the strand-safety version as `APPROVED`; the changelog entry is preserved + and remains true only of that historical version, while the register row + now records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. For +the candidate version, independent exact-candidate inspection, CTO exact +candidate-content approval, the approval-state commit, exact-final-blob +approval, independent exact-head review, CTO merge authorization, guarded +merge, programme-local reliance and later migration, deployment, activation +and observation are distinct controlled lifecycle categories; GitHub, through +#958 and PR #960, is authoritative for which of them is pending, satisfied or +blocked for any head. ## 3. Commits -Amendment-10 Route-B approval-state commit (the commit that contains this -report): +Amendment-11 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: record Amendment-10 strand-safety content approval (#958)` - - direct parent: the content-approved Amendment-10 replacement candidate - `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` - - changes only the four authorized paths, and only to record the approval - state (section 4); the approval-state ADR-0012 blob is - `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`, the approval-state - decision-register blob `2ed1499172780c6db82bb5f8808f2368fd8b96d1` and the - approval-state changelog blob `b9596d44ff260d6b818ad9c8174e843cf4d0c5b1` + `THOTH-ASYNC-01-ADR-01: propose Amendment-11 routed-job atomicity correction (#958)` + - direct parent: the historical Amendment-10 approval-state head + `576a860be47aa1ebe39968f4d3297b26ee1d17df` + - changes only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `7e461106986fe6efad2021d1ad6f52573e6e9d66`, the + candidate decision-register blob `98cf125665a3f0011a6ca9c7f90b2b9562931fe7` + and the candidate changelog blob `91218d7aee40053dfb84c83414f50434e1398695` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the approval-state ADR-0012, +here because this report is part of that tree; the candidate ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its candidate blob (section 9). The -commit identities are verified against the remote task-branch head after push -and recorded in the post-publication handoff; once published, the PR #960 -head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The -authorized topology is a non-force fast-forward of the task branch from the -content-approved candidate to the approval-state commit; no merge commit, -rebase, squash, amend, force update or second approval-state commit is -authorized, and no further source commit exists implicitly after it. +recorded above; every other file keeps its approval-state-head blob (section +9). The commit identities are verified against the remote task-branch head +after push and recorded in the post-publication handoff; once published, the +PR #960 head and head tree are GitHub-owned lifecycle evidence under +`ADR-0005`. The authorized topology is a non-force fast-forward of the task +branch from the approval-state head to the candidate commit; no merge commit, +rebase, squash, amend, force update or second candidate commit is authorized, +and no further source commit exists implicitly after it. + +Amendment-10 Route-B approval-state commit (historical): + +- `576a860be47aa1ebe39968f4d3297b26ee1d17df` - + `THOTH-ASYNC-01-ADR-01: record Amendment-10 strand-safety content approval (#958)` + - ordinary single-parent additive commit; direct parent: the content-approved + Amendment-10 replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` + - tree `f3c43ab726f4ffbf273e6fe1103b0c7229991e4f` + - changed only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`, which received independent + approval-state inspection on 2026-10-10 and CTO exact-final-blob approval + at that head on 2026-10-12 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + candidate; the remote head, tree, parent, ADR-0012, decision-register, + changelog and report blobs and the ADR approval state matched the locally + validated object Amendment-10 Route-B replacement candidate commit (content-approved; historical): @@ -1390,11 +1542,23 @@ first-parent order, newest first: ## 4. Files changed -Amendment-10 Route-B approval-state write budget for the commit that contains -this report (authorization #958 comment `6083303288`), identical to the -consistency-correction and Amendment-10 candidate write budgets; exactly four -existing paths, no new file, no deletion, and every other path byte-identical -to the content-approved replacement candidate +Amendment-11 Route-B candidate write budget for the commit that contains this +report (authorization #958 comment `6125285446`; the same four existing paths +as the Amendment-10 budgets); exactly four existing paths, no new file, no +deletion, and every other path byte-identical to the historical Amendment-10 +approval-state head +`576a860be47aa1ebe39968f4d3297b26ee1d17df`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-10 Route-B approval-state write budget (authorization +#958 comment `6083303288`), identical to the consistency-correction and +Amendment-10 candidate write budgets; exactly four existing paths, no new +file, no deletion, and every other path byte-identical to the content-approved +replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`: - `CHANGELOG.md` @@ -1512,12 +1676,115 @@ Authorized new-file paths: NONE at the approval-state, recovery, Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 -approval-state, Amendment-10 candidate, Amendment-10 consistency-correction -and Amendment-10 approval-state stages; all four correction paths already -existed. +approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, +Amendment-10 approval-state and Amendment-11 candidate stages; all four +correction paths already existed. + +Amendment-11 candidate changes in the commit that contains this report, +relative to the approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` +(exactly the four authorized paths): -Amendment-10 approval-state changes in the commit that contains this report, -relative to the content-approved replacement candidate +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement Specification Amendment 11 and record the corrected + version as `PROPOSED`. + - behavioural effect: architecture proposal only. The header carries + `Status: PROPOSED` and no current approver or approval date. Section 3.2 + states, beside the event/business-mutation atomicity rule, that a + separate all-or-nothing rule binds a new routed job to its disposition, + and, after the disposition-cardinality paragraph, that a job newly + created for an obligation becomes durable only together with its + materialized disposition and indispensable input facts; the "In-epoch + contract mismatch" section states that the two resolution paths are + mutually exclusive for one logical anomaly, that every mismatch has one + canonical logical anomaly identity (a function of the mismatch facts, + composition and storage left to the implementation specification) so that + duplicate detection, a generic backfill request or a separately chosen + resolution identifier cannot mint a parallel anomaly, that each anomaly + reaches at most one terminal resolution arbitrated by a database-enforced + decision shared by both paths, and that a retry after terminal resolution + reports the same outcome; the enrollment refusal paragraph binds the + enrollment checks, the anomaly's open state and the terminal decision to + one boundary; the "Historical enrollment, backfill and replay" section + gains the unnumbered subsection "Mutually exclusive terminal resolution + of a mismatch anomaly" (the shared decision, commit-time checks, both + commit orders, loser detection and abort, abort/crash before commit, + retries after commit, count/age/attention transition, generic backfill as + enrollment, separately governed replay, separately fenced external + compensation, distinction from the terminal non-materialized route + disposition); section 3.5 states that a job newly created for an + obligation commits together with its disposition and gains the + unnumbered subsection "Atomic creation of a routed job" (definition of a + routed job, the one-transaction boundary, no precommit claim or effect, + rollback on failure or lost uniqueness race, collision not accepted as + satisfaction, lost-acknowledgment recovery, existing-job attachment + under the Amendment-9 boundary, scope and the direct-job exception + decided by obligation and authority rather than label). Invariants 44 + and 45 are appended; section 8 item 1 and section 9 rollout proofs 3 and + 5 name both rules; section 11 adds the fourteen Amendment-11 validation + cases immediately after the Amendment-9 composition bullet. Section 12 + records the `PROPOSED` state, adds both rules to the description of this + material correction and binds the 2026-09-30, 2026-10-05, 2026-10-06, + 2026-10-07, 2026-10-08 and 2026-10-09 approvals to the historical earlier + versions. Event-emission floors, producer version sets, revision + signatures, eligibility, permanent retirement, mismatch detection + semantics and fact-only anomaly recording, the Amendment-8 + withdrawal/selection order, the Amendment-9 coalescing boundary, the + Amendment-10 strand-safety decision, operator authority, domain + ownership, staff-command authorization, claims and leases, target-scoped + external-effect protection and the BE-04 transition are unchanged. + Candidate blob: `7e461106986fe6efad2021d1ad6f52573e6e9d66`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the material correction makes the ADR-0012 row's `APPROVED` + assertion untrue for the new version. + - behavioural effect: the ADR-0012 row records `PROPOSED` / `Pending`, + binds the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and + 2026-10-09 approvals to the historical earlier exact versions and adds one + summary of the atomic routed-job commit and the mutually exclusive + terminal resolution; its partial-supersession, authority-condition, + ADR-0013 and implementation-authorization sentences are unchanged. The + header `Last updated` advances from `2026-10-09` to `2026-10-12`, the + date of this row change (section 5, decision 121). No other row changes. + Candidate blob: `98cf125665a3f0011a6ca9c7f90b2b9562931fe7`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: required changelog entry. + - behavioural effect: exactly one new + `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry at the top + of `[Unreleased] -> Changed`, describing the corrected version as + `PROPOSED` and stating that the earlier + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-CORRECTION`, `THOTH-ASYNC-01-ADR-01-APPROVAL` and + `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries and the approvals they + record apply only to the historical earlier exact versions. No heading is + added and every existing entry is byte-preserved. Candidate blob: + `91218d7aee40053dfb84c83414f50434e1398695`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-11 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate the current + stage; records the approval-state inspection, the exact-final-blob + approval of the Amendment-10 approval-state blob, the final-head review of + that head, `R-576A-01` and `R-576A-02`, Amendment 11, its CTO approval, + its independent review and the candidate authorization as governing + evidence; records the candidate's validation (section 9); and reframes + statements that would otherwise describe the approval-state commit as the + commit containing this report, recording that commit's identities as + historical facts. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the approval-state head through the Amendment-11 candidate: +every path other than the four correction paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical Amendment-10 approval-state changes in the approval-state commit +`576a860be47aa1ebe39968f4d3297b26ee1d17df`, relative to the content-approved +replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` (exactly the four authorized paths, approval state only): @@ -1576,13 +1843,14 @@ approval state only): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-10 approval-state stage truthfully. - - behavioural effect: documentation only. Makes the approval-state commit - the current stage; records the reinspection, the CTO candidate-content - approval and the approval-state authorization; records the approval-state - validation and no-drift proof (section 9); and reframes statements that - would otherwise describe the replacement candidate as the commit - containing this report, recording that candidate's identities as - historical facts. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the approval-state commit + the then-current stage; recorded the reinspection, the CTO + candidate-content approval and the approval-state authorization; recorded + the approval-state validation and no-drift proof (section 9); and + reframed statements that would otherwise describe the replacement + candidate as the commit containing the report, recording that candidate's + identities as historical facts. All still-true earlier evidence was + preserved. - within authorized write budget: YES Byte-identical to the content-approved replacement candidate through the @@ -2304,7 +2572,8 @@ approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate, the Amendment-8 approval-state commit, the Amendment-9 candidate, the Amendment-9 approval-state commit, the Amendment-10 candidate, its consistency -correction and the Amendment-10 approval-state commit: +correction, the Amendment-10 approval-state commit and the Amendment-11 +candidate: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -2483,7 +2752,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-10 approval-state commit differs from the content-approved +The Amendment-11 candidate differs from the approval-state head +`576a860be47aa1ebe39968f4d3297b26ee1d17df` in exactly the four authorized +correction paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-11 candidate validation"). The +Amendment-10 approval-state commit differed from the content-approved replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-10 approval-state @@ -2555,8 +2828,9 @@ approval-state stage (#958 comment `6045126538`), the Amendment-9 candidate stage (#958 comment `6046895895`), the Amendment-9 approval-state stage (#958 comment `6065879000`), the Amendment-10 candidate stage (#958 comment `6067886387`), the F-A10-CAND-01 consistency-correction stage (#958 comment -`6069074613`) and the Amendment-10 approval-state stage (#958 comment -`6083303288`). This matrix +`6069074613`), the Amendment-10 approval-state stage (#958 comment +`6083303288`) and the Amendment-11 candidate stage (#958 comment +`6125285446`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -2933,8 +3207,8 @@ the Amendment-10 approval-state commit: agent; control reinspected the published replacement candidate and returned `APPROVED / SATISFIED` (section 2) -Amendment-10 approval-state stage - done up to and including the creation of -the commit that contains this report: +Amendment-10 approval-state stage - historical, completed before the +Amendment-11 candidate: - repository inspection: USED (read-only verification of the exact refs, PR #960 open, draft, unmerged and mergeable with head @@ -2955,18 +3229,56 @@ the commit that contains this report: exact at the content-approved replacement candidate and touching no other worktree, branch or ref, was reused for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent approval-state commit that contains this - report) +- commit: USED (the single-parent approval-state commit + `576a860be47aa1ebe39968f4d3297b26ee1d17df`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` to the approval-state commit; the + remote head, tree, parent, ADR-0012, decision-register, changelog and report + blobs and the ADR approval state were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the approval-state head, and returned it to Draft before the + Amendment-11 candidate (section 2) + +Amendment-11 candidate stage - done up to and including the creation of the +commit that contains this report: -Authorized post-commit actions for the approval-state commit - GitHub-owned +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base, PR #960 open, + draft, unmerged and mergeable with head + `576a860be47aa1ebe39968f4d3297b26ee1d17df` and target `feature/worker`, + the approval-state head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008, + ADR-0010 and ADR-0013 blobs, the six-path cumulative footprint, the + absence of any ADR-0012 commit reachable from `develop`, `master` or + `feature/worker` (Route-B eligibility), the presence of the Amendment 11, + CTO specification-approval, independent-review and candidate-authorization + records on #958 with no later control record, the pull_request-triggered + workflow set and its documentation-only classification gate, and, as + stage-time evidence, the PR #960 review-thread state in GitHub; all matched + the authorization; current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 had been returned to draft by control + under the candidate authorization before this stage, so no PR-state + mutation was needed; no PR-state, title or description change is + authorized at this stage) +- source edit: USED (the four authorized correction paths only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; the same detached local + worktree, verified clean and exact at the approval-state head and touching + no other worktree, branch or ref, was reused for construction, and the + existing task branch is the fast-forward target) +- commit: USED (the single-parent candidate commit that contains this report) + +Authorized post-commit actions for the candidate commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` to the approval-state commit; + `576a860be47aa1ebe39968f4d3297b26ee1d17df` to the candidate commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, ADR approval - state, changed path set, cumulative footprint and PR #960 state, and that - no review thread was replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, changed path set, + cumulative footprint and PR #960 state, and that no review thread was + replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -2974,23 +3286,24 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-10 approval-state +Not authorized for the implementing agent at the Amendment-11 candidate stage, each a separate controlled lifecycle category where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): -- any edit to a path other than the four authorized paths: NOT USED (not - authorized) -- any ADR-0012 change outside the approval-state representation, including - any architecture change: NOT USED (not authorized) -- independent inspection or exact-head review: NOT USED (never on the - implementing agent's authority) -- exact-final-blob approval or confirmation: NOT USED (not authorized; never +- any edit to a path other than the four authorized correction paths: NOT USED + (not authorized) +- any ADR-0012 change outside Amendment 11 and the candidate `PROPOSED` state: + NOT USED (not authorized) +- independent exact-candidate inspection or exact-head review: NOT USED (never on the implementing agent's authority) +- CTO candidate-content approval, approval-state commit, fresh `APPROVED` + status, approver or approval date, exact-final-blob approval: NOT USED (not + authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second approval-state commit, amend, rebase, squash, force push or - history rewrite: NOT USED (not authorized) +- any second candidate commit, amend, rebase, squash, force push or history + rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -3026,18 +3339,18 @@ and no migration execution is expected. Under Amendment 3 C2/C11, Amendments authorization, the factual-clarification authorization, the Amendment-8 candidate and approval-state authorizations, the Amendment-9 candidate and approval-state authorizations, the Amendment-10 candidate authorization, the -F-A10-CAND-01 consistency-correction authorization and the Amendment-10 -approval-state authorization, configured Codex/app review is not expected -from a push to a draft PR and is not relied upon at the Amendment-6/7 -candidate, approval-state, control-record recovery, transient-state -correction, factual-clarification, Amendment-8 candidate, Amendment-8 -approval-state, Amendment-9 candidate, Amendment-9 approval-state, -Amendment-10 candidate, consistency-correction or Amendment-10 approval-state -stage; configured Codex/app review runs on separately authorized Ready -transitions, which are GitHub-owned lifecycle events, and any automated -review that nevertheless runs on a draft head is inspected read-only. The -actual job outcomes for the approval-state commit exist only after its -publication; they +F-A10-CAND-01 consistency-correction authorization, the Amendment-10 +approval-state authorization and the Amendment-11 candidate authorization, +configured Codex/app review is not expected from a push to a draft PR and is +not relied upon at the Amendment-6/7 candidate, approval-state, +control-record recovery, transient-state correction, factual-clarification, +Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, +Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, +Amendment-10 approval-state or Amendment-11 candidate stage; configured +Codex/app review runs on separately authorized Ready transitions, which are +GitHub-owned lifecycle events, and any automated review that nevertheless +runs on a draft head is inspected read-only. The actual job outcomes for the +candidate commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -3047,8 +3360,9 @@ Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, -consistency-correction or Amendment-10 approval-state stage, followed the -same documentation-only classification, with no manual dispatch or rerun: +consistency-correction, Amendment-10 approval-state or Amendment-11 candidate +stage, followed the same documentation-only classification, with no manual +dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -3068,6 +3382,7 @@ e2588f7b7e63a5c71968d87a3409f0ff48635e6d: 4 success (classify x3, check-changelo e41f377202379238b91eb43d60f5115aa8adc20f: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-9 approval-state head) 6e521f00b039769b28a26ab069c7e97d22de4e08: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-10 candidate) e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-10 replacement candidate) +576a860be47aa1ebe39968f4d3297b26ee1d17df: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-10 approval-state head) ``` Manually initiated external actions (anything the implementing agent @@ -3891,7 +4206,8 @@ candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`): before editing, touching no other worktree, branch, ref, stash or uncommitted change. -Amendment-10 approval-state stage (the commit that contains this report): +Amendment-10 approval-state stage (historical approval-state head +`576a860be47aa1ebe39968f4d3297b26ee1d17df`): 105. The approval-state header uses the same field order as the earlier approved ADR-0012 versions (`Status`, `Date`, `Approved by`, @@ -3948,14 +4264,150 @@ Amendment-10 approval-state stage (the commit that contains this report): byte-identical. 111. Architecture no-drift is proven by the exact normalized comparison in section 9 ("Amendment-10 approval-state validation"), not by assertion. -112. This report does not self-pin the approval-state commit's SHA, its tree - or its own final blob; it records only non-self-referential evidence and - the approval-state ADR-0012, decision-register and changelog blobs, which - do not depend on its bytes. Because the report now lives in the - approval-state commit, statements that described the replacement - candidate as the commit containing this report are reframed as history, - and the replacement candidate's identities, now the approval-state - parent, are recorded in section 1 as historical facts. +112. The approval-state report did not self-pin the approval-state commit's + SHA, its tree or its own final blob; it recorded only non-self-referential + evidence and the approval-state ADR-0012, decision-register and changelog + blobs, which did not depend on its bytes. Because the report then lived in + the approval-state commit, statements that described the replacement + candidate as the commit containing the report were reframed as history, + and the replacement candidate's identities, the approval-state parent, + were recorded in section 1 as historical facts; section 1 now records the + approval-state commit's identities as historical facts because that head + is the Amendment-11 candidate's parent. + +Amendment-11 Route-B candidate (the commit that contains this report). Items +115, 117 and 120 record interpretations of the approved specification for +explicit control inspection: + +113. The two Amendment-11 rules are each stated once, in a new unnumbered + subsection following the style of the Amendment-9 and Amendment-10 + corrections: A11-1 as "Atomic creation of a routed job" at the end of + section 3.5, next to the coalescing boundary it composes with, and A11-2 + as "Mutually exclusive terminal resolution of a mismatch anomaly" at the + end of "Historical enrollment, backfill and replay" in section 3.2, next + to the enrollment validation and immediately before the withdrawal + section whose order it composes with. The places that depend on them + point to them: the section-3.2 event-atomicity paragraph, the + disposition-cardinality paragraph, the "In-epoch contract mismatch" + resolution paragraphs, the enrollment refusal paragraph and the + section-3.5 disposition paragraph. The heading outline gains exactly + those two headings. +114. A11-1 is stated as Amendment 11 states it: a routed job is a job created + to satisfy an event-route obligation through event-route materialization + or a first historical enrollment; the new job, its materialized + `(event_id, route_key)` disposition and every indispensable durable + job-input or membership fact become durable in one PostgreSQL + transaction, visible together on commit; a failure, uniqueness conflict + or crash before commit leaves neither; no precommit claim, start or + external effect; rollback on disposition insertion failure or lost + uniqueness race with no orphan job, phantom satisfaction, consumed slot + or unaccounted effect; a collision never accepted alone as satisfaction; + lost-acknowledgment recovery observing the same identities with no + duplicate; and no table, constraint or SQL selected. +115. The direct-job exception is written as the Amendment and the independent + review require: an independently authorized scheduled, operator, + reconciliation, replay or current-state job that is not created to + satisfy a route disposition keeps its existing semantics, the rule + invents no disposition for it, and such a job may not be used to satisfy + a route obligation without the disposition the boundary requires. + Interpretation: the sentence "whether a job is routed is decided by the + obligation it is created to satisfy and the authority under which it is + created, not by a label" expresses the review's observation 8 that the + exception cannot be claimed by labelling; it introduces no new job class. +116. Existing-job attachment is left to the Amendment-9 boundary unchanged: + the new subsection states that an attachment to an open job commits a + disposition under that boundary, a sealed job acquires no new + disposition, a losing attempt leaves its obligation owed and follows the + existing error and recovery path, and a simultaneously created routed + job and an other-route attachment obey the one linearized order. No + sentence of the "Coalescing-open and coalescing-sealed jobs" subsection + changes. +117. A11-2 is stated as Amendment 11 states it: one canonical logical anomaly + identity, one database-enforced terminal-resolution decision shared by + enrollment and divergence, commit-time validation of authorization, open + state and current compatibility, availability or reconciliation + conditions, both commit orders with their exact consequences, loser + detection and abort or deterministic refusal, abort or crash before + commit leaving the anomaly open and retryable, retries after commit + observing one outcome, append-only detection and resolution evidence, + generic backfill treated as enrollment that cannot bypass divergence, + separately governed replay, separately fenced external compensation and + the distinction from the terminal non-materialized route disposition. + Interpretation: the canonical identity is described as "a function of + the mismatch facts rather than of the detection, backfill request or + operator procedure that observed it", with composition and storage left + to the implementation specification; this expresses the Amendment's + requirement that parallel resolution identities cannot bypass the one + terminal choice without selecting an identity scheme. The outcome names + `ENROLLED` and `RECONCILED/DIVERGED` are the Amendment's names; exact + spelling remains an implementation detail under section 3.4's existing + rule for state names. +118. The enrollment-first order binds the terminal outcome, the disposition + and, where a new routed job is created, the A11-1 transaction into one + decision (validation case 13); where enrollment attaches to an existing + open job, the Amendment-9 boundary applies instead, as the Amendment and + the review require. +119. A11-3 composition is stated in both subsections and in invariants 44 and + 45: the rules compose with `(event_id, route_key)` uniqueness across + epochs and revisions, the Amendment-8 withdrawal/selection order (the + enrollment path's availability check is placed inside that order), the + Amendment-9 seal and the Amendment-10 strand-safety decision, with no + second lifecycle, lock order or write skew; event-emission floors, + producer version sets, revision signatures, eligibility, retirement, + detection semantics, fact-only anomaly recording, operator authority, + domain ownership, staff-command authorization, claims and leases, + target-scoped external-effect protection and the BE-04 transition are + byte-identical. No exactly-once external effect is assumed; the + subsection states that PostgreSQL is never assumed to commit atomically + with a third-party provider write. +120. Invariants 44 and 45 are appended after invariant 43; invariants 1-43 + are byte-identical and unrenumbered (decision 23 pattern). + Interpretation: invariant 45 carries the "generic backfill cannot bypass + a terminal divergence" and "replay replaces no terminal outcome" + sentences so that the binding invariant, not validation prose alone, + forbids the bypass the independent review's recommended negative vectors + target; the four recommended vectors themselves are left to the later + implementation specification, as the review intended. +121. The decision-register header `Last updated` advances to `2026-10-12`, + the date on which the candidate changes the ADR-0012 row (decisions 25, + 57, 78, 85 and 109 pattern); only the ADR-0012 row changes otherwise. +122. The fourteen Amendment 11 validation cases are added to section 11 as + fourteen bullets, one-to-one and in order with the Amendment's cases 1-14, + immediately after the Amendment-9 composition bullet so that the A11-1 + cases sit with the coalescing and first-claim tests and the A11-2 cases + follow them; every Amendment 1-10 validation requirement is retained. The + candidate is documentation-only: it specifies future deterministic + runtime validation and executes no SQL, migration, test harness or + concurrency test. +123. The header drops `Approved by` / `Approval date` and section 12 restores + the two candidate-state paragraphs used by the earlier Route-B candidates + (decisions 8, 9, 58, 80 and 99), adds both rules to the description of + this material correction and binds the 2026-10-09 approval, like the + 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 and 2026-10-08 approvals, + to the historical earlier version without embedding its identities. The + changelog gains one new entry and every historical entry, including the + record of the 2026-10-09 approval, stays byte-preserved (decisions 26 and + 59). The approval-state inspection and exact-final-blob approval of the + approval-state ADR blob are recorded through owning issue #958, their + dates and the blob and head they concern; their GitHub comment + identifiers are not copied, consistent with decisions 37, 64 and 108; no + review, thread or review-comment identifier of the final-head automated + review is copied; and `R-576A-01` and `R-576A-02` are recorded by their + control-assigned names. The Amendment 11, CTO specification-approval, + independent-review and candidate-authorization records are cited by + their comment identifiers as specification and authorization evidence, + consistent with section 2. +124. This report does not self-pin the candidate commit's SHA, its tree or its + own final blob; because the report now lives in the candidate commit, + statements that described the approval-state commit as the commit + containing this report are reframed as history, and that commit's + identities, now the candidate parent, are recorded in section 1 as + historical facts. Construction reused the same detached scratch + worktree, verified clean and exact at the approval-state head before + editing, touching no other worktree, branch, ref, stash or uncommitted + change. The PR #960 title and description describe the historical + Amendment-10 source; this stage is not authorized to change them, and + their reconciliation is reported to control as a separate action. List any deviation from the specification requiring authorization: @@ -4057,8 +4509,14 @@ List any deviation from the specification requiring authorization: - Amendment-10 approval-state stage: NONE. The approval-state transformation followed its separate authorization exactly; no fifth path, new file, deletion, architecture change, second commit, amend, rebase, squash, force - push or history rewrite was needed or performed before the commit that - contains this report was created. + push or history rewrite was needed or performed before the approval-state + commit was created. +- Amendment-11 candidate stage: NONE. No fifth path, new file, deletion, ADR + change outside Amendment 11 and the candidate state, second commit, amend, + rebase, squash, force push or history rewrite was needed or performed + before the commit that contains this report was created. Decisions 115, + 117 and 120 are recorded for control inspection as interpretations within + the authorized scope, not as deviations. ## 6. Database and migration effects @@ -4068,8 +4526,10 @@ No schema, data, index, constraint or `thoth-api/src/schema.rs` effect. The activation-epoch and route-contract-revision model, including its required database-level revision-equivalence property and its required database-atomic ordering of revision withdrawal against new revision selection and of -coalescing attachment against a job's first claim/start and its required -database-consistent strand-safety decision for revision withdrawal, is +coalescing attachment against a job's first claim/start, its required +database-consistent strand-safety decision for revision withdrawal, its +required atomic creation of a routed job with its disposition and its +required mutually exclusive terminal resolution of a mismatch anomaly, is architecture for a future separately authorized shared-engine implementation; it creates no migration obligation by itself and names no table, column or index. @@ -4085,8 +4545,10 @@ candidate recorded here. The verified BE-04 per-consumer impact record for the ADR's later legacy-contract retirement is preserved unchanged in Appendix A; neither the activation-epoch correction, the route-contract-revision correction, the withdrawal/selection ordering -correction, the coalescing-seal ordering correction nor the withdrawal -strand-safety correction changes a released contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts +correction, the coalescing-seal ordering correction, the withdrawal +strand-safety correction nor the routed-job atomicity and anomaly +terminal-resolution correction changes a released contract or adds a +consumer. The route, revision, anomaly and materializer-compatibility concepts are internal to the future shared engine; any later external read or executor contract derived from them needs its own specification, merged upstream contract and downstream task. @@ -4133,8 +4595,10 @@ first candidate `6e521f00b039769b28a26ab069c7e97d22de4e08`. "Consistency-correction validation" records the validation of the historical content-approved replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`. "Amendment-10 approval-state -validation" at the end of this section records the validation of the commit -that contains this report. +validation" records the validation of the historical approval-state commit +`576a860be47aa1ebe39968f4d3297b26ee1d17df`. "Amendment-11 candidate validation" +at the end of this section records the validation of the commit that +contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -7175,17 +7639,23 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 ``` -The approval-state ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above; the ADR-0012 blob `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` is the exact -final blob presented for CTO exact-final-blob approval at this stage. This -report's own final blob, the approval-state tree and the approval-state commit -SHA depend on this report's bytes and are not self-pinned; they are verified -against the remote head after push and recorded in the post-publication -handoff. +The approval-state ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the approval-state report's bytes, so they +were recorded above; the ADR-0012 blob `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` +was the exact final blob presented for CTO exact-final-blob approval at that +stage and received it on 2026-10-12 after independent approval-state +inspection on 2026-10-10. The approval-state report's own final blob, the +approval-state tree and the approval-state commit SHA depended on that +report's bytes and were not self-pinned in it; they were verified against the +remote head after push and recorded in the post-publication handoff, and +section 1 now records them as historical facts: commit +`576a860be47aa1ebe39968f4d3297b26ee1d17df`, tree +`f3c43ab726f4ffbf273e6fe1103b0c7229991e4f`, report blob +`65fdd7677b12a10dcf8e53f1f173531e9c088d6b`. -Required topology of the approval-state commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the approval-state commit, checked after it was created +and before push; the published approval-state commit satisfied every +requirement: ```text git rev-list --parents -n 1 HEAD @@ -7204,6 +7674,242 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-11 candidate validation + +Environment: the same detached local Git worktree, verified clean (no tracked +or untracked change) and exact at the historical Amendment-10 approval-state +head `576a860be47aa1ebe39968f4d3297b26ee1d17df` (tree +`f3c43ab726f4ffbf273e6fe1103b0c7229991e4f`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, no ADR-0012 commit reachable from +`develop`, `master` or `feature/worker` (Route-B eligibility), +`feature/async/adr-0012` at +`576a860be47aa1ebe39968f4d3297b26ee1d17df` with single parent +`e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and no descendant, ADR-0012 blob +`f8c258b0f1b0d19b401c4184ca4dc03a15c865d3`, report blob +`65fdd7677b12a10dcf8e53f1f173531e9c088d6b`, decision-register blob +`2ed1499172780c6db82bb5f8808f2368fd8b96d1` and changelog blob +`b9596d44ff260d6b818ad9c8174e843cf4d0c5b1` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs, PR #960 open, draft, unmerged and +mergeable with that head and target `feature/worker`, the cumulative PR +footprint exactly the six paths, the Amendment 11, CTO specification-approval, +independent-review and candidate-authorization records present on #958 with +no later control record, the pull_request-triggered workflows gated by the +documentation-only classifier, and PR #960 review-thread state that matched +the stage authorization (stage-time evidence; current review-thread state +remains GitHub-owned). ADR-0012, the decision register and the changelog were +edited first and their deterministic blobs recorded; then only this report +was edited; all four were staged, and every check below ran against that +exact final staged index before the candidate commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 576a860be47aa1ebe39968f4d3297b26ee1d17df +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the approval-state head (exactly the four authorized +paths): + +```text +git diff --cached --name-only 576a860be47aa1ebe39968f4d3297b26ee1d17df +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 576a860be47aa1ebe39968f4d3297b26ee1d17df +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 576a860be47aa1ebe39968f4d3297b26ee1d17df +1 0 CHANGELOG.md +993 181 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +279 26 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (the `Last updated` header line and the ADR-0012 row +only): + +```text +git diff --cached -U0 576a860be47aa1ebe39968f4d3297b26ee1d17df -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-09 + +diff <(git show 576a860be47aa1ebe39968f4d3297b26ee1d17df:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-12 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 576a860be47aa1ebe39968f4d3297b26ee1d17df -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 576a860be47aa1ebe39968f4d3297b26ee1d17df:CHANGELOG.md) <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state, outline and hunks: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +3186:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'APPROVED' +every match is in section 12 (lines 3189-3257): the sentence that the version +may carry APPROVED only after CTO approval, the historical-approval paragraph +and the authority condition; no sentence asserts that this version is approved + +diff <(git show 576a860be47aa1ebe39968f4d3297b26ee1d17df:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +12a13 +> ### Mutually exclusive terminal resolution of a mismatch anomaly +18a20 +> ### Atomic creation of a routed job +exit 1 (the two added headings are the complete outline change) + +git diff --cached -U0 576a860be47aa1ebe39968f4d3297b26ee1d17df -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -5,2 +4,0 @@ Date: 2026-09-29 +@@ -229 +227,2 @@ and event record are written in the same transaction. Either both commit or +@@ -377,0 +377,9 @@ which points to no job (section 3.5 and invariant 8). +@@ -557,3 +565,21 @@ The anomaly remains open until, under explicit authorization, either: +@@ -615 +641,4 @@ If any check fails, or the revision has been withdrawn, the enrollment is +@@ -636,0 +666,73 @@ not by itself grant permission to duplicate an external effect. +@@ -1157 +1259,3 @@ eligible route obligation eventually receives exactly one. A successfully +@@ -1294,0 +1399,54 @@ order for claim/start against attachment. +@@ -2270,0 +2429,38 @@ execution until a separate approved architecture decision says otherwise. +@@ -2289,0 +2486,3 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: +@@ -2500 +2699,2 @@ Rollout should prove in order: +@@ -2503 +2703,2 @@ Rollout should prove in order: +@@ -2839,0 +3041,50 @@ Before shared implementation can be approved, evidence must include: +@@ -2935,4 +3186 @@ shared-engine tests. +@@ -2940,9 +3188,3 @@ Approval date: 2026-10-09 +@@ -2974 +3216,8 @@ re-evaluation, so that a stale strand-prevention scan can never commit a +@@ -2989 +3238,5 @@ database-consistent strand-safety decision for revision withdrawal, carried + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^### Mutually exclusive\|^### Atomic creation\|^44\. \|^45\. ' +666:### Mutually exclusive terminal resolution of a mismatch anomaly +1399:### Atomic creation of a routed job +2429:44. A new logical job created to satisfy an event-route obligation - through +2446:45. Every in-epoch contract-mismatch anomaly has one canonical logical + +diff <(git show 576a860be47aa1ebe39968f4d3297b26ee1d17df:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^43\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^43\. /p') +exit 0; no output (the section heading and invariants 1-42 and the first line of invariant 43 byte-identical; the remainder of invariant 43 is inside the unchanged region between hunks) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'routed job' +26 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'terminal resolution\|terminal-resolution' +15 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'canonical logical' +2 +``` + +The eighteen hunks are: the header status and approval fields (two hunks); +the section-3.2 event-atomicity pointer; the section-3.2 routed-job +paragraph after the disposition-cardinality paragraph; the "In-epoch contract +mismatch" mutual-exclusion, canonical-identity and resolution-recording +paragraphs; the enrollment refusal pointer; the new "Mutually exclusive +terminal resolution of a mismatch anomaly" subsection; the section-3.5 +disposition pointer; the new "Atomic creation of a routed job" subsection; +invariants 44 and 45; section 8 item 1; section 9 rollout proofs 3 and 5 (two +hunks); the fourteen section-11 validation cases; and the four section-12 +hunks that record the candidate state, extend the description of this +material correction and bind the 2026-10-09 approval to the historical +earlier version. No other line of the ADR changes; in particular the +Amendment-8 withdrawal/selection paragraphs, the Amendment-9 +"Coalescing-open and coalescing-sealed jobs" subsection, the Amendment-10 +decision-stability paragraphs and both anomaly/withdrawal orders, the +mismatch definition and its recorded-facts list, the detection paragraph, +the enrollment validation list, the claim, lease and attempt-phase text of +sections 3.4 and 3.6 and the disposition-uniqueness text of section 3.2 are +byte-identical to the approval-state head. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) 7e461106986fe6efad2021d1ad6f52573e6e9d66 +decision-register.md 98cf125665a3f0011a6ca9c7f90b2b9562931fe7 +CHANGELOG.md 91218d7aee40053dfb84c83414f50434e1398695 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +7e461106986fe6efad2021d1ad6f52573e6e9d66 +``` + +The candidate ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the candidate tree and the candidate +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the candidate commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 576a860be47aa1ebe39968f4d3297b26ee1d17df (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 576a860be47aa1ebe39968f4d3297b26ee1d17df HEAD +required: exit 0; no output + +git diff --name-only 576a860be47aa1ebe39968f4d3297b26ee1d17df HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -7385,6 +8091,30 @@ the approval-state commit, in particular any statement naming the first candidate as the direct parent of the commit containing this report or describing the replacement candidate as still the current `PROPOSED` candidate, and reframed them. +Amendment-11 candidate stage: compared the staged ADR-0012 with the +approval-state head's ADR blob `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` hunk +by hunk against every requirement of Amendment 11 A11-1, A11-2 and A11-3 and +the fourteen validation cases, and against the independent review's four +recommended negative vectors; confirmed each requirement is expressed in +section 3.2 (the event-atomicity pointer, the routed-job paragraph, the +mismatch resolution paragraphs, the enrollment refusal pointer and the new +terminal-resolution subsection), section 3.5 (the disposition pointer and the +new routed-job subsection), invariants 44 and 45, sections 8, 9 and 11, and +the candidate state in the header and section 12; confirmed that every +changed hunk concerns routed-job atomicity, anomaly terminal resolution, +their validation or the candidate state, and that the ADR-0008/ADR-0010 +partial-supersession header text, the Amendment-8, Amendment-9 and +Amendment-10 paragraphs, the mismatch definition and detection paragraph, the +enrollment validation list, the claim, attempt-phase and effect-boundary text +and all other architecture are byte-identical to the approval-state head; +compared the staged register and changelog with the approval-state-head blobs +(only the `Last updated` header and the ADR-0012 row, and only one added +entry); confirmed the protected decision and control blobs are unchanged; and +read the report for statements that would become false once it moves into +the candidate commit, in particular any statement naming the replacement +candidate as the direct parent of the commit containing this report or +presenting the approval-state commit as the current stage, and reframed +them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -7402,9 +8132,10 @@ the content-approved Amendment-9 candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, the Amendment-9 approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`, the first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` and the content-approved -replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, are -historical evidence; the live PR #960 diff of the approval-state commit -exists only after its publication and is GitHub-owned lifecycle evidence. +replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and the Amendment-10 +approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, are +historical evidence; the live PR #960 diff of the candidate commit exists +only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -7419,9 +8150,10 @@ transient-state correction authorization, the factual-clarification authorization, the Amendment-8 candidate authorization, the Amendment-8 approval-state authorization, the Amendment-9 candidate authorization, the Amendment-9 approval-state authorization, the Amendment-10 candidate -authorization, the F-A10-CAND-01 consistency-correction authorization and the -Amendment-10 approval-state authorization, an automatic CI failure, or any -migration execution or Docker publication, is a HOLD condition. +authorization, the F-A10-CAND-01 consistency-correction authorization, the +Amendment-10 approval-state authorization and the Amendment-11 candidate +authorization, an automatic CI failure, or any migration execution or Docker +publication, is a HOLD condition. ## 12. Rollout and rollback @@ -7455,20 +8187,23 @@ four-path candidate commit, the Amendment-9 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-10 candidate authorization permitted exactly one four-path candidate commit, the F-A10-CAND-01 consistency-correction authorization permitted exactly one -direct-child four-path correction commit, and the Amendment-10 approval-state -authorization permits exactly one direct-child approval-state commit; any -further source correction exhausts those authorizations and requires a fresh -CTO authorization (Amendment 3 C10; Amendments 6-10; -`docs/engineering/decisions/README.md`). +direct-child four-path correction commit, the Amendment-10 approval-state +authorization permitted exactly one direct-child approval-state commit, and +the Amendment-11 candidate authorization permits exactly one four-path +candidate commit; any further source correction exhausts those +authorizations and requires a fresh CTO authorization (Amendment 3 C10; +Amendments 6-11; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-10 approval-state stage - the implementing agent performed no independent inspection or review, - exact-final-blob approval or confirmation, PR #960 title/description - reconciliation, Draft/Ready change, automated-review request, thread - adjudication, exact-head review or merge (section 4.2). +- Historical stage-time evidence: at the Amendment-11 candidate stage the + implementing agent performed no independent inspection or review, + candidate-content approval, PR #960 title/description reconciliation, + Draft/Ready change, automated-review request, thread adjudication, + exact-head review or merge (section 4.2). The PR #960 title and + description describe the historical Amendment-10 source; their + reconciliation is a separate control action. Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -7512,9 +8247,12 @@ Monitoring required: NONE review and a separate authorization for one four-path candidate commit, the invariant-43 wording inconsistency found by the independent exact-candidate inspection of that candidate only under a separate - authorization for one four-path correction commit, and its approval state - only under a separate authorization for this one direct-child - approval-state commit. + authorization for one four-path correction commit, its approval state only + under a separate authorization for one direct-child approval-state commit, + and the routed-job atomicity and anomaly terminal-resolution defects found + by the final-head review of that approval-state head only under + Specification Amendment 11, its independent review and a separate + authorization for this one four-path candidate commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -7536,8 +8274,8 @@ Monitoring required: NONE approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, - the Amendment-10 candidate, its consistency correction and this - Amendment-10 approval-state commit. + the Amendment-10 candidate, its consistency correction and approval-state + commit and this Amendment-11 candidate. ## 14. Unresolved issues @@ -7678,10 +8416,39 @@ Monitoring required: NONE decisions 101-104); control's independent reinspection of that candidate found `F-A10-CAND-01` resolved in source and no blocking exact-candidate finding; Javi, CTO approved its exact architecture content on 2026-10-09; - and the approval-state commit that contains this report records that - approval. `R-E41F-01` is closed at specification level by Amendment 10; its - source closure is adjudicated by the independent exact-head review controls - that precede any merge, which GitHub records. + the historical approval-state commit + `576a860be47aa1ebe39968f4d3297b26ee1d17df` recorded that approval; and the + CTO recorded exact-final-blob approval of the resulting ADR-0012 blob + `f8c258b0f1b0d19b401c4184ca4dc03a15c865d3` on 2026-10-12. `R-E41F-01` is + closed at specification level by Amendment 10 and, per the CTO's + Amendment-11 record, corrected in the architecture of that approval-state + head; its thread adjudication belongs to the review controls that GitHub + records. +- The configured final-head automated review of the Amendment-10 + approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` opened two + further threads, identified here substantively and by their + control-assigned finding names. `R-576A-01`: nothing required a newly + created routed job and the materialized disposition that gives it meaning + to commit in one transaction, so a router could commit the job, crash + before the disposition, let a worker claim and start it, and leave + recovery owing an obligation that the now-sealed job could no longer + represent. `R-576A-02`: compatible historical enrollment and approved + reconciliation/divergence of one mismatch anomaly were not arbitrated by a + shared database-enforced decision, and because divergence creates no + disposition the `(event_id, route_key)` key could not exclude it, so + contradictory terminal outcomes could both commit. Control accepted both + as valid, HIGH and blocking and classified their correction as one + material architectural correction; the CTO approved Specification Amendment + 11; a fresh independent CRITICAL specification review of the base + specification and Amendments 1-11 returned `APPROVED`, confirming both + findings, the classification, both rules, the composition and the + fourteen-case validation matrix and recommending four non-blocking negative + vectors for the implementation specification; and the Route-B candidate + that contains this report implements the correction in source as + `PROPOSED` (section 4; section 5, decisions 113-124). Both findings are + closed at specification level by Amendment 11; whether the candidate + closes them in source is decided by the independent inspection and review + of the candidate, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -7695,9 +8462,10 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-10 approval-state commit also -implemented the Amendment-10 replacement candidate and first candidate, the -Amendment-9 approval-state commit and candidate, the Amendment-8 +work. The implementing agent of this Amendment-11 candidate also implemented +the Amendment-10 approval-state commit, the Amendment-10 replacement +candidate and first candidate, the Amendment-9 approval-state commit and +candidate, the Amendment-8 approval-state commit and candidate, the factual clarification, the transient-state correction, the control-record recovery, the Amendment-6/7 approval-state commit and candidate and earlier Route-B source stages of this @@ -7708,8 +8476,39 @@ eligible to act as an independent inspector or reviewer of this task Suggested review focus: +- that the candidate changes exactly the four authorized paths from the + approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, that the + candidate ADR-0012 blob is `7e461106986fe6efad2021d1ad6f52573e6e9d66`, and that + ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that every Amendment 11 A11-1 requirement is expressed in ADR-0012: the + routed-job definition, the one-transaction boundary for job, disposition + and indispensable input or membership facts, no precommit claim or + effect, rollback on failure, uniqueness race or crash with no orphan job, + phantom satisfaction, consumed slot or unaccounted effect, collision never + accepted alone as satisfaction, lost-acknowledgment recovery without + duplicates, existing-job attachment under the Amendment-9 boundary, and + the direct-job exception decided by obligation and authority rather than + label (section 5, decisions 114-116); +- that every Amendment 11 A11-2 requirement is expressed in ADR-0012: one + canonical logical anomaly identity, one database-enforced + terminal-resolution decision shared by both paths, commit-time checks, + both commit orders with their exact consequences, loser detection and + abort or refusal, abort or crash before commit, retries after commit, + append-only evidence, generic backfill unable to bypass divergence, + separately governed replay, separately fenced external compensation and + the distinction from the terminal non-materialized route disposition + (section 5, decisions 117-118); +- that A11-3 composition with the Amendment-8 order, the Amendment-9 seal and + the Amendment-10 strand-safety decision is stated without a second + lifecycle, lock order or write skew, that invariants 44 and 45 and the + fourteen section-11 cases are present one-to-one with the Amendment, that + invariants 1-43 and every earlier validation case are byte-identical, and + that decisions 115, 117 and 120 decide no new architecture; +- that every ADR-0012 hunk outside those concerns is limited to the candidate + `PROPOSED` state (header and section 12) and that all other architecture is + byte-identical to the approval-state head; - that the approval-state delta from the content-approved replacement - candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` is limited to the ADR + candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` was limited to the ADR header approval fields, the two leading section-12 paragraphs, the ADR-0012 register row and `Last updated` header and the strand-safety correction changelog entry, that the normalized comparison in section 9 shows no @@ -7887,18 +8686,15 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `APPROVED` with Javi, CTO and - 2026-10-09, identify the approved content by candidate head - `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob - `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`, bind the 2026-09-30, - 2026-10-05, 2026-10-06, 2026-10-07 and 2026-10-08 approvals to the earlier - exact versions and keep the repository authority condition and the ADR-0013 - reliance distinction; -- that the register row, the strand-safety correction changelog entry and - this report describe the corrected version as `APPROVED` on 2026-10-09, - that the coalescing-seal correction entry and every other historical - changelog entry are byte-preserved, and that the register header - `Last updated` is `2026-10-09`; +- that the header and section 12 carry `PROPOSED` with no current approver or + approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, + 2026-10-08 and 2026-10-09 approvals to the earlier exact versions and keep + the repository authority condition and the ADR-0013 reliance distinction; +- that the register row, the new routed-job atomicity correction changelog + entry and this report describe the corrected version as `PROPOSED`, that + the strand-safety correction entry and every other historical changelog + entry are byte-preserved, and that the register header `Last updated` is + `2026-10-12`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. @@ -7915,8 +8711,8 @@ append-only activation-epoch model, and the route-level event kind/version contract and "emits K" emission floor described in earlier rounds are superseded by immutable route-contract revisions and producer-version coverage in the current section 3.2. The 2026-09-30, 2026-10-05, 2026-10-06, -2026-10-07 and 2026-10-08 approvals and every review recorded here are bound -to the exact historical versions and heads they name. +2026-10-07, 2026-10-08 and 2026-10-09 approvals and every review recorded +here are bound to the exact historical versions and heads they name. ### Initial candidate and review round 1 @@ -8714,9 +9510,9 @@ stage: deduplication boundary. 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO approval of that exact corrected content; earlier approvals and reviews, - including the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07 and - 2026-10-08 approvals, remain bound to the earlier exact versions and heads - they name. + including the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, + 2026-10-08 and 2026-10-09 approvals, remain bound to the earlier exact + versions and heads they name. 11. The route-contract-revision model does not permit silent reinterpretation or loss either: every epoch is bound to one immutable revision, an event outside its epoch revision's accepted versions becomes a visible anomaly @@ -8751,5 +9547,21 @@ stage: current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. +15. The routed-job atomicity rule does not permit an orphan or claimable + routed job or a phantom route satisfaction: a new job created to satisfy + a route obligation becomes durable only together with its materialized + disposition and indispensable input facts in one transaction, is never + claimable or effectful before that commit, rolls back with a failed + disposition, and is recovered as the same job and disposition after a + lost acknowledgment, with attachment to an existing job governed by the + coalescing seal and independent direct jobs keeping their own semantics. +16. The anomaly terminal-resolution rule does not permit contradictory + outcomes: each mismatch anomaly has one canonical identity and at most + one database-enforced terminal resolution between compatible historical + enrollment and approved reconciliation/divergence, in both commit orders + the loser is refused before any disposition, job or effect, divergence + leaves the slot unused and unavailable to the losing enrollment, + detection and resolution evidence stay append-only, and external + compensation remains separately fenced. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index f8c258b0f..7e4611069 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-09 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -226,7 +224,8 @@ operator actions. Where an event is caused by a PostgreSQL business mutation, the business change and event record are written in the same transaction. Either both commit or -neither commits. +neither commits. A separate all-or-nothing rule binds a newly created routed +job to its route disposition ("Atomic creation of a routed job", section 3.5). Every event has a unique durable `event_id`. Route eligibility and routing completion are determined from durable database state, not from worker-local @@ -375,6 +374,15 @@ pending, running, waiting or terminal. The only alternative is the explicit, authorized and audited terminal non-materialized disposition described below, which points to no job (section 3.5 and invariant 8). +When materializing an obligation requires a **new** logical job, that job is +created only together with its disposition: the new routed job, the +materialized `(event_id, route_key)` disposition that gives it meaning and +every durable job-input or membership fact indispensable for that +disposition's effect semantics become durable in one PostgreSQL transaction, +or none of them does. "Atomic creation of a routed job" in section 3.5 defines +that boundary, its failure, crash and acknowledgment-loss behaviour and its +interaction with coalescing into an existing job. + Disposition uniqueness is per logical route, not per epoch or per revision: `(event_id, route_key)` is the complete uniqueness boundary across every epoch and every route-contract revision of that route. A disposition may record the @@ -554,9 +562,27 @@ The anomaly remains open until, under explicit authorization, either: route disposition will be created and what compensating state or effect covers the event. -Resolution is recorded as append-only, audited evidence linked to the anomaly, -including actor, authority, reason and the resulting disposition or divergence -record. Resolving an anomaly never deletes it or its detection evidence. +Those two paths are mutually exclusive for one logical anomaly. Every in-epoch +contract mismatch has one **canonical logical anomaly identity**, a function +of the mismatch facts rather than of the detection, backfill request or +operator procedure that observed it, whose exact composition and storage are +implementation-specification details; duplicate detection, a generic backfill +request or a separately chosen resolution identifier can therefore never mint +a parallel anomaly that reaches a second terminal outcome. Each logical +anomaly reaches at most one effective **terminal resolution** - `ENROLLED` +through path 1 or `RECONCILED/DIVERGED` through path 2 - arbitrated by a +database-enforced terminal-resolution decision that both paths share. An +application-level "read unresolved, act, then append the outcome" sequence +without that enforcement does not satisfy this rule. "Mutually exclusive +terminal resolution of a mismatch anomaly", in "Historical enrollment, +backfill and replay" below, defines the decision boundary and both commit +orders. + +Resolution is recorded as append-only, audited evidence linked to the one +anomaly, including actor, authority, reason and the resulting disposition or +divergence record. Resolving an anomaly never deletes it or its detection +evidence, and a retry after terminal resolution reads and reports the same +already-effective outcome rather than creating a second terminal decision. Detection records the facts above and raises attention; it need not calculate or persist which resolution paths are available, and any @@ -612,7 +638,10 @@ route retirement" below, in the same transaction that would create the disposition, never from an earlier separate read. If any check fails, or the revision has been withdrawn, the enrollment is -refused before any route disposition or job is created. The +refused before any route disposition or job is created. Where the enrollment +resolves an in-epoch contract-mismatch anomaly, these checks, the anomaly's +still-open state and the terminal-resolution decision are made inside the one +database-enforced boundary defined below. The `(event_id, route_key)` uniqueness slot remains unused, so a later enrollment under a compatible revision remains possible. The refused attempt and its reason are durably audited, and any existing anomaly, backlog or attention state for @@ -634,6 +663,79 @@ rules of sections 3.3 and 3.5 remain mandatory, so historical enrollment cannot produce an unintended duplicate or regressive effect. A new route identity does not by itself grant permission to duplicate an external effect. +### Mutually exclusive terminal resolution of a mismatch anomaly + +An unresolved in-epoch contract-mismatch anomaly (above) is resolved by +exactly one of two authorized paths: compatible historical enrollment, which +creates the one allowed `(event_id, route_key)` disposition, or an explicitly +approved reconciliation/divergence procedure, which creates none. Because +divergence creates no disposition, `(event_id, route_key)` uniqueness alone +cannot arbitrate between them. Both paths therefore compete through one +database-enforced terminal-resolution decision on the anomaly's canonical +logical identity: a terminal-resolution transition, a uniqueness constraint +over the anomaly's terminal outcome or an equivalent serialized durable +decision, selected by the implementation specification, that admits at most +one effective terminal outcome per logical anomaly. Each path validates its +authorization, the anomaly's still-open state and its applicable current +conditions - for enrollment, the selected revision's field-by-field +compatibility and its availability under the withdrawal/selection order of +"Revision withdrawal and route retirement" below; for divergence, the +reconciliation authority and approved reason - inside that database-enforced +decision boundary, in the transaction that makes the outcome durable, never +from a stale earlier observation. + +Both commit orders are defined: + +- enrollment first: the transaction that durably selects the terminal + `ENROLLED` outcome also creates the allowed `(event_id, route_key)` + disposition and, where the enrollment creates a new routed job rather than + attaching to an existing coalescing-open job, that job and its + indispensable input or membership facts under "Atomic creation of a routed + job" (section 3.5); a concurrent or later divergence attempt for the same + anomaly detects the effective terminal decision and cannot record a + contradictory accepted no-disposition outcome; +- divergence first: the transaction that durably selects the terminal + `RECONCILED/DIVERGED` outcome persists the approved reason, authority and + the durable resolution or compensation reference; it creates no route + disposition and no job merely by resolving the anomaly, and the + `(event_id, route_key)` slot remains unused; a concurrent or later first + historical enrollment for that same anomaly is refused before it can create + a disposition, job or external effect, and the unused slot is not available + to that losing enrollment. + +In either order the losing path detects the durable terminal decision before +committing its own conflicting record or work, aborts or deterministically +refuses, and leaves accurate audit and attention state; no two contradictory +terminal outcomes can both commit, regardless of the absence of a disposition +from divergence. A transaction that aborts or crashes before its terminal +decision commits leaves the anomaly unresolved and independently retryable. +After a terminal decision commits, repeated requests, retries and audit reads +observe the same effective outcome and create no second or contradictory +terminal result. The anomaly's open count, age and attention evidence +transition from open to exactly one terminal outcome without erasing the +detection facts. + +A generic backfill or replay request that targets the event and route of a +resolved anomaly is, for that anomaly, a first historical enrollment governed +by this rule: it cannot bypass a terminal divergence. A later deliberately +authorized replay, recovery or new independent effect remains governed by its +separate existing permissions, replay/correlation metadata and target-scoped +idempotency rules (section 3.5); it does not retroactively replace the +anomaly's terminal outcome, does not issue a second disposition for the same +`(event_id, route_key)` and does not treat the other path's losing attempt as +approved. + +A reconciliation/divergence procedure's external compensating action, where it +has one, obeys the existing durable job, `EFFECT_STARTED`, at-least-once, +ambiguity and reconciliation controls (sections 3.4 and 3.6) and the authority +rules of sections 3.14 and 3.15: the terminal decision and its canonical +durable intent and evidence are what this rule makes consistent, and +PostgreSQL is never assumed to commit atomically with a third-party provider +write or to grant new external-write authority. This anomaly-resolution +divergence is distinct from the separately authorized terminal non-materialized +route disposition of an eligible obligation (section 3.2): under the mismatch +semantics above it creates no disposition. + ### Revision withdrawal and route retirement A route-contract revision that an epoch references, or that an enrollment, @@ -1154,7 +1256,9 @@ distinction between retry, replay and current-state redistribution. Event routing identity and job idempotency are related but distinct. At most one durable disposition may exist for a given `(event_id, route_key)`, and an eligible route obligation eventually receives exactly one. A successfully -materialized disposition points to exactly one materialized job. Several +materialized disposition points to exactly one materialized job, and a job +newly created for a route obligation becomes durable only together with that +disposition ("Atomic creation of a routed job" below). Several materialized route dispositions may point to one **not-yet-started** job only when the kind defines an explicit durable coalescing rule; a job is not yet started, and so coalescing-open, only until its first successful execution @@ -1292,6 +1396,60 @@ scheduler's deadlock and lock-ordering controls. It introduces no second job identity, job lifecycle, route lifecycle or claim-lock mode, and no second order for claim/start against attachment. +### Atomic creation of a routed job + +A **routed job** is a logical job created to satisfy an event-route +obligation: through ordinary event-route materialization, or through a first +historical enrollment that creates a routed job. When a route materializer +creates a brand-new routed job, the new logical job, the materialized +`(event_id, route_key)` disposition that it represents and every durable +job-input or membership fact indispensable for that disposition's effect +semantics under the kind's input or coalescing contract become durable in one +PostgreSQL transaction. The job and the disposition become visible together on +commit; a failure, a uniqueness conflict or a crash before commit leaves +neither durably created by that attempt. The exact tables, constraints and SQL +are implementation-specification details; the all-or-nothing boundary is not. + +The scheduler and the workers must never be able to claim, start or produce +an external effect from a partially created routed job before that commit: a +routed job is claimable only once its disposition and indispensable input +facts are durable with it. After commit the job is a valid executable +representative of its committed disposition and its approved input semantics. + +If disposition insertion or validation fails, or loses the +`(event_id, route_key)` uniqueness race, the new job and its input or +membership facts roll back with it: no orphan or claimable routed job, no +phantom "route satisfied" result, no consumed disposition slot and no +unaccounted external effect remain, and the route obligation stays owed and +visible in the route backlog until a valid disposition exists. An +idempotency-key or disposition-uniqueness collision is reconciled with the +transaction's outcome; a collision is never accepted on its own as proof that +the obligation is satisfied. + +If the transaction commits but the router or materializer loses its +acknowledgment, its retry and the reconciliation path observe the same durable +job and disposition identity and create no second job, second disposition or +duplicate effect. + +When a concurrent router wants to use an **existing** logical job for the +obligation, the coalescing-open/coalescing-sealed boundary above and the +kind's explicit coalescing semantics apply unchanged: an attachment to an +open job commits a disposition under that boundary; a sealed job acquires no +new disposition; a losing attempt leaves its obligation owed and follows the +existing deterministic error and recovery path. Two routes may share one +eligible not-yet-started job only under the already-approved coalescing rule, +and the creation of a new routed job and a concurrent other-route attachment +to it obey that one linearized order. + +This boundary applies to event-route materialization and to first historical +enrollment that creates a routed job. An independently authorized scheduled, +operator, reconciliation, replay or current-state job that is not created to +satisfy a route disposition keeps its existing direct-job semantics; the rule +invents no route disposition for such a job, and such a job may not be used +to satisfy a route obligation without the disposition this boundary requires. +Whether a job is routed is decided by the obligation it is created to satisfy +and the authority under which it is created, not by a label. + ### 3.6 Delivery semantics, checkpoints and ambiguous writes The engine guarantees durable at-least-once execution opportunities. It does @@ -2268,6 +2426,44 @@ execution until a separate approved architecture decision says otherwise. lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the mechanism to the implementation specification. +44. A new logical job created to satisfy an event-route obligation - through + event-route materialization or a first historical enrollment that creates + a routed job - becomes durable only together with its materialized + `(event_id, route_key)` disposition and every indispensable durable + job-input or membership fact, in one PostgreSQL transaction: both are + visible together on commit, and a failure, uniqueness conflict or crash + before commit leaves neither. A routed job is never claimable or + effectful before that commit; a disposition insertion failure or lost + uniqueness race rolls the job back, leaving no orphan job, no phantom + satisfaction, no consumed slot and the obligation owed and visible; an + idempotency or disposition collision is reconciled with the transaction, + never accepted alone as satisfaction; and a committed transaction whose + acknowledgment is lost is observed by retry and reconciliation as the + same job and disposition, with no duplicate job, disposition or effect. + Attachment to an existing job remains governed by invariant 42; + independently authorized direct jobs keep their own semantics and cannot + be used to disguise a routed job. +45. Every in-epoch contract-mismatch anomaly has one canonical logical + identity and at most one effective terminal resolution - compatible + historical enrollment or approved reconciliation/divergence - arbitrated + by a database-enforced terminal-resolution decision shared by both paths; + each path validates authorization, the anomaly's open state and its + current compatibility, availability or reconciliation conditions inside + that boundary, never from a stale observation. Enrollment-first commits + the terminal outcome, the allowed disposition and any new routed job and + input facts in one transaction and excludes a later accepted divergence; + divergence-first commits the approved reason, authority and resolution + reference, creates no disposition or job, leaves the + `(event_id, route_key)` slot unused and refuses the losing enrollment for + that anomaly before any disposition, job or effect. The loser detects the + terminal decision before committing, aborts or deterministically refuses + and preserves audit and attention state; abort or crash before commit + leaves the anomaly open and retryable; retries after commit observe one + outcome. Detection and resolution evidence remain append-only; a generic + backfill cannot bypass a terminal divergence; separately authorized + replay is governed by its own rules and replaces no terminal outcome; and + external compensation remains fenced by the existing effect, ambiguity + and reconciliation controls. ## 8. Implementation impact and decomposition @@ -2287,6 +2483,9 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - effect-scoped idempotency and explicit coalescing, with the database-atomic coalescing-open/coalescing-sealed boundary between attachment and first claim/start; + - atomic creation of a routed job with its materialized disposition and + indispensable input facts, and the database-enforced mutually exclusive + terminal resolution of a mismatch anomaly; - revision semantics plus domain-owned canonical effect-target identity and target-scoped ordering/evidence contract; - new kind-scoped executor API; @@ -2497,10 +2696,12 @@ Rollout should prove in order: behaviour, including in-epoch contract-mismatch detection, the ordering of revision withdrawal against epoch opening and first enrollment, and the stability of withdrawal's strand-safety decision against concurrent - anomaly creation and resolution-path removal; + anomaly creation and resolution-path removal, and the mutually exclusive + terminal resolution of a mismatch anomaly in both commit orders; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing, including the - ordering of coalescing attachment against first claim/start; + ordering of coalescing attachment against first claim/start and the atomic + creation of a routed job with its disposition; 6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; 7. one or more non-destructive representative handlers; 8. protected operator controls and reconciliation exit path; @@ -2837,6 +3038,56 @@ Before shared implementation can be approved, evidence must include: first claim/start composes with `(event_id, route_key)` disposition uniqueness, idempotency-key uniqueness, worker claim locking and target-scoped concurrency without deadlock or split ordering; +- proof that a new routed job and its one materialized disposition commit + together, with the required durable initial membership/input facts, and + that workers can claim the job only after that commit; +- proof that a crash or failure after the job insert but before the + disposition insert or transaction commit leaves no committed or claimable + routed job, no disposition, no input membership and an unused route slot, + with the route obligation still owed and visible and a retry recovering it; +- proof that a crash or lost acknowledgment after both have committed permits + exact state recovery and replay-safe idempotent observation, with no + duplicate job, disposition or external effect; +- proof that duplicate same-event/same-route materializations and + disposition-uniqueness races leave exactly one successful commit, no orphan + job and no false route completion; +- proof that a disposition-insert error or constraint failure rolls back the + new job, that a job-idempotency collision with an existing coalescing-open + job uses only the approved coalescing path, and that an existing sealed job + rejects attachment without consuming the losing event's slot; +- proof that concurrent first-claim and coalescing attempts, including a + simultaneously created initial routed job and an other-route attachment to + it, obey the atomic attachment/first-claim order above and represent every + accepted pre-seal input; +- deterministic concurrency tests of simultaneous authorized compatible + enrollment and approved divergence of one mismatch anomaly, enrollment + first: one terminal enrollment resolution, one allowed disposition and job + where materialized, and no accepted divergence; +- the same anomaly-resolution race, divergence first: one terminal + divergence with append-only authority and evidence, no disposition or job + from the losing enrollment and no external effect authorized by it; +- proof that a stale "anomaly still open" observation in either competing + path cannot bypass the database-enforced winning resolution at commit; +- proof that a transaction abort or crash before the resolution commits + leaves the anomaly unresolved and independently retryable, and that after + commit repeated requests and audit retries create no second or + contradictory terminal result; +- proof that a terminal divergence leaves the `(event_id, route_key)` slot + unused without permitting the losing enrollment for that same resolved + anomaly to spend it, while a later deliberately authorized independent + replay remains separately governed; +- proof that the anomaly count, open age, attention and append-only audit and + evidence transition accurately from open to exactly one terminal outcome + without erasing the mismatch detection facts; +- proof that the atomic enrollment/job creation and the terminal-outcome + selection commit as one coherent database decision where enrollment creates + a new routed job; +- composition tests of the routed-job and terminal-resolution boundaries with + the revision-withdrawal/selection races above, the anomaly-creation/ + withdrawal strand-safety and last-resolution-path-removal cases above, the + claim/attachment races above, uniqueness and idempotency constraints and + deterministic lock and deadlock handling, proving no split ordering, no + stale strand decision and no permitted effect after a losing terminal path; - current-token lease renewal and refusal to renew superseded/expired claims; - stale-token refusal for completion, failure, cancellation, checkpointing and reconciliation; @@ -2932,20 +3183,11 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-09 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-09 under the -repository decision process, as the architecture content represented by the -Route-B candidate head `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and -candidate ADR-0012 blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c`; this -version differs from that candidate only in this approval-state -representation. That approval does not by itself make this ADR -repository-authoritative: the authority condition below still applies. Any -ADR-0013 programme-local reliance on this exact version is a separate state -that is not effective until its own conditions are satisfied for this version. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the @@ -2971,7 +3213,14 @@ unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that a stale strand-prevention scan can never commit a -withdrawal that strands existing work. +withdrawal that strands existing work. It further requires a new routed job +to become durable only together with its materialized disposition and +indispensable input facts in one transaction, so that no orphan or claimable +routed job and no phantom route satisfaction can survive a failure, race or +crash, and requires compatible historical enrollment and approved +reconciliation/divergence of one mismatch anomaly to compete through one +database-enforced terminal-resolution decision, so that no two contradictory +terminal outcomes can both commit. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO approval dated 2026-09-30. A later corrected version, which introduced the @@ -2986,7 +3235,11 @@ between coalescing attachment and a job's first claim/start, carried `Status: APPROVED` with CTO approval dated 2026-10-07. A further corrected version, which introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, carried -`Status: APPROVED` with CTO approval dated 2026-10-08. Those approvals, and +`Status: APPROVED` with CTO approval dated 2026-10-08. A further corrected +version, which introduced that strand-safety decision without an atomic +routed-job/disposition commit or a mutually exclusive terminal resolution of +mismatch anomalies, carried `Status: APPROVED` with CTO approval dated +2026-10-09. Those approvals, and every review of those earlier versions, remain historical evidence bound to those exact earlier versions and source heads only; they do not approve this version. The exact historical identities are recorded in the task diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 2ed149917..98cf12566 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-09 +Last updated: 2026-10-12 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-09 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-09 under the repository decision process, as the architecture content represented by Route-B candidate head `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and candidate ADR-0012 blob `b41e674dc5e3f1ba03715538ff2396e4d2f50c6c` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only: exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, and the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, and the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 21e19e81d0acf1aed57952ec00e2653771fb6f47 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Mon, 12 Oct 2026 18:20:14 +0100 Subject: [PATCH 42/48] THOTH-ASYNC-01-ADR-01: record Amendment-11 routed-job atomicity content approval (#958) --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 977 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 21 +- .../decisions/decision-register.md | 2 +- 4 files changed, 801 insertions(+), 201 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91218d7ae..a4af0397d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs and immutable route-contract revisions beneath one stable logical `route_key` and closes a race between revision withdrawal and revision selection: withdrawal from future selection and every operation that creates a new durable selection of, or reference to, a revision - at minimum a first activation, reactivation or revision-transition epoch opening and a historical enrollment that would create an event's first route disposition - must share one database-atomic serialization or constraint boundary, giving withdrawal and new future selection one durable linearized order. A selection that linearizes first remains valid historical state that a later withdrawal does not rewrite; after a withdrawal linearizes, a later epoch opening is refused before the epoch is created and a later first historical enrollment is refused before any route disposition or job is created, leaving the `(event_id, route_key)` slot unused; a losing transaction re-reads durable state before any retry and never commits from a stale pre-withdrawal observation; an application-level check-then-insert without database-enforced ordering is insufficient; the concrete lock or constraint mechanism is left to the implementation specification and must compose with the existing per-`route_key` lifecycle serialization, equivalent-revision convergence, enrollment validation, disposition uniqueness, strand prevention and route retirement without a second contradictory lifecycle order; explicit replay/current-state work for an already-dispositioned event stays outside the rule; and withdrawal otherwise keeps its append-only, audited, future-selection-only, non-destructive and strand-prevention semantics. Invariant 41 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: the CTO approved the exact corrected content on 2026-10-07.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced immutable route-contract revisions without this ordering rule and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 6919cf8f5..daeff73cf 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -27,14 +27,16 @@ independent exact-candidate inspection, the Route-B approval-state stage of that corrected candidate, and the Route-B candidate stage of the atomic routed-job creation and mutually exclusive anomaly-resolution correction specified by Specification Amendment 11 after the configured final-head -automated review of that Amendment-10 approval-state head. It records +automated review of that Amendment-10 approval-state head, and the Route-B +approval-state stage of that Amendment-11 candidate. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-11 Route-B candidate as the -current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-11 Route-B approval-state +commit as the current stage and every earlier candidate, approval-state, +recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -56,11 +58,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-11 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the +Head commit: the Amendment-11 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-11 candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`; its SHA is deliberately not +self-pinned here (section 3). That candidate is the direct child of the historical Amendment-10 approval-state head -`576a860be47aa1ebe39968f4d3297b26ee1d17df`; its SHA is deliberately not -self-pinned here (section 3). That approval-state head is the direct child of +`576a860be47aa1ebe39968f4d3297b26ee1d17df`, which is the direct child of the content-approved Amendment-10 replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, which is the direct child of the historical first Amendment-10 candidate @@ -89,7 +93,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -105,9 +109,9 @@ approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 -consistency-correction, Amendment-10 approval-state and Amendment-11 -candidate stages - Claude (Fable 5.1), bounded implementing agent. Earlier -rounds are recorded in Appendix A. +consistency-correction, Amendment-10 approval-state, Amendment-11 candidate +and Amendment-11 approval-state stages - Claude (Fable 5.1), bounded +implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -267,17 +271,26 @@ decision-register blob 2ed1499172780c6db82bb5f8808f2368fd8b96d1 CHANGELOG blob b9596d44ff260d6b818ad9c8174e843cf4d0c5b1 implementation-report blob 65fdd7677b12a10dcf8e53f1f173531e9c088d6b (superseded by the Amendment-11 candidate) -Amendment-11 Route-B candidate (the commit that contains this report): -ADR-0012 blob 7e461106986fe6efad2021d1ad6f52573e6e9d66 (PROPOSED; material architectural correction of f8c258b0f1b0d19b401c4184ca4dc03a15c865d3) +content-approved Amendment-11 candidate / approval-state parent: +528c2db04af5c53b9820b946ddac2ebb936eb4cd +tree 7de8c64a1bf570526822002b9d2f1bb81baef424 +ADR-0012 blob 7e461106986fe6efad2021d1ad6f52573e6e9d66 (PROPOSED; content-approved; material architectural correction of f8c258b0f1b0d19b401c4184ca4dc03a15c865d3; independent exact-candidate source review APPROVED) decision-register blob 98cf125665a3f0011a6ca9c7f90b2b9562931fe7 +implementation-report blob 6919cf8f5b5f854d56d93df7c283b416b5b4b84d CHANGELOG blob 91218d7aee40053dfb84c83414f50434e1398695 +Amendment-11 Route-B approval-state commit (the commit that contains this report): +ADR-0012 blob df8017c57d74eaecf13041e231ef97625260fab1 (APPROVED; Javi, CTO; 2026-10-12; architecture-equivalent to 7e461106986fe6efad2021d1ad6f52573e6e9d66) +decision-register blob c9ff92119095a162cf736304eb88ba595d3685ab +CHANGELOG blob a4af0397dc37fdf782124573d9a4b7a7b55e8abe + feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, -its consistency correction, the Amendment-10 approval-state commit and the -Amendment-11 candidate (unchanged): +its consistency correction, the Amendment-10 approval-state commit, the +Amendment-11 candidate and the Amendment-11 approval-state commit +(unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -427,7 +440,19 @@ Approved specification: and Amendments 1-11: #958 comment `6125170650`; - Amendment-11 Route-B candidate implementation authorization, which also authorized engineering control to return PR #960 to Draft once before the - candidate was written: #958 comment `6125285446`. + candidate was written: #958 comment `6125285446`; +- independent CRITICAL exact-candidate source review of the Amendment-11 + candidate, `APPROVED`: recorded on #958 on 2026-10-12; its GitHub comment + identifier is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B + doctrine and is not copied here; +- CTO exact candidate-content approval of the Amendment-11 candidate: recorded + on #958 on 2026-10-12 by Javi, CTO for candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; +- Amendment-11 Route-B approval-state implementation authorization: #958 + comment `6128858897`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -493,15 +518,22 @@ register row and the strand-safety correction changelog entry moved to the approved state), and the CTO recorded exact-final-blob approval of that ADR blob. The configured final-head automated review of that approval-state head exposed two further architecture defects, `R-576A-01` and `R-576A-02`, -specified by Specification Amendment 11. The commit that contains this report -is its Route-B candidate: one ordinary single-parent commit on the -approval-state head that changes exactly the four authorized correction -paths, returns ADR-0012 to `Status: PROPOSED`, requires a new routed job to -become durable only together with its materialized disposition and -indispensable input facts in one transaction, and requires compatible -historical enrollment and approved reconciliation/divergence of one mismatch -anomaly to compete through one database-enforced terminal-resolution -decision. +specified by Specification Amendment 11. Its `PROPOSED` Route-B candidate, +one ordinary single-parent commit on the approval-state head that changed +exactly the four authorized correction paths, returned ADR-0012 to +`Status: PROPOSED`, requires a new routed job to become durable only together +with its materialized disposition and indispensable input facts in one +transaction, and requires compatible historical enrollment and approved +reconciliation/divergence of one mismatch anomaly to compete through one +database-enforced terminal-resolution decision, received independent +CRITICAL exact-candidate source review (`APPROVED`) and CTO exact +candidate-content approval on 2026-10-12. The commit that contains this +report is the one bounded Route-B approval-state commit that records that +approval as durable decision state: ADR-0012 `PROPOSED -> APPROVED` with +`Approved by: Javi, CTO` and `Approval date: 2026-10-12`, the ADR-0012 +register row and the routed-job atomicity correction changelog entry moved to +the approved state, and this report's approval-state evidence. No +architecture content changes. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -931,7 +963,8 @@ decision. stage authorized no PR-state or PR-metadata change. - **Exact-final-blob approval, final-head review of the Amendment-10 approval-state head, Specification Amendment 11 and the Amendment-11 Route-B - candidate (the commit that contains this report).** Authorized by #958 + candidate (historical content-approved candidate + `528c2db04af5c53b9820b946ddac2ebb936eb4cd`).** Authorized by #958 comment `6125285446` after the CTO approved Specification Amendment 11 (#958 comments `6123767275` and `6124032150`) and a fresh independent CRITICAL specification review of the base specification and Amendments 1-11 returned @@ -973,19 +1006,43 @@ decision. to the implementation specification. The independent review confirmed both findings, the classification, both rules, the composition and the sufficiency of the fourteen-case matrix, and recommended four non-blocking - negative vectors for the later implementation specification. The commit - that contains this report is one ordinary single-parent commit directly on - the approval-state head that changes exactly the four authorized correction - paths: ADR-0012 returns to `Status: PROPOSED` with no current approver or - approval date and implements Amendment 11 (section 4; section 5, decisions - 113-124); the decision-register ADR-0012 row, one new changelog entry and - this report describe the corrected version as `PROPOSED`; and the - 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and 2026-10-09 - CTO approvals remain historical evidence bound to the earlier exact - versions. This stage authorizes no PR-state or PR-metadata change; the PR - #960 title and description, which describe the historical Amendment-10 - source, are GitHub-owned and their reconciliation is a separate control - action. + negative vectors for the later implementation specification. The candidate + was one ordinary single-parent commit directly on the approval-state head + that changed exactly the four authorized correction paths: ADR-0012 + returned to `Status: PROPOSED` with no current approver or approval date + and implements Amendment 11 (section 4; section 5, decisions 113-124); the + decision-register ADR-0012 row, one new changelog entry and the + candidate-stage report described the corrected version as `PROPOSED`; and + the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and + 2026-10-09 CTO approvals remain historical evidence bound to the earlier + exact versions. An independent CRITICAL exact-candidate source review of + the published candidate against the actual GitHub source returned + `APPROVED` with no blocking candidate-content finding, and Javi, CTO + approved its exact architecture content - head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66` - on 2026-10-12 in a durable + record on #958. That stage authorized no PR-state or PR-metadata change. +- **Amendment-11 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6128858897`. One ordinary + single-parent commit directly on the content-approved candidate records + that approval as durable decision state, changing only the four authorized + paths: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and + `Approval date: 2026-10-12` and the leading section-12 approval paragraphs, + which identify the approved content as the content represented by candidate + head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`; the ADR-0012 register row; the + `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` changelog entry; + and this report. No architecture content changes (section 9, "Amendment-11 + approval-state validation"). The content approval is recorded here through + owning issue #958, approver Javi, CTO, approval date 2026-10-12, the + candidate head and the candidate ADR-0012 blob; its GitHub comment + identifier is not copied into repository source, as the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` and the approval-state authorization + itself require (section 5, decision 128). PR #960 was already a draft; this + stage authorizes no PR-state or PR-metadata change; the PR #960 title and + description, which describe the historical Amendment-10 source, are + GitHub-owned and their reconciliation is a separate control action. Out-of-scope changes made: NONE @@ -999,8 +1056,16 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-12) +of the content-approved Amendment-11 candidate blob 7e461106986fe6efad2021d1ad6f52573e6e9d66 +at candidate head 528c2db04af5c53b9820b946ddac2ebb936eb4cd; +no architecture change + +historical Amendment-11 candidate at 528c2db04af5c53b9820b946ddac2ebb936eb4cd: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 -(Specification Amendment 11; Route-B candidate stage; candidate status PROPOSED) +(Specification Amendment 11; Route-B candidate stage; candidate status PROPOSED; +independent CRITICAL exact-candidate source review APPROVED; CTO exact +candidate-content approval on 2026-10-12) historical Amendment-10 approval-state at 576a860be47aa1ebe39968f4d3297b26ee1d17df: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-09) @@ -1261,40 +1326,60 @@ well: and remains true only of that historical version, while the register row now records the candidate as `PROPOSED`. -They remain historical evidence bound to their exact versions and heads. For -the candidate version, independent exact-candidate inspection, CTO exact -candidate-content approval, the approval-state commit, exact-final-blob -approval, independent exact-head review, CTO merge authorization, guarded -merge, programme-local reliance and later migration, deployment, activation -and observation are distinct controlled lifecycle categories; GitHub, through -#958 and PR #960, is authoritative for which of them is pending, satisfied or -blocked for any head. +They remain historical evidence bound to their exact versions and heads. The +Amendment-11 candidate version received its own independent CRITICAL +exact-candidate source review and CTO exact candidate-content approval on +2026-10-12, and the approval-state commit that contains this report records +it. For this version, exact-final-blob approval, independent exact-head +review, CTO merge authorization, guarded merge, programme-local reliance and +later migration, deployment, activation and observation are distinct +controlled lifecycle categories; GitHub, through #958 and PR #960, is +authoritative for which of them is pending, satisfied or blocked for any +head. ## 3. Commits -Amendment-11 Route-B candidate commit (the commit that contains this report): +Amendment-11 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose Amendment-11 routed-job atomicity correction (#958)` - - direct parent: the historical Amendment-10 approval-state head - `576a860be47aa1ebe39968f4d3297b26ee1d17df` - - changes only the four authorized correction paths (section 4); the - candidate ADR-0012 blob is `7e461106986fe6efad2021d1ad6f52573e6e9d66`, the - candidate decision-register blob `98cf125665a3f0011a6ca9c7f90b2b9562931fe7` - and the candidate changelog blob `91218d7aee40053dfb84c83414f50434e1398695` + `THOTH-ASYNC-01-ADR-01: record Amendment-11 routed-job atomicity content approval (#958)` + - direct parent: the content-approved Amendment-11 candidate + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` + - changes only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `df8017c57d74eaecf13041e231ef97625260fab1`, the approval-state + decision-register blob `c9ff92119095a162cf736304eb88ba595d3685ab` and the + approval-state changelog blob `a4af0397dc37fdf782124573d9a4b7a7b55e8abe` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the candidate ADR-0012, +here because this report is part of that tree; the approval-state ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its approval-state-head blob (section -9). The commit identities are verified against the remote task-branch head -after push and recorded in the post-publication handoff; once published, the -PR #960 head and head tree are GitHub-owned lifecycle evidence under -`ADR-0005`. The authorized topology is a non-force fast-forward of the task -branch from the approval-state head to the candidate commit; no merge commit, -rebase, squash, amend, force update or second candidate commit is authorized, -and no further source commit exists implicitly after it. +recorded above; every other file keeps its candidate blob (section 9). The +commit identities are verified against the remote task-branch head after push +and recorded in the post-publication handoff; once published, the PR #960 +head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The +authorized topology is a non-force fast-forward of the task branch from the +content-approved candidate to the approval-state commit; no merge commit, +rebase, squash, amend, force update or second approval-state commit is +authorized, and no further source commit exists implicitly after it. + +Amendment-11 Route-B candidate commit (content-approved; historical): + +- `528c2db04af5c53b9820b946ddac2ebb936eb4cd` - + `THOTH-ASYNC-01-ADR-01: propose Amendment-11 routed-job atomicity correction (#958)` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-10 approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` + - tree `7de8c64a1bf570526822002b9d2f1bb81baef424` + - changed only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `7e461106986fe6efad2021d1ad6f52573e6e9d66`, + independently reviewed `APPROVED` and content-approved by Javi, CTO on + 2026-10-12 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + approval-state head; the remote head, tree, parent and ADR-0012, + decision-register, changelog and report blobs matched the locally + validated object Amendment-10 Route-B approval-state commit (historical): @@ -1542,11 +1627,22 @@ first-parent order, newest first: ## 4. Files changed -Amendment-11 Route-B candidate write budget for the commit that contains this -report (authorization #958 comment `6125285446`; the same four existing paths -as the Amendment-10 budgets); exactly four existing paths, no new file, no -deletion, and every other path byte-identical to the historical Amendment-10 -approval-state head +Amendment-11 Route-B approval-state write budget for the commit that contains +this report (authorization #958 comment `6128858897`), identical to the +Amendment-11 candidate write budget; exactly four existing paths, no new file, +no deletion, and every other path byte-identical to the content-approved +candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-11 Route-B candidate write budget (authorization #958 +comment `6125285446`; the same four existing paths as the Amendment-10 +budgets); exactly four existing paths, no new file, no deletion, and every +other path byte-identical to the historical Amendment-10 approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`: - `CHANGELOG.md` @@ -1677,12 +1773,88 @@ Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, -Amendment-10 approval-state and Amendment-11 candidate stages; all four -correction paths already existed. +Amendment-10 approval-state, Amendment-11 candidate and Amendment-11 +approval-state stages; all four correction paths already existed. -Amendment-11 candidate changes in the commit that contains this report, -relative to the approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` -(exactly the four authorized paths): +Amendment-11 approval-state changes in the commit that contains this report, +relative to the content-approved candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate-content approval of 2026-10-12. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-12`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + "Current decision state: **APPROVED**.", the same approver and date, and + a paragraph stating that the CTO approved this exact corrected content on + 2026-10-12 as the architecture content represented by candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that any ADR-0013 programme-local reliance on this exact version is a + separate state not effective until its own conditions are satisfied. + Every other line, including all architecture, the Amendment-11 + routed-job and terminal-resolution subsections, invariants 1-45, the + validation requirements, the historical-approval paragraph, the authority + condition and the architecture-versus-implementation separation, is + byte-identical to the candidate (section 9). Approval-state blob: + `df8017c57d74eaecf13041e231ef97625260fab1`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, "its corrected content awaits CTO + approval" becomes "Javi, CTO approved its exact corrected content on + 2026-10-12", bound to candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66` as historical content-approval + evidence, with the statement that content approval is architecture-content + approval only and that exact-final-blob approval, independent final + source-head review, merge authorization and ADR-0013 reliance are separate + gates recorded in GitHub. The header `Last updated: 2026-10-12` already + carries the date of this row change and is unchanged (section 5, decision + 129). Every other row is unchanged. Approval-state blob: + `c9ff92119095a162cf736304eb88ba595d3685ab`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the routed-job atomicity correction + entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry only - + "propose" becomes "record a CTO-approved", "`PROPOSED`: the corrected + content is not approved" becomes "`APPROVED`: Javi, CTO approved the + exact corrected content on 2026-10-12", and "proposal only" becomes + "decision only". Its statements that the earlier entries and the + 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and 2026-10-09 + approvals apply only to historical exact versions are unchanged, and + every other entry is byte-identical. Approval-state blob: + `a4af0397dc37fdf782124573d9a4b7a7b55e8abe`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-11 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit + the current stage; records the independent exact-candidate source review, + the CTO candidate-content approval and the approval-state authorization; + records the approval-state validation and no-drift proof (section 9); and + reframes statements that would otherwise describe the candidate as the + commit containing this report, recording the candidate's identities as + historical facts. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the content-approved candidate through the approval-state +commit: every path other than the four authorized paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical Amendment-11 candidate changes in the content-approved candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`, relative to the approval-state head +`576a860be47aa1ebe39968f4d3297b26ee1d17df` (exactly the four authorized +paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement Specification Amendment 11 and record the corrected @@ -1765,15 +1937,16 @@ relative to the approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-11 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate the current - stage; records the approval-state inspection, the exact-final-blob - approval of the Amendment-10 approval-state blob, the final-head review of - that head, `R-576A-01` and `R-576A-02`, Amendment 11, its CTO approval, - its independent review and the candidate authorization as governing - evidence; records the candidate's validation (section 9); and reframes - statements that would otherwise describe the approval-state commit as the - commit containing this report, recording that commit's identities as - historical facts. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the candidate the + then-current stage; recorded the approval-state inspection, the + exact-final-blob approval of the Amendment-10 approval-state blob, the + final-head review of that head, `R-576A-01` and `R-576A-02`, Amendment + 11, its CTO approval, its independent review and the candidate + authorization as governing evidence; recorded the candidate's validation + (section 9); and reframed statements that would otherwise describe the + approval-state commit as the commit containing the report, recording that + commit's identities as historical facts. All still-true earlier evidence + was preserved. - within authorized write budget: YES Byte-identical to the approval-state head through the Amendment-11 candidate: @@ -2572,8 +2745,8 @@ approval-state commit, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate, the Amendment-8 approval-state commit, the Amendment-9 candidate, the Amendment-9 approval-state commit, the Amendment-10 candidate, its consistency -correction, the Amendment-10 approval-state commit and the Amendment-11 -candidate: +correction, the Amendment-10 approval-state commit, the Amendment-11 +candidate and the Amendment-11 approval-state commit: - ADR-0008 - blob `622060ad90eec41c792f110c373efffbb11a4b56`; - ADR-0010 - blob `aca2142a3387785e80db908b3a5c1b0afd82ab51`; @@ -2752,7 +2925,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-11 candidate differs from the approval-state head +The Amendment-11 approval-state commit differs from the content-approved +candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd` in exactly the four +authorized paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-11 approval-state validation"). +The Amendment-11 candidate differed from the approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-11 candidate validation"). The @@ -2829,8 +3006,9 @@ stage (#958 comment `6046895895`), the Amendment-9 approval-state stage (#958 comment `6065879000`), the Amendment-10 candidate stage (#958 comment `6067886387`), the F-A10-CAND-01 consistency-correction stage (#958 comment `6069074613`), the Amendment-10 approval-state stage (#958 comment -`6083303288`) and the Amendment-11 candidate stage (#958 comment -`6125285446`). This matrix +`6083303288`), the Amendment-11 candidate stage (#958 comment +`6125285446`) and the Amendment-11 approval-state stage (#958 comment +`6128858897`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -3240,8 +3418,8 @@ Amendment-11 candidate: review of the approval-state head, and returned it to Draft before the Amendment-11 candidate (section 2) -Amendment-11 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-11 candidate stage - historical, completed before the Amendment-11 +approval-state commit: - repository inspection: USED (read-only verification of the exact refs, including `develop` and `feature/worker` at the same base, PR #960 open, @@ -3268,17 +3446,53 @@ commit that contains this report: worktree, verified clean and exact at the approval-state head and touching no other worktree, branch or ref, was reused for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent candidate commit that contains this report) +- commit: USED (the single-parent candidate commit + `528c2db04af5c53b9820b946ddac2ebb936eb4cd`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `576a860be47aa1ebe39968f4d3297b26ee1d17df` to the candidate; the remote head, + tree, parent and ADR-0012, decision-register, changelog and report blobs + were verified; no automated review ran on the draft candidate head) +- independent exact-candidate source review: NOT USED by the implementing + agent; an independent reviewer reviewed the published candidate and + returned `APPROVED` (section 2) + +Amendment-11 approval-state stage - done up to and including the creation of +the commit that contains this report: -Authorized post-commit actions for the candidate commit - GitHub-owned +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base, PR #960 open, + draft, unmerged and mergeable with head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and target `feature/worker`, + the candidate's tree, single parent and ADR-0012, report, decision-register + and changelog blobs, the protected ADR-0008, ADR-0010 and ADR-0013 blobs + and the Route-B doctrine blob, the six-path cumulative footprint, the + absence of any ADR-0012 commit reachable from `develop`, `master` or + `feature/worker` (Route-B eligibility), the presence of the + exact-candidate review, content-approval and approval-state authorization + records on #958 with no later control record, the pull_request-triggered + workflow set and its documentation-only classification gate, and, as + stage-time evidence, the PR #960 review-thread state in GitHub; all matched + the authorization; current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, branch creation: NOT USED + (not authorized; the same detached local worktree, verified clean and + exact at the content-approved candidate and touching no other worktree, + branch or ref, was reused for construction, and the existing task branch + is the fast-forward target) +- commit: USED (the single-parent approval-state commit that contains this + report) + +Authorized post-commit actions for the approval-state commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `576a860be47aa1ebe39968f4d3297b26ee1d17df` to the candidate commit; + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` to the approval-state commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, changed path set, - cumulative footprint and PR #960 state, and that no review thread was - replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, ADR approval + state, changed path set, cumulative footprint and PR #960 state, and that + no review thread was replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -3286,24 +3500,23 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-11 candidate +Not authorized for the implementing agent at the Amendment-11 approval-state stage, each a separate controlled lifecycle category where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): -- any edit to a path other than the four authorized correction paths: NOT USED - (not authorized) -- any ADR-0012 change outside Amendment 11 and the candidate `PROPOSED` state: - NOT USED (not authorized) -- independent exact-candidate inspection or exact-head review: NOT USED (never - on the implementing agent's authority) -- CTO candidate-content approval, approval-state commit, fresh `APPROVED` - status, approver or approval date, exact-final-blob approval: NOT USED (not +- any edit to a path other than the four authorized paths: NOT USED (not authorized) +- any ADR-0012 change outside the approval-state representation, including + any architecture change: NOT USED (not authorized) +- independent inspection or exact-head review: NOT USED (never on the + implementing agent's authority) +- exact-final-blob approval or confirmation: NOT USED (not authorized; never + on the implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second candidate commit, amend, rebase, squash, force push or history - rewrite: NOT USED (not authorized) +- any second approval-state commit, amend, rebase, squash, force push or + history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -3340,17 +3553,19 @@ authorization, the factual-clarification authorization, the Amendment-8 candidate and approval-state authorizations, the Amendment-9 candidate and approval-state authorizations, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 -approval-state authorization and the Amendment-11 candidate authorization, -configured Codex/app review is not expected from a push to a draft PR and is -not relied upon at the Amendment-6/7 candidate, approval-state, -control-record recovery, transient-state correction, factual-clarification, -Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, -Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, -Amendment-10 approval-state or Amendment-11 candidate stage; configured -Codex/app review runs on separately authorized Ready transitions, which are -GitHub-owned lifecycle events, and any automated review that nevertheless -runs on a draft head is inspected read-only. The actual job outcomes for the -candidate commit exist only after its publication; they +approval-state authorization, the Amendment-11 candidate authorization and +the Amendment-11 approval-state authorization, configured Codex/app review is +not expected from a push to a draft PR and is not relied upon at the +Amendment-6/7 candidate, approval-state, control-record recovery, +transient-state correction, factual-clarification, Amendment-8 candidate, +Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 +approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 +approval-state, Amendment-11 candidate or Amendment-11 approval-state stage; +configured Codex/app review runs on separately authorized Ready transitions, +which are GitHub-owned lifecycle events, and any automated review that +nevertheless runs on a draft head is inspected read-only. The actual job +outcomes for the approval-state commit exist only after its publication; +they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -3360,9 +3575,9 @@ Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, -consistency-correction, Amendment-10 approval-state or Amendment-11 candidate -stage, followed the same documentation-only classification, with no manual -dispatch or rerun: +consistency-correction, Amendment-10 approval-state, Amendment-11 candidate or +Amendment-11 approval-state stage, followed the same documentation-only +classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -3383,6 +3598,7 @@ e41f377202379238b91eb43d60f5115aa8adc20f: 4 success (classify x3, check-changelo 6e521f00b039769b28a26ab069c7e97d22de4e08: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-10 candidate) e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-10 replacement candidate) 576a860be47aa1ebe39968f4d3297b26ee1d17df: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-10 approval-state head) +528c2db04af5c53b9820b946ddac2ebb936eb4cd: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-11 candidate) ``` Manually initiated external actions (anything the implementing agent @@ -4275,9 +4491,10 @@ Amendment-10 approval-state stage (historical approval-state head approval-state commit's identities as historical facts because that head is the Amendment-11 candidate's parent. -Amendment-11 Route-B candidate (the commit that contains this report). Items -115, 117 and 120 record interpretations of the approved specification for -explicit control inspection: +Amendment-11 Route-B candidate (historical content-approved candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`). Items 115, 117 and 120 recorded +interpretations of the approved specification for explicit control +inspection: 113. The two Amendment-11 rules are each stated once, in a new unnumbered subsection following the style of the Amendment-9 and Amendment-10 @@ -4397,17 +4614,87 @@ explicit control inspection: independent-review and candidate-authorization records are cited by their comment identifiers as specification and authorization evidence, consistent with section 2. -124. This report does not self-pin the candidate commit's SHA, its tree or its - own final blob; because the report now lives in the candidate commit, - statements that described the approval-state commit as the commit - containing this report are reframed as history, and that commit's - identities, now the candidate parent, are recorded in section 1 as - historical facts. Construction reused the same detached scratch - worktree, verified clean and exact at the approval-state head before - editing, touching no other worktree, branch, ref, stash or uncommitted - change. The PR #960 title and description describe the historical - Amendment-10 source; this stage is not authorized to change them, and - their reconciliation is reported to control as a separate action. +124. The candidate report did not self-pin the candidate commit's SHA, its + tree or its own final blob; because the report then lived in the + candidate commit, statements that described the approval-state commit as + the commit containing the report were reframed as history, and that + commit's identities, the candidate parent, were recorded in section 1 as + historical facts; section 1 now records the candidate's identities as + historical facts because that head is the Amendment-11 approval-state + commit's parent. Construction reused the same detached scratch worktree, + verified clean and exact at the approval-state head before editing, + touching no other worktree, branch, ref, stash or uncommitted change. The + PR #960 title and description describe the historical Amendment-10 + source; that stage was not authorized to change them, and their + reconciliation was reported to control as a separate action. + +Amendment-11 approval-state stage (the commit that contains this report): + +125. The approval-state header uses the same field order as the earlier + approved ADR-0012 versions (`Status`, `Date`, `Approved by`, + `Approval date`, `Decision owner`), and section 12 repeats the approver + and date; the original decision date `2026-09-29` is preserved. +126. The approval date carried by the ADR is the date of the CTO exact + candidate-content approval record, 2026-10-12, as Route B and the + approval-state authorization require; it is neither back-dated nor + anticipated, and it is not replaced by a commit or push date. +127. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by "Current decision state: + **APPROVED**.", the approver/date and one paragraph stating that the CTO + approved this exact corrected content on 2026-10-12 as the architecture + content represented by candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that ADR-0013 programme-local reliance on this exact version is separate + and not effective until its own conditions are satisfied (decisions 83 + and 107 pattern). The historical-approval, material-correction, + architecture-only, authority-condition, partial-supersession and + amendment paragraphs were already true in the approved state (decision + 123) and are unchanged. +128. The content approval is recorded only through owning issue #958, approver + Javi, CTO, approval date 2026-10-12, candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`. Its GitHub comment identifier + is not copied into repository source: the repository-authoritative Route-B + rule in `docs/engineering/decisions/README.md` forbids it, the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + applied that rule to this task (decisions 36-37, 64, 84 and 108), and the + approval-state authorization for this stage requires the same; the + independent exact-candidate review receipt is likewise recorded by + outcome and date only. +129. The decision-register header `Last updated: 2026-10-12` already carries + the date on which the approval-state commit changes the ADR-0012 row, so + it is unchanged, as the approval-state authorization permits (decisions + 25, 78 and 97 pattern); only the ADR-0012 row changes. The row binds the + content approval to the candidate head and blob as historical + content-approval evidence and states, in the durable form that + `docs/engineering/AGENTS.md` section 1.1 requires, that exact-final-blob + approval, independent final source-head review, merge authorization and + ADR-0013 reliance are separate gates recorded in GitHub; the existing + authority-condition sentence is unchanged. +130. Only the `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` + changelog entry changes, following the earlier approval-state wording + pattern (decisions 66, 86 and 110: "propose" to "record a CTO-approved", + the `PROPOSED` sentence to the `APPROVED` sentence naming the approver + and date, and "proposal only" to "decision only"); every other entry, + including the historical approval entries, is byte-identical. +131. Architecture no-drift is proven by the exact normalized comparison in + section 9 ("Amendment-11 approval-state validation"), not by assertion: + outside the header approval fields and the section-12 opening block, the + approval-state ADR is byte-identical to the content-approved candidate + blob, so every A11-1, A11-2 and A11-3 requirement, invariants 1-45 and + the complete validation matrix are unchanged. +132. This report does not self-pin the approval-state commit's SHA, its tree + or its own final blob; it records only non-self-referential evidence and + the approval-state ADR-0012, decision-register and changelog blobs, which + do not depend on its bytes. Because the report now lives in the + approval-state commit, statements that described the candidate as the + commit containing this report are reframed as history, and the + candidate's identities, now the approval-state parent, are recorded in + section 1 as historical facts. The approval-state ADR blob is recorded as + a result; it is not represented as exact-final-blob approved. List any deviation from the specification requiring authorization: @@ -4514,9 +4801,15 @@ List any deviation from the specification requiring authorization: - Amendment-11 candidate stage: NONE. No fifth path, new file, deletion, ADR change outside Amendment 11 and the candidate state, second commit, amend, rebase, squash, force push or history rewrite was needed or performed - before the commit that contains this report was created. Decisions 115, - 117 and 120 are recorded for control inspection as interpretations within - the authorized scope, not as deviations. + before the candidate commit was created. Decisions 115, 117 and 120 were + recorded for control inspection as interpretations within the authorized + scope, not as deviations, and the independent exact-candidate review + raised no finding against them. +- Amendment-11 approval-state stage: NONE. The approval-state transformation + followed its separate authorization exactly; no fifth path, new file, + deletion, architecture change, second commit, amend, rebase, squash, force + push or history rewrite was needed or performed before the commit that + contains this report was created. ## 6. Database and migration effects @@ -4597,8 +4890,10 @@ content-approved replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`. "Amendment-10 approval-state validation" records the validation of the historical approval-state commit `576a860be47aa1ebe39968f4d3297b26ee1d17df`. "Amendment-11 candidate validation" -at the end of this section records the validation of the commit that -contains this report. +records the validation of the historical content-approved candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`. "Amendment-11 approval-state +validation" at the end of this section records the validation of the commit +that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -7883,15 +8178,18 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 7e461106986fe6efad2021d1ad6f52573e6e9d66 ``` -The candidate ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the candidate tree and the candidate -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and recorded in the -post-publication handoff. +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate report's bytes, so they +were recorded above. The candidate report's own final blob, the candidate +tree and the candidate commit SHA depended on that report's bytes and were +not self-pinned in it; they were verified against the remote head after push +and recorded in the post-publication handoff, and section 1 now records them +as historical facts: commit `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, tree +`7de8c64a1bf570526822002b9d2f1bb81baef424`, report blob +`6919cf8f5b5f854d56d93df7c283b416b5b4b84d`. -Required topology of the candidate commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the candidate commit, checked after it was created and +before push; the published candidate satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -7910,6 +8208,256 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-11 approval-state validation + +Environment: the same detached local Git worktree, verified clean (no tracked +or untracked change) and exact at the content-approved Amendment-11 candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd` (tree +`7de8c64a1bf570526822002b9d2f1bb81baef424`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, no ADR-0012 commit reachable from +`develop`, `master` or `feature/worker` (Route-B eligibility), +`feature/async/adr-0012` at +`528c2db04af5c53b9820b946ddac2ebb936eb4cd` with single parent +`576a860be47aa1ebe39968f4d3297b26ee1d17df` and no descendant, ADR-0012 blob +`7e461106986fe6efad2021d1ad6f52573e6e9d66`, report blob +`6919cf8f5b5f854d56d93df7c283b416b5b4b84d`, decision-register blob +`98cf125665a3f0011a6ca9c7f90b2b9562931fe7` and changelog blob +`91218d7aee40053dfb84c83414f50434e1398695` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs and the Route-B doctrine blob, PR #960 +open, draft, unmerged and mergeable with that head and target +`feature/worker`, the cumulative PR footprint exactly the six paths, the +exact-candidate review, content-approval and approval-state authorization +records present on #958 as the three latest comments with no later control +record, the pull_request-triggered workflows gated by the documentation-only +classifier, and PR #960 review-thread state that matched the stage +authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the approval-state commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 528c2db04af5c53b9820b946ddac2ebb936eb4cd +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths): + +```text +git diff --cached --name-only 528c2db04af5c53b9820b946ddac2ebb936eb4cd +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 528c2db04af5c53b9820b946ddac2ebb936eb4cd +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 528c2db04af5c53b9820b946ddac2ebb936eb4cd +1 1 CHANGELOG.md +783 194 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +16 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 approval-state delta (four hunks: the header status line, the two +inserted header approval lines, the section-12 decision-state line and the +section-12 approval paragraphs; no other hunk): + +```text +git diff --cached -U0 528c2db04af5c53b9820b946ddac2ebb936eb4cd -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -4,0 +5,2 @@ Date: 2026-09-29 +@@ -3186 +3188 @@ shared-engine tests. +@@ -3188,3 +3190,12 @@ Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: APPROVED +5:Approved by: Javi, CTO +6:Approval date: 2026-10-12 +3188:Current decision state: **APPROVED**. +3190:Approved by: Javi, CTO +3191:Approval date: 2026-10-12 +``` + +Architecture no-drift proof. The normalization `N` drops the header +`Status`, `Approved by` and `Approval date` lines before the first `---` +separator and the section-12 lines from the heading up to, but not +including, the line beginning "This version is a material architectural +correction"; it keeps every other byte. The content-approved candidate blob +and the staged approval-state ADR normalize to identical content, so every +A11-1, A11-2 and A11-3 requirement, invariants 1-45 and the complete +validation matrix are byte-identical: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p 7e461106986fe6efad2021d1ad6f52573e6e9d66 | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p 7e461106986fe6efad2021d1ad6f52573e6e9d66 | awk "$N" | shasum -a 256 +d9ea7edf6a663d7e4a4eb636109f80c04e89396863c1cbc62f0c05cfd6173bf1 - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +d9ea7edf6a663d7e4a4eb636109f80c04e89396863c1cbc62f0c05cfd6173bf1 - +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display), so that the complete raw delta is visible: + +```text +candidate 7e461106986fe6efad2021d1ad6f52573e6e9d66: +Status: PROPOSED +Current decision state: **PROPOSED**. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. + +approval-state ADR: +Status: APPROVED +Approved by: Javi, CTO +Approval date: 2026-10-12 +Current decision state: **APPROVED**. +Approved by: Javi, CTO +Approval date: 2026-10-12 +The CTO approved this exact corrected content on 2026-10-12 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and +candidate ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66`; this +version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. +``` + +Decision-register delta (the ADR-0012 row only; the `Last updated: +2026-10-12` header and every other line byte-identical): + +```text +git diff --cached -U0 528c2db04af5c53b9820b946ddac2ebb936eb4cd -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-12 + +diff <(git show 528c2db04af5c53b9820b946ddac2ebb936eb4cd:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-12 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and +``` + +Changelog delta (the existing +`THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry only; every +other line byte-identical): + +```text +git diff --cached -U0 528c2db04af5c53b9820b946ddac2ebb936eb4cd -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 528c2db04af5c53b9820b946ddac2ebb936eb4cd:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 528c2db04af5c53b9820b946ddac2ebb936eb4cd -- CHANGELOG.md | grep -o '\[-[^]]*-\]\|{+[^}]*+}' +[-propose-] +{+record+} +{+CTO-approved+} +[-`PROPOSED`:-] +{+`APPROVED`: Javi, CTO approved+} +{+exact+} +[-is not approved.**-] +{+on 2026-10-12.**+} +[-proposal-] +{+decision+} +``` + +Content-approval identifier (the command is shown with the identifier replaced +by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) df8017c57d74eaecf13041e231ef97625260fab1 +decision-register.md c9ff92119095a162cf736304eb88ba595d3685ab +CHANGELOG.md a4af0397dc37fdf782124573d9a4b7a7b55e8abe +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +df8017c57d74eaecf13041e231ef97625260fab1 +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above; the ADR-0012 blob `df8017c57d74eaecf13041e231ef97625260fab1` is the exact +final blob presented for CTO exact-final-blob approval at this stage and is +not yet so approved. This report's own final blob, the approval-state tree +and the approval-state commit SHA depend on this report's bytes and are not +self-pinned; they are verified against the remote head after push and +recorded in the post-publication handoff. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 528c2db04af5c53b9820b946ddac2ebb936eb4cd (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 528c2db04af5c53b9820b946ddac2ebb936eb4cd HEAD +required: exit 0; no output + +git diff --name-only 528c2db04af5c53b9820b946ddac2ebb936eb4cd HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -8115,6 +8663,19 @@ the candidate commit, in particular any statement naming the replacement candidate as the direct parent of the commit containing this report or presenting the approval-state commit as the current stage, and reframed them. +Amendment-11 approval-state stage: compared the staged ADR-0012 with the +content-approved candidate blob `7e461106986fe6efad2021d1ad6f52573e6e9d66` by +the exact normalized comparison in section 9 (no difference), and read the +raw delta line by line; compared the staged register and changelog with the +candidate blobs (only the ADR-0012 row, and only the routed-job atomicity +correction entry, change); confirmed the protected decision and control +blobs are unchanged; confirmed by fixed-string search that the +content-approval comment identifier does not occur in this report; and read +the report for statements that would become false once it moves into the +approval-state commit, in particular any statement naming the Amendment-10 +approval-state head as the direct parent of the commit containing this +report or describing the candidate as still the current `PROPOSED` +candidate, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -8132,10 +8693,12 @@ the content-approved Amendment-9 candidate `8699af5635b8f18691ceaeccd3b24707a71ef4f2`, the Amendment-9 approval-state head `e41f377202379238b91eb43d60f5115aa8adc20f`, the first Amendment-10 candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` and the content-approved -replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8` and the Amendment-10 -approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, are -historical evidence; the live PR #960 diff of the candidate commit exists -only after its publication and is GitHub-owned lifecycle evidence. +replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, the Amendment-10 +approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` and the +content-approved Amendment-11 candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`, are historical evidence; the live +PR #960 diff of the approval-state commit exists only after its publication +and is GitHub-owned lifecycle evidence. ## 11. CI @@ -8151,9 +8714,10 @@ authorization, the Amendment-8 candidate authorization, the Amendment-8 approval-state authorization, the Amendment-9 candidate authorization, the Amendment-9 approval-state authorization, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the -Amendment-10 approval-state authorization and the Amendment-11 candidate -authorization, an automatic CI failure, or any migration execution or Docker -publication, is a HOLD condition. +Amendment-10 approval-state authorization, the Amendment-11 candidate +authorization and the Amendment-11 approval-state authorization, an +automatic CI failure, or any migration execution or Docker publication, is a +HOLD condition. ## 12. Rollout and rollback @@ -8188,21 +8752,23 @@ permitted exactly one direct-child approval-state commit, the Amendment-10 candidate authorization permitted exactly one four-path candidate commit, the F-A10-CAND-01 consistency-correction authorization permitted exactly one direct-child four-path correction commit, the Amendment-10 approval-state -authorization permitted exactly one direct-child approval-state commit, and -the Amendment-11 candidate authorization permits exactly one four-path -candidate commit; any further source correction exhausts those -authorizations and requires a fresh CTO authorization (Amendment 3 C10; -Amendments 6-11; `docs/engineering/decisions/README.md`). +authorization permitted exactly one direct-child approval-state commit, the +Amendment-11 candidate authorization permitted exactly one four-path +candidate commit, and the Amendment-11 approval-state authorization permits +exactly one direct-child approval-state commit; any further source +correction exhausts those authorizations and requires a fresh CTO +authorization (Amendment 3 C10; Amendments 6-11; +`docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-11 candidate stage the - implementing agent performed no independent inspection or review, - candidate-content approval, PR #960 title/description reconciliation, - Draft/Ready change, automated-review request, thread adjudication, - exact-head review or merge (section 4.2). The PR #960 title and - description describe the historical Amendment-10 source; their +- Historical stage-time evidence: at the Amendment-11 approval-state stage + the implementing agent performed no independent inspection or review, + exact-final-blob approval or confirmation, PR #960 title/description + reconciliation, Draft/Ready change, automated-review request, thread + adjudication, exact-head review or merge (section 4.2). The PR #960 title + and description describe the historical Amendment-10 source; their reconciliation is a separate control action. Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the @@ -8249,10 +8815,11 @@ Monitoring required: NONE exact-candidate inspection of that candidate only under a separate authorization for one four-path correction commit, its approval state only under a separate authorization for one direct-child approval-state commit, - and the routed-job atomicity and anomaly terminal-resolution defects found - by the final-head review of that approval-state head only under - Specification Amendment 11, its independent review and a separate - authorization for this one four-path candidate commit. + the routed-job atomicity and anomaly terminal-resolution defects found by + the final-head review of that approval-state head only under Specification + Amendment 11, its independent review and a separate authorization for one + four-path candidate commit, and its approval state only under a separate + authorization for this one direct-child approval-state commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -8275,7 +8842,8 @@ Monitoring required: NONE correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction and approval-state - commit and this Amendment-11 candidate. + commit, the Amendment-11 candidate and this Amendment-11 approval-state + commit. ## 14. Unresolved issues @@ -8443,12 +9011,16 @@ Monitoring required: NONE specification and Amendments 1-11 returned `APPROVED`, confirming both findings, the classification, both rules, the composition and the fourteen-case validation matrix and recommending four non-blocking negative - vectors for the implementation specification; and the Route-B candidate - that contains this report implements the correction in source as - `PROPOSED` (section 4; section 5, decisions 113-124). Both findings are - closed at specification level by Amendment 11; whether the candidate - closes them in source is decided by the independent inspection and review - of the candidate, which GitHub records. + vectors for the implementation specification; the Route-B candidate + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` implemented the correction in + source as `PROPOSED` (section 4; section 5, decisions 113-124); an + independent CRITICAL exact-candidate source review of that candidate found + no blocking candidate-content finding; Javi, CTO approved its exact + architecture content on 2026-10-12; and the approval-state commit that + contains this report records that approval. Both findings are closed at + specification level by Amendment 11; their source closure is adjudicated + by the independent exact-head review controls that precede any merge, + which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -8462,10 +9034,10 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-11 candidate also implemented -the Amendment-10 approval-state commit, the Amendment-10 replacement -candidate and first candidate, the Amendment-9 approval-state commit and -candidate, the Amendment-8 +work. The implementing agent of this Amendment-11 approval-state commit also +implemented the Amendment-11 candidate, the Amendment-10 approval-state +commit, the Amendment-10 replacement candidate and first candidate, the +Amendment-9 approval-state commit and candidate, the Amendment-8 approval-state commit and candidate, the factual clarification, the transient-state correction, the control-record recovery, the Amendment-6/7 approval-state commit and candidate and earlier Route-B source stages of this @@ -8476,10 +9048,24 @@ eligible to act as an independent inspector or reviewer of this task Suggested review focus: -- that the candidate changes exactly the four authorized paths from the +- that the approval-state delta from the content-approved Amendment-11 + candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd` is limited to the ADR + header approval fields, the two leading section-12 paragraphs, the ADR-0012 + register row and the routed-job atomicity correction changelog entry, that + the normalized comparison in section 9 shows no architecture drift from + candidate blob `7e461106986fe6efad2021d1ad6f52573e6e9d66`, and that the + approval-state ADR-0012 blob is `df8017c57d74eaecf13041e231ef97625260fab1`; +- that the content approval is recorded only through owning issue #958, + approver Javi, CTO, approval date 2026-10-12, candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`, that the approval date matches + the CTO candidate-content approval record, and that no content-approval, + exact-final-blob approval, review or merge identifier is copied into this + report (section 5, decision 128); +- that the candidate changed exactly the four authorized paths from the approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, that the - candidate ADR-0012 blob is `7e461106986fe6efad2021d1ad6f52573e6e9d66`, and that - ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; + candidate ADR-0012 blob was `7e461106986fe6efad2021d1ad6f52573e6e9d66`, and that + ADR-0008, ADR-0010 and ADR-0013 kept their protected blobs; - that every Amendment 11 A11-1 requirement is expressed in ADR-0012: the routed-job definition, the one-transaction boundary for job, disposition and indispensable input or membership facts, no precommit claim or @@ -8686,15 +9272,18 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `PROPOSED` with no current approver or - approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, - 2026-10-08 and 2026-10-09 approvals to the earlier exact versions and keep - the repository authority condition and the ADR-0013 reliance distinction; -- that the register row, the new routed-job atomicity correction changelog - entry and this report describe the corrected version as `PROPOSED`, that - the strand-safety correction entry and every other historical changelog - entry are byte-preserved, and that the register header `Last updated` is - `2026-10-12`; +- that the header and section 12 carry `APPROVED` with Javi, CTO and + 2026-10-12, identify the approved content by candidate head + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`, bind the 2026-09-30, + 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and 2026-10-09 approvals to + the earlier exact versions and keep the repository authority condition and + the ADR-0013 reliance distinction; +- that the register row, the routed-job atomicity correction changelog entry + and this report describe the corrected version as `APPROVED` on + 2026-10-12, that the strand-safety correction entry and every other + historical changelog entry are byte-preserved, and that the register header + `Last updated` is `2026-10-12`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 7e4611069..df8017c57 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-12 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -3183,11 +3185,20 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +Current decision state: **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-12 + +The CTO approved this exact corrected content on 2026-10-12 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and +candidate ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66`; this +version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 98cf12566..c9ff92119 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-12 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, and the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-12 under the repository decision process, as the architecture content represented by Route-B candidate head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only: exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, and the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 06cc13685f1df5ec41da4f7958e8780a0c63ead9 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 14 Oct 2026 12:21:00 +0100 Subject: [PATCH 43/48] THOTH-ASYNC-01-ADR-01: propose Amendment-12 post-seal recovery correction (#958) --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1287 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 431 +++++- .../decisions/decision-register.md | 4 +- 4 files changed, 1470 insertions(+), 253 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4af0397d..a7848c867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while blocked recovery stays backlogged, age-visible and attention-raising with bounded backoff and bounded generations; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index daeff73cf..b5656eed3 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -27,16 +27,18 @@ independent exact-candidate inspection, the Route-B approval-state stage of that corrected candidate, and the Route-B candidate stage of the atomic routed-job creation and mutually exclusive anomaly-resolution correction specified by Specification Amendment 11 after the configured final-head -automated review of that Amendment-10 approval-state head, and the Route-B -approval-state stage of that Amendment-11 candidate. It records +automated review of that Amendment-10 approval-state head, the Route-B +approval-state stage of that Amendment-11 candidate, and the Route-B +candidate stage of the post-seal materialization correction specified by +Specification Amendment 12 after the configured final-head automated review +of that Amendment-11 approval-state head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-11 Route-B approval-state -commit as the current stage and every earlier candidate, approval-state, -recovery, +Route-B correction stages, with the Amendment-12 Route-B candidate as the +current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -58,11 +60,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-11 Route-B approval-state commit that contains -this report, an ordinary single-parent additive commit whose direct parent is -the content-approved Amendment-11 candidate -`528c2db04af5c53b9820b946ddac2ebb936eb4cd`; its SHA is deliberately not -self-pinned here (section 3). That candidate is the direct child of the +Head commit: the Amendment-12 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical Amendment-11 approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47`; its SHA is deliberately not +self-pinned here (section 3). That approval-state head is the direct child +of the content-approved Amendment-11 candidate +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`, which is the direct child of the historical Amendment-10 approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, which is the direct child of the content-approved Amendment-10 replacement candidate @@ -93,7 +97,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -109,9 +113,9 @@ approval-state and control-record recovery stages - Claude (Opus 5.5), bounded implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 -consistency-correction, Amendment-10 approval-state, Amendment-11 candidate -and Amendment-11 approval-state stages - Claude (Fable 5.1), bounded -implementing agent. Earlier rounds are recorded in Appendix A. +consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, +Amendment-11 approval-state and Amendment-12 candidate stages - Claude (Fable +5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -271,7 +275,7 @@ decision-register blob 2ed1499172780c6db82bb5f8808f2368fd8b96d1 CHANGELOG blob b9596d44ff260d6b818ad9c8174e843cf4d0c5b1 implementation-report blob 65fdd7677b12a10dcf8e53f1f173531e9c088d6b (superseded by the Amendment-11 candidate) -content-approved Amendment-11 candidate / approval-state parent: +content-approved Amendment-11 candidate / Amendment-11 approval-state parent: 528c2db04af5c53b9820b946ddac2ebb936eb4cd tree 7de8c64a1bf570526822002b9d2f1bb81baef424 ADR-0012 blob 7e461106986fe6efad2021d1ad6f52573e6e9d66 (PROPOSED; content-approved; material architectural correction of f8c258b0f1b0d19b401c4184ca4dc03a15c865d3; independent exact-candidate source review APPROVED) @@ -279,18 +283,26 @@ decision-register blob 98cf125665a3f0011a6ca9c7f90b2b9562931fe7 implementation-report blob 6919cf8f5b5f854d56d93df7c283b416b5b4b84d CHANGELOG blob 91218d7aee40053dfb84c83414f50434e1398695 -Amendment-11 Route-B approval-state commit (the commit that contains this report): -ADR-0012 blob df8017c57d74eaecf13041e231ef97625260fab1 (APPROVED; Javi, CTO; 2026-10-12; architecture-equivalent to 7e461106986fe6efad2021d1ad6f52573e6e9d66) +historical Amendment-11 approval-state head / Amendment-12 candidate parent: +21e19e81d0acf1aed57952ec00e2653771fb6f47 +tree 5ed622ac36e3c608af4a9f7c2821016084b9875b +ADR-0012 blob df8017c57d74eaecf13041e231ef97625260fab1 (APPROVED; Javi, CTO; 2026-10-12; architecture-equivalent to 7e461106986fe6efad2021d1ad6f52573e6e9d66; independent approval-state inspection and CTO exact-final-blob approval recorded on #958 on 2026-10-12; the exact prior approved blob that the Amendment-12 correction corrects) decision-register blob c9ff92119095a162cf736304eb88ba595d3685ab CHANGELOG blob a4af0397dc37fdf782124573d9a4b7a7b55e8abe +implementation-report blob daeff73cf6f6b6da413ba506cc43cd1d8510f673 (superseded by the Amendment-12 candidate) + +Amendment-12 Route-B candidate (the commit that contains this report): +ADR-0012 blob 678541fe5904ae4ea816cafa46c2e68780b51f4e (PROPOSED; material architectural correction of df8017c57d74eaecf13041e231ef97625260fab1) +decision-register blob c3b827d5fe5309dbc6ccd84a67d704aca198fe2c +CHANGELOG blob a7848c8672984f7ff2fad997c7674b04d94ea04a feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction, the Amendment-10 approval-state commit, the -Amendment-11 candidate and the Amendment-11 approval-state commit -(unchanged): +Amendment-11 candidate, the Amendment-11 approval-state commit and the +Amendment-12 candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -452,7 +464,29 @@ Approved specification: is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and is not copied here; - Amendment-11 Route-B approval-state implementation authorization: #958 - comment `6128858897`. + comment `6128858897`; +- independent exact approval-state source inspection of the Amendment-11 + approval-state head: recorded on #958 on 2026-10-12; its GitHub comment + identifier is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B + doctrine and is not copied here; +- CTO exact-final-blob approval of the Amendment-11 approval-state ADR-0012 + blob `df8017c57d74eaecf13041e231ef97625260fab1` at head + `21e19e81d0acf1aed57952ec00e2653771fb6f47`: recorded on #958 on 2026-10-12; + its GitHub comment identifier is GitHub-owned lifecycle evidence under + `ADR-0005` and Route-B doctrine and is not copied here; +- Specification Amendment 12 / post-seal materialization and successor + generations, which accepts the final-head finding `R-21E-01` as valid and + blocking and classifies its correction as a material architectural + correction: #958 comment `6149301174`; +- CTO specification approval of Amendment 12, A12-1 through A12-6 and all + twenty validation cases: #958 comment `6150795589`; +- fresh independent CRITICAL specification approval of the base specification + and Amendments 1-12, with seven non-blocking candidate-clarity + observations: #958 comment `6151515739`; +- PR #960 Ready -> Draft authorization for Amendment-12 Route-B preparation, + a control action that preceded this stage: #958 comment `6151609272`; +- Amendment-12 Route-B candidate implementation authorization: #958 comment + `6151955721`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -527,13 +561,22 @@ transaction, and requires compatible historical enrollment and approved reconciliation/divergence of one mismatch anomaly to compete through one database-enforced terminal-resolution decision, received independent CRITICAL exact-candidate source review (`APPROVED`) and CTO exact -candidate-content approval on 2026-10-12. The commit that contains this -report is the one bounded Route-B approval-state commit that records that -approval as durable decision state: ADR-0012 `PROPOSED -> APPROVED` with -`Approved by: Javi, CTO` and `Approval date: 2026-10-12`, the ADR-0012 -register row and the routed-job atomicity correction changelog entry moved to -the approved state, and this report's approval-state evidence. No -architecture content changes. +candidate-content approval on 2026-10-12. The one bounded Route-B +approval-state commit then recorded that approval as durable decision state +(ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +`Approval date: 2026-10-12`, the ADR-0012 register row and the routed-job +atomicity correction changelog entry moved to the approved state), and the +CTO recorded exact-final-blob approval of that ADR blob. The configured +final-head automated review of that approval-state head exposed a further +architecture defect, `R-21E-01`, specified by Specification Amendment 12. The +commit that contains this report is its Route-B candidate: one ordinary +single-parent commit on the approval-state head that changes exactly the four +authorized correction paths, returns ADR-0012 to `Status: PROPOSED`, and +defines the ordinary post-seal materialization path under which an eligible +obligation refused after a permanent coalescing seal remains owed and visible +and is materialized through a different eligible coalescing-open successor or +a distinct, stably identified routed successor generation created together +with its disposition. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -1022,27 +1065,85 @@ architecture content changes. `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66` - on 2026-10-12 in a durable record on #958. That stage authorized no PR-state or PR-metadata change. -- **Amendment-11 approval-state stage (the commit that contains this - report).** Authorized by #958 comment `6128858897`. One ordinary - single-parent commit directly on the content-approved candidate records - that approval as durable decision state, changing only the four authorized - paths: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +- **Amendment-11 approval-state stage (historical approval-state head + `21e19e81d0acf1aed57952ec00e2653771fb6f47`).** Authorized by #958 comment + `6128858897`. One ordinary single-parent commit directly on the + content-approved candidate recorded that approval as durable decision + state, changing only the four authorized paths: ADR-0012 + `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and `Approval date: 2026-10-12` and the leading section-12 approval paragraphs, which identify the approved content as the content represented by candidate head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66`; the ADR-0012 register row; the `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` changelog entry; - and this report. No architecture content changes (section 9, "Amendment-11 - approval-state validation"). The content approval is recorded here through - owning issue #958, approver Javi, CTO, approval date 2026-10-12, the - candidate head and the candidate ADR-0012 blob; its GitHub comment - identifier is not copied into repository source, as the + and the approval-state report. No architecture content changed (section 9, + "Amendment-11 approval-state validation"). The content approval is + recorded here through owning issue #958, approver Javi, CTO, approval date + 2026-10-12, the candidate head and the candidate ADR-0012 blob; its GitHub + comment identifier is not copied into repository source, as the repository-authoritative Route-B rule in `docs/engineering/decisions/README.md` and the approval-state authorization - itself require (section 5, decision 128). PR #960 was already a draft; this - stage authorizes no PR-state or PR-metadata change; the PR #960 title and - description, which describe the historical Amendment-10 source, are - GitHub-owned and their reconciliation is a separate control action. + itself require (section 5, decision 128). PR #960 was already a draft; that + stage authorized no PR-state or PR-metadata change. +- **Exact-final-blob approval, final-head review of the Amendment-11 + approval-state head, Specification Amendment 12 and the Amendment-12 Route-B + candidate (the commit that contains this report).** Authorized by #958 + comment `6151955721` after the CTO approved Specification Amendment 12 (#958 + comments `6149301174` and `6150795589`) and a fresh independent CRITICAL + specification review of the base specification and Amendments 1-12 returned + `APPROVED` with seven non-blocking candidate-clarity observations (#958 + comment `6151515739`). After the approval-state head was published, control + independently inspected it and the CTO recorded exact-final-blob approval of + ADR-0012 blob `df8017c57d74eaecf13041e231ef97625260fab1` at head + `21e19e81d0acf1aed57952ec00e2653771fb6f47`, both on 2026-10-12; control then + separately marked PR #960 Ready, which triggered the configured final-head + automated review of that head on 2026-10-13, and returned PR #960 to Draft + under a separate authorization (#958 comment `6151609272`) before any + candidate source was staged. That review raised one new finding, recorded + by control as `R-21E-01` (HIGH, valid and blocking) and classified as a + `MATERIAL ARCHITECTURAL CORRECTION`: section 3.5 made a job's idempotency + key unique across every job state and allowed coalescing only into a + not-yet-started job, Amendment 9 sealed the job permanently at its first + successful claim/start and sent the losing attachment to retry after + rereading state without creating any new logical-job identity, so an + eligible obligation of the same coalescing group or window that arrived + after the seal could resolve only to the same permanently unattachable + identity on every retry and remain owed indefinitely with healthy workers + and a healthy materializer. Amendment 12 selects A12-1, a mandatory + ordinary post-seal materialization path in which the obligation remains + owed and visible and is materialized through a different eligible + coalescing-open successor or a distinct legitimate routed successor, with + no automatic exceptional disposition and fail-closed activation for a kind + without a safe successor contract; A12-2, group/window identity + distinguished from executable generation identity, stable unique successor + identity, an unchanged predecessor, no identity on every retry and + database-arbitrated convergence; A12-3, the Amendment-9 boundary for + successor attachment versus first claim, the Amendment-11 one-transaction + creation of a successor with its disposition and input facts, no duplicate + dispositions, orphan jobs, write skew or deadlock, and composition with the + Amendment-11 terminal anomaly decision; A12-4, unchanged target-scoped + fencing, applied evidence, effect phases, ambiguity handling, + `CURRENT_STATE` and `REVISION_BOUND` semantics and route, epoch, revision + and authorization identity; A12-5, conditional convergence, visible bounded + backoff for blocked recovery and pre-activation documentation; A12-6, + narrow preservation of every earlier rule; and twenty deterministic + validation cases, with the concrete tables, constraints, sequences, locks + and SQL left to the implementation specification. The independent review + confirmed the finding, the classification, all six provisions, the + composition and the sufficiency of the twenty-case matrix, and recorded + seven non-blocking clarity observations. The commit that contains this + report is one ordinary single-parent commit directly on the approval-state + head that changes exactly the four authorized correction paths: ADR-0012 + returns to `Status: PROPOSED` with no current approver or approval date and + implements Amendment 12 (section 4; section 5, decisions 133-144); the + decision-register ADR-0012 row, one new changelog entry and this report + describe the corrected version as `PROPOSED`; and the 2026-09-30, + 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09 and 2026-10-12 + CTO approvals remain historical evidence bound to the earlier exact + versions. This stage authorizes no PR-state or PR-metadata change; the PR + #960 title and description, which describe the historical Amendment-11 + source, are GitHub-owned and their reconciliation is a separate control + action. Out-of-scope changes made: NONE @@ -1056,10 +1157,15 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +MATERIAL ARCHITECTURAL CORRECTION of prior approved blob df8017c57d74eaecf13041e231ef97625260fab1 +(Specification Amendment 12; Route-B candidate stage; candidate status PROPOSED) + +historical Amendment-11 approval-state at 21e19e81d0acf1aed57952ec00e2653771fb6f47: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-12) of the content-approved Amendment-11 candidate blob 7e461106986fe6efad2021d1ad6f52573e6e9d66 at candidate head 528c2db04af5c53b9820b946ddac2ebb936eb4cd; -no architecture change +no architecture change; independent approval-state inspection and CTO exact-final-blob +approval of df8017c57d74eaecf13041e231ef97625260fab1 recorded on #958 on 2026-10-12 historical Amendment-11 candidate at 528c2db04af5c53b9820b946ddac2ebb936eb4cd: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob f8c258b0f1b0d19b401c4184ca4dc03a15c865d3 @@ -1329,41 +1435,82 @@ well: They remain historical evidence bound to their exact versions and heads. The Amendment-11 candidate version received its own independent CRITICAL exact-candidate source review and CTO exact candidate-content approval on -2026-10-12, and the approval-state commit that contains this report records -it. For this version, exact-final-blob approval, independent exact-head -review, CTO merge authorization, guarded merge, programme-local reliance and -later migration, deployment, activation and observation are distinct -controlled lifecycle categories; GitHub, through #958 and PR #960, is -authoritative for which of them is pending, satisfied or blocked for any -head. +2026-10-12; the historical approval-state commit +`21e19e81d0acf1aed57952ec00e2653771fb6f47` recorded that approval in ADR-0012 +blob `df8017c57d74eaecf13041e231ef97625260fab1`, and that blob received +independent approval-state inspection and CTO exact-final-blob approval at +that head on 2026-10-12. + +The Amendment-12 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every record bound to the routed-job atomicity +version as well: + +- the CTO content approval of candidate + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` / ADR blob + `7e461106986fe6efad2021d1ad6f52573e6e9d66`; +- the 2026-10-12 approval state carried by ADR blob + `df8017c57d74eaecf13041e231ef97625260fab1`, its independent approval-state + inspection and its exact-final-blob approval at head + `21e19e81d0acf1aed57952ec00e2653771fb6f47`; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the approval-state head; +- the approval-state-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` changelog entry that + record the routed-job atomicity version as `APPROVED`; the changelog entry + is preserved and remains true only of that historical version, while the + register row now records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. For +the candidate version, independent exact-candidate inspection, CTO exact +candidate-content approval, the approval-state commit, exact-final-blob +approval, independent exact-head review, CTO merge authorization, guarded +merge, programme-local reliance and later migration, deployment, activation +and observation are distinct controlled lifecycle categories; GitHub, through +#958 and PR #960, is authoritative for which of them is pending, satisfied or +blocked for any head. ## 3. Commits -Amendment-11 Route-B approval-state commit (the commit that contains this -report): +Amendment-12 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: record Amendment-11 routed-job atomicity content approval (#958)` - - direct parent: the content-approved Amendment-11 candidate - `528c2db04af5c53b9820b946ddac2ebb936eb4cd` - - changes only the four authorized paths, and only to record the approval - state (section 4); the approval-state ADR-0012 blob is - `df8017c57d74eaecf13041e231ef97625260fab1`, the approval-state - decision-register blob `c9ff92119095a162cf736304eb88ba595d3685ab` and the - approval-state changelog blob `a4af0397dc37fdf782124573d9a4b7a7b55e8abe` + `THOTH-ASYNC-01-ADR-01: propose Amendment-12 post-seal recovery correction (#958)` + - direct parent: the historical Amendment-11 approval-state head + `21e19e81d0acf1aed57952ec00e2653771fb6f47` + - changes only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `678541fe5904ae4ea816cafa46c2e68780b51f4e`, the + candidate decision-register blob `c3b827d5fe5309dbc6ccd84a67d704aca198fe2c` + and the candidate changelog blob `a7848c8672984f7ff2fad997c7674b04d94ea04a` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the approval-state ADR-0012, +here because this report is part of that tree; the candidate ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its candidate blob (section 9). The -commit identities are verified against the remote task-branch head after push -and recorded in the post-publication handoff; once published, the PR #960 -head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The -authorized topology is a non-force fast-forward of the task branch from the -content-approved candidate to the approval-state commit; no merge commit, -rebase, squash, amend, force update or second approval-state commit is -authorized, and no further source commit exists implicitly after it. +recorded above; every other file keeps its approval-state-head blob (section +9). The commit identities are verified against the remote task-branch head +after push and recorded in the post-publication handoff; once published, the +PR #960 head and head tree are GitHub-owned lifecycle evidence under +`ADR-0005`. The authorized topology is a non-force fast-forward of the task +branch from the approval-state head to the candidate commit; no merge commit, +rebase, squash, amend, force update or second candidate commit is authorized, +and no further source commit exists implicitly after it. + +Amendment-11 Route-B approval-state commit (historical): + +- `21e19e81d0acf1aed57952ec00e2653771fb6f47` - + `THOTH-ASYNC-01-ADR-01: record Amendment-11 routed-job atomicity content approval (#958)` + - ordinary single-parent additive commit; direct parent: the content-approved + Amendment-11 candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd` + - tree `5ed622ac36e3c608af4a9f7c2821016084b9875b` + - changed only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `df8017c57d74eaecf13041e231ef97625260fab1`, which received independent + approval-state inspection and CTO exact-final-blob approval at that head + on 2026-10-12 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + candidate; the remote head, tree, parent, ADR-0012, decision-register, + changelog and report blobs and the ADR approval state matched the locally + validated object Amendment-11 Route-B candidate commit (content-approved; historical): @@ -1627,11 +1774,22 @@ first-parent order, newest first: ## 4. Files changed -Amendment-11 Route-B approval-state write budget for the commit that contains -this report (authorization #958 comment `6128858897`), identical to the -Amendment-11 candidate write budget; exactly four existing paths, no new file, -no deletion, and every other path byte-identical to the content-approved -candidate +Amendment-12 Route-B candidate write budget for the commit that contains this +report (authorization #958 comment `6151955721`; the same four existing paths +as the Amendment-11 budgets); exactly four existing paths, no new file, no +deletion, and every other path byte-identical to the historical Amendment-11 +approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-11 Route-B approval-state write budget (authorization +#958 comment `6128858897`), identical to the Amendment-11 candidate write +budget; exactly four existing paths, no new file, no deletion, and every other +path byte-identical to the content-approved candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`: - `CHANGELOG.md` @@ -1773,13 +1931,122 @@ Amendment-6/7 candidate, Amendment-6/7 approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, -Amendment-10 approval-state, Amendment-11 candidate and Amendment-11 -approval-state stages; all four correction paths already existed. +Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 +approval-state and Amendment-12 candidate stages; all four correction paths +already existed. -Amendment-11 approval-state changes in the commit that contains this report, -relative to the content-approved candidate -`528c2db04af5c53b9820b946ddac2ebb936eb4cd` (exactly the four authorized paths, -approval state only): +Amendment-12 candidate changes in the commit that contains this report, +relative to the approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47` +(exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement Specification Amendment 12 and record the corrected + version as `PROPOSED`. + - behavioural effect: architecture proposal only. The header carries + `Status: PROPOSED` and no current approver or approval date. Section 3.2 + states, after the routed-job paragraph, that a coalescing seal closes one + logical job and never the obligations that follow it, that an obligation + refused because its job is permanently sealed remains owed, + undispositioned and visible until a different eligible coalescing-open + successor accepts it or a distinct legitimate routed successor is created + for it under the same all-or-nothing boundary, and that the sealed job, + the key collision, retry exhaustion and an unavailable materializer are + never a disposition and never convert the obligation into the exceptional + terminal non-materialized disposition; its unavailable-materializer + paragraph extends the backlog and attention rule to obligations refused + after a seal and to successors held by effect ambiguity. Section 3.5 + names the coalescing group or window together with its executable + generation among the key dimensions, adds the post-seal path as the + second branch of the idempotency-conflict outcome list, binds the + claim/start-first bullet's "no new identity" rule to the losing stale + attachment transaction and routes the loser's recovery to the ordinary + post-seal path, clarifies the boundary's closing sentence ("no second + identity for one logical job"; a successor generation is a distinct + logical job), and gains the unnumbered subsection "Post-seal + materialization and successor generations" between the coalescing-sealed + and atomic-creation subsections (the owed and visible obligation; the two + normal paths and successor eligibility; the sealed job never a path; the + creation trigger; successor identity; database-arbitrated convergence; + historical enrollment and anomaly resolution; effect semantics, fencing + and ambiguity; route identity unchanged; liveness and operational + admission; unsupported contracts failing closed; exceptional dispositions + unchanged; narrow preservation); the atomic-creation existing-job + paragraph routes a sealed-job loser to that path and makes a successor a + brand-new routed job under the boundary. Section 3.15 states that a + refused obligation follows the path and stays in the route backlog. + Invariant 42's closing clause refers recovery to invariant 46 and binds + the no-identity rule to the losing transaction; invariant 46 is appended; + invariants 1-41 and 43-45 are byte-identical. Section 8 item 1, the + section-9 activation sentence for coalescing modes and rollout proof 5 + name the rule; section 11 adds the twenty Amendment-12 validation cases + immediately after the Amendment-11 composition bullet. Section 12 + records the `PROPOSED` state, adds the rule to the description of this + material correction and binds the 2026-09-30, 2026-10-05, 2026-10-06, + 2026-10-07, 2026-10-08, 2026-10-09 and 2026-10-12 approvals to the + historical earlier versions. Event-emission floors, producer version + sets, revision signatures, eligibility, permanent retirement, mismatch + detection and resolution semantics, the Amendment-8 withdrawal/selection + order, the Amendment-9 seal and attachment-first order, the Amendment-10 + strand-safety decision, the Amendment-11 atomic creation and terminal + resolution, operator authority, domain ownership, staff-command + authorization, claims and leases, target-scoped external-effect + protection and the BE-04 transition are unchanged. Candidate blob: + `678541fe5904ae4ea816cafa46c2e68780b51f4e`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the material correction makes the ADR-0012 row's `APPROVED` + assertion untrue for the new version. + - behavioural effect: the ADR-0012 row records `PROPOSED` / `Pending`, + binds the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, + 2026-10-09 and 2026-10-12 approvals to the historical earlier exact + versions and adds one summary of the post-seal materialization path; its + partial-supersession, authority-condition, ADR-0013 and + implementation-authorization sentences are unchanged. The header + `Last updated` advances from `2026-10-12` to `2026-10-14`, the date of + this row change (section 5, decision 141). No other row changes. + Candidate blob: `c3b827d5fe5309dbc6ccd84a67d704aca198fe2c`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: required changelog entry. + - behavioural effect: exactly one new + `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry at the top of + `[Unreleased] -> Changed`, describing the corrected version as + `PROPOSED` and stating that the earlier + `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION`, + `THOTH-ASYNC-01-ADR-01-CORRECTION`, `THOTH-ASYNC-01-ADR-01-APPROVAL` and + `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries and the approvals they + record apply only to the historical earlier exact versions. No heading is + added and every existing entry is byte-preserved. Candidate blob: + `a7848c8672984f7ff2fad997c7674b04d94ea04a`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-12 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate the current + stage; records the approval-state inspection, the exact-final-blob + approval of the Amendment-11 approval-state blob, the final-head review of + that head, `R-21E-01`, Amendment 12, its CTO approval, its independent + review, the Ready-to-Draft control action and the candidate authorization + as governing evidence; records the candidate's validation (section 9); + and reframes statements that would otherwise describe the approval-state + commit as the commit containing this report, recording that commit's + identities as historical facts. All still-true earlier evidence is + preserved. + - within authorized write budget: YES + +Byte-identical to the approval-state head through the Amendment-12 candidate: +every path other than the four correction paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical Amendment-11 approval-state changes in the approval-state commit +`21e19e81d0acf1aed57952ec00e2653771fb6f47`, relative to the content-approved +candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd` (exactly the four +authorized paths, approval state only): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: record the CTO exact candidate-content approval of 2026-10-12. @@ -1836,13 +2103,14 @@ approval state only): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-11 approval-state stage truthfully. - - behavioural effect: documentation only. Makes the approval-state commit - the current stage; records the independent exact-candidate source review, - the CTO candidate-content approval and the approval-state authorization; - records the approval-state validation and no-drift proof (section 9); and - reframes statements that would otherwise describe the candidate as the - commit containing this report, recording the candidate's identities as - historical facts. All still-true earlier evidence is preserved. + - behavioural effect: documentation only. Made the approval-state commit + the then-current stage; recorded the independent exact-candidate source + review, the CTO candidate-content approval and the approval-state + authorization; recorded the approval-state validation and no-drift proof + (section 9); and reframed statements that would otherwise describe the + candidate as the commit containing the report, recording the candidate's + identities as historical facts. All still-true earlier evidence was + preserved. - within authorized write budget: YES Byte-identical to the content-approved candidate through the approval-state @@ -2925,7 +3193,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-11 approval-state commit differs from the content-approved +The Amendment-12 candidate differs from the approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47` in exactly the four authorized +correction paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-12 candidate validation"). The +Amendment-11 approval-state commit differed from the content-approved candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd` in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-11 approval-state validation"). @@ -3007,8 +3279,9 @@ stage (#958 comment `6046895895`), the Amendment-9 approval-state stage `6067886387`), the F-A10-CAND-01 consistency-correction stage (#958 comment `6069074613`), the Amendment-10 approval-state stage (#958 comment `6083303288`), the Amendment-11 candidate stage (#958 comment -`6125285446`) and the Amendment-11 approval-state stage (#958 comment -`6128858897`). This matrix +`6125285446`), the Amendment-11 approval-state stage (#958 comment +`6128858897`) and the Amendment-12 candidate stage (#958 comment +`6151955721`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -3456,8 +3729,8 @@ approval-state commit: agent; an independent reviewer reviewed the published candidate and returned `APPROVED` (section 2) -Amendment-11 approval-state stage - done up to and including the creation of -the commit that contains this report: +Amendment-11 approval-state stage - historical, completed before the +Amendment-12 candidate: - repository inspection: USED (read-only verification of the exact refs, including `develop` and `feature/worker` at the same base, PR #960 open, @@ -3481,18 +3754,59 @@ the commit that contains this report: exact at the content-approved candidate and touching no other worktree, branch or ref, was reused for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent approval-state commit that contains this - report) +- commit: USED (the single-parent approval-state commit + `21e19e81d0acf1aed57952ec00e2653771fb6f47`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `528c2db04af5c53b9820b946ddac2ebb936eb4cd` to the approval-state commit; the + remote head, tree, parent, ADR-0012, decision-register, changelog and report + blobs and the ADR approval state were verified) +- Draft -> Ready: NOT USED by the implementing agent; control later separately + marked PR #960 Ready, which triggered the configured final-head automated + review of the approval-state head, and returned it to Draft before the + Amendment-12 candidate (section 2) + +Amendment-12 candidate stage - done up to and including the creation of the +commit that contains this report: -Authorized post-commit actions for the approval-state commit - GitHub-owned +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base, PR #960 open, + draft, unmerged and mergeable with head + `21e19e81d0acf1aed57952ec00e2653771fb6f47` and target `feature/worker`, + the approval-state head's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008, + ADR-0010 and ADR-0013 blobs and the Route-B doctrine blob, the six-path + cumulative footprint, the absence of any ADR-0012 commit reachable from + `develop`, `master` or `feature/worker` (Route-B eligibility), the presence + of the Amendment 12, CTO specification-approval, independent-review, + Ready-to-Draft and candidate-authorization records on #958 with no later + control record, the pull_request-triggered workflow set and its + documentation-only classification gate, the job-level outcomes of the four + workflow runs on the approval-state head, and, as stage-time evidence, the + PR #960 review-thread state in GitHub; all matched the authorization; + current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 had been returned to draft by control + under a separate authorization before this stage, so no PR-state mutation + was needed; no PR-state, title or description change is authorized at this + stage) +- source edit: USED (the four authorized correction paths only) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; the implementing agent's own + existing session worktree, verified clean and detached at the + approval-state head and touching no other worktree, branch or ref, was + used for construction, and the existing task branch is the fast-forward + target) +- commit: USED (the single-parent candidate commit that contains this report) + +Authorized post-commit actions for the candidate commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `528c2db04af5c53b9820b946ddac2ebb936eb4cd` to the approval-state commit; + `21e19e81d0acf1aed57952ec00e2653771fb6f47` to the candidate commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, ADR approval - state, changed path set, cumulative footprint and PR #960 state, and that - no review thread was replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, changed path set, + cumulative footprint and PR #960 state, and that no review thread was + replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -3500,23 +3814,24 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-11 approval-state +Not authorized for the implementing agent at the Amendment-12 candidate stage, each a separate controlled lifecycle category where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): -- any edit to a path other than the four authorized paths: NOT USED (not - authorized) -- any ADR-0012 change outside the approval-state representation, including - any architecture change: NOT USED (not authorized) -- independent inspection or exact-head review: NOT USED (never on the - implementing agent's authority) -- exact-final-blob approval or confirmation: NOT USED (not authorized; never +- any edit to a path other than the four authorized correction paths: NOT USED + (not authorized) +- any ADR-0012 change outside Amendment 12 and the candidate `PROPOSED` state: + NOT USED (not authorized) +- independent exact-candidate inspection or exact-head review: NOT USED (never on the implementing agent's authority) +- CTO candidate-content approval, approval-state commit, fresh `APPROVED` + status, approver or approval date, exact-final-blob approval: NOT USED (not + authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second approval-state commit, amend, rebase, squash, force push or - history rewrite: NOT USED (not authorized) +- any second candidate commit, amend, rebase, squash, force push or history + rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -3553,19 +3868,19 @@ authorization, the factual-clarification authorization, the Amendment-8 candidate and approval-state authorizations, the Amendment-9 candidate and approval-state authorizations, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 -approval-state authorization, the Amendment-11 candidate authorization and -the Amendment-11 approval-state authorization, configured Codex/app review is -not expected from a push to a draft PR and is not relied upon at the -Amendment-6/7 candidate, approval-state, control-record recovery, -transient-state correction, factual-clarification, Amendment-8 candidate, -Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 -approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 -approval-state, Amendment-11 candidate or Amendment-11 approval-state stage; -configured Codex/app review runs on separately authorized Ready transitions, -which are GitHub-owned lifecycle events, and any automated review that -nevertheless runs on a draft head is inspected read-only. The actual job -outcomes for the approval-state commit exist only after its publication; -they +approval-state authorization, the Amendment-11 candidate authorization, the +Amendment-11 approval-state authorization and the Amendment-12 candidate +authorization, configured Codex/app review is not expected from a push to a +draft PR and is not relied upon at the Amendment-6/7 candidate, +approval-state, control-record recovery, transient-state correction, +factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, +Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, +consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, +Amendment-11 approval-state or Amendment-12 candidate stage; configured +Codex/app review runs on separately authorized Ready transitions, which are +GitHub-owned lifecycle events, and any automated review that nevertheless +runs on a draft head is inspected read-only. The actual job outcomes for the +candidate commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -3575,9 +3890,9 @@ Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, -consistency-correction, Amendment-10 approval-state, Amendment-11 candidate or -Amendment-11 approval-state stage, followed the same documentation-only -classification, with no manual dispatch or rerun: +consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, +Amendment-11 approval-state or Amendment-12 candidate stage, followed the same +documentation-only classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -3599,6 +3914,7 @@ e41f377202379238b91eb43d60f5115aa8adc20f: 4 success (classify x3, check-changelo e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-10 replacement candidate) 576a860be47aa1ebe39968f4d3297b26ee1d17df: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-10 approval-state head) 528c2db04af5c53b9820b946ddac2ebb936eb4cd: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-11 candidate) +21e19e81d0acf1aed57952ec00e2653771fb6f47: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-11 approval-state head) ``` Manually initiated external actions (anything the implementing agent @@ -4628,7 +4944,8 @@ inspection: source; that stage was not authorized to change them, and their reconciliation was reported to control as a separate action. -Amendment-11 approval-state stage (the commit that contains this report): +Amendment-11 approval-state stage (historical approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47`): 125. The approval-state header uses the same field order as the earlier approved ADR-0012 versions (`Status`, `Date`, `Approved by`, @@ -4686,15 +5003,188 @@ Amendment-11 approval-state stage (the commit that contains this report): approval-state ADR is byte-identical to the content-approved candidate blob, so every A11-1, A11-2 and A11-3 requirement, invariants 1-45 and the complete validation matrix are unchanged. -132. This report does not self-pin the approval-state commit's SHA, its tree - or its own final blob; it records only non-self-referential evidence and - the approval-state ADR-0012, decision-register and changelog blobs, which - do not depend on its bytes. Because the report now lives in the - approval-state commit, statements that described the candidate as the - commit containing this report are reframed as history, and the - candidate's identities, now the approval-state parent, are recorded in - section 1 as historical facts. The approval-state ADR blob is recorded as - a result; it is not represented as exact-final-blob approved. +132. The approval-state report did not self-pin the approval-state commit's + SHA, its tree or its own final blob; it recorded only non-self-referential + evidence and the approval-state ADR-0012, decision-register and changelog + blobs, which did not depend on its bytes. Because the report then lived in + the approval-state commit, statements that described the candidate as the + commit containing the report were reframed as history, and the + candidate's identities, the approval-state parent, were recorded in + section 1 as historical facts; section 1 now records the approval-state + commit's identities as historical facts because that head is the + Amendment-12 candidate's parent. The approval-state ADR blob was recorded + as a result and was not represented as exact-final-blob approved; it + received that approval on 2026-10-12. + +Amendment-12 Route-B candidate (the commit that contains this report). Items +134, 135, 136 and 139 record interpretations of the approved specification +for explicit control inspection: + +133. The Amendment-12 rule is stated once, in a new unnumbered subsection + "Post-seal materialization and successor generations" in section 3.5, + placed between the "Coalescing-open and coalescing-sealed jobs" + subsection whose claim/start-first order it supplements and the "Atomic + creation of a routed job" subsection whose boundary every new successor + uses, as A12-6 asks ("beside coalescing/seal and idempotency in section + 3.5"). The places that depend on it point to it: a new section-3.2 + paragraph after the routed-job paragraph, the section-3.2 + unavailable-materializer paragraph, the section-3.5 key-dimension list, + the idempotency-conflict outcome list, the claim/start-first bullet, the + closing sentence of the attachment/seal boundary, the existing-job + paragraph of the atomic-creation subsection and section 3.15. The heading + outline gains exactly that one heading. +134. A12-1 is stated as Amendment 12 states it: the refused obligation stays + owed, undispositioned and visible and is never retroactively attached, + counted complete or silently represented; every coalescing kind has an + approved deterministic ordinary post-seal materialization contract with + two normal paths - attachment to a different eligible coalescing-open + successor under the Amendment-9 boundary, or creation of a distinct + legitimate routed successor under A11-1 - of which at least one is always + available and the sealed job is never one; the key collision is neither + proof of completion nor a reason to retry the same identity; a kind + without a safe successor contract may not enable that coalescing mode and + may use a separately approved event-distinct identity or hold activation; + and the exceptional terminal non-materialized disposition stays under + section 3.2 and ADR-0010 and is never automatic. Interpretation + (independent review observation O-6): the section-3.5 + idempotency-conflict outcome list gains the post-seal path as its second + branch, between attachment to an open job and the deterministic error, + and the claim/start-first bullet says the ordinary path is the normal + path with attention raised only while recovery is pending or blocked; + this restates A12-1's third bullet and selects no mechanism. +135. A12-2 is stated as Amendment 12 states it: group or window identity is + distinguishable from executable generation identity wherever successive + generations may be needed; a successor is a distinct logical job + representing a previously unrepresented still-owed input, with a unique, + stable idempotency key, for example a durably selected generation or an + explicitly approved event-derived identity; the predecessor's key, seal, + dispositions, attempts and history are unchanged; predecessor retry or + reclaim is never successor creation; a successor is no covert retry, + replay or forced resend; and the correlation and relationships remain + auditable with no rewriting. Interpretations (observations O-3 and O-4): + the subsection phrases the "where more than one executable generation + may be needed" qualifier as the default for every coalescing kind, + disclaimable only by demonstrating that no eligible obligation can arrive + after a seal, which applies A12-1's fourth bullet ("If a kind cannot + define and demonstrate a safe coalescing successor contract, it must not + enable that coalescing mode") to the qualifier; and it states that + independently authorized direct, replay or current-state jobs keep their + own effect identity under section 3.5 and consume no successor generation + of a routed coalescing group, which follows from the existing + section-3.5 rule that such jobs carry their own effect identity and from + A11-1's direct-job exception. Neither selects a key composition, sequence + or table. +136. A12-3 is stated as Amendment 12 states it: attachment to an open + successor and its first claim/start share the Amendment-9 boundary; a new + successor, its one disposition and its indispensable input facts commit + under A11-1, with no orphan, consumed slot or false completion on failure + and no executability before commit; two same-obligation materializers + cannot create two dispositions, two jobs or a phantom route; different + obligations may share an open successor and follow the next generation + after a seal; the group and `(event_id, route_key)` boundaries compose + without write skew or deadlock; retries, restarts and acknowledgment loss + recover from committed state and create no successor for an already + represented obligation; and enrollment and the Amendment-11 terminal + decision stay in one boundary, an enrollment that loses a successor's + seal race aborting as a whole. Interpretations (observation O-2): the + creation trigger is stated as "only after the reread confirms that the + obligation is still owed and that no eligible open successor exists", + which reads A12-2's third bullet and A12-3's fourth bullet together; and + the subsection records, as the reason uncontrolled generations cannot + arise, that a successor is created only together with the disposition it + represents, so every generation represents at least one obligation - a + consequence of A11-1 and A12-3, not a new rule. +137. A12-4 is stated as Amendment 12 states it: the same canonical target + identity, target-scoped concurrency namespace, applied evidence, lease + and claim fencing, effect phases, bounded retries, `WAITING` and + `RECONCILIATION_REQUIRED` semantics; no partitioning of conflicting + target keys; no bypass of an ambiguous predecessor by a new key, the + successor waiting visibly while safe execution is blocked; + `CURRENT_STATE` no-op on proven applied fingerprint and `REVISION_BOUND` + non-regression and supersession; a new identity never evidence of a + needed effect; no exactly-once or cross-system atomicity claim; and no + redefinition of revision, epoch, eligibility, producer constraint, + `route_key`, target identity or provider authorization. The sentence + that the predecessor retains its target-scoped boundary until reconciled + restates section 3.4's existing rule that `WAITING` and + `RECONCILIATION_REQUIRED` retain exclusive ownership of their concurrency + key by default; no new fencing is introduced. +138. A12-5 is stated as Amendment 12 states it: convergence under finite owed + inputs, a compatible available materializer, fair capacity and no + legitimate blocker, with the sealed identity never reselected + indefinitely and its collision not itself a legitimate blocker; + continuous input, rate limits and unavailable workers left to section + 3.15; blocked recovery durable, age-visible and attention-raising with + bounded backoff, bounded generations and no hot loop; and pre-activation + documentation of the five items, failing closed. The new section-3.2 + backlog sentence and the section-3.15 coalescing sentence carry the + visibility; the section-9 activation sentence for coalescing modes + carries the documentation requirement. The independent review's + observation O-7 (two additional validation vectors) is not acted on: the + twenty approved cases are added exactly, because the candidate + authorization treats the observations as clarity checks, not as + authority for extra validation architecture. +139. A12-6 preservation: the claim/start-first bullet now says that the + losing stale attachment transaction attaches nothing, invents no + logical-job identity and introduces no new replay or current-state rule, + and that the loser's recovery after reread is the ordinary post-seal + path, a separate database-arbitrated decision and never a continuation + of the losing transaction; invariant 42's closing clause is changed to + the same effect and refers recovery to invariant 46, which is the only + change to invariants 1-45; and the boundary's closing sentence now reads + "no second identity for one logical job", naming a successor generation + as a distinct logical job. Interpretation: that sentence previously read + "no second job identity"; because a successor generation is a new + logical job rather than a second identity of the sealed one, the + clarification preserves the sentence's meaning while removing a reading + under which successor generations would contradict it. Every other + Amendment-8, Amendment-9, Amendment-10 and Amendment-11 paragraph is + byte-identical (section 9). +140. Invariant 46 is appended after invariant 45; invariants 1-41 and 43-45 + are byte-identical and unrenumbered (decision 23 pattern); invariant 42 + changes only in its closing clause (decision 139). +141. The decision-register header `Last updated` advances to `2026-10-14`, + the date on which the candidate changes the ADR-0012 row (decisions 25, + 57, 78, 85, 109 and 121 pattern); only the ADR-0012 row changes + otherwise. +142. The twenty Amendment 12 validation cases are added to section 11 as + twenty bullets, one-to-one and in order with the Amendment's cases 1-20, + immediately after the Amendment-11 composition bullet so that they follow + the routed-job, anomaly and claim/attachment cases they build on; every + Amendment 1-11 validation requirement is retained. The candidate is + documentation-only: it specifies future deterministic runtime validation + and executes no SQL, migration, test harness or concurrency test. +143. The header drops `Approved by` / `Approval date` and section 12 restores + the two candidate-state paragraphs used by the earlier Route-B candidates + (decisions 8, 9, 58, 80, 99 and 123), adds the post-seal rule to the + description of this material correction and binds the 2026-10-12 + approval, like the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, + 2026-10-08 and 2026-10-09 approvals, to the historical earlier version + without embedding its identities. The changelog gains one new entry and + every historical entry, including the record of the 2026-10-12 approval, + stays byte-preserved (decisions 26, 59 and 123). The approval-state + inspection and exact-final-blob approval of the approval-state ADR blob + are recorded through owning issue #958, their date and the blob and head + they concern; their GitHub comment identifiers are not copied, consistent + with decisions 37, 64, 108, 123 and 128; no review, thread or + review-comment identifier of the final-head automated review is copied; + and `R-21E-01` is recorded by its control-assigned name. The Amendment + 12, CTO specification-approval, independent-review, Ready-to-Draft and + candidate-authorization records are cited by their comment identifiers + as specification and authorization evidence, consistent with section 2. +144. This report does not self-pin the candidate commit's SHA, its tree or its + own final blob; because the report now lives in the candidate commit, + statements that described the approval-state commit as the commit + containing this report are reframed as history, and that commit's + identities, now the candidate parent, are recorded in section 1 as + historical facts. Construction used the implementing agent's own existing + clean session worktree, detached at the approval-state head before + editing (the detached scratch worktree used by the earlier stages no + longer existed), touching no other worktree, branch, ref, stash or + uncommitted change. The PR #960 title and description describe the + historical Amendment-11 source; this stage is not authorized to change + them, and their reconciliation is reported to control as a separate + action. List any deviation from the specification requiring authorization: @@ -4808,8 +5298,14 @@ List any deviation from the specification requiring authorization: - Amendment-11 approval-state stage: NONE. The approval-state transformation followed its separate authorization exactly; no fifth path, new file, deletion, architecture change, second commit, amend, rebase, squash, force - push or history rewrite was needed or performed before the commit that - contains this report was created. + push or history rewrite was needed or performed before the approval-state + commit was created. +- Amendment-12 candidate stage: NONE. No fifth path, new file, deletion, ADR + change outside Amendment 12 and the candidate state, second commit, amend, + rebase, squash, force push or history rewrite was needed or performed + before the commit that contains this report was created. Decisions 134, + 135, 136 and 139 are recorded for control inspection as interpretations + within the authorized scope, not as deviations. ## 6. Database and migration effects @@ -4821,9 +5317,10 @@ database-level revision-equivalence property and its required database-atomic ordering of revision withdrawal against new revision selection and of coalescing attachment against a job's first claim/start, its required database-consistent strand-safety decision for revision withdrawal, its -required atomic creation of a routed job with its disposition and its -required mutually exclusive terminal resolution of a mismatch anomaly, is -architecture for a future +required atomic creation of a routed job with its disposition, its +required mutually exclusive terminal resolution of a mismatch anomaly and +its required post-seal materialization path with successor generations for +coalescing kinds, is architecture for a future separately authorized shared-engine implementation; it creates no migration obligation by itself and names no table, column or index. @@ -4839,9 +5336,9 @@ per-consumer impact record for the ADR's later legacy-contract retirement is preserved unchanged in Appendix A; neither the activation-epoch correction, the route-contract-revision correction, the withdrawal/selection ordering correction, the coalescing-seal ordering correction, the withdrawal -strand-safety correction nor the routed-job atomicity and anomaly -terminal-resolution correction changes a released contract or adds a -consumer. The route, revision, anomaly and materializer-compatibility concepts +strand-safety correction, the routed-job atomicity and anomaly +terminal-resolution correction nor the post-seal materialization correction +changes a released contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts are internal to the future shared engine; any later external read or executor contract derived from them needs its own specification, merged upstream contract and downstream task. @@ -4892,6 +5389,8 @@ validation" records the validation of the historical approval-state commit `576a860be47aa1ebe39968f4d3297b26ee1d17df`. "Amendment-11 candidate validation" records the validation of the historical content-approved candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`. "Amendment-11 approval-state +validation" records the validation of the historical approval-state commit +`21e19e81d0acf1aed57952ec00e2653771fb6f47`. "Amendment-12 candidate validation" at the end of this section records the validation of the commit that contains this report. @@ -8429,17 +8928,23 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro df8017c57d74eaecf13041e231ef97625260fab1 ``` -The approval-state ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above; the ADR-0012 blob `df8017c57d74eaecf13041e231ef97625260fab1` is the exact -final blob presented for CTO exact-final-blob approval at this stage and is -not yet so approved. This report's own final blob, the approval-state tree -and the approval-state commit SHA depend on this report's bytes and are not -self-pinned; they are verified against the remote head after push and -recorded in the post-publication handoff. +The approval-state ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the approval-state report's bytes, so +they were recorded above; the ADR-0012 blob `df8017c57d74eaecf13041e231ef97625260fab1` +was the exact final blob presented for CTO exact-final-blob approval at that +stage and received it on 2026-10-12 after independent approval-state +inspection on the same day. The approval-state report's own final blob, the +approval-state tree and the approval-state commit SHA depended on that +report's bytes and were not self-pinned in it; they were verified against the +remote head after push and recorded in the post-publication handoff, and +section 1 now records them as historical facts: commit +`21e19e81d0acf1aed57952ec00e2653771fb6f47`, tree +`5ed622ac36e3c608af4a9f7c2821016084b9875b`, report blob +`daeff73cf6f6b6da413ba506cc43cd1d8510f673`. -Required topology of the approval-state commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the approval-state commit, checked after it was created +and before push; the published approval-state commit satisfied every +requirement: ```text git rev-list --parents -n 1 HEAD @@ -8458,6 +8963,271 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-12 candidate validation + +Environment: the implementing agent's own existing local Git worktree of +`thoth-pub/thoth`, verified clean (no tracked or untracked change) and then +detached at the historical Amendment-11 approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47` (tree +`5ed622ac36e3c608af4a9f7c2821016084b9875b`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, no ADR-0012 commit reachable from +`develop`, `master` or `feature/worker` (Route-B eligibility), +`feature/async/adr-0012` at +`21e19e81d0acf1aed57952ec00e2653771fb6f47` with single parent +`528c2db04af5c53b9820b946ddac2ebb936eb4cd` and no descendant, ADR-0012 blob +`df8017c57d74eaecf13041e231ef97625260fab1`, report blob +`daeff73cf6f6b6da413ba506cc43cd1d8510f673`, decision-register blob +`c9ff92119095a162cf736304eb88ba595d3685ab` and changelog blob +`a4af0397dc37fdf782124573d9a4b7a7b55e8abe` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs and the Route-B doctrine blob, PR #960 +open, draft, unmerged and mergeable with that head and target +`feature/worker`, the cumulative PR footprint exactly the six paths, the +Amendment 12, CTO specification-approval, independent-review, Ready-to-Draft +and candidate-authorization records present on #958 as the five latest +comments with no later control record, the pull_request-triggered workflows +gated by the documentation-only classifier with every heavy job of the four +runs on that head skipped, and PR #960 review-thread state that matched the +stage authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the candidate commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 21e19e81d0acf1aed57952ec00e2653771fb6f47 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the approval-state head (exactly the four authorized +paths): + +```text +git diff --cached --name-only 21e19e81d0acf1aed57952ec00e2653771fb6f47 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 21e19e81d0acf1aed57952ec00e2653771fb6f47 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 21e19e81d0acf1aed57952ec00e2653771fb6f47 +1 0 CHANGELOG.md +1076 211 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +391 40 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (the `Last updated` header line and the ADR-0012 row +only): + +```text +git diff --cached -U0 21e19e81d0acf1aed57952ec00e2653771fb6f47 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-12 + +diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-14 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 21e19e81d0acf1aed57952ec00e2653771fb6f47 -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:CHANGELOG.md) <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state, outline and hunks: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +3537:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'APPROVED' +every match is in section 12 (lines 3540-3619): the sentence that the version +may carry APPROVED only after CTO approval, the historical-approval paragraph +and the authority condition; no sentence asserts that this version is approved + +diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +19a20 +> ### Post-seal materialization and successor generations +exit 1 (the one added heading is the complete outline change) + +git diff --cached -U0 21e19e81d0acf1aed57952ec00e2653771fb6f47 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -5,2 +4,0 @@ Date: 2026-09-29 +@@ -387,0 +386,12 @@ interaction with coalescing into an existing job. +@@ -447 +457,5 @@ signals raise attention, whether the route is active, temporarily inactive or +@@ -1247 +1261,4 @@ meaningfully new, for example: +@@ -1276 +1293,3 @@ never erases the per-event route records needed for audit/completeness. +@@ -1280,2 +1299,6 @@ An idempotency conflict never silently means "route satisfied". It either: +@@ -1352,7 +1375,11 @@ Both commit orders are defined: +@@ -1397,3 +1424,193 @@ RECONCILIATION_REQUIRED, retry and reconciliation transitions and the +@@ -1441 +1658,3 @@ new disposition; a losing attempt leaves its obligation owed and follows the +@@ -1938 +2157,3 @@ that committed before its first successful claim/start sealed it (section +@@ -2398,2 +2619,4 @@ execution until a separate approved architecture decision says otherwise. +@@ -2468,0 +2692,39 @@ execution until a separate approved architecture decision says otherwise. +@@ -2490,0 +2753,3 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: +@@ -2690 +2955,3 @@ kind-specific retention, coalescing and admission/backpressure limits before +@@ -2705,2 +2972,4 @@ Rollout should prove in order: +@@ -3092,0 +3362,80 @@ Before shared implementation can be approved, evidence must include: +@@ -3188,4 +3537 @@ shared-engine tests. +@@ -3193,9 +3539,3 @@ Approval date: 2026-10-12 +@@ -3234 +3574,9 @@ database-enforced terminal-resolution decision, so that no two contradictory +@@ -3253 +3601,4 @@ mismatch anomalies, carried `Status: APPROVED` with CTO approval dated + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^### Coalescing-open\|^### Post-seal\|^### Atomic creation\|^42\. \|^46\. ' +1327:### Coalescing-open and coalescing-sealed jobs +1430:### Post-seal materialization and successor generations +1618:### Atomic creation of a routed job +2607:42. For every logical job of a kind that permits many-route-to-one-job +2692:46. A coalescing seal closes one logical job, not the route obligations that + +diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^42\. /p' | sed '$d') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^42\. /p' | sed '$d') +exit 0; no output (the section heading and invariants 1-41 byte-identical) + +diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^43\. /,/^## 8\. /p' | sed '$d' | sed '$d') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^43\. /,/^46\. /p' | sed '$d') +exit 0; no output (invariants 43-45 byte-identical) + +diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^42\. /,/^43\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^42\. /,/^43\. /p') +13,14c13,16 +< unused, the obligation still owed and visible and the deterministic +< idempotency-conflict error path taken. A stale pre-claim observation never +--- +> unused, the obligation still owed and visible and its recovery governed +> by invariant 46 after the losing materializer rereads durable state; the +> losing stale attachment transaction invents no logical-job identity. A +> stale pre-claim observation never +(invariant 42 changes only in that closing clause) + +for s in 'Revision withdrawal and route retirement' 'Mutually exclusive terminal resolution of a mismatch anomaly'; do diff <(git show 21e19e81d0acf1aed57952ec00e2653771fb6f47:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n "/^### $s/,/^### /p") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n "/^### $s/,/^### /p") >/dev/null && echo "$s: identical"; done +Revision withdrawal and route retirement: identical +Mutually exclusive terminal resolution of a mismatch anomaly: identical + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'successor' +73 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'Post-seal materialization and successor generations' +2 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | wc -l +3644 +git diff --cached -U0 21e19e81d0acf1aed57952ec00e2653771fb6f47 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^+' | grep -v '^+++' | awk 'length>81' | wc -l +0 +``` + +The twenty-one hunks are: the header status and approval fields (two hunks); +the section-3.2 post-seal paragraph after the routed-job paragraph; the +section-3.2 backlog and attention sentence; the section-3.5 key-dimension +item; the idempotency-conflict sentence and outcome list (two hunks); the +claim/start-first bullet; the boundary's closing sentence together with the +new "Post-seal materialization and successor generations" subsection (one +hunk); the existing-job paragraph of the atomic-creation subsection; the +section-3.15 coalescing sentence; the invariant-42 closing clause; invariant +46; section 8 item 1; the section-9 activation sentence for coalescing modes; +section-9 rollout proof 5; the twenty section-11 validation cases; and the +four section-12 hunks that record the candidate state, extend the description +of this material correction and bind the 2026-10-12 approval to the +historical earlier version. No other line of the ADR changes; in particular +the Amendment-8 withdrawal/selection paragraphs, the attachment-first order +and every other sentence of the Amendment-9 "Coalescing-open and +coalescing-sealed jobs" subsection, the Amendment-10 decision-stability +paragraphs and both anomaly/withdrawal orders, the Amendment-11 +terminal-resolution subsection and every other sentence of the +atomic-creation subsection, the mismatch definition and its recorded-facts +list, the detection paragraph, the enrollment validation list, the claim, +lease and attempt-phase text of sections 3.4 and 3.6 and the +disposition-uniqueness text of section 3.2 are byte-identical to the +approval-state head. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) 678541fe5904ae4ea816cafa46c2e68780b51f4e +decision-register.md c3b827d5fe5309dbc6ccd84a67d704aca198fe2c +CHANGELOG.md a7848c8672984f7ff2fad997c7674b04d94ea04a +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +678541fe5904ae4ea816cafa46c2e68780b51f4e +``` + +The candidate ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the candidate tree and the candidate +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the candidate commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 21e19e81d0acf1aed57952ec00e2653771fb6f47 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 21e19e81d0acf1aed57952ec00e2653771fb6f47 HEAD +required: exit 0; no output + +git diff --name-only 21e19e81d0acf1aed57952ec00e2653771fb6f47 HEAD +required: exactly the four candidate paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -8676,6 +9446,31 @@ approval-state commit, in particular any statement naming the Amendment-10 approval-state head as the direct parent of the commit containing this report or describing the candidate as still the current `PROPOSED` candidate, and reframed them. +Amendment-12 candidate stage: compared the staged ADR-0012 with the +approval-state head's ADR blob `df8017c57d74eaecf13041e231ef97625260fab1` hunk +by hunk against every requirement of Amendment 12 A12-1 through A12-6 and the +twenty validation cases, and against the independent review's seven +non-blocking observations; confirmed each requirement is expressed in section +3.2 (the post-seal paragraph and the backlog sentence), section 3.5 (the +key-dimension list, the conflict outcome list, the claim/start-first bullet, +the boundary's closing sentence, the new post-seal subsection and the +atomic-creation existing-job paragraph), section 3.15, invariants 42 and 46, +sections 8, 9 and 11, and the candidate state in the header and section 12; +confirmed that every changed hunk concerns post-seal materialization, +successor generations, their validation or the candidate state, and that the +ADR-0008/ADR-0010 partial-supersession header text, the Amendment-8, +Amendment-10 and Amendment-11 paragraphs, the attachment-first order and +every other sentence of the coalescing-sealed subsection, the mismatch +definition and detection paragraph, the enrollment validation list, the +claim, attempt-phase and effect-boundary text and all other architecture are +byte-identical to the approval-state head; compared the staged register and +changelog with the approval-state-head blobs (only the `Last updated` header +and the ADR-0012 row, and only one added entry); confirmed the protected +decision and control blobs are unchanged; and read the report for statements +that would become false once it moves into the candidate commit, in +particular any statement naming the Amendment-11 candidate as the direct +parent of the commit containing this report or presenting the approval-state +commit as the current stage, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -8696,8 +9491,9 @@ candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` and the content-approved replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, the Amendment-10 approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` and the content-approved Amendment-11 candidate -`528c2db04af5c53b9820b946ddac2ebb936eb4cd`, are historical evidence; the live -PR #960 diff of the approval-state commit exists only after its publication +`528c2db04af5c53b9820b946ddac2ebb936eb4cd` and the Amendment-11 approval-state +head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, are historical evidence; the +live PR #960 diff of the candidate commit exists only after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -8715,9 +9511,9 @@ approval-state authorization, the Amendment-9 candidate authorization, the Amendment-9 approval-state authorization, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate -authorization and the Amendment-11 approval-state authorization, an -automatic CI failure, or any migration execution or Docker publication, is a -HOLD condition. +authorization, the Amendment-11 approval-state authorization and the +Amendment-12 candidate authorization, an automatic CI failure, or any +migration execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -8754,21 +9550,22 @@ F-A10-CAND-01 consistency-correction authorization permitted exactly one direct-child four-path correction commit, the Amendment-10 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-11 candidate authorization permitted exactly one four-path -candidate commit, and the Amendment-11 approval-state authorization permits -exactly one direct-child approval-state commit; any further source -correction exhausts those authorizations and requires a fresh CTO -authorization (Amendment 3 C10; Amendments 6-11; +candidate commit, the Amendment-11 approval-state authorization permitted +exactly one direct-child approval-state commit, and the Amendment-12 +candidate authorization permits exactly one four-path candidate commit; any +further source correction exhausts those authorizations and requires a fresh +CTO authorization (Amendment 3 C10; Amendments 6-12; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-11 approval-state stage - the implementing agent performed no independent inspection or review, - exact-final-blob approval or confirmation, PR #960 title/description - reconciliation, Draft/Ready change, automated-review request, thread - adjudication, exact-head review or merge (section 4.2). The PR #960 title - and description describe the historical Amendment-10 source; their +- Historical stage-time evidence: at the Amendment-12 candidate stage the + implementing agent performed no independent inspection or review, + candidate-content approval, PR #960 title/description reconciliation, + Draft/Ready change, automated-review request, thread adjudication, + exact-head review or merge (section 4.2). The PR #960 title and + description describe the historical Amendment-11 source; their reconciliation is a separate control action. Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the @@ -8818,8 +9615,12 @@ Monitoring required: NONE the routed-job atomicity and anomaly terminal-resolution defects found by the final-head review of that approval-state head only under Specification Amendment 11, its independent review and a separate authorization for one - four-path candidate commit, and its approval state only under a separate - authorization for this one direct-child approval-state commit. + four-path candidate commit, its approval state only under a separate + authorization for one direct-child approval-state commit, and the + post-seal materialization defect found by the final-head review of that + approval-state head only under Specification Amendment 12, its independent + review and a separate authorization for this one four-path candidate + commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -8842,8 +9643,8 @@ Monitoring required: NONE correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction and approval-state - commit, the Amendment-11 candidate and this Amendment-11 approval-state - commit. + commit, the Amendment-11 candidate and approval-state commit and this + Amendment-12 candidate. ## 14. Unresolved issues @@ -9016,11 +9817,35 @@ Monitoring required: NONE source as `PROPOSED` (section 4; section 5, decisions 113-124); an independent CRITICAL exact-candidate source review of that candidate found no blocking candidate-content finding; Javi, CTO approved its exact - architecture content on 2026-10-12; and the approval-state commit that - contains this report records that approval. Both findings are closed at - specification level by Amendment 11; their source closure is adjudicated - by the independent exact-head review controls that precede any merge, - which GitHub records. + architecture content on 2026-10-12; the historical approval-state commit + `21e19e81d0acf1aed57952ec00e2653771fb6f47` recorded that approval; and the + CTO recorded exact-final-blob approval of the resulting ADR-0012 blob + `df8017c57d74eaecf13041e231ef97625260fab1` on 2026-10-12. Both findings are + closed at specification level by Amendment 11; their source closure is + adjudicated by the independent exact-head review controls that precede any + merge, which GitHub records. +- The configured final-head automated review of the Amendment-11 + approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47` opened one + further thread, identified here substantively and by its control-assigned + finding name. `R-21E-01`: with job idempotency-key uniqueness absolute + across job states, coalescing permitted only into a not-yet-started job + and the job permanently sealed at its first successful claim/start, a + later eligible obligation of the same coalescing group or window could + resolve only to the sealed job's reserved identity on every retry, so it + could remain undispositioned indefinitely despite healthy workers and a + healthy materializer; the existing text defined no successor identity and + expressly created none. Control accepted the finding as valid, HIGH and + blocking and classified its correction as a material architectural + correction; the CTO approved Specification Amendment 12; a fresh + independent CRITICAL specification review of the base specification and + Amendments 1-12 returned `APPROVED`, confirming the finding, the + classification, all six provisions, the composition and the twenty-case + validation matrix and recording seven non-blocking clarity observations; + and the Route-B candidate that contains this report implements the + correction in source as `PROPOSED` (section 4; section 5, decisions + 133-144). The finding is closed at specification level by Amendment 12; + whether the candidate closes it in source is decided by the independent + inspection and review of the candidate, which GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -9034,34 +9859,64 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-11 approval-state commit also -implemented the Amendment-11 candidate, the Amendment-10 approval-state -commit, the Amendment-10 replacement candidate and first candidate, the -Amendment-9 approval-state commit and candidate, the Amendment-8 +work. The implementing agent of this Amendment-12 candidate also implemented +the Amendment-11 approval-state commit and candidate, the Amendment-10 +approval-state commit, the Amendment-10 replacement candidate and first +candidate, the Amendment-9 approval-state commit and candidate, the +Amendment-8 approval-state commit and candidate, the factual clarification, the transient-state correction, the control-record recovery, the Amendment-6/7 approval-state commit and candidate and earlier Route-B source stages of this task and prepared the earlier uncommitted activation-epoch candidate referred to in Amendment 1 A10, so it is not eligible to act as an independent inspector or reviewer of this task -(Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). +(Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). The same implementing +agent also performed the external independent specification-level review of +Amendment 12 whose acceptance is recorded on #958, with its +source-transcription history disclosed there; control accepted that review +as independent of the amendment's authorship only. That review is not +approval of this candidate's source, and this agent remains ineligible to +inspect, review or approve it. Suggested review focus: -- that the approval-state delta from the content-approved Amendment-11 - candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd` is limited to the ADR - header approval fields, the two leading section-12 paragraphs, the ADR-0012 - register row and the routed-job atomicity correction changelog entry, that - the normalized comparison in section 9 shows no architecture drift from - candidate blob `7e461106986fe6efad2021d1ad6f52573e6e9d66`, and that the - approval-state ADR-0012 blob is `df8017c57d74eaecf13041e231ef97625260fab1`; -- that the content approval is recorded only through owning issue #958, - approver Javi, CTO, approval date 2026-10-12, candidate head - `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob - `7e461106986fe6efad2021d1ad6f52573e6e9d66`, that the approval date matches - the CTO candidate-content approval record, and that no content-approval, - exact-final-blob approval, review or merge identifier is copied into this - report (section 5, decision 128); +- that the candidate changes exactly the four authorized paths from the + approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, that the + candidate ADR-0012 blob is `678541fe5904ae4ea816cafa46c2e68780b51f4e`, and that + ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that every Amendment 12 A12-1 requirement is expressed in ADR-0012: the + refused obligation owed, undispositioned and visible and never attached, + counted complete or silently represented; the approved deterministic + post-seal contract with its two normal paths, at least one always + available and the sealed job never one; the key collision neither + completion nor a reason to retry the same identity; fail-closed activation + or a separately approved event-distinct identity for unsupported kinds; and + no automatic exceptional disposition (section 5, decision 134); +- that every A12-2 and A12-3 requirement is expressed: group/window identity + distinct from executable generation identity, unique stable successor + identity, the predecessor unchanged, no successor from predecessor retry, + replay or forced resend, the creation trigger, database-arbitrated + convergence under same- and different-obligation races, uniqueness losses, + rollback and acknowledgment loss, the Amendment-9 boundary for successor + attachment, the Amendment-11 one-transaction creation and the Amendment-11 + terminal decision in one boundary, and no write skew or deadlock (section + 5, decisions 135-136); +- that every A12-4 and A12-5 requirement is expressed: unchanged target + identity, fencing, evidence, effect phases, ambiguity handling, + `CURRENT_STATE` and `REVISION_BOUND` semantics and route, epoch, revision + and authorization identity, conditional convergence, visible bounded + backoff and bounded generations, pre-activation documentation and no + exactly-once claim (section 5, decisions 137-138); +- that A12-6 preservation holds: the claim/start-first bullet binds the + no-identity rule to the losing stale attachment transaction only, + invariant 42 changes only in its closing clause, invariant 46 and the + twenty section-11 cases are present one-to-one with the Amendment, + invariants 1-41 and 43-45 and every earlier validation case are + byte-identical, and decisions 134, 135, 136 and 139 decide no new + architecture; +- that every ADR-0012 hunk outside those concerns is limited to the candidate + `PROPOSED` state (header and section 12) and that all other architecture is + byte-identical to the approval-state head; - that the candidate changed exactly the four authorized paths from the approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, that the candidate ADR-0012 blob was `7e461106986fe6efad2021d1ad6f52573e6e9d66`, and that @@ -9272,18 +10127,16 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `APPROVED` with Javi, CTO and - 2026-10-12, identify the approved content by candidate head - `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob - `7e461106986fe6efad2021d1ad6f52573e6e9d66`, bind the 2026-09-30, - 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08 and 2026-10-09 approvals to - the earlier exact versions and keep the repository authority condition and - the ADR-0013 reliance distinction; -- that the register row, the routed-job atomicity correction changelog entry - and this report describe the corrected version as `APPROVED` on - 2026-10-12, that the strand-safety correction entry and every other - historical changelog entry are byte-preserved, and that the register header - `Last updated` is `2026-10-12`; +- that the header and section 12 carry `PROPOSED` with no current approver or + approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, + 2026-10-08, 2026-10-09 and 2026-10-12 approvals to the earlier exact + versions and keep the repository authority condition and the ADR-0013 + reliance distinction; +- that the register row, the new post-seal recovery correction changelog entry + and this report describe the corrected version as `PROPOSED`, that the + routed-job atomicity correction entry and every other historical changelog + entry are byte-preserved, and that the register header `Last updated` is + `2026-10-14`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. @@ -9300,8 +10153,8 @@ append-only activation-epoch model, and the route-level event kind/version contract and "emits K" emission floor described in earlier rounds are superseded by immutable route-contract revisions and producer-version coverage in the current section 3.2. The 2026-09-30, 2026-10-05, 2026-10-06, -2026-10-07, 2026-10-08 and 2026-10-09 approvals and every review recorded -here are bound to the exact historical versions and heads they name. +2026-10-07, 2026-10-08, 2026-10-09 and 2026-10-12 approvals and every review +recorded here are bound to the exact historical versions and heads they name. ### Initial candidate and review round 1 @@ -10100,8 +10953,8 @@ stage: 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO approval of that exact corrected content; earlier approvals and reviews, including the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, - 2026-10-08 and 2026-10-09 approvals, remain bound to the earlier exact - versions and heads they name. + 2026-10-08, 2026-10-09 and 2026-10-12 approvals, remain bound to the + earlier exact versions and heads they name. 11. The route-contract-revision model does not permit silent reinterpretation or loss either: every epoch is bound to one immutable revision, an event outside its epoch revision's accepted versions becomes a visible anomaly @@ -10152,5 +11005,17 @@ stage: leaves the slot unused and unavailable to the losing enrollment, detection and resolution evidence stay append-only, and external compensation remains separately fenced. +17. The post-seal materialization rule does not permit an eligible + obligation to be owed forever against a permanently sealed identity: an + obligation refused after a coalescing seal remains owed and visible, is + never attached to the sealed job or dispositioned by the key collision, + the seal, retry exhaustion or an unavailable materializer, and is + materialized through a different eligible coalescing-open successor or a + distinct, stably identified routed successor created together with its + disposition, with group identity distinct from generation identity, + database-arbitrated convergence, unchanged target-scoped effect safety + and route identity, conditional convergence under finite inputs and fair + capacity, and fail-closed activation for a kind without a demonstrated + successor contract. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index df8017c57..678541fe5 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-12 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -385,6 +383,18 @@ or none of them does. "Atomic creation of a routed job" in section 3.5 defines that boundary, its failure, crash and acknowledgment-loss behaviour and its interaction with coalescing into an existing job. +A coalescing seal closes one logical job, never the obligations that follow +it. When an eligible obligation cannot attach to a job of a coalescing kind +because that job is permanently coalescing-sealed (section 3.5), the +obligation remains owed, undispositioned and visible in the route backlog +until a different eligible coalescing-open successor accepts it or a distinct +legitimate routed successor is created for it under the same all-or-nothing +boundary; "Post-seal materialization and successor generations" in section +3.5 defines that ordinary path. The sealed job, the permanent collision with +its idempotency key, retry exhaustion and an unavailable materializer are +never themselves a disposition and never convert the obligation into the +exceptional terminal non-materialized disposition below. + Disposition uniqueness is per logical route, not per epoch or per revision: `(event_id, route_key)` is the complete uniqueness boundary across every epoch and every route-contract revision of that route. A disposition may record the @@ -444,7 +454,11 @@ can actually be emitted. If compatible materialization later becomes unavailable for any revision or version with outstanding obligations, those obligations remain owed and the unavailable-materializer and route-backlog signals raise attention, whether the route is active, temporarily inactive or -permanently retired. +permanently retired. The same holds for an obligation refused after a +coalescing seal while it awaits an eligible successor, and for a successor +whose safe execution is held by target-scoped effect ambiguity (section 3.5): +the obligation stays in that backlog with its reason, age-visible and +attention-raising, and is never counted as dispositioned. Outstanding obligations are interpreted against the immutable revision of the epoch that made each event eligible. After a newer revision becomes active, an @@ -1244,7 +1258,10 @@ meaningfully new, for example: - a source revision/fingerprint; - a schedule slot; - an explicit command/request id; -- an approved coalescing/debounce window. +- an approved coalescing/debounce group or window together with the + executable generation that represents it, wherever successive generations + of one group may be needed ("Post-seal materialization and successor + generations" below). A completed Crossref sync, search-index update, invalidation or reconciliation must not suppress a later effect for a newer revision merely because both target @@ -1273,12 +1290,18 @@ cannot coexist for the same route/event pair. Coalescing therefore forms a many-route-to-one-job relationship only among materialized dispositions; it never erases the per-event route records needed for audit/completeness. -An idempotency conflict never silently means "route satisfied". It either: +An idempotency conflict never silently means "route satisfied", and it is +never a reason to retry the same permanently unusable identity indefinitely. +It either: 1. attaches the route to an existing not-yet-started job when the kind's coalescing rule explicitly permits it, inside the database-atomic - coalescing boundary below; or -2. produces a deterministic routing/materialization error surfaced for + coalescing boundary below; +2. for a coalescing kind whose existing job is permanently coalescing-sealed, + follows the ordinary post-seal materialization path below, which selects a + different eligible coalescing-open successor or creates a distinct + legitimate routed successor after rereading durable state; or +3. produces a deterministic routing/materialization error surfaced for attention/recovery. The owning domain defines the canonical effect-target identity and its @@ -1349,13 +1372,17 @@ Both commit orders are defined: attachment is refused; no materialized route disposition is committed for the losing route and event; the `(event_id, route_key)` uniqueness slot remains unused; the eligible route obligation remains owed and visible in - the route backlog; and the idempotency-conflict rule above follows its - deterministic routing/materialization error and attention/recovery path - rather than treating the route as represented by the started job. The losing - router or materializer may retry only after re-reading current durable state - and under the kind's existing authorized materialization and recovery - semantics; this rule creates no new logical-job identity and no new replay - or current-state rule. + the route backlog; and the route is never treated as represented by the + started job. The losing stale attachment transaction attaches nothing, + invents no logical-job identity and introduces no new replay or + current-state rule. The losing router or materializer may retry only after + re-reading current durable state and under the kind's existing authorized + materialization and recovery semantics; that recovery is the ordinary + post-seal materialization path of "Post-seal materialization and successor + generations" below - a separate, database-arbitrated materialization + decision taken from the reread state, never a continuation of the losing + transaction - and it is the normal path, with attention raised only while + recovery is pending or blocked. Sealing is bound to the durable committed transition, not to an attempted claim. A worker attempt whose first claim/start transaction acquires transient @@ -1394,9 +1421,199 @@ idempotency-key uniqueness, explicit durable coalescing rules, `CURRENT_STATE` and `REVISION_BOUND` semantics, target-scoped concurrency and applied-revision/fingerprint evidence, the attempt phases, WAITING and RECONCILIATION_REQUIRED, retry and reconciliation transitions and the -scheduler's deadlock and lock-ordering controls. It introduces no second job -identity, job lifecycle, route lifecycle or claim-lock mode, and no second -order for claim/start against attachment. +scheduler's deadlock and lock-ordering controls. It introduces no second +identity for one logical job, no second job lifecycle, route lifecycle or +claim-lock mode, and no second order for claim/start against attachment; a +successor generation (below) is a distinct logical job, not a second identity +of the sealed one. + +### Post-seal materialization and successor generations + +A coalescing seal closes one logical job, not the route obligations that +arrive after it. An eligible event-route obligation whose attachment is +refused because the logical job it would have joined is permanently +coalescing-sealed - whether it lost the claim/start-first order above or +arrived after the seal - remains undispositioned, owed and visible in the +route backlog. It is never retroactively attached to the sealed job, never +treated as complete because that job exists or has run, and never silently +represented by that job's effect. The permanent idempotency-key collision +with the sealed job is not proof of route completion and is not a reason to +keep retrying that permanently unusable identity: because key uniqueness is +absolute across job states and the seal never reopens, a reread that could +resolve only to that identity would leave the obligation owed forever. + +Every job kind that permits many-route-to-one-job coalescing therefore has, +as part of its approved coalescing contract, a deterministic **ordinary +post-seal materialization contract** under which each such obligation +eventually receives its one materialized disposition whenever compatible +materialization is available and no genuine external reconciliation or +authority blocker prevents progress. After re-reading canonical current +durable state, the materializer takes exactly one of two normal paths, and +the contract guarantees that at least one of them is always available: + +- it attaches the obligation to a **different eligible coalescing-open + successor job** of the same group under the attachment/seal boundary + above; a successor is eligible only where the kind's coalescing contract + admits it for the obligation's route, epoch, route-contract revision and + event version under the compatibility rules of sections 3.2 and 3.9, and an + ineligible open successor never prevents a compatible one from being + selected or created; or +- it creates a **distinct legitimate routed successor job** that represents + the still-owed input, committing the job, its one `(event_id, route_key)` + disposition and its indispensable input or membership facts under "Atomic + creation of a routed job" below. + +The sealed job is not such a path. A new generation is considered only after +the reread confirms that the obligation is still owed and that no eligible +open successor exists; an unchanged retry that merely encounters the same +conflict manufactures no identity, and an obligation already represented by a +committed disposition receives no successor on any retry. + +**Successor identity.** For any coalescing kind in which more than one +executable generation of one group may be needed - the default for every +coalescing kind, which a kind may disclaim only by demonstrating that no +eligible obligation can arrive after a seal - the coalescing group or window +identity and the individual executable logical-job effect/idempotency +identity are distinguishable: an approved group or window never irreversibly +reserves the only executable identity when later eligible events of that same +group can arrive after the first job seals. A successor is a distinct logical +job representing a previously unrepresented, still-owed event-route input, +with its own deterministic idempotency key that is unique and stable for that +intended successor effect across retries and terminal states - for example a +durably selected successor generation of the group, or another explicitly +approved event-derived identity. The sealed predecessor's idempotency key, +seal, dispositions, attempts and effect history are unchanged: a retry, lease +reclaim, WAITING resume or reconciliation of the predecessor remains work on +the predecessor and never becomes successor creation, and a successor created +for a new owed obligation is not a covert retry, replay or forced external +resend of the predecessor. Independently authorized direct, replay or +current-state jobs keep their own effect identity under this section and +consume no successor generation of a routed coalescing group. The group +correlation, the predecessor/successor relationship and every represented +event-route input remain auditable; successor generations never erase or +rewrite predecessor dispositions or job or effect history. + +**Database-arbitrated convergence.** Selection of an eligible open successor +or creation of the next executable generation of a group is +database-arbitrated, with a deterministic observation and recovery result +after competing materializers, uniqueness losses, transaction rollback or +acknowledgment loss. A valid attachment to an open successor and that +successor's first successful claim/start participate in the same +database-enforced linearized boundary defined above, so an attachment after a +successor's seal is refused without consuming the route slot and the +materializer rereads and follows this path again. Two simultaneous +materializers for the same obligation cannot create two dispositions, two +jobs representing the same required work or a phantom satisfied route: +`(event_id, route_key)` uniqueness, the successor's identity uniqueness and +the atomic creation boundary arbitrate them, and the loser rereads committed +state. Concurrent different owed obligations may attach to the same eligible +open successor under the kind's coalescing contract; when that successor +seals first, later obligations follow the next valid generation or path. +Because a new successor is created only together with the disposition it +represents, every generation represents at least one obligation, and +materializers converge on committed successor state or serialize into a +subsequent generation only when the earlier one is already sealed. Repeated +retries, worker restarts and acknowledgment loss recover from the durably +committed route-disposition, job and generation state; after a creation +commits but its acknowledgment is lost, the retry observes the same job and +disposition and creates no further generation, disposition or effect. The +group, generation and `(event_id, route_key)` uniqueness boundaries compose +with the attachment/seal boundary, the atomic creation boundary and the +scheduler's lock-ordering controls without write skew or deadlock; the exact +tables, constraints, sequences and locks are implementation-specification +details. + +**Historical enrollment and anomaly resolution.** A first historical +enrollment that uses this path obeys the same rules. Where the enrollment +resolves an in-epoch contract-mismatch anomaly, the terminal-resolution +decision, the enrollment disposition and any newly created successor and its +input facts commit within one consistent terminal-decision transaction +boundary ("Mutually exclusive terminal resolution of a mismatch anomaly", +section 3.2); an enrollment that loses a successor's seal race aborts as a +whole, leaving the anomaly open and retryable. Historical enrollment, anomaly +terminal resolution, revision selection and withdrawal and route retirement +keep their existing orders. + +**Effect semantics, fencing and ambiguity.** A successor executes under the +same canonical external effect-target identity, target-scoped concurrency +namespace, applied-revision/fingerprint evidence, lease and claim-token +fencing, `PRE_WRITE` / `EFFECT_STARTED` / `EFFECT_CONFIRMED` phases, bounded +retries, `WAITING` and `RECONCILIATION_REQUIRED` semantics as every other job +(sections 3.3, 3.4 and 3.6). Generations never partition conflicting target +keys and never relax target-scoped applied-state ordering. A predecessor +whose effect is ambiguous at or after `EFFECT_STARTED` cannot be bypassed by +creating a successor with a different key and asserting a safe second write: +the predecessor retains its target-scoped boundary until reconciled, the +successor may exist durably and wait, visibly, while safe execution is +blocked, and the kind's approved contract holds, serializes or reconciles +before any conflicting successor effect. A `CURRENT_STATE` successor reads +canonical current state, acts on the complete target effect fingerprint and +completes as a deterministic no-op where target-scoped evidence proves that +the same fingerprint is already applied; a `REVISION_BOUND` successor retains +the source revision/fingerprint its route requires and never regresses newer +confirmed target state or bypasses supersession and replay controls. A new +job identity alone is never evidence that another external effect is needed, +and nothing here claims exactly-once provider effects or atomicity between +PostgreSQL and a third-party provider write. + +**Route identity unchanged.** No source revision, route-contract revision, +activation epoch, epoch eligibility, producer-version constraint, +`route_key`, effect-target identity or provider authorization is redefined by +successor materialization. The late obligation remains the original eligible +`(event_id, route_key)` obligation, bound to its original route, epoch and +revision; a successor never permits a newer incompatible materializer and +never invents a new route or source event. Successor generation is +job-materialization state, not route history. + +**Liveness and operational admission.** Under finite owed inputs, a +compatible and available materializer, fair progress capacity and no +outstanding legitimate dependency, authority or effect-ambiguity blocker, +this path converges on one durable disposition for every eligible obligation; +the engine never indefinitely reselects a permanently sealed job or group +identity, and that collision is not itself a legitimate blocker. Continuous +input, rate limits and unavailable workers remain governed by the bounded +scheduler and backpressure requirements of section 3.15, not by a promise of +real-time completion. While a successor cannot presently be admitted, no +compatible worker exists or external ambiguity blocks safe execution, the +owed obligation and its reason remain durable, age-visible and +attention-raising in the route backlog without false success; errors and +retries are bounded and backed off, create no unbounded generations and never +hot-loop on one conflict; recovery after the blocker clears follows the +kind's approved contract. + +**Unsupported contracts fail closed.** Before activation, a coalescing kind +documents how late obligations select or generate safe executable +identities, how candidate-generation growth is bounded under contention, how +predecessor ambiguity gates successor effects, how its `CURRENT_STATE` or +`REVISION_BOUND` semantics prevent duplicate or regressive effects, and what +operational evidence demonstrates outstanding obligations and safe +completion. A kind that cannot define and demonstrate a safe successor +contract may not enable that coalescing mode; it may instead use a separately +approved event-distinct, non-coalesced routed job identity whose effect +semantics are safe under this section, and where neither is safe its +activation is held for a design or authority decision. A generic +implementation never invents a domain's intended-effect identity, and the +engine never promises ordinary forward progress from an unsatisfiable +permanent-key collision. + +**Exceptional dispositions unchanged.** The explicit, authorized and audited +terminal non-materialized disposition of section 3.2 remains available only +under its existing section-3.2 and ADR-0010 controls, with real covering +reconciliation or expressly accepted divergence. A seal, an idempotency +collision, retry exhaustion or an unavailable materializer never creates it +automatically and is never success. + +This rule supplements only the post-seal recovery outcome of the +claim/start-first order above. It keeps the first successful claim/start as +the permanent seal and both attachment/claim commit orders, the atomic +creation boundary below, the mutually exclusive anomaly resolution, one +`(event_id, route_key)` namespace across epochs and revisions, the +withdrawal/selection and strand-safety orders, event and route immutability, +the job-attempt lifecycle and target-scoped effect ordering. It creates no +generic route retry bypass, no free-form external-effect authority, no hidden +terminal non-materialized disposition, no second claim lifecycle, no +reopening of sealed jobs, no duplicate route effects and no new `route_key` +for another generation of the same logical route. ### Atomic creation of a routed job @@ -1438,7 +1655,9 @@ obligation, the coalescing-open/coalescing-sealed boundary above and the kind's explicit coalescing semantics apply unchanged: an attachment to an open job commits a disposition under that boundary; a sealed job acquires no new disposition; a losing attempt leaves its obligation owed and follows the -existing deterministic error and recovery path. Two routes may share one +post-seal materialization path above, under which a successor created for +that obligation is a brand-new routed job governed by this boundary. Two +routes may share one eligible not-yet-started job only under the already-approved coalescing rule, and the creation of a new routed job and a concurrent other-route attachment to it obey that one linearized order. @@ -1935,7 +2154,9 @@ domain rule and must retain enough correlation to explain which events or requests were represented by the surviving job. A surviving job accepts attachments only while it is coalescing-open and represents every attachment that committed before its first successful claim/start sealed it (section -3.5). +3.5); an obligation refused after that seal follows the post-seal +materialization path of section 3.5 and stays in the route backlog, with its +reason and age visible, until it is dispositioned. --- @@ -2395,8 +2616,10 @@ execution until a separate approved architecture decision says otherwise. represents and that is never rewritten or detached; a claim/start that linearizes first seals the job before execution, and the later attachment is refused with no disposition committed, the `(event_id, route_key)` slot - unused, the obligation still owed and visible and the deterministic - idempotency-conflict error path taken. A stale pre-claim observation never + unused, the obligation still owed and visible and its recovery governed + by invariant 46 after the losing materializer rereads durable state; the + losing stale attachment transaction invents no logical-job identity. A + stale pre-claim observation never commits an attachment after sealing; an application-level pre-check does not satisfy this; a rolled-back first claim does not seal. The seal is earlier than and distinct from `EFFECT_STARTED`. @@ -2466,6 +2689,45 @@ execution until a separate approved architecture decision says otherwise. replay is governed by its own rules and replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls. +46. A coalescing seal closes one logical job, not the route obligations that + follow it. An eligible obligation refused because its job is permanently + coalescing-sealed remains owed, undispositioned and visible; it is never + attached to the sealed job, never counted complete because that job + exists or ran, and never dispositioned by the permanent idempotency-key + collision, a seal, retry exhaustion or an unavailable materializer. Every + coalescing kind has an approved deterministic post-seal materialization + contract under which, after rereading canonical durable state, the + obligation attaches to a different eligible coalescing-open successor + under invariant 42 or is represented by a distinct legitimate routed + successor created under invariant 44 together with its disposition; the + sealed job is never such a path, a new generation is created only when + the reread shows the obligation still owed and no eligible open + successor, and an already represented obligation receives no successor on + any retry. Group or window identity is distinct from executable + generation identity wherever successive generations may be needed; a + successor has a unique, stable idempotency key for its intended successor + effect; the predecessor's key, seal, attempts and history are unchanged; + predecessor retry or reclaim is never successor creation; and a successor + is not a replay or forced resend. Successor selection and creation are + database-arbitrated with deterministic recovery after races, uniqueness + losses, rollback and acknowledgment loss: no duplicate disposition, no + two jobs for one obligation, no orphan or unrepresented successor, no + write skew and no deadlock. Successors keep the canonical target + identity, target-scoped concurrency, applied-fingerprint/revision + evidence, claim fencing, effect phases, `WAITING` and + `RECONCILIATION_REQUIRED` semantics; an ambiguous predecessor effect is + never bypassed by a new key; `CURRENT_STATE` and `REVISION_BOUND` + semantics prevent duplicate or regressive effects; and no `route_key`, + epoch, revision, event, eligibility or provider authorization is + redefined. Under finite owed inputs, a compatible available materializer, + fair capacity and no legitimate blocker the path converges on one + disposition per obligation and never reselects a sealed identity + indefinitely; blocked recovery stays backlogged, age-visible and + attention-raising with bounded backoff and bounded generations. A kind + without a demonstrated safe successor contract may not activate that + coalescing mode and may use only a separately approved safe + event-distinct identity; the exceptional terminal non-materialized + disposition stays under its existing controls and is never automatic. ## 8. Implementation impact and decomposition @@ -2488,6 +2750,9 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - atomic creation of a routed job with its materialized disposition and indispensable input facts, and the database-enforced mutually exclusive terminal resolution of a mismatch anomaly; + - post-seal materialization of obligations refused after a coalescing seal + through eligible open successors or distinct successor generations, with + database-arbitrated convergence; - revision semantics plus domain-owned canonical effect-target identity and target-scoped ordering/evidence contract; - new kind-scoped executor API; @@ -2687,7 +2952,9 @@ separately authorized. Effectful handler activation is prohibited until its High-volume handlers such as indexing/revalidation additionally require kind-specific retention, coalescing and admission/backpressure limits before -activation. +activation; any coalescing mode additionally requires the documented and +demonstrated post-seal successor contract of section 3.5, failing closed +without it. Rollout should prove in order: @@ -2702,8 +2969,10 @@ Rollout should prove in order: terminal resolution of a mismatch anomaly in both commit orders; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing, including the - ordering of coalescing attachment against first claim/start and the atomic - creation of a routed job with its disposition; + ordering of coalescing attachment against first claim/start, the atomic + creation of a routed job with its disposition and the post-seal + materialization of an obligation refused after a coalescing seal through + successor generations; 6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; 7. one or more non-destructive representative handlers; 8. protected operator controls and reconciliation exit path; @@ -3090,6 +3359,86 @@ Before shared implementation can be approved, evidence must include: claim/attachment races above, uniqueness and idempotency constraints and deterministic lock and deadlock handling, proving no split ordering, no stale strand decision and no permitted effect after a losing terminal path; +- deterministic post-seal baseline tests in which an original coalescing job + `J0` seals before an eligible `E2` can attach, proving `E2` remains owed and + visible, its `(event_id, route_key)` slot unused, and that it is never + retroactively attached to `J0`; +- proof that the same `E2` is materialized through one different eligible + coalescing-open successor or one newly created successor `J1` with a + genuinely distinct approved effect identity, and that eventually exactly one + `(E2, route_key)` disposition exists; +- proof that retries encountering the same sealed `J0` idempotency key + neither loop indefinitely nor declare success, and that the supported + successor-selection path advances; +- deterministic concurrency tests of two materializers racing the same owed + `E2` after the seal, proving one disposition, no duplicate successor or + external effect for the obligation, and deterministic loser recovery from + committed state; +- deterministic concurrency tests of different owed obligations `E2` and `E3` + racing after `J0` seals, proving they converge on an eligible `J1` while it + is open or, when it seals first, follow correctly ordered later + generations, with no obligation dropped or represented by a job whose input + omits it; +- deterministic concurrency tests of an attachment to successor `J1` racing + its first claim/start in both orders, proving one durable order and that + claim/start-first leaves the refused obligation owed and safely eligible for + a later successor, never stale-attached; +- successive-seal tests in which `J0`, `J1` and `J2` are needed within one + approved group or window, proving distinct stable identities, no + permanently stuck eligible obligation and no false route completion; +- precommit-crash tests proving that a crash after allocating or starting + creation of `J1` but before its disposition and input commit leaves no + durable orphan or claimable job, no consumed route slot and no false + materialization; +- postcommit acknowledgment-loss tests proving that after the successor job, + disposition and input transaction commits, a retry observes that exact + state and creates no `J2`, second disposition or effect; +- uniqueness and rollback race tests proving that generation arbitration, + original-group job-key collisions and `(event_id, route_key)` uniqueness + races abort or recover safely under one database ordering, with no write + skew, deadlock, phantom job, missing input or duplicate route; +- predecessor tests proving that a pre-write retry, lease expiry and reclaim, + WAITING resume and reconciliation of `J0` never reopen it and never silently + absorb an owed `E2`, and that successor semantics alter no predecessor + attempt; +- ambiguous-predecessor tests in which `J0` holds an unresolved + `EFFECT_STARTED` outcome, proving `J1` may be tracked durably but performs + no unsafe overlapping or duplicate effect before the required target-scoped + fencing and reconciliation evidence; +- `CURRENT_STATE` successor tests proving `J1` acts on the current canonical + complete effect fingerprint, no-ops safely where that fingerprint is + already applied, and never reapplies an identical provider effect merely + because its key differs; +- `REVISION_BOUND` successor tests proving older and newer revisions under one + target stay ordered and a later successor cannot regress newer applied + evidence or bypass supersession and replay controls; +- historical-enrollment and anomaly tests proving that a compatible first + enrollment into a safe successor composes with revision-withdrawal + availability and the one terminal anomaly resolution: divergence-first + creates no successor or disposition, and enrollment-first commits its + allowed terminal resolution and routed facts atomically; +- blocked-admission tests with an unavailable or incompatible materializer, + proving accurate backlog count and oldest age, bounded retry and backoff, + no fabricated success, no uncontrolled successor growth, and resumed safe + progress when an authorized compatible path returns; +- escape-path negative tests proving that no staff exception is synthesized + from an ordinary sealed-key collision, that direct-job or replay identities + cannot evade a route obligation, and that no new route key or epoch is + invented as a fallback; +- cross-target and cross-kind ordering tests proving that multiple generation + keys still serialize conflicting provider writes under one canonical target + lock and evidence namespace, including ambiguous provider outcomes and + multiple workers; +- finite-workload liveness tests under an adversarial deterministic schedule + of late obligations and successively sealing jobs with a healthy compatible + materializer and no legitimate blocker, proving one disposition for every + eligible event-route pair within bounded fair recovery rather than an + indefinitely repeating permanent collision; +- non-coalesced alternative tests proving a kind without a safe successor + contract cannot activate that coalescing mode, and that an explicitly + approved event-distinct non-coalesced mode keeps stable idempotency keys, + atomic job and disposition creation and no duplicate or regressive external + effects; - current-token lease renewal and refusal to renew superseded/expired claims; - stale-token refusal for completion, failure, cancellation, checkpointing and reconciliation; @@ -3185,20 +3534,11 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-12 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-12 under the -repository decision process, as the architecture content represented by the -Route-B candidate head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and -candidate ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66`; this -version differs from that candidate only in this approval-state -representation. That approval does not by itself make this ADR -repository-authoritative: the authority condition below still applies. Any -ADR-0013 programme-local reliance on this exact version is a separate state -that is not effective until its own conditions are satisfied for this version. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the @@ -3231,7 +3571,15 @@ routed job and no phantom route satisfaction can survive a failure, race or crash, and requires compatible historical enrollment and approved reconciliation/divergence of one mismatch anomaly to compete through one database-enforced terminal-resolution decision, so that no two contradictory -terminal outcomes can both commit. +terminal outcomes can both commit. It further defines the ordinary post-seal +materialization path in section 3.5, under which an eligible obligation +refused after a permanent coalescing seal remains owed and visible and is +materialized through a different eligible coalescing-open successor or a +distinct, stably identified routed successor generation created together +with its disposition, with group identity distinct from executable +generation identity and database-arbitrated convergence, so that no eligible +obligation can remain owed indefinitely against a permanently sealed +idempotency key and no successor can bypass target-scoped effect safety. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO approval dated 2026-09-30. A later corrected version, which introduced the @@ -3250,7 +3598,10 @@ database-consistent strand-safety decision for revision withdrawal, carried version, which introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, carried `Status: APPROVED` with CTO approval dated -2026-10-09. Those approvals, and +2026-10-09. A further corrected version, which introduced that atomic commit +and terminal resolution without a defined post-seal materialization path for +an obligation refused after a coalescing seal, carried `Status: APPROVED` +with CTO approval dated 2026-10-12. Those approvals, and every review of those earlier versions, remain historical evidence bound to those exact earlier versions and source heads only; they do not approve this version. The exact historical identities are recorded in the task diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index c9ff92119..c3b827d5f 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-12 +Last updated: 2026-10-14 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-12 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-12 under the repository decision process, as the architecture content represented by Route-B candidate head `528c2db04af5c53b9820b946ddac2ebb936eb4cd` and candidate ADR-0012 blob `7e461106986fe6efad2021d1ad6f52573e6e9d66` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only: exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, and the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for blocked recovery, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 15 Oct 2026 14:29:39 +0100 Subject: [PATCH 44/48] THOTH-ASYNC-01-ADR-01: align post-seal backlog and generation wording (#958) --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 947 +++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 99 +- .../decisions/decision-register.md | 4 +- 4 files changed, 856 insertions(+), 196 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7848c867..e90109604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while blocked recovery stays backlogged, age-visible and attention-raising with bounded backoff and bounded generations; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while an obligation for which no eligible successor can be selected or admitted stays undispositioned, backlogged, age-visible and attention-raising with bounded backoff and bounded generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state rather than the route backlog; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index b5656eed3..e3e31754f 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -31,14 +31,17 @@ automated review of that Amendment-10 approval-state head, the Route-B approval-state stage of that Amendment-11 candidate, and the Route-B candidate stage of the post-seal materialization correction specified by Specification Amendment 12 after the configured final-head automated review -of that Amendment-11 approval-state head. It records +of that Amendment-11 approval-state head, and the four-path replacement +candidate of that candidate after its independent exact-candidate source +review. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-12 Route-B candidate as the -current stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-12 Route-B replacement +candidate (the `F-A12-CAND-01` and `F-A12-CAND-02` correction) as the current +stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -60,12 +63,15 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-12 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the -historical Amendment-11 approval-state head -`21e19e81d0acf1aed57952ec00e2653771fb6f47`; its SHA is deliberately not -self-pinned here (section 3). That approval-state head is the direct child -of the content-approved Amendment-11 candidate +Head commit: the Amendment-12 Route-B replacement candidate commit that +contains this report, an ordinary single-parent additive commit whose direct +parent is the historical first Amendment-12 candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9`, inspected `CHANGES REQUIRED / +F-A12-CAND-01, F-A12-CAND-02`; its SHA is deliberately not self-pinned here +(section 3). That first candidate is the direct child of the historical +Amendment-11 approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47`, which is the direct child of +the content-approved Amendment-11 candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, which is the direct child of the historical Amendment-10 approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df`, which is the direct child of @@ -114,8 +120,9 @@ implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, -Amendment-11 approval-state and Amendment-12 candidate stages - Claude (Fable -5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. +Amendment-11 approval-state, Amendment-12 candidate and Amendment-12 +replacement-candidate stages - Claude (Fable 5.1), bounded implementing +agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -291,18 +298,26 @@ decision-register blob c9ff92119095a162cf736304eb88ba595d3685ab CHANGELOG blob a4af0397dc37fdf782124573d9a4b7a7b55e8abe implementation-report blob daeff73cf6f6b6da413ba506cc43cd1d8510f673 (superseded by the Amendment-12 candidate) -Amendment-12 Route-B candidate (the commit that contains this report): -ADR-0012 blob 678541fe5904ae4ea816cafa46c2e68780b51f4e (PROPOSED; material architectural correction of df8017c57d74eaecf13041e231ef97625260fab1) +historical first Amendment-12 candidate / replacement-candidate parent: +06cc13685f1df5ec41da4f7958e8780a0c63ead9 +tree 3bb39f3d6a252fdecdaae36e482c0723f9c56aa2 +ADR-0012 blob 678541fe5904ae4ea816cafa46c2e68780b51f4e (PROPOSED; material architectural correction of df8017c57d74eaecf13041e231ef97625260fab1; independent exact-candidate source review CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02; never content-approved) decision-register blob c3b827d5fe5309dbc6ccd84a67d704aca198fe2c CHANGELOG blob a7848c8672984f7ff2fad997c7674b04d94ea04a +implementation-report blob b5656eed3d030fc9997623f101fff2aee3f03776 (superseded by the replacement candidate) + +Amendment-12 Route-B replacement candidate (the commit that contains this report): +ADR-0012 blob c4ca99ce2e538ae607496ecf7c89108501b5fe76 (PROPOSED; F-A12-CAND-01 and F-A12-CAND-02 consistency correction of 678541fe5904ae4ea816cafa46c2e68780b51f4e) +decision-register blob efab47f6928ab23df560a96e6bb2e5e2da34a216 +CHANGELOG blob e901096042559985471c49182e404409d218e891 feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction, the Amendment-10 approval-state commit, the -Amendment-11 candidate, the Amendment-11 approval-state commit and the -Amendment-12 candidate (unchanged): +Amendment-11 candidate, the Amendment-11 approval-state commit, the +Amendment-12 candidate and its replacement candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -486,7 +501,12 @@ Approved specification: - PR #960 Ready -> Draft authorization for Amendment-12 Route-B preparation, a control action that preceded this stage: #958 comment `6151609272`; - Amendment-12 Route-B candidate implementation authorization: #958 comment - `6151955721`. + `6151955721` (consumed by the first candidate); +- independent CRITICAL exact-candidate source review of the first + Amendment-12 candidate, `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`: + #958 comment `6169631899`; +- Amendment-12 replacement-candidate implementation authorization: #958 + comment `6172224137`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -568,15 +588,23 @@ approval-state commit then recorded that approval as durable decision state atomicity correction changelog entry moved to the approved state), and the CTO recorded exact-final-blob approval of that ADR blob. The configured final-head automated review of that approval-state head exposed a further -architecture defect, `R-21E-01`, specified by Specification Amendment 12. The -commit that contains this report is its Route-B candidate: one ordinary -single-parent commit on the approval-state head that changes exactly the four -authorized correction paths, returns ADR-0012 to `Status: PROPOSED`, and -defines the ordinary post-seal materialization path under which an eligible -obligation refused after a permanent coalescing seal remains owed and visible -and is materialized through a different eligible coalescing-open successor or -a distinct, stably identified routed successor generation created together -with its disposition. +architecture defect, `R-21E-01`, specified by Specification Amendment 12. Its +first `PROPOSED` Route-B candidate, one ordinary single-parent commit on the +approval-state head that changed exactly the four authorized correction +paths, returned ADR-0012 to `Status: PROPOSED` and defines the ordinary +post-seal materialization path under which an eligible obligation refused +after a permanent coalescing seal remains owed and visible and is +materialized through a different eligible coalescing-open successor or a +distinct, stably identified routed successor generation created together +with its disposition, was independently reviewed and returned +`CHANGES REQUIRED` on two candidate-source consistency findings, +`F-A12-CAND-01` and `F-A12-CAND-02`. The commit that contains this report is +the authorized replacement candidate: one ordinary single-parent commit on +that first candidate that changes exactly the same four paths, separates the +undispositioned route backlog from the job-level state of a committed but +held successor, and lets a compatible generation be created beside an open +but ineligible one. The Amendment-12 architecture is otherwise unchanged and +the version remains `PROPOSED`. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -1087,7 +1115,8 @@ with its disposition. stage authorized no PR-state or PR-metadata change. - **Exact-final-blob approval, final-head review of the Amendment-11 approval-state head, Specification Amendment 12 and the Amendment-12 Route-B - candidate (the commit that contains this report).** Authorized by #958 + candidate (historical first candidate + `06cc13685f1df5ec41da4f7958e8780a0c63ead9`).** Authorized by #958 comment `6151955721` after the CTO approved Specification Amendment 12 (#958 comments `6149301174` and `6150795589`) and a fresh independent CRITICAL specification review of the base specification and Amendments 1-12 returned @@ -1131,19 +1160,54 @@ with its disposition. and SQL left to the implementation specification. The independent review confirmed the finding, the classification, all six provisions, the composition and the sufficiency of the twenty-case matrix, and recorded - seven non-blocking clarity observations. The commit that contains this - report is one ordinary single-parent commit directly on the approval-state - head that changes exactly the four authorized correction paths: ADR-0012 - returns to `Status: PROPOSED` with no current approver or approval date and + seven non-blocking clarity observations. The first candidate was one + ordinary single-parent commit directly on the approval-state head that + changed exactly the four authorized correction paths: ADR-0012 returned to + `Status: PROPOSED` with no current approver or approval date and implements Amendment 12 (section 4; section 5, decisions 133-144); the - decision-register ADR-0012 row, one new changelog entry and this report - describe the corrected version as `PROPOSED`; and the 2026-09-30, - 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09 and 2026-10-12 - CTO approvals remain historical evidence bound to the earlier exact - versions. This stage authorizes no PR-state or PR-metadata change; the PR - #960 title and description, which describe the historical Amendment-11 - source, are GitHub-owned and their reconciliation is a separate control - action. + decision-register ADR-0012 row, one new changelog entry and the + candidate-stage report described the corrected version as `PROPOSED`; and + the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09 + and 2026-10-12 CTO approvals remain historical evidence bound to the + earlier exact versions. That stage authorized no PR-state or PR-metadata + change. +- **Independent exact-candidate source review and the F-A12-CAND-01 / + F-A12-CAND-02 replacement candidate (the commit that contains this + report).** Authorized by #958 comment `6172224137` after an independent + CRITICAL exact-candidate source review of the published first candidate + returned `CHANGES REQUIRED` (#958 comment `6169631899`) with two HIGH + blocking candidate-source consistency findings. `F-A12-CAND-01`: section + 3.2 establishes that an obligation stays in the undispositioned route + backlog only until its one `(event_id, route_key)` disposition commits, + and that a materialized disposition remains regardless of whether its job + is pending, running, waiting or terminal, yet the first candidate's new + section-3.2 backlog sentence and section-3.5 liveness paragraph said that a + successor whose safe execution is held by predecessor effect ambiguity + stays in that backlog and is never counted as dispositioned, contradicting + the one-transaction creation of the successor with its disposition. + `F-A12-CAND-02`: the post-seal subsection correctly said that an + ineligible open successor never prevents a compatible one from being + selected or created, yet its convergence paragraph said that materializers + serialize into a subsequent generation only when the earlier one is + already sealed, which would block a compatible generation while an + ineligible generation remained open. The review confirmed everything else + about the first candidate: the single-parent topology, the four-path + delta, the six-path footprint, the protected blobs, the `PROPOSED` state + and the twenty validation additions, and assessed both findings as + reconcilable within approved Amendment 12 without an Amendment 13. The + commit that contains this report is one ordinary single-parent commit + directly on the first candidate that changes exactly the same four paths: + in ADR-0012 the section-3.2 backlog sentence, the section-3.5 + effect-semantics and liveness paragraphs, the generation-creation sentence + of the convergence paragraph, two clauses of invariant 46 and validation + cases 5, 7, 10, 12 and 16 (section 4; section 5, decisions 145-148); the + ADR-0012 register row and the register header date; the existing post-seal + recovery changelog entry; and this report. ADR-0012 remains `PROPOSED`; + the first candidate is historical `CHANGES REQUIRED` evidence and was + never content-approved. This stage authorizes no PR-state or PR-metadata + change; the PR #960 title and description, which describe the historical + Amendment-11 source, are GitHub-owned and their reconciliation is a + separate control action. Out-of-scope changes made: NONE @@ -1157,8 +1221,16 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +F-A12-CAND-01 / F-A12-CAND-02 consistency correction of first-candidate blob 678541fe5904ae4ea816cafa46c2e68780b51f4e +(route backlog versus committed but held successor; compatible generation beside an +open ineligible one; Specification Amendment 12; Route-B replacement-candidate stage; +candidate status PROPOSED) + +historical first Amendment-12 candidate at 06cc13685f1df5ec41da4f7958e8780a0c63ead9: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob df8017c57d74eaecf13041e231ef97625260fab1 -(Specification Amendment 12; Route-B candidate stage; candidate status PROPOSED) +(Specification Amendment 12; Route-B candidate stage; candidate status PROPOSED; +independent exact-candidate source review CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02; +never content-approved) historical Amendment-11 approval-state at 21e19e81d0acf1aed57952ec00e2653771fb6f47: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-12) @@ -1469,31 +1541,56 @@ and observation are distinct controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for which of them is pending, satisfied or blocked for any head. +The first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` +received independent CRITICAL exact-candidate source review, which returned +`CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`; it was never +content-approved. The replacement candidate creates a new exact ADR-0012 +version, so that review outcome is bound to the first candidate's ADR blob +`678541fe5904ae4ea816cafa46c2e68780b51f4e` and head, and the replacement +candidate requires its own fresh independent exact-candidate review before +any content approval. + ## 3. Commits -Amendment-12 Route-B candidate commit (the commit that contains this report): +Amendment-12 Route-B replacement candidate commit, the F-A12-CAND-01 and +F-A12-CAND-02 consistency correction (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose Amendment-12 post-seal recovery correction (#958)` - - direct parent: the historical Amendment-11 approval-state head - `21e19e81d0acf1aed57952ec00e2653771fb6f47` - - changes only the four authorized correction paths (section 4); the - candidate ADR-0012 blob is `678541fe5904ae4ea816cafa46c2e68780b51f4e`, the - candidate decision-register blob `c3b827d5fe5309dbc6ccd84a67d704aca198fe2c` - and the candidate changelog blob `a7848c8672984f7ff2fad997c7674b04d94ea04a` + `THOTH-ASYNC-01-ADR-01: align post-seal backlog and generation wording (#958)` + - direct parent: the historical first Amendment-12 candidate + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` + - changes only the four authorized paths (section 4); the corrected + ADR-0012 blob is `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, the corrected + decision-register blob `efab47f6928ab23df560a96e6bb2e5e2da34a216` and the + corrected changelog blob `e901096042559985471c49182e404409d218e891` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the candidate ADR-0012, +here because this report is part of that tree; the corrected ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its approval-state-head blob (section -9). The commit identities are verified against the remote task-branch head -after push and recorded in the post-publication handoff; once published, the -PR #960 head and head tree are GitHub-owned lifecycle evidence under -`ADR-0005`. The authorized topology is a non-force fast-forward of the task -branch from the approval-state head to the candidate commit; no merge commit, -rebase, squash, amend, force update or second candidate commit is authorized, -and no further source commit exists implicitly after it. +recorded above; every other file keeps its first-candidate blob (section 9). +The commit identities are verified against the remote task-branch head after +push and recorded in the post-publication handoff; once published, the PR +#960 head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. +The authorized topology is a non-force fast-forward of the task branch from +the first candidate to the replacement commit; no merge commit, rebase, +squash, amend, force update or second replacement commit is authorized, and +no further source commit exists implicitly after it. + +Amendment-12 Route-B first candidate commit (historical; reviewed +`CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`; never content-approved): + +- `06cc13685f1df5ec41da4f7958e8780a0c63ead9` - + `THOTH-ASYNC-01-ADR-01: propose Amendment-12 post-seal recovery correction (#958)` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-11 approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47` + - tree `3bb39f3d6a252fdecdaae36e482c0723f9c56aa2` + - changed only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `678541fe5904ae4ea816cafa46c2e68780b51f4e` + - published by a non-force fast-forward of `feature/async/adr-0012` from the + approval-state head; the remote head, tree, parent and ADR-0012, + decision-register, changelog and report blobs matched the locally + validated object Amendment-11 Route-B approval-state commit (historical): @@ -1774,11 +1871,22 @@ first-parent order, newest first: ## 4. Files changed -Amendment-12 Route-B candidate write budget for the commit that contains this -report (authorization #958 comment `6151955721`; the same four existing paths -as the Amendment-11 budgets); exactly four existing paths, no new file, no -deletion, and every other path byte-identical to the historical Amendment-11 -approval-state head +F-A12-CAND-01 / F-A12-CAND-02 replacement-candidate write budget for the +commit that contains this report (authorization #958 comment `6172224137`; +the same four existing paths as the Amendment-12 candidate budget); exactly +four existing paths, no new file, no deletion, and every other path +byte-identical to the historical first Amendment-12 candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-12 Route-B candidate write budget (authorization #958 +comment `6151955721`; the same four existing paths as the Amendment-11 +budgets); exactly four existing paths, no new file, no deletion, and every +other path byte-identical to the historical Amendment-11 approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`: - `CHANGELOG.md` @@ -1932,12 +2040,98 @@ recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 -approval-state and Amendment-12 candidate stages; all four correction paths -already existed. +approval-state, Amendment-12 candidate and Amendment-12 replacement-candidate +stages; all four correction paths already existed. -Amendment-12 candidate changes in the commit that contains this report, -relative to the approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47` -(exactly the four authorized paths): +Replacement-candidate changes in the commit that contains this report, +relative to the first Amendment-12 candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9` (exactly the four authorized paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: correct `F-A12-CAND-01` (a committed but held successor described + as a still-undispositioned route obligation) and `F-A12-CAND-02` (a + compatible generation conditioned on every earlier generation being + sealed). + - behavioural effect: architecture proposal only; twelve hunks, all inside + text the first candidate added. For `F-A12-CAND-01`, the section-3.2 + backlog sentence now keeps an obligation refused after a seal in the + undispositioned route backlog only while no eligible successor can be + selected or admitted and until its disposition commits, and states that + once a successor and the disposition have committed together under the + atomic creation boundary the obligation is dispositioned and leaves the + route backlog even where the successor's execution is then held, the + hold being job-level `WAITING`, `RECONCILIATION_REQUIRED`, + blocked-successor and attention state with no provider effect claimed; + the section-3.5 effect-semantics paragraph names the held successor as a + dispositioned job in that job-level state; the section-3.5 liveness + paragraph accounts for the undispositioned and the committed-but-held + states separately; the closing clause of invariant 46 does the same; and + validation cases 12 and 16 state that the held successor's obligation is + absent from the route backlog and that backlog counts cover only + obligations without a committed disposition. For `F-A12-CAND-02`, the + convergence paragraph now says that materializers converge on an + existing eligible coalescing-open successor whenever one exists, + attaching under the approved rule rather than bypassing it, and create a + subsequent generation only when no eligible open successor can represent + the obligation, whether because every earlier generation is sealed or + because an open earlier generation is ineligible for the obligation's + route, epoch, revision or event version, so that an open ineligible + generation never blocks a compatible one; the creation clause of + invariant 46 and validation cases 5, 7 and 10 carry the same rule. The + header and section 12 (`PROPOSED`, no current approver or approval + date), the post-seal subsection's two-path and eligibility paragraph, + invariants 1-45, the Amendment-8 through Amendment-11 subsections, the + other fifteen Amendment-12 validation cases and all earlier validation + are byte-identical to the first candidate (section 9). Corrected blob: + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: the ADR-0012 row summarized both defective rules. + - behavioural effect: in the ADR-0012 row only, "visible bounded backoff + for blocked recovery" becomes the two-state rule and the + generation-creation clause gains "whether earlier generations are sealed + or an open one is ineligible for it"; `PROPOSED`, `Pending`, every + historical approval binding, the partial-supersession, + authority-condition, ADR-0013 and implementation-authorization sentences + and every other row are unchanged. The header `Last updated` advances + from `2026-10-14` to `2026-10-15`, the date of this row change (section + 5, decision 147). Corrected blob: `efab47f6928ab23df560a96e6bb2e5e2da34a216`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: the existing post-seal recovery entry summarized both defective + rules. + - behavioural effect: only the existing + `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry changes, + replacing "blocked recovery stays backlogged, age-visible and + attention-raising" with the two-state rule and adding "whether earlier + generations are sealed or an open one is ineligible for it" to the + generation-creation clause; no entry is added and every other entry and + heading is byte-preserved. Corrected blob: `e901096042559985471c49182e404409d218e891`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: carry the same distinction in Appendix B item 17 and record the + replacement-candidate stage truthfully. + - behavioural effect: documentation only. Appendix B item 17 keeps the + obligation owed only until its disposition commits; the first candidate + becomes historical `CHANGES REQUIRED` evidence with its now-known SHA, + tree and report blob recorded in section 1; the replacement becomes the + current stage with the first candidate as its direct parent; the review + and authorization records are added to section 2; the replacement's + validation is recorded in section 9 (section 5, decisions 145-148). The + first candidate's section-4 description is unchanged apart from lineage + reframing. All still-true earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the first candidate through the replacement candidate: +every path other than the four authorized paths, including ADR-0008 +`622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. + +Historical Amendment-12 candidate changes in the first candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9`, relative to the approval-state head +`21e19e81d0acf1aed57952ec00e2653771fb6f47` (exactly the four authorized +paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement Specification Amendment 12 and record the corrected @@ -2025,16 +2219,16 @@ relative to the approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47` - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-12 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate the current - stage; records the approval-state inspection, the exact-final-blob - approval of the Amendment-11 approval-state blob, the final-head review of - that head, `R-21E-01`, Amendment 12, its CTO approval, its independent - review, the Ready-to-Draft control action and the candidate authorization - as governing evidence; records the candidate's validation (section 9); - and reframes statements that would otherwise describe the approval-state - commit as the commit containing this report, recording that commit's - identities as historical facts. All still-true earlier evidence is - preserved. + - behavioural effect: documentation only. Made the candidate the + then-current stage; recorded the approval-state inspection, the + exact-final-blob approval of the Amendment-11 approval-state blob, the + final-head review of that head, `R-21E-01`, Amendment 12, its CTO + approval, its independent review, the Ready-to-Draft control action and + the candidate authorization as governing evidence; recorded the + candidate's validation (section 9); and reframed statements that would + otherwise describe the approval-state commit as the commit containing + the report, recording that commit's identities as historical facts. All + still-true earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the approval-state head through the Amendment-12 candidate: @@ -3193,7 +3387,11 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-12 candidate differs from the approval-state head +The replacement candidate differs from the first Amendment-12 candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9` in exactly the four authorized paths +and from refreshed `feature/worker` in exactly the six cumulative PR paths +(section 9, "Replacement-candidate validation"). The Amendment-12 candidate +differed from the approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47` in exactly the four authorized correction paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-12 candidate validation"). The @@ -3280,8 +3478,9 @@ stage (#958 comment `6046895895`), the Amendment-9 approval-state stage `6069074613`), the Amendment-10 approval-state stage (#958 comment `6083303288`), the Amendment-11 candidate stage (#958 comment `6125285446`), the Amendment-11 approval-state stage (#958 comment -`6128858897`) and the Amendment-12 candidate stage (#958 comment -`6151955721`). This matrix +`6128858897`), the Amendment-12 candidate stage (#958 comment +`6151955721`) and the Amendment-12 replacement-candidate stage (#958 comment +`6172224137`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -3765,8 +3964,8 @@ Amendment-12 candidate: review of the approval-state head, and returned it to Draft before the Amendment-12 candidate (section 2) -Amendment-12 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-12 candidate stage - historical, completed before the replacement +candidate: - repository inspection: USED (read-only verification of the exact refs, including `develop` and `feature/worker` at the same base, PR #960 open, @@ -3796,13 +3995,52 @@ commit that contains this report: approval-state head and touching no other worktree, branch or ref, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent candidate commit that contains this report) +- commit: USED (the single-parent candidate commit + `06cc13685f1df5ec41da4f7958e8780a0c63ead9`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `21e19e81d0acf1aed57952ec00e2653771fb6f47` to the candidate; the remote head, + tree, parent and ADR-0012, decision-register, changelog and report blobs + were verified; no automated review ran on the draft candidate head) +- independent exact-candidate source review: NOT USED by the implementing + agent; an independent reviewer reviewed the published candidate and + returned `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02` (section 2) -Authorized post-commit actions for the candidate commit - GitHub-owned -lifecycle evidence under `ADR-0005`, not self-recorded here: +Amendment-12 replacement-candidate stage - done up to and including the +creation of the commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base, PR #960 open, + draft, unmerged and mergeable with head + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` and target `feature/worker`, + the first candidate's tree and single parent, the ADR-0012, report, + decision-register and changelog blobs at that head, the protected ADR-0008, + ADR-0010 and ADR-0013 blobs and the Route-B doctrine blob, the six-path + cumulative footprint, the absence of any ADR-0012 commit reachable from + `develop`, `master` or `feature/worker` (Route-B eligibility), the presence + of the review and authorization records on #958 as the two latest comments + with no later control record, the pull_request-triggered workflow set and + its documentation-only classification gate, the job-level outcomes of the + four workflow runs on the first candidate, and, as stage-time evidence, the + PR #960 review-thread state in GitHub; all matched the authorization; + current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths only; in ADR-0012 only the + passages named by the two findings and the five cases that express them) +- new file creation: NOT USED (not authorized) +- file deletion/move/rename: NOT USED (not authorized) +- branch creation: NOT USED (not authorized; the implementing agent's own + existing session worktree, verified clean and detached at the first + candidate and touching no other worktree, branch or ref, was used for + construction, and the existing task branch is the fast-forward target) +- commit: USED (the single-parent replacement-candidate commit that contains + this report) + +Authorized post-commit actions for the replacement-candidate commit - +GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `21e19e81d0acf1aed57952ec00e2653771fb6f47` to the candidate commit; + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` to the replacement-candidate commit; - post-push read-only verification of the remote head, tree, direct parent, ADR-0012, decision-register, changelog and report blobs, changed path set, cumulative footprint and PR #960 state, and that no review thread was @@ -3814,15 +4052,16 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-12 candidate -stage, each a separate controlled lifecycle category where applicable +Not authorized for the implementing agent at the Amendment-12 +replacement-candidate stage, each a separate controlled lifecycle category +where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): - any edit to a path other than the four authorized correction paths: NOT USED (not authorized) -- any ADR-0012 change outside Amendment 12 and the candidate `PROPOSED` state: - NOT USED (not authorized) +- any ADR-0012 change outside the passages named by the two findings and the + candidate `PROPOSED` state: NOT USED (not authorized) - independent exact-candidate inspection or exact-head review: NOT USED (never on the implementing agent's authority) - CTO candidate-content approval, approval-state commit, fresh `APPROVED` @@ -3830,8 +4069,8 @@ authoritative for the current state of each): authorized) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second candidate commit, amend, rebase, squash, force push or history - rewrite: NOT USED (not authorized) +- any second replacement commit, amend, rebase, squash, force push or + history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) - manual CI dispatch/rerun: NOT USED (not authorized) @@ -3869,18 +4108,20 @@ candidate and approval-state authorizations, the Amendment-9 candidate and approval-state authorizations, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the -Amendment-11 approval-state authorization and the Amendment-12 candidate -authorization, configured Codex/app review is not expected from a push to a -draft PR and is not relied upon at the Amendment-6/7 candidate, -approval-state, control-record recovery, transient-state correction, -factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, -Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, -consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, -Amendment-11 approval-state or Amendment-12 candidate stage; configured -Codex/app review runs on separately authorized Ready transitions, which are -GitHub-owned lifecycle events, and any automated review that nevertheless -runs on a draft head is inspected read-only. The actual job outcomes for the -candidate commit exist only after its publication; they +Amendment-11 approval-state authorization, the Amendment-12 candidate +authorization and the Amendment-12 replacement-candidate authorization, +configured Codex/app review is not expected from a push to a draft PR and is +not relied upon at the Amendment-6/7 candidate, approval-state, +control-record recovery, transient-state correction, factual-clarification, +Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, +Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, +Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 +approval-state, Amendment-12 candidate or Amendment-12 replacement-candidate +stage; configured Codex/app review runs on separately authorized Ready +transitions, which are GitHub-owned lifecycle events, and any automated +review that nevertheless runs on a draft head is inspected read-only. The +actual job outcomes for the replacement-candidate commit exist only after +its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -3891,8 +4132,9 @@ this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, -Amendment-11 approval-state or Amendment-12 candidate stage, followed the same -documentation-only classification, with no manual dispatch or rerun: +Amendment-11 approval-state, Amendment-12 candidate or Amendment-12 +replacement-candidate stage, followed the same documentation-only +classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -3915,6 +4157,7 @@ e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelo 576a860be47aa1ebe39968f4d3297b26ee1d17df: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-10 approval-state head) 528c2db04af5c53b9820b946ddac2ebb936eb4cd: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-11 candidate) 21e19e81d0acf1aed57952ec00e2653771fb6f47: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-11 approval-state head) +06cc13685f1df5ec41da4f7958e8780a0c63ead9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-12 candidate) ``` Manually initiated external actions (anything the implementing agent @@ -5016,9 +5259,11 @@ Amendment-11 approval-state stage (historical approval-state head as a result and was not represented as exact-final-blob approved; it received that approval on 2026-10-12. -Amendment-12 Route-B candidate (the commit that contains this report). Items -134, 135, 136 and 139 record interpretations of the approved specification -for explicit control inspection: +Amendment-12 Route-B candidate (historical first candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9`, reviewed `CHANGES REQUIRED / +F-A12-CAND-01, F-A12-CAND-02`). Items 134, 135, 136 and 139 recorded +interpretations of the approved specification for explicit control +inspection: 133. The Amendment-12 rule is stated once, in a new unnumbered subsection "Post-seal materialization and successor generations" in section 3.5, @@ -5172,19 +5417,105 @@ for explicit control inspection: 12, CTO specification-approval, independent-review, Ready-to-Draft and candidate-authorization records are cited by their comment identifiers as specification and authorization evidence, consistent with section 2. -144. This report does not self-pin the candidate commit's SHA, its tree or its - own final blob; because the report now lives in the candidate commit, - statements that described the approval-state commit as the commit - containing this report are reframed as history, and that commit's - identities, now the candidate parent, are recorded in section 1 as - historical facts. Construction used the implementing agent's own existing - clean session worktree, detached at the approval-state head before - editing (the detached scratch worktree used by the earlier stages no - longer existed), touching no other worktree, branch, ref, stash or - uncommitted change. The PR #960 title and description describe the - historical Amendment-11 source; this stage is not authorized to change - them, and their reconciliation is reported to control as a separate - action. +144. The first-candidate report did not self-pin the candidate commit's SHA, + its tree or its own final blob; because the report then lived in the + candidate commit, statements that described the approval-state commit as + the commit containing the report were reframed as history, and that + commit's identities, the candidate parent, were recorded in section 1 as + historical facts; section 1 now records the first candidate's identities + as historical facts because that head is the replacement candidate's + parent. Construction used the implementing agent's own existing clean + session worktree, detached at the approval-state head before editing + (the detached scratch worktree used by the earlier stages no longer + existed), touching no other worktree, branch, ref, stash or uncommitted + change. The PR #960 title and description described the historical + Amendment-11 source; that stage was not authorized to change them, and + their reconciliation was reported to control as a separate action. + +F-A12-CAND-01 / F-A12-CAND-02 replacement candidate (the commit that contains +this report): + +145. `F-A12-CAND-01` is corrected by separating two states that the first + candidate conflated. Before: section 3.2 said that a successor whose safe + execution is held by target-scoped effect ambiguity "stays in that + backlog ... and is never counted as dispositioned", and the section-3.5 + liveness paragraph said that while "external ambiguity blocks safe + execution, the owed obligation and its reason remain ... in the route + backlog". Counterexample: `J0` is sealed with an ambiguous + `EFFECT_STARTED` effect; eligible `E2` is owed; the materializer commits + `J1`, the `(E2, route_key)` disposition and `J1`'s input facts in one + transaction; `J1` is held in `WAITING` or `RECONCILIATION_REQUIRED` + behind `J0`'s target boundary. Under the first candidate's words `E2` was + simultaneously dispositioned (section 3.2, lines 365-375) and "never + counted as dispositioned". After: an obligation refused after a seal + stays in the undispositioned route backlog only while no eligible + successor can be selected or admitted and until its disposition commits; + once `J1` and the disposition commit together, `E2` is dispositioned and + leaves the route backlog, and `J1`'s held execution is job-level + `WAITING`, `RECONCILIATION_REQUIRED`, blocked-successor and attention + state under sections 3.4 and 3.15, with no provider effect claimed. The + section-3.2 backlog sentence, the section-3.5 effect-semantics and + liveness paragraphs, the closing clause of invariant 46, validation cases + 12 and 16, the register row, the changelog entry and Appendix B item 17 + now say exactly that. Backlog is not redefined: it remains the set of + eligible obligations without a committed disposition, as section 3.2 and + invariant 8 already define it; A11-1 atomicity, target-scoped fencing, + at-least-once delivery and `(event_id, route_key)` uniqueness are + unchanged. +146. `F-A12-CAND-02` is corrected by removing the unconditional seal + precondition. Before: the convergence paragraph said that materializers + "serialize into a subsequent generation only when the earlier one is + already sealed", contradicting the preceding two-path paragraph under + which "an ineligible open successor never prevents a compatible one from + being selected or created". Counterexample: group `G` has an open + successor `J1` whose immutable contract cannot represent eligible `E2`; + under the first candidate's words no compatible `J2` could be created + while `J1` remained open, leaving `E2` owed with no normal path. After: + materializers converge on an existing eligible coalescing-open successor + whenever one exists, attaching under the approved rule rather than + bypassing it to create another generation, and create a subsequent + generation only when no eligible open successor can represent the + obligation, whether because every earlier generation is sealed or + because an open earlier generation is ineligible for the obligation's + route, epoch, revision or event version; an open ineligible generation + never blocks a compatible one, and database arbitration, stable + identity and the atomic creation boundary apply to the generation + created beside it. The creation clause of invariant 46, validation cases + 5, 7 and 10, the register row and the changelog entry carry the same + rule. Eligibility is still decided by the kind's contract under sections + 3.2 and 3.9 through the unchanged two-path paragraph; no cross-revision + semantics, SQL, provider key, route identity or external-effect + authority is introduced, and an unchanged retry still manufactures no + identity. +147. The replacement is recorded as a candidate-source consistency correction + inside the approved Specification Amendment 12, not as an Amendment 13 + and not as a factual clarification of an approved version: A12-1 through + A12-6 are preserved, the twenty validation cases remain one-to-one with + the Amendment (five reworded to express the corrected states, none + removed or added), invariants 1-45 and the Amendment-8 through + Amendment-11 subsections are byte-identical to the first candidate + (section 9), and no mechanism is selected. ADR-0012 remains `PROPOSED` + with no current approver or approval date; the first candidate is + historical `CHANGES REQUIRED` evidence and is not presented as + content-approved. The two findings are recorded by their control-assigned + names, and the review and authorization records by their comment + identifiers as evidence, consistent with section 2. The + decision-register header `Last updated` advances to `2026-10-15`, the + date on which the replacement changes the ADR-0012 row (decisions 25, 57, + 78, 85, 109, 121 and 141 pattern); only the ADR-0012 row changes + otherwise. +148. This report does not self-pin the replacement commit's SHA, its tree or + its own final blob; because the report now lives in the replacement + commit, statements that described the first candidate as the commit + containing this report are reframed as history, and the first + candidate's identities, now the replacement parent, are recorded in + section 1 as historical facts. Construction used the implementing agent's + own existing clean session worktree, detached at the first candidate + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` before editing, touching no + other worktree, branch, ref, stash or uncommitted change. The PR #960 + title and description describe the historical Amendment-11 source; this + stage is not authorized to change them, and their reconciliation is + reported to control as a separate action. List any deviation from the specification requiring authorization: @@ -5303,9 +5634,18 @@ List any deviation from the specification requiring authorization: - Amendment-12 candidate stage: NONE. No fifth path, new file, deletion, ADR change outside Amendment 12 and the candidate state, second commit, amend, rebase, squash, force push or history rewrite was needed or performed - before the commit that contains this report was created. Decisions 134, - 135, 136 and 139 are recorded for control inspection as interpretations - within the authorized scope, not as deviations. + before the candidate commit was created. Decisions 134, 135, 136 and 139 + were recorded for control inspection as interpretations within the + authorized scope, not as deviations; the independent review raised no + finding against them and two findings, `F-A12-CAND-01` and + `F-A12-CAND-02`, against the candidate's backlog and generation-creation + wording. +- Amendment-12 replacement-candidate stage: NONE. The correction followed + its separate authorization exactly; no fifth path, new file, deletion, ADR + change outside the passages named by the two findings and the candidate + state, second commit, amend, rebase, squash, force push or history rewrite + was needed or performed before the commit that contains this report was + created. ## 6. Database and migration effects @@ -5391,6 +5731,8 @@ records the validation of the historical content-approved candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`. "Amendment-11 approval-state validation" records the validation of the historical approval-state commit `21e19e81d0acf1aed57952ec00e2653771fb6f47`. "Amendment-12 candidate +validation" records the validation of the historical first candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9`. "Replacement-candidate validation" at the end of this section records the validation of the commit that contains this report. @@ -9201,15 +9543,18 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 678541fe5904ae4ea816cafa46c2e68780b51f4e ``` -The candidate ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the candidate tree and the candidate -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and recorded in the -post-publication handoff. +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate report's bytes, so they +were recorded above. The candidate report's own final blob, the candidate +tree and the candidate commit SHA depended on that report's bytes and were +not self-pinned in it; they were verified against the remote head after push +and recorded in the post-publication handoff, and section 1 now records them +as historical facts: commit `06cc13685f1df5ec41da4f7958e8780a0c63ead9`, tree +`3bb39f3d6a252fdecdaae36e482c0723f9c56aa2`, report blob +`b5656eed3d030fc9997623f101fff2aee3f03776`. -Required topology of the candidate commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the candidate commit, checked after it was created and +before push; the published candidate satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -9228,6 +9573,225 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Replacement-candidate validation + +Environment: the implementing agent's own existing local Git worktree of +`thoth-pub/thoth`, verified clean (no tracked or untracked change) and then +detached at the historical first Amendment-12 candidate +`06cc13685f1df5ec41da4f7958e8780a0c63ead9` (tree +`3bb39f3d6a252fdecdaae36e482c0723f9c56aa2`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, no ADR-0012 commit reachable from +`develop`, `master` or `feature/worker` (Route-B eligibility), +`feature/async/adr-0012` at +`06cc13685f1df5ec41da4f7958e8780a0c63ead9` with single parent +`21e19e81d0acf1aed57952ec00e2653771fb6f47` and no descendant, ADR-0012 blob +`678541fe5904ae4ea816cafa46c2e68780b51f4e`, report blob +`b5656eed3d030fc9997623f101fff2aee3f03776`, decision-register blob +`c3b827d5fe5309dbc6ccd84a67d704aca198fe2c` and changelog blob +`a7848c8672984f7ff2fad997c7674b04d94ea04a` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs and the Route-B doctrine blob, PR #960 +open, draft, unmerged and mergeable with that head and target +`feature/worker`, the cumulative PR footprint exactly the six paths, the +review and authorization records present on #958 as the two latest comments +with no later control record, the pull_request-triggered workflows gated by +the documentation-only classifier with every heavy job of the four runs on +that head skipped, and PR #960 review-thread state that matched the stage +authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the replacement commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 06cc13685f1df5ec41da4f7958e8780a0c63ead9 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Replacement delta against the first candidate (exactly the four authorized +paths): + +```text +git diff --cached --name-only 06cc13685f1df5ec41da4f7958e8780a0c63ead9 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 06cc13685f1df5ec41da4f7958e8780a0c63ead9 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 06cc13685f1df5ec41da4f7958e8780a0c63ead9 +1 1 CHANGELOG.md +784 163 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +69 30 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +Decision-register delta (the `Last updated` header line and the ADR-0012 row +only): + +```text +git diff --cached -U0 06cc13685f1df5ec41da4f7958e8780a0c63ead9 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-14 + +diff <(git show 06cc13685f1df5ec41da4f7958e8780a0c63ead9:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p' ; git show :docs/engineering/decisions/decision-register.md | sed -n '20p' | grep -o 'whether earlier generations are sealed or an open one is ineligible for it' +Last updated: 2026-10-15 +whether earlier generations are sealed or an open one is ineligible for it +``` + +Changelog delta (the existing +`THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry only; every other +line byte-identical): + +```text +git diff --cached -U0 06cc13685f1df5ec41da4f7958e8780a0c63ead9 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 06cc13685f1df5ec41da4f7958e8780a0c63ead9:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 06cc13685f1df5ec41da4f7958e8780a0c63ead9 -- CHANGELOG.md | grep -o '\[-[^]]*-\]\|{+[^}]*+}' | cut -c1-120 +{+whether earlier generations are sealed or an open one is ineligible for it,+} +[-blocked recovery-] +{+an obligation for which no eligible successor can be selected or admitted+} +{+undispositioned,+} +[-generations;-] +{+generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting +``` + +ADR-0012 delta (twelve hunks, all inside text added by the first candidate; +header, section 12, outline, invariants 1-45 and the Amendment-8 through +Amendment-11 subsections byte-identical): + +```text +git diff --cached -U0 06cc13685f1df5ec41da4f7958e8780a0c63ead9 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -458,4 +458,11 @@ permanently retired. The same holds for an obligation refused after a +@@ -1513,3 +1520,11 @@ Because a new successor is created only together with the disposition it +@@ -1547 +1562,3 @@ the predecessor retains its target-scoped boundary until reconciled, the +@@ -1576 +1593,7 @@ scheduler and backpressure requirements of section 3.15, not by a promise of +@@ -1578,5 +1601,6 @@ compatible worker exists or external ambiguity blocks safe execution, the +@@ -2705,3 +2729,5 @@ execution until a separate approved architecture decision says otherwise. +@@ -2725,2 +2751,6 @@ execution until a separate approved architecture decision says otherwise. +@@ -3380,2 +3410,3 @@ Before shared implementation can be approved, evidence must include: +@@ -3387 +3418,2 @@ Before shared implementation can be approved, evidence must include: +@@ -3398,2 +3430,4 @@ Before shared implementation can be approved, evidence must include: +@@ -3405,3 +3439,6 @@ Before shared implementation can be approved, evidence must include: +@@ -3421,3 +3458,5 @@ Before shared implementation can be approved, evidence must include: + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +3576:Current decision state: **PROPOSED**. + +diff <(git show 06cc13685f1df5ec41da4f7958e8780a0c63ead9:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +exit 0; no output + +diff <(git show 06cc13685f1df5ec41da4f7958e8780a0c63ead9:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^46\. /p' | sed '$d') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^46\. /p' | sed '$d') +exit 0; no output (the section heading and invariants 1-45 byte-identical) + +for s in 'Coalescing-open and coalescing-sealed jobs' 'Atomic creation of a routed job' 'Mutually exclusive terminal resolution of a mismatch anomaly' 'Revision withdrawal and route retirement'; do diff <(git show 06cc13685f1df5ec41da4f7958e8780a0c63ead9:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n "/^### $s/,/^### /p") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n "/^### $s/,/^### /p") >/dev/null && echo "$s: identical"; done +Coalescing-open and coalescing-sealed jobs: identical +Atomic creation of a routed job: identical +Mutually exclusive terminal resolution of a mismatch anomaly: identical +Revision withdrawal and route retirement: identical + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'only when the earlier one is already sealed\|never counted as dispositioned\|blocked recovery stays backlogged' +0 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -c 'successor' +88 +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | wc -l +3683 +git diff --cached -U0 06cc13685f1df5ec41da4f7958e8780a0c63ead9 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^+' | grep -v '^+++' | awk 'length>81' | wc -l +0 +``` + +The twelve hunks are: the section-3.2 backlog and attention sentence; the +generation-creation sentence of the section-3.5 convergence paragraph; the +held-successor sentence of the section-3.5 effect-semantics paragraph; the +section-3.5 liveness paragraph (two hunks); the creation clause and the +closing clause of invariant 46 (two hunks); and validation cases 5, 7, 10, +12 and 16 (five hunks). No other line of the ADR changes. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED replacement candidate) c4ca99ce2e538ae607496ecf7c89108501b5fe76 +decision-register.md efab47f6928ab23df560a96e6bb2e5e2da34a216 +CHANGELOG.md e901096042559985471c49182e404409d218e891 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +c4ca99ce2e538ae607496ecf7c89108501b5fe76 +``` + +The corrected ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the replacement tree and the +replacement commit SHA depend on this report's bytes and are not self-pinned; +they are verified against the remote head after push and recorded in the +post-publication handoff. + +Required topology of the replacement commit, checked after it is created and +before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 06cc13685f1df5ec41da4f7958e8780a0c63ead9 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 06cc13685f1df5ec41da4f7958e8780a0c63ead9 HEAD +required: exit 0; no output + +git diff --name-only 06cc13685f1df5ec41da4f7958e8780a0c63ead9 HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -9471,6 +10035,23 @@ that would become false once it moves into the candidate commit, in particular any statement naming the Amendment-11 candidate as the direct parent of the commit containing this report or presenting the approval-state commit as the current stage, and reframed them. +Replacement-candidate stage: compared the staged ADR-0012 with the first +candidate's ADR blob `678541fe5904ae4ea816cafa46c2e68780b51f4e` hunk by hunk +against both findings' counterexamples, Amendment 12 A12-1 through A12-6 and +Amendments 8-11; confirmed that every changed hunk concerns the +backlog-versus-held-successor distinction, the generation-creation condition +or the five validation cases that express them, and nothing else; confirmed +that the header and section 12 (`PROPOSED`), the post-seal subsection's +two-path and eligibility paragraph, invariants 1-45, the Amendment-8 through +Amendment-11 subsections and all other architecture are byte-identical to the +first candidate; compared the staged register and changelog with the +first-candidate blobs (only the `Last updated` header and the ADR-0012 row, +and only the existing post-seal recovery entry); confirmed the protected +decision and control blobs are unchanged; and read the report for statements +that would become false once it moves into the replacement candidate, in +particular any statement naming the approval-state head as the direct parent +of the commit containing this report or presenting the first candidate as +the current stage, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -9491,10 +10072,11 @@ candidate `6e521f00b039769b28a26ab069c7e97d22de4e08` and the content-approved replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, the Amendment-10 approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` and the content-approved Amendment-11 candidate -`528c2db04af5c53b9820b946ddac2ebb936eb4cd` and the Amendment-11 approval-state -head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, are historical evidence; the -live PR #960 diff of the candidate commit exists only after its publication -and is GitHub-owned lifecycle evidence. +`528c2db04af5c53b9820b946ddac2ebb936eb4cd`, the Amendment-11 approval-state +head `21e19e81d0acf1aed57952ec00e2653771fb6f47` and the first Amendment-12 +candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`, are historical evidence; +the live PR #960 diff of the replacement-candidate commit exists only after +its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -9511,8 +10093,9 @@ approval-state authorization, the Amendment-9 candidate authorization, the Amendment-9 approval-state authorization, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate -authorization, the Amendment-11 approval-state authorization and the -Amendment-12 candidate authorization, an automatic CI failure, or any +authorization, the Amendment-11 approval-state authorization, the +Amendment-12 candidate authorization and the Amendment-12 +replacement-candidate authorization, an automatic CI failure, or any migration execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -9551,18 +10134,20 @@ direct-child four-path correction commit, the Amendment-10 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-11 candidate authorization permitted exactly one four-path candidate commit, the Amendment-11 approval-state authorization permitted -exactly one direct-child approval-state commit, and the Amendment-12 -candidate authorization permits exactly one four-path candidate commit; any -further source correction exhausts those authorizations and requires a fresh -CTO authorization (Amendment 3 C10; Amendments 6-12; +exactly one direct-child approval-state commit, the Amendment-12 candidate +authorization permitted exactly one four-path candidate commit, and the +Amendment-12 replacement-candidate authorization permits exactly one +direct-child four-path replacement commit; any further source correction +exhausts those authorizations and requires a fresh CTO authorization +(Amendment 3 C10; Amendments 6-12; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-12 candidate stage the - implementing agent performed no independent inspection or review, - candidate-content approval, PR #960 title/description reconciliation, +- Historical stage-time evidence: at the Amendment-12 replacement-candidate + stage the implementing agent performed no independent inspection or + review, candidate-content approval, PR #960 title/description reconciliation, Draft/Ready change, automated-review request, thread adjudication, exact-head review or merge (section 4.2). The PR #960 title and description describe the historical Amendment-11 source; their @@ -9619,8 +10204,10 @@ Monitoring required: NONE authorization for one direct-child approval-state commit, and the post-seal materialization defect found by the final-head review of that approval-state head only under Specification Amendment 12, its independent - review and a separate authorization for this one four-path candidate - commit. + review and a separate authorization for one four-path candidate commit, + and the two candidate-source consistency findings found by the independent + review of that candidate only under a separate authorization for this one + four-path replacement commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -9643,8 +10230,8 @@ Monitoring required: NONE correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction and approval-state - commit, the Amendment-11 candidate and approval-state commit and this - Amendment-12 candidate. + commit, the Amendment-11 candidate and approval-state commit, the + Amendment-12 candidate and this replacement candidate. ## 14. Unresolved issues @@ -9841,11 +10428,19 @@ Monitoring required: NONE Amendments 1-12 returned `APPROVED`, confirming the finding, the classification, all six provisions, the composition and the twenty-case validation matrix and recording seven non-blocking clarity observations; - and the Route-B candidate that contains this report implements the - correction in source as `PROPOSED` (section 4; section 5, decisions - 133-144). The finding is closed at specification level by Amendment 12; - whether the candidate closes it in source is decided by the independent - inspection and review of the candidate, which GitHub records. + and the first Route-B candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` + implemented the correction in source as `PROPOSED` (section 4; section 5, + decisions 133-144). An independent CRITICAL exact-candidate source review + of that candidate returned `CHANGES REQUIRED` with two HIGH blocking + consistency findings, `F-A12-CAND-01` (a committed but held successor was + described as a still-undispositioned route obligation) and `F-A12-CAND-02` + (a compatible generation was conditioned on every earlier generation + being sealed, blocking it behind an open ineligible one); the replacement + candidate that contains this report corrects both (section 4; section 5, + decisions 145-148). The finding is closed at specification level by + Amendment 12; whether the replacement candidate closes it in source is + decided by the independent review of the replacement candidate, which + GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -9859,8 +10454,9 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-12 candidate also implemented -the Amendment-11 approval-state commit and candidate, the Amendment-10 +work. The implementing agent of this Amendment-12 replacement candidate also +implemented the first Amendment-12 candidate, the Amendment-11 +approval-state commit and candidate, the Amendment-10 approval-state commit, the Amendment-10 replacement candidate and first candidate, the Amendment-9 approval-state commit and candidate, the Amendment-8 @@ -9880,10 +10476,34 @@ inspect, review or approve it. Suggested review focus: -- that the candidate changes exactly the four authorized paths from the - approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, that the - candidate ADR-0012 blob is `678541fe5904ae4ea816cafa46c2e68780b51f4e`, and that - ADR-0008, ADR-0010 and ADR-0013 keep their protected blobs; +- that the replacement delta from the first Amendment-12 candidate + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` is limited to the section-3.2 + backlog sentence, the section-3.5 effect-semantics, liveness and + convergence paragraphs, two clauses of invariant 46, validation cases 5, + 7, 10, 12 and 16, the ADR-0012 register row and header date, the existing + post-seal recovery changelog entry and this report, that the replacement + ADR-0012 blob is `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, and that ADR-0008, + ADR-0010 and ADR-0013 keep their protected blobs; +- that `F-A12-CAND-01` is closed: an obligation stays in the undispositioned + route backlog only until its disposition commits, a successor committed + with that disposition is a dispositioned job whose held execution is + job-level `WAITING`, `RECONCILIATION_REQUIRED` or blocked-successor state, + no provider effect is claimed for it, and A11-1 atomicity, target-scoped + fencing and `(event_id, route_key)` uniqueness are unchanged (section 5, + decision 145); +- that `F-A12-CAND-02` is closed: a compatible generation may be created + whenever no eligible open successor exists, including beside an open + ineligible generation, an open eligible successor is attached to rather + than bypassed, and database arbitration, stable identity, atomic creation + and the Amendment-9 order are unchanged (section 5, decision 146); +- that the twenty cases remain present one-to-one with the Amendment, with + cases 5, 7, 10, 12 and 16 expressing the corrected states, and that + invariants 1-45 and the Amendment-8 through Amendment-11 subsections are + byte-identical to the first candidate; +- that the first candidate changed exactly the four authorized paths from the + approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, that its + ADR-0012 blob was `678541fe5904ae4ea816cafa46c2e68780b51f4e`, and that + ADR-0008, ADR-0010 and ADR-0013 kept their protected blobs; - that every Amendment 12 A12-1 requirement is expressed in ADR-0012: the refused obligation owed, undispositioned and visible and never attached, counted complete or silently represented; the approved deterministic @@ -10136,7 +10756,7 @@ Suggested review focus: and this report describe the corrected version as `PROPOSED`, that the routed-job atomicity correction entry and every other historical changelog entry are byte-preserved, and that the register header `Last updated` is - `2026-10-14`; + `2026-10-15`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. @@ -11007,8 +11627,9 @@ stage: compensation remains separately fenced. 17. The post-seal materialization rule does not permit an eligible obligation to be owed forever against a permanently sealed identity: an - obligation refused after a coalescing seal remains owed and visible, is - never attached to the sealed job or dispositioned by the key collision, + obligation refused after a coalescing seal remains owed and visible until + its disposition commits, is never attached to the sealed job or + dispositioned by the key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through a different eligible coalescing-open successor or a distinct, stably identified routed successor created together with its diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 678541fe5..c4ca99ce2 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -455,10 +455,17 @@ unavailable for any revision or version with outstanding obligations, those obligations remain owed and the unavailable-materializer and route-backlog signals raise attention, whether the route is active, temporarily inactive or permanently retired. The same holds for an obligation refused after a -coalescing seal while it awaits an eligible successor, and for a successor -whose safe execution is held by target-scoped effect ambiguity (section 3.5): -the obligation stays in that backlog with its reason, age-visible and -attention-raising, and is never counted as dispositioned. +coalescing seal while no eligible successor can be selected or admitted for +it (section 3.5): until its materialized disposition commits, it stays in +that backlog with its reason, age-visible and attention-raising. Once a +successor and the obligation's disposition have committed together under +"Atomic creation of a routed job" (section 3.5), the obligation is +dispositioned and leaves the route backlog, even where that successor's +execution is then held by target-scoped effect ambiguity or by the absence +of a compatible worker; the held execution is job-level state, visible +through the job's `WAITING`, `RECONCILIATION_REQUIRED`, blocked-successor +and attention signals (sections 3.4 and 3.15), and no provider effect is +claimed for it. Outstanding obligations are interpreted against the immutable revision of the epoch that made each event eligible. After a newer revision becomes active, an @@ -1510,9 +1517,17 @@ state. Concurrent different owed obligations may attach to the same eligible open successor under the kind's coalescing contract; when that successor seals first, later obligations follow the next valid generation or path. Because a new successor is created only together with the disposition it -represents, every generation represents at least one obligation, and -materializers converge on committed successor state or serialize into a -subsequent generation only when the earlier one is already sealed. Repeated +represents, every generation represents at least one obligation. +Materializers converge on an existing eligible coalescing-open successor +whenever one exists, attaching under the approved coalescing rule rather +than bypassing it to create another generation, and create a subsequent +generation only when no eligible open successor can represent the +obligation: because every earlier generation is sealed, or because an +earlier generation that remains open is ineligible for the obligation's +route, epoch, route-contract revision or event version under the kind's +contract. An open ineligible generation therefore never blocks a compatible +generation, and the same database arbitration, stable identity and +atomic-creation rules apply to a generation created beside it. Repeated retries, worker restarts and acknowledgment loss recover from the durably committed route-disposition, job and generation state; after a creation commits but its acknowledgment is lost, the retry observes the same job and @@ -1544,7 +1559,9 @@ keys and never relax target-scoped applied-state ordering. A predecessor whose effect is ambiguous at or after `EFFECT_STARTED` cannot be bypassed by creating a successor with a different key and asserting a safe second write: the predecessor retains its target-scoped boundary until reconciled, the -successor may exist durably and wait, visibly, while safe execution is +successor - already a dispositioned job whose obligation has left the route +backlog - may exist durably and wait, visibly, in job-level `WAITING`, +`RECONCILIATION_REQUIRED` or blocked-successor state while safe execution is blocked, and the kind's approved contract holds, serializes or reconciles before any conflicting successor effect. A `CURRENT_STATE` successor reads canonical current state, acts on the complete target effect fingerprint and @@ -1573,13 +1590,20 @@ the engine never indefinitely reselects a permanently sealed job or group identity, and that collision is not itself a legitimate blocker. Continuous input, rate limits and unavailable workers remain governed by the bounded scheduler and backpressure requirements of section 3.15, not by a promise of -real-time completion. While a successor cannot presently be admitted, no +real-time completion. The two states are accounted for separately. While no +eligible successor can presently be selected or admitted for an obligation, +or no compatible materializer exists, the obligation remains undispositioned +and its reason durable, age-visible and attention-raising in the route +backlog without false success. Once a successor has committed together with +the obligation's disposition, the obligation is dispositioned and no longer +in the route backlog; if that successor's execution is then held because no compatible worker exists or external ambiguity blocks safe execution, the -owed obligation and its reason remain durable, age-visible and -attention-raising in the route backlog without false success; errors and -retries are bounded and backed off, create no unbounded generations and never -hot-loop on one conflict; recovery after the blocker clears follows the -kind's approved contract. +hold is job-level state visible through the job's `WAITING`, +`RECONCILIATION_REQUIRED`, blocked-successor and attention signals (sections +3.4 and 3.15), with no claim that an effect has executed. In both states, +errors and retries are bounded and backed off, create no unbounded +generations and never hot-loop on one conflict; recovery after the blocker +clears follows the kind's approved contract. **Unsupported contracts fail closed.** Before activation, a coalescing kind documents how late obligations select or generate safe executable @@ -2702,9 +2726,11 @@ execution until a separate approved architecture decision says otherwise. successor created under invariant 44 together with its disposition; the sealed job is never such a path, a new generation is created only when the reread shows the obligation still owed and no eligible open - successor, and an already represented obligation receives no successor on - any retry. Group or window identity is distinct from executable - generation identity wherever successive generations may be needed; a + successor - whether every earlier generation is sealed or an earlier open + generation is ineligible for the obligation - and an already represented + obligation receives no successor on any retry. Group or window identity + is distinct from executable generation identity wherever successive + generations may be needed; a successor has a unique, stable idempotency key for its intended successor effect; the predecessor's key, seal, attempts and history are unchanged; predecessor retry or reclaim is never successor creation; and a successor @@ -2722,8 +2748,12 @@ execution until a separate approved architecture decision says otherwise. redefined. Under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity - indefinitely; blocked recovery stays backlogged, age-visible and - attention-raising with bounded backoff and bounded generations. A kind + indefinitely; an obligation for which no eligible successor can be + selected or admitted stays undispositioned, backlogged, age-visible and + attention-raising, while a committed successor whose execution is held + is a dispositioned job visible through job-level waiting, reconciliation + and blocked-successor state, in both cases with bounded backoff and + bounded generations. A kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; the exceptional terminal non-materialized @@ -3377,14 +3407,16 @@ Before shared implementation can be approved, evidence must include: - deterministic concurrency tests of different owed obligations `E2` and `E3` racing after `J0` seals, proving they converge on an eligible `J1` while it is open or, when it seals first, follow correctly ordered later - generations, with no obligation dropped or represented by a job whose input - omits it; + generations, and that where `J1` is open but ineligible for `E3`, `E3` + proceeds to a compatible generation while `J1` remains open, with no + obligation dropped or represented by a job whose input omits it; - deterministic concurrency tests of an attachment to successor `J1` racing its first claim/start in both orders, proving one durable order and that claim/start-first leaves the refused obligation owed and safely eligible for a later successor, never stale-attached; - successive-seal tests in which `J0`, `J1` and `J2` are needed within one - approved group or window, proving distinct stable identities, no + approved group or window, including a generation created while an earlier + ineligible generation is still open, proving distinct stable identities, no permanently stuck eligible obligation and no false route completion; - precommit-crash tests proving that a crash after allocating or starting creation of `J1` but before its disposition and input commit leaves no @@ -3395,16 +3427,21 @@ Before shared implementation can be approved, evidence must include: state and creates no `J2`, second disposition or effect; - uniqueness and rollback race tests proving that generation arbitration, original-group job-key collisions and `(event_id, route_key)` uniqueness - races abort or recover safely under one database ordering, with no write - skew, deadlock, phantom job, missing input or duplicate route; + races - including a concurrent attachment to an open eligible successor + and the creation of a compatible generation beside an open ineligible one - + abort or recover safely under one database ordering, with no write skew, + deadlock, phantom job, missing input or duplicate route; - predecessor tests proving that a pre-write retry, lease expiry and reclaim, WAITING resume and reconciliation of `J0` never reopen it and never silently absorb an owed `E2`, and that successor semantics alter no predecessor attempt; - ambiguous-predecessor tests in which `J0` holds an unresolved - `EFFECT_STARTED` outcome, proving `J1` may be tracked durably but performs - no unsafe overlapping or duplicate effect before the required target-scoped - fencing and reconciliation evidence; + `EFFECT_STARTED` outcome, proving `J1` may be tracked durably as a + dispositioned job - its `(E2, route_key)` obligation absent from the route + backlog and its held execution visible through job-level waiting, + reconciliation or blocked-successor state - but performs no unsafe + overlapping or duplicate effect before the required target-scoped fencing + and reconciliation evidence; - `CURRENT_STATE` successor tests proving `J1` acts on the current canonical complete effect fingerprint, no-ops safely where that fingerprint is already applied, and never reapplies an identical provider effect merely @@ -3418,9 +3455,11 @@ Before shared implementation can be approved, evidence must include: creates no successor or disposition, and enrollment-first commits its allowed terminal resolution and routed facts atomically; - blocked-admission tests with an unavailable or incompatible materializer, - proving accurate backlog count and oldest age, bounded retry and backoff, - no fabricated success, no uncontrolled successor growth, and resumed safe - progress when an authorized compatible path returns; + proving accurate backlog count and oldest age that count only obligations + without a committed disposition, with committed but held successors + reported through job-level signals rather than the route backlog, bounded + retry and backoff, no fabricated success, no uncontrolled successor growth, + and resumed safe progress when an authorized compatible path returns; - escape-path negative tests proving that no staff exception is synthesized from an ordinary sealed-key collision, that direct-job or replay identities cannot evade a route obligation, and that no new route key or epoch is diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index c3b827d5f..efab47f69 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-14 +Last updated: 2026-10-15 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-14 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for blocked recovery, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From a913232a6623936855d635885d513d4c778c4b25 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 15 Oct 2026 22:24:21 +0100 Subject: [PATCH 45/48] THOTH-ASYNC-01-ADR-01: record Amendment-12 architecture approval (#958) --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 991 ++++++++++++++---- ...ed-asynchronous-event-and-job-execution.md | 21 +- .../decisions/decision-register.md | 2 +- 4 files changed, 824 insertions(+), 192 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e90109604..5ca616041 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while an obligation for which no eligible successor can be selected or admitted stays undispositioned, backlogged, age-visible and attention-raising with bounded backoff and bounded generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state rather than the route backlog; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`: the corrected content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** + - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while an obligation for which no eligible successor can be selected or admitted stays undispositioned, backlogged, age-visible and attention-raising with bounded backoff and bounded generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state rather than the route backlog; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-15.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions and withdrawal/selection ordering beneath one stable logical `route_key` and closes a race between many-route-to-one-job coalescing and job start: every logical job of a kind that permits coalescing is coalescing-open only before its first successful execution claim/start commits and is permanently coalescing-sealed by that transition, which is the same durable transition as the existing "not-yet-started" boundary; the seal belongs to the logical job, and retry, lease reclaim, WAITING/resume, RECONCILIATION_REQUIRED/reconciliation, cancellation/recovery and later claims never reopen it; a coalescing attachment and that first claim/start must share one database-atomic serialization, lock or compare-and-set boundary giving one durable linearized order, the attachment write itself proving the job is still open, so that an application-level pre-check is insufficient. An attachment that linearizes first commits a disposition that the started job genuinely represents under the kind's coalescing contract and is never rewritten or detached; a claim/start that linearizes first seals the job before execution, the later attachment is refused, no disposition is committed, the `(event_id, route_key)` slot stays unused, the obligation stays owed and visible and the existing deterministic idempotency-conflict error path applies; a rolled-back first claim does not seal; input/effect completeness stays mechanism-neutral (`CURRENT_STATE` through current-state/effect-fingerprint semantics, other kinds through a durable retained input set or equivalent); every attachment path this ADR already permits uses the same boundary; the seal is earlier than and distinct from `EFFECT_STARTED`; and the concrete SQL, lock or state encoding is left to the implementation specification. Invariant 42 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-08.** The earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal/selection ordering without this coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index e3e31754f..e852e4297 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -31,17 +31,18 @@ automated review of that Amendment-10 approval-state head, the Route-B approval-state stage of that Amendment-11 candidate, and the Route-B candidate stage of the post-seal materialization correction specified by Specification Amendment 12 after the configured final-head automated review -of that Amendment-11 approval-state head, and the four-path replacement +of that Amendment-11 approval-state head, the four-path replacement candidate of that candidate after its independent exact-candidate source -review. It records +review, and the Route-B approval-state stage of that replacement candidate. +It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-12 Route-B replacement -candidate (the `F-A12-CAND-01` and `F-A12-CAND-02` correction) as the current -stage and every earlier candidate, approval-state, recovery, +Route-B correction stages, with the Amendment-12 Route-B approval-state +commit as the current stage and every earlier candidate, approval-state, +recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the @@ -63,12 +64,14 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-12 Route-B replacement candidate commit that -contains this report, an ordinary single-parent additive commit whose direct -parent is the historical first Amendment-12 candidate +Head commit: the Amendment-12 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-12 replacement candidate +`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`; its SHA is deliberately not +self-pinned here (section 3). That replacement candidate is the direct child +of the historical first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`, inspected `CHANGES REQUIRED / -F-A12-CAND-01, F-A12-CAND-02`; its SHA is deliberately not self-pinned here -(section 3). That first candidate is the direct child of the historical +F-A12-CAND-01, F-A12-CAND-02`, which is the direct child of the historical Amendment-11 approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, which is the direct child of the content-approved Amendment-11 candidate @@ -103,7 +106,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -120,9 +123,9 @@ implementing agent; transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, -Amendment-11 approval-state, Amendment-12 candidate and Amendment-12 -replacement-candidate stages - Claude (Fable 5.1), bounded implementing -agent. Earlier rounds are recorded in Appendix A. +Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 +replacement-candidate and Amendment-12 approval-state stages - Claude (Fable +5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -306,18 +309,27 @@ decision-register blob c3b827d5fe5309dbc6ccd84a67d704aca198fe2c CHANGELOG blob a7848c8672984f7ff2fad997c7674b04d94ea04a implementation-report blob b5656eed3d030fc9997623f101fff2aee3f03776 (superseded by the replacement candidate) -Amendment-12 Route-B replacement candidate (the commit that contains this report): -ADR-0012 blob c4ca99ce2e538ae607496ecf7c89108501b5fe76 (PROPOSED; F-A12-CAND-01 and F-A12-CAND-02 consistency correction of 678541fe5904ae4ea816cafa46c2e68780b51f4e) +content-approved Amendment-12 replacement candidate / approval-state parent: +9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 +tree a14206077a1cc9cb44b65f96788e79ee79406d65 +ADR-0012 blob c4ca99ce2e538ae607496ecf7c89108501b5fe76 (PROPOSED; content-approved; F-A12-CAND-01 and F-A12-CAND-02 consistency correction of 678541fe5904ae4ea816cafa46c2e68780b51f4e; independent exact-replacement-candidate source review APPROVED) decision-register blob efab47f6928ab23df560a96e6bb2e5e2da34a216 +implementation-report blob e3e31754faa0a48ea1e573fefb65975bcb01b4d2 CHANGELOG blob e901096042559985471c49182e404409d218e891 +Amendment-12 Route-B approval-state commit (the commit that contains this report): +ADR-0012 blob 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e (APPROVED; Javi, CTO; 2026-10-15; architecture-equivalent to c4ca99ce2e538ae607496ecf7c89108501b5fe76) +decision-register blob 6033bd0743a68d47b9415c5cdaf0e03129400a03 +CHANGELOG blob 5ca616041f46aa2a8a380518a1c803430cad7713 + feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction, the Amendment-10 approval-state commit, the Amendment-11 candidate, the Amendment-11 approval-state commit, the -Amendment-12 candidate and its replacement candidate (unchanged): +Amendment-12 candidate, its replacement candidate and the Amendment-12 +approval-state commit (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -506,7 +518,19 @@ Approved specification: Amendment-12 candidate, `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`: #958 comment `6169631899`; - Amendment-12 replacement-candidate implementation authorization: #958 - comment `6172224137`. + comment `6172224137`; +- independent CRITICAL exact-replacement-candidate source review of the + Amendment-12 replacement candidate, `APPROVED`: recorded on #958 on + 2026-10-15; its GitHub comment identifier is GitHub-owned lifecycle + evidence under `ADR-0005` and Route-B doctrine and is not copied here; +- CTO exact candidate-content approval of the Amendment-12 replacement + candidate: recorded on #958 on 2026-10-15 by Javi, CTO for candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; +- Amendment-12 Route-B approval-state implementation authorization: #958 + comment `6202232188`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -598,13 +622,19 @@ materialized through a different eligible coalescing-open successor or a distinct, stably identified routed successor generation created together with its disposition, was independently reviewed and returned `CHANGES REQUIRED` on two candidate-source consistency findings, -`F-A12-CAND-01` and `F-A12-CAND-02`. The commit that contains this report is -the authorized replacement candidate: one ordinary single-parent commit on -that first candidate that changes exactly the same four paths, separates the -undispositioned route backlog from the job-level state of a committed but -held successor, and lets a compatible generation be created beside an open -but ineligible one. The Amendment-12 architecture is otherwise unchanged and -the version remains `PROPOSED`. +`F-A12-CAND-01` and `F-A12-CAND-02`. The authorized replacement candidate, +one ordinary single-parent commit on that first candidate that changed +exactly the same four paths, separates the undispositioned route backlog from +the job-level state of a committed but held successor and lets a compatible +generation be created beside an open but ineligible one; it received +independent CRITICAL exact-replacement-candidate source review (`APPROVED`, +both findings satisfied in source) and CTO exact candidate-content approval +on 2026-10-15. The commit that contains this report is the one bounded +Route-B approval-state commit that records that approval as durable decision +state: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +`Approval date: 2026-10-15`, the ADR-0012 register row and the post-seal +recovery correction changelog entry moved to the approved state, and this +report's approval-state evidence. No architecture content changes. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -1172,8 +1202,9 @@ the version remains `PROPOSED`. earlier exact versions. That stage authorized no PR-state or PR-metadata change. - **Independent exact-candidate source review and the F-A12-CAND-01 / - F-A12-CAND-02 replacement candidate (the commit that contains this - report).** Authorized by #958 comment `6172224137` after an independent + F-A12-CAND-02 replacement candidate (historical content-approved + replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`).** + Authorized by #958 comment `6172224137` after an independent CRITICAL exact-candidate source review of the published first candidate returned `CHANGES REQUIRED` (#958 comment `6169631899`) with two HIGH blocking candidate-source consistency findings. `F-A12-CAND-01`: section @@ -1195,19 +1226,49 @@ the version remains `PROPOSED`. delta, the six-path footprint, the protected blobs, the `PROPOSED` state and the twenty validation additions, and assessed both findings as reconcilable within approved Amendment 12 without an Amendment 13. The - commit that contains this report is one ordinary single-parent commit - directly on the first candidate that changes exactly the same four paths: - in ADR-0012 the section-3.2 backlog sentence, the section-3.5 - effect-semantics and liveness paragraphs, the generation-creation sentence - of the convergence paragraph, two clauses of invariant 46 and validation - cases 5, 7, 10, 12 and 16 (section 4; section 5, decisions 145-148); the - ADR-0012 register row and the register header date; the existing post-seal - recovery changelog entry; and this report. ADR-0012 remains `PROPOSED`; - the first candidate is historical `CHANGES REQUIRED` evidence and was - never content-approved. This stage authorizes no PR-state or PR-metadata - change; the PR #960 title and description, which describe the historical - Amendment-11 source, are GitHub-owned and their reconciliation is a - separate control action. + replacement candidate was one ordinary single-parent commit directly on + the first candidate that changed exactly the same four paths: in ADR-0012 + the section-3.2 backlog sentence, the section-3.5 effect-semantics and + liveness paragraphs, the generation-creation sentence of the convergence + paragraph, two clauses of invariant 46 and validation cases 5, 7, 10, 12 + and 16 (section 4; section 5, decisions 145-148); the ADR-0012 register + row and the register header date; the existing post-seal recovery + changelog entry; and the replacement-stage report. ADR-0012 remained + `PROPOSED`; the first candidate is historical `CHANGES REQUIRED` evidence + and was never content-approved. An independent CRITICAL + exact-replacement-candidate source review of the published replacement + candidate against the actual GitHub source returned `APPROVED` with no + blocking candidate finding, both findings satisfied in source and one + non-blocking later-verification observation (a successor held before its + own attempt must occupy an already lawful job-lifecycle or + blocked-successor state rather than a premature `WAITING` or + `RECONCILIATION_REQUIRED` transition), and Javi, CTO approved its exact + architecture content - head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, + ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76` - on 2026-10-15 in a + durable record on #958. That stage authorized no PR-state or PR-metadata + change. +- **Amendment-12 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6202232188`. One ordinary + single-parent commit directly on the content-approved replacement candidate + records that approval as durable decision state, changing only the four + authorized paths: ADR-0012 `PROPOSED -> APPROVED` with + `Approved by: Javi, CTO` and `Approval date: 2026-10-15` and the leading + section-12 approval paragraphs, which identify the approved content as the + content represented by candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; the ADR-0012 register row; the + `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` changelog entry; and + this report. No architecture content changes (section 9, "Amendment-12 + approval-state validation"). The content approval is recorded here through + owning issue #958, approver Javi, CTO, approval date 2026-10-15, the + candidate head and the candidate ADR-0012 blob; its GitHub comment + identifier is not copied into repository source, as the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` and the approval-state authorization + itself require (section 5, decision 152). PR #960 was already a draft; this + stage authorizes no PR-state or PR-metadata change; the PR #960 title and + description, which describe the historical Amendment-11 source, are + GitHub-owned and their reconciliation is a separate control action. Out-of-scope changes made: NONE @@ -1221,10 +1282,17 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-15) +of the content-approved Amendment-12 replacement candidate blob c4ca99ce2e538ae607496ecf7c89108501b5fe76 +at candidate head 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9; +no architecture change + +historical Amendment-12 replacement candidate at 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9: F-A12-CAND-01 / F-A12-CAND-02 consistency correction of first-candidate blob 678541fe5904ae4ea816cafa46c2e68780b51f4e (route backlog versus committed but held successor; compatible generation beside an open ineligible one; Specification Amendment 12; Route-B replacement-candidate stage; -candidate status PROPOSED) +candidate status PROPOSED; independent CRITICAL exact-replacement-candidate source +review APPROVED; CTO exact candidate-content approval on 2026-10-15) historical first Amendment-12 candidate at 06cc13685f1df5ec41da4f7958e8780a0c63ead9: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob df8017c57d74eaecf13041e231ef97625260fab1 @@ -1546,36 +1614,60 @@ received independent CRITICAL exact-candidate source review, which returned `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`; it was never content-approved. The replacement candidate creates a new exact ADR-0012 version, so that review outcome is bound to the first candidate's ADR blob -`678541fe5904ae4ea816cafa46c2e68780b51f4e` and head, and the replacement -candidate requires its own fresh independent exact-candidate review before -any content approval. +`678541fe5904ae4ea816cafa46c2e68780b51f4e` and head. The replacement +candidate received its own independent CRITICAL exact-replacement-candidate +source review and CTO exact candidate-content approval on 2026-10-15, and the +approval-state commit that contains this report records it. For this version, +exact-final-blob approval, independent exact-head review, CTO merge +authorization, guarded merge, programme-local reliance and later migration, +deployment, activation and observation are distinct controlled lifecycle +categories; GitHub, through #958 and PR #960, is authoritative for which of +them is pending, satisfied or blocked for any head. ## 3. Commits -Amendment-12 Route-B replacement candidate commit, the F-A12-CAND-01 and -F-A12-CAND-02 consistency correction (the commit that contains this report): +Amendment-12 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: align post-seal backlog and generation wording (#958)` - - direct parent: the historical first Amendment-12 candidate - `06cc13685f1df5ec41da4f7958e8780a0c63ead9` - - changes only the four authorized paths (section 4); the corrected - ADR-0012 blob is `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, the corrected - decision-register blob `efab47f6928ab23df560a96e6bb2e5e2da34a216` and the - corrected changelog blob `e901096042559985471c49182e404409d218e891` + `THOTH-ASYNC-01-ADR-01: record Amendment-12 architecture approval (#958)` + - direct parent: the content-approved Amendment-12 replacement candidate + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` + - changes only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, the approval-state + decision-register blob `6033bd0743a68d47b9415c5cdaf0e03129400a03` and the + approval-state changelog blob `5ca616041f46aa2a8a380518a1c803430cad7713` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the corrected ADR-0012, +here because this report is part of that tree; the approval-state ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its first-candidate blob (section 9). -The commit identities are verified against the remote task-branch head after -push and recorded in the post-publication handoff; once published, the PR -#960 head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. -The authorized topology is a non-force fast-forward of the task branch from -the first candidate to the replacement commit; no merge commit, rebase, -squash, amend, force update or second replacement commit is authorized, and -no further source commit exists implicitly after it. +recorded above; every other file keeps its replacement-candidate blob +(section 9). The commit identities are verified against the remote +task-branch head after push and recorded in the post-publication handoff; +once published, the PR #960 head and head tree are GitHub-owned lifecycle +evidence under `ADR-0005`. The authorized topology is a non-force +fast-forward of the task branch from the content-approved replacement +candidate to the approval-state commit; no merge commit, rebase, squash, +amend, force update or second approval-state commit is authorized, and no +further source commit exists implicitly after it. + +Amendment-12 Route-B replacement candidate commit, the F-A12-CAND-01 and +F-A12-CAND-02 consistency correction (content-approved; historical): + +- `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` - + `THOTH-ASYNC-01-ADR-01: align post-seal backlog and generation wording (#958)` + - ordinary single-parent additive commit; direct parent: the historical + first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` + - tree `a14206077a1cc9cb44b65f96788e79ee79406d65` + - changed only the four authorized paths (section 4); the corrected + ADR-0012 blob is `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, independently + reviewed `APPROVED` and content-approved by Javi, CTO on 2026-10-15 + - published by a non-force fast-forward of `feature/async/adr-0012` from the + first candidate; the remote head, tree, parent and ADR-0012, + decision-register, changelog and report blobs matched the locally + validated object Amendment-12 Route-B first candidate commit (historical; reviewed `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`; never content-approved): @@ -1871,11 +1963,23 @@ first-parent order, newest first: ## 4. Files changed -F-A12-CAND-01 / F-A12-CAND-02 replacement-candidate write budget for the -commit that contains this report (authorization #958 comment `6172224137`; -the same four existing paths as the Amendment-12 candidate budget); exactly -four existing paths, no new file, no deletion, and every other path -byte-identical to the historical first Amendment-12 candidate +Amendment-12 Route-B approval-state write budget for the commit that contains +this report (authorization #958 comment `6202232188`), identical to the +replacement-candidate write budget; exactly four existing paths, no new file, +no deletion, and every other path byte-identical to the content-approved +replacement candidate +`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical F-A12-CAND-01 / F-A12-CAND-02 replacement-candidate write budget +(authorization #958 comment `6172224137`; the same four existing paths as the +Amendment-12 candidate budget); exactly four existing paths, no new file, no +deletion, and every other path byte-identical to the historical first +Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`: - `CHANGELOG.md` @@ -2040,12 +2144,92 @@ recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 -approval-state, Amendment-12 candidate and Amendment-12 replacement-candidate -stages; all four correction paths already existed. +approval-state, Amendment-12 candidate, Amendment-12 replacement-candidate +and Amendment-12 approval-state stages; all four correction paths already +existed. + +Amendment-12 approval-state changes in the commit that contains this report, +relative to the content-approved replacement candidate +`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate-content approval of 2026-10-15. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-15`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + "Current decision state: **APPROVED**.", the same approver and date, and + a paragraph stating that the CTO approved this exact corrected content on + 2026-10-15 as the architecture content represented by candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that any ADR-0013 programme-local reliance on this exact version is a + separate state not effective until its own conditions are satisfied. + Every other line, including all architecture, the Amendment-12 post-seal + subsection as corrected by the replacement candidate, invariants 1-46, + the validation requirements with all twenty Amendment-12 cases, the + historical-approval paragraph, the authority condition and the + architecture-versus-implementation separation, is byte-identical to the + replacement candidate (section 9). Approval-state blob: + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row - `PROPOSED` becomes `APPROVED`, + `Pending` becomes `Satisfied`, "its corrected content awaits CTO + approval" becomes "Javi, CTO approved its exact corrected content on + 2026-10-15", bound to candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76` as historical content-approval + evidence, with the statement that content approval is architecture-content + approval only and that exact-final-blob approval, independent final + source-head review, merge authorization and ADR-0013 reliance are separate + gates recorded in GitHub. The header `Last updated: 2026-10-15` already + carries the date of this row change and is unchanged (section 5, decision + 153). Every other row is unchanged. Approval-state blob: + `6033bd0743a68d47b9415c5cdaf0e03129400a03`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the post-seal recovery correction + entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry only - + "propose" becomes "record a CTO-approved", "`PROPOSED`: the corrected + content is not approved" becomes "`APPROVED`: Javi, CTO approved the + exact corrected content on 2026-10-15", and "proposal only" becomes + "decision only". Its architectural description, its statements that the + earlier entries and the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, + 2026-10-08, 2026-10-09 and 2026-10-12 approvals apply only to historical + exact versions, and its no-runtime-authorization boundary are unchanged, + and every other entry is byte-identical. Approval-state blob: + `5ca616041f46aa2a8a380518a1c803430cad7713`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-12 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit + the current stage; records the independent exact-replacement-candidate + source review, the CTO candidate-content approval and the approval-state + authorization; records the approval-state validation and no-drift proof + (section 9); and reframes statements that would otherwise describe the + replacement candidate as the commit containing this report, recording the + replacement candidate's identities as historical facts. All still-true + earlier evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the content-approved replacement candidate through the +approval-state commit: every path other than the four authorized paths, +including ADR-0008 `622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51` and ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`. -Replacement-candidate changes in the commit that contains this report, -relative to the first Amendment-12 candidate -`06cc13685f1df5ec41da4f7958e8780a0c63ead9` (exactly the four authorized paths): +Historical replacement-candidate changes in the content-approved replacement +candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, relative to the first +Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` (exactly the +four authorized paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: correct `F-A12-CAND-01` (a committed but held successor described @@ -2111,15 +2295,15 @@ relative to the first Amendment-12 candidate - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: carry the same distinction in Appendix B item 17 and record the replacement-candidate stage truthfully. - - behavioural effect: documentation only. Appendix B item 17 keeps the + - behavioural effect: documentation only. Appendix B item 17 kept the obligation owed only until its disposition commits; the first candidate - becomes historical `CHANGES REQUIRED` evidence with its now-known SHA, - tree and report blob recorded in section 1; the replacement becomes the - current stage with the first candidate as its direct parent; the review - and authorization records are added to section 2; the replacement's - validation is recorded in section 9 (section 5, decisions 145-148). The - first candidate's section-4 description is unchanged apart from lineage - reframing. All still-true earlier evidence is preserved. + became historical `CHANGES REQUIRED` evidence with its now-known SHA, + tree and report blob recorded in section 1; the replacement became the + then-current stage with the first candidate as its direct parent; the + review and authorization records were added to section 2; the + replacement's validation was recorded in section 9 (section 5, decisions + 145-148). The first candidate's section-4 description was unchanged apart + from lineage reframing. All still-true earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the first candidate through the replacement candidate: @@ -3387,7 +3571,11 @@ Files deleted, moved or renamed: NONE PASS -The replacement candidate differs from the first Amendment-12 candidate +The Amendment-12 approval-state commit differs from the content-approved +replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` in exactly the +four authorized paths and from refreshed `feature/worker` in exactly the six +cumulative PR paths (section 9, "Amendment-12 approval-state validation"). +The replacement candidate differed from the first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Replacement-candidate validation"). The Amendment-12 candidate @@ -3479,8 +3667,9 @@ stage (#958 comment `6046895895`), the Amendment-9 approval-state stage `6083303288`), the Amendment-11 candidate stage (#958 comment `6125285446`), the Amendment-11 approval-state stage (#958 comment `6128858897`), the Amendment-12 candidate stage (#958 comment -`6151955721`) and the Amendment-12 replacement-candidate stage (#958 comment -`6172224137`). This matrix +`6151955721`), the Amendment-12 replacement-candidate stage (#958 comment +`6172224137`) and the Amendment-12 approval-state stage (#958 comment +`6202232188`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -4005,8 +4194,8 @@ candidate: agent; an independent reviewer reviewed the published candidate and returned `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02` (section 2) -Amendment-12 replacement-candidate stage - done up to and including the -creation of the commit that contains this report: +Amendment-12 replacement-candidate stage - historical, completed before the +Amendment-12 approval-state commit: - repository inspection: USED (read-only verification of the exact refs, including `develop` and `feature/worker` at the same base, PR #960 open, @@ -4033,18 +4222,56 @@ creation of the commit that contains this report: existing session worktree, verified clean and detached at the first candidate and touching no other worktree, branch or ref, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent replacement-candidate commit that contains - this report) +- commit: USED (the single-parent replacement-candidate commit + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`) +- push: USED (one non-force fast-forward of `feature/async/adr-0012` from + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` to the replacement candidate; the + remote head, tree, parent and ADR-0012, decision-register, changelog and + report blobs were verified; no automated review ran on the draft + replacement head) +- independent exact-replacement-candidate source review: NOT USED by the + implementing agent; an independent reviewer reviewed the published + replacement candidate and returned `APPROVED` (section 2) + +Amendment-12 approval-state stage - done up to and including the creation of +the commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base, #957 and #958 + open, PR #960 open, draft, unmerged and mergeable with head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and target `feature/worker`, + the replacement candidate's tree, single parent and ADR-0012, report, + decision-register and changelog blobs, the protected ADR-0008, ADR-0010 + and ADR-0013 blobs and the Route-B doctrine blob, the six-path cumulative + footprint, the absence of any ADR-0012 commit reachable from `develop`, + `master` or `feature/worker` (Route-B eligibility), the presence of the + exact-replacement-candidate review, content-approval and approval-state + authorization records on #958 as the three latest comments with no later + control record, the pull_request-triggered workflow set and its + documentation-only classification gate, the job-level outcomes of the four + workflow runs on the replacement candidate, and, as stage-time evidence, + the PR #960 review-thread state in GitHub; all matched the authorization; + current review-thread state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, branch creation: NOT USED + (not authorized; the implementing agent's own existing session worktree, + verified clean and detached at the content-approved replacement candidate + and touching no other worktree, branch or ref, was used for construction, + and the existing task branch is the fast-forward target) +- commit: USED (the single-parent approval-state commit that contains this + report) -Authorized post-commit actions for the replacement-candidate commit - -GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: +Authorized post-commit actions for the approval-state commit - GitHub-owned +lifecycle evidence under `ADR-0005`, not self-recorded here: - the non-force push that fast-forwards `feature/async/adr-0012` from - `06cc13685f1df5ec41da4f7958e8780a0c63ead9` to the replacement-candidate commit; + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` to the approval-state commit; - post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, changed path set, - cumulative footprint and PR #960 state, and that no review thread was - replied to, resolved or dismissed; + ADR-0012, decision-register, changelog and report blobs, ADR approval + state, changed path set, cumulative footprint and PR #960 state, and that + no review thread was replied to, resolved or dismissed; - the automatic PR CI that the push causes, and read-only inspection of any automated review that runs on the draft head. @@ -4052,24 +4279,22 @@ These actions necessarily follow the creation of the commit that contains this report. Their outcomes are recorded in the post-publication handoff and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-12 -replacement-candidate stage, each a separate controlled lifecycle category -where applicable +Not authorized for the implementing agent at the Amendment-12 approval-state +stage, each a separate controlled lifecycle category where applicable (historical stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): -- any edit to a path other than the four authorized correction paths: NOT USED - (not authorized) -- any ADR-0012 change outside the passages named by the two findings and the - candidate `PROPOSED` state: NOT USED (not authorized) -- independent exact-candidate inspection or exact-head review: NOT USED (never - on the implementing agent's authority) -- CTO candidate-content approval, approval-state commit, fresh `APPROVED` - status, approver or approval date, exact-final-blob approval: NOT USED (not +- any edit to a path other than the four authorized paths: NOT USED (not authorized) +- any ADR-0012 change outside the approval-state representation, including + any architecture change: NOT USED (not authorized) +- independent inspection or exact-head review: NOT USED (never on the + implementing agent's authority) +- exact-final-blob approval or confirmation: NOT USED (not authorized; never + on the implementing agent's authority) - PR #960 title or description update: NOT USED (not authorized at this stage) - Draft -> Ready: NOT USED (not authorized) -- any second replacement commit, amend, rebase, squash, force push or +- any second approval-state commit, amend, rebase, squash, force push or history rewrite: NOT USED (not authorized) - issue/comment mutation: NOT USED (not authorized) - review-thread resolution/dismissal/reply: NOT USED (not authorized) @@ -4109,19 +4334,20 @@ approval-state authorizations, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the Amendment-11 approval-state authorization, the Amendment-12 candidate -authorization and the Amendment-12 replacement-candidate authorization, -configured Codex/app review is not expected from a push to a draft PR and is -not relied upon at the Amendment-6/7 candidate, approval-state, -control-record recovery, transient-state correction, factual-clarification, -Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, -Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, -Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 -approval-state, Amendment-12 candidate or Amendment-12 replacement-candidate -stage; configured Codex/app review runs on separately authorized Ready -transitions, which are GitHub-owned lifecycle events, and any automated -review that nevertheless runs on a draft head is inspected read-only. The -actual job outcomes for the replacement-candidate commit exist only after -its publication; they +authorization, the Amendment-12 replacement-candidate authorization and the +Amendment-12 approval-state authorization, configured Codex/app review is +not expected from a push to a draft PR and is not relied upon at the +Amendment-6/7 candidate, approval-state, control-record recovery, +transient-state correction, factual-clarification, Amendment-8 candidate, +Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 +approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 +approval-state, Amendment-11 candidate, Amendment-11 approval-state, +Amendment-12 candidate, Amendment-12 replacement-candidate or Amendment-12 +approval-state stage; configured Codex/app review runs on separately +authorized Ready transitions, which are GitHub-owned lifecycle events, and +any automated review that nevertheless runs on a draft head is inspected +read-only. The actual job outcomes for the approval-state commit exist only +after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the post-publication handoff and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -4132,9 +4358,9 @@ this report or read back from GitHub at the transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, -Amendment-11 approval-state, Amendment-12 candidate or Amendment-12 -replacement-candidate stage, followed the same documentation-only -classification, with no manual dispatch or rerun: +Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 +replacement-candidate or Amendment-12 approval-state stage, followed the same +documentation-only classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -4158,6 +4384,7 @@ e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelo 528c2db04af5c53b9820b946ddac2ebb936eb4cd: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-11 candidate) 21e19e81d0acf1aed57952ec00e2653771fb6f47: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-11 approval-state head) 06cc13685f1df5ec41da4f7958e8780a0c63ead9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-12 candidate) +9db7f7760f22ed2f6e7826b323f5d09c032ed4f9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-12 replacement candidate) ``` Manually initiated external actions (anything the implementing agent @@ -5432,8 +5659,8 @@ inspection: Amendment-11 source; that stage was not authorized to change them, and their reconciliation was reported to control as a separate action. -F-A12-CAND-01 / F-A12-CAND-02 replacement candidate (the commit that contains -this report): +F-A12-CAND-01 / F-A12-CAND-02 replacement candidate (historical +content-approved replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`): 145. `F-A12-CAND-01` is corrected by separating two states that the first candidate conflated. Before: section 3.2 said that a successor whose safe @@ -5504,18 +5731,100 @@ this report): date on which the replacement changes the ADR-0012 row (decisions 25, 57, 78, 85, 109, 121 and 141 pattern); only the ADR-0012 row changes otherwise. -148. This report does not self-pin the replacement commit's SHA, its tree or - its own final blob; because the report now lives in the replacement - commit, statements that described the first candidate as the commit - containing this report are reframed as history, and the first - candidate's identities, now the replacement parent, are recorded in - section 1 as historical facts. Construction used the implementing agent's - own existing clean session worktree, detached at the first candidate - `06cc13685f1df5ec41da4f7958e8780a0c63ead9` before editing, touching no - other worktree, branch, ref, stash or uncommitted change. The PR #960 - title and description describe the historical Amendment-11 source; this - stage is not authorized to change them, and their reconciliation is - reported to control as a separate action. +148. The replacement-stage report did not self-pin the replacement commit's + SHA, its tree or its own final blob; because the report then lived in the + replacement commit, statements that described the first candidate as the + commit containing the report were reframed as history, and the first + candidate's identities, the replacement parent, were recorded in section + 1 as historical facts; section 1 now records the replacement candidate's + identities as historical facts because that head is the Amendment-12 + approval-state commit's parent. Construction used the implementing + agent's own existing clean session worktree, detached at the first + candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` before editing, + touching no other worktree, branch, ref, stash or uncommitted change. The + PR #960 title and description described the historical Amendment-11 + source; that stage was not authorized to change them, and their + reconciliation was reported to control as a separate action. + +Amendment-12 approval-state stage (the commit that contains this report): + +149. The approval-state header uses the same field order as the earlier + approved ADR-0012 versions (`Status`, `Date`, `Approved by`, + `Approval date`, `Decision owner`), and section 12 repeats the approver + and date; the original decision date `2026-09-29` is preserved. +150. The approval date carried by the ADR is the date of the CTO exact + candidate-content approval record for the replacement candidate, + 2026-10-15, as Route B and the approval-state authorization require; it + is neither back-dated nor anticipated, it is not replaced by a commit or + push date, and the historical Amendment-11 approval date 2026-10-12 is + not reused. +151. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by "Current decision state: + **APPROVED**.", the approver/date and one paragraph stating that the CTO + approved this exact corrected content on 2026-10-15 as the architecture + content represented by candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, that this version differs + from that candidate only in its approval-state representation, that the + approval does not by itself make the ADR repository-authoritative and + that ADR-0013 programme-local reliance on this exact version is separate + and not effective until its own conditions are satisfied (decisions 83, + 107 and 127 pattern). The historical-approval, material-correction, + architecture-only, authority-condition, partial-supersession and + amendment paragraphs were already true in the approved state (decision + 143) and are unchanged. +152. The content approval is recorded only through owning issue #958, approver + Javi, CTO, approval date 2026-10-15, candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`. Its GitHub comment identifier + is not copied into repository source: the repository-authoritative Route-B + rule in `docs/engineering/decisions/README.md` forbids it, the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + applied that rule to this task (decisions 36-37, 64, 84, 108 and 128), + and the approval-state authorization for this stage requires the same; + the independent exact-replacement-candidate review receipt is likewise + recorded by outcome and date only, and its non-blocking + later-verification observation is recorded substantively in section 2. +153. The decision-register header `Last updated: 2026-10-15` already carries + the date on which the approval-state commit changes the ADR-0012 row, so + it is unchanged, as the approval-state authorization permits (decisions + 25, 78, 97 and 129 pattern); only the ADR-0012 row changes. The row binds + the content approval to the candidate head and blob as historical + content-approval evidence and states, in the durable form that + `docs/engineering/AGENTS.md` section 1.1 requires, that exact-final-blob + approval, independent final source-head review, merge authorization and + ADR-0013 reliance are separate gates recorded in GitHub; the existing + authority-condition sentence is unchanged. +154. Only the `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` changelog + entry changes, following the earlier approval-state wording pattern + (decisions 66, 86, 110 and 130: "propose" to "record a CTO-approved", the + `PROPOSED` sentence to the `APPROVED` sentence naming the approver and + date, and "proposal only" to "decision only"); every other entry, + including the historical approval entries, is byte-identical. +155. Architecture no-drift is proven by the exact normalized comparison in + section 9 ("Amendment-12 approval-state validation"), not by assertion: + outside the header approval fields and the section-12 opening block, the + approval-state ADR is byte-identical to the content-approved replacement + candidate blob, so every A12-1 through A12-6 requirement, the + `F-A12-CAND-01` and `F-A12-CAND-02` corrections, invariants 1-46 and the + complete validation matrix with all twenty Amendment-12 cases are + unchanged. +156. This report does not self-pin the approval-state commit's SHA, its tree + or its own final blob; it records only non-self-referential evidence and + the approval-state ADR-0012, decision-register and changelog blobs, which + do not depend on its bytes. Because the report now lives in the + approval-state commit, statements that described the replacement + candidate as the commit containing this report are reframed as history, + and the replacement candidate's identities, now the approval-state + parent, are recorded in section 1 as historical facts. The approval-state + ADR blob is recorded as a result; it is not represented as + exact-final-blob approved. Construction used the implementing agent's own + existing clean session worktree, detached at the content-approved + replacement candidate before editing, touching no other worktree, branch, + ref, stash or uncommitted change. The PR #960 title and description + describe the historical Amendment-11 source; this stage is not authorized + to change them, and their reconciliation is reported to control as a + separate action. List any deviation from the specification requiring authorization: @@ -5644,8 +5953,13 @@ List any deviation from the specification requiring authorization: its separate authorization exactly; no fifth path, new file, deletion, ADR change outside the passages named by the two findings and the candidate state, second commit, amend, rebase, squash, force push or history rewrite - was needed or performed before the commit that contains this report was - created. + was needed or performed before the replacement commit was created; the + independent review raised no finding against it. +- Amendment-12 approval-state stage: NONE. The approval-state transformation + followed its separate authorization exactly; no fifth path, new file, + deletion, architecture change, second commit, amend, rebase, squash, force + push or history rewrite was needed or performed before the commit that + contains this report was created. ## 6. Database and migration effects @@ -5733,8 +6047,10 @@ validation" records the validation of the historical approval-state commit `21e19e81d0acf1aed57952ec00e2653771fb6f47`. "Amendment-12 candidate validation" records the validation of the historical first candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`. "Replacement-candidate -validation" at the end of this section records the validation of the commit -that contains this report. +validation" records the validation of the historical content-approved +replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`. "Amendment-12 +approval-state validation" at the end of this section records the validation +of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -9765,15 +10081,18 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro c4ca99ce2e538ae607496ecf7c89108501b5fe76 ``` -The corrected ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the replacement tree and the -replacement commit SHA depend on this report's bytes and are not self-pinned; -they are verified against the remote head after push and recorded in the -post-publication handoff. +The corrected ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the replacement-stage report's bytes, so +they were recorded above. That report's own final blob, the replacement tree +and the replacement commit SHA depended on its bytes and were not self-pinned +in it; they were verified against the remote head after push and recorded in +the post-publication handoff, and section 1 now records them as historical +facts: commit `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, tree +`a14206077a1cc9cb44b65f96788e79ee79406d65`, report blob +`e3e31754faa0a48ea1e573fefb65975bcb01b4d2`. -Required topology of the replacement commit, checked after it is created and -before any push, with results recorded in the post-publication handoff: +Required topology of the replacement commit, checked after it was created and +before push; the published replacement candidate satisfied every requirement: ```text git rev-list --parents -n 1 HEAD @@ -9792,6 +10111,268 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-12 approval-state validation + +Environment: the implementing agent's own existing local Git worktree of +`thoth-pub/thoth`, verified clean (no tracked or untracked change) and then +detached at the content-approved Amendment-12 replacement candidate +`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` (tree +`a14206077a1cc9cb44b65f96788e79ee79406d65`) before editing, touching no other +worktree, branch or ref. Immediately before construction, read-only GitHub +inspection showed #957 and #958 open, `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, no ADR-0012 commit reachable from +`develop`, `master` or `feature/worker` (Route-B eligibility), +`feature/async/adr-0012` at +`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` with single parent +`06cc13685f1df5ec41da4f7958e8780a0c63ead9` and no descendant, ADR-0012 blob +`c4ca99ce2e538ae607496ecf7c89108501b5fe76`, report blob +`e3e31754faa0a48ea1e573fefb65975bcb01b4d2`, decision-register blob +`efab47f6928ab23df560a96e6bb2e5e2da34a216` and changelog blob +`e901096042559985471c49182e404409d218e891` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs and the Route-B doctrine blob, PR #960 +open, draft, unmerged and mergeable with that head and target +`feature/worker`, the cumulative PR footprint exactly the six paths, the +exact-replacement-candidate review, content-approval and approval-state +authorization records present on #958 as the three latest comments with no +later control record, the pull_request-triggered workflows gated by the +documentation-only classifier with every heavy job of the four runs on that +head skipped, and PR #960 review-thread state that matched the stage +authorization (stage-time evidence; current review-thread state remains +GitHub-owned). ADR-0012, the decision register and the changelog were edited +first and their deterministic blobs recorded; then only this report was +edited; all four were staged, and every check below ran against that exact +final staged index before the approval-state commit was created from it; the +report's numeric line counts below were filled in and the complete check set +rerun with identical results. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved replacement candidate +(exactly the four authorized paths): + +```text +git diff --cached --name-only 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-status 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 +1 1 CHANGELOG.md +806 185 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +16 5 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths): + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md +``` + +ADR-0012 approval-state delta (four hunks: the header status line, the two +inserted header approval lines, the section-12 decision-state line and the +section-12 approval paragraphs; no other hunk): + +```text +git diff --cached -U0 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -4,0 +5,2 @@ Date: 2026-09-29 +@@ -3576 +3578 @@ shared-engine tests. +@@ -3578,3 +3580,12 @@ Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: APPROVED +5:Approved by: Javi, CTO +6:Approval date: 2026-10-15 +3578:Current decision state: **APPROVED**. +3580:Approved by: Javi, CTO +3581:Approval date: 2026-10-15 +``` + +Architecture no-drift proof. The normalization `N` drops the header +`Status`, `Approved by` and `Approval date` lines before the first `---` +separator and the section-12 lines from the heading up to, but not +including, the line beginning "This version is a material architectural +correction"; it keeps every other byte. The content-approved replacement +candidate blob and the staged approval-state ADR normalize to identical +content, so every A12-1 through A12-6 requirement, the `F-A12-CAND-01` and +`F-A12-CAND-02` corrections, invariants 1-46 and the complete validation +matrix with all twenty Amendment-12 cases are byte-identical: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p c4ca99ce2e538ae607496ecf7c89108501b5fe76 | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p c4ca99ce2e538ae607496ecf7c89108501b5fe76 | awk "$N" | shasum -a 256 +93543008eb2000aa16c5b456fd81d31455b88533208a1396d21fceecf57ab6e3 - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +93543008eb2000aa16c5b456fd81d31455b88533208a1396d21fceecf57ab6e3 - + +diff <(git show 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^## 8\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^## 8\. /p') +exit 0; no output (invariants 1-46 byte-identical) + +diff <(git show 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 11. Validation requirements/,/^## 12\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 11. Validation requirements/,/^## 12\. /p') +exit 0; no output (section 11 byte-identical) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^- deterministic post-seal baseline tests/,/^- non-coalesced alternative tests/p' | grep -c '^- ' +20 +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display), so that the complete raw delta is visible: + +```text +replacement candidate c4ca99ce2e538ae607496ecf7c89108501b5fe76: +Status: PROPOSED +Current decision state: **PROPOSED**. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. + +approval-state ADR: +Status: APPROVED +Approved by: Javi, CTO +Approval date: 2026-10-15 +Current decision state: **APPROVED**. +Approved by: Javi, CTO +Approval date: 2026-10-15 +The CTO approved this exact corrected content on 2026-10-15 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and +candidate ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; this +version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. +``` + +Decision-register delta (the ADR-0012 row only; the `Last updated: +2026-10-15` header and every other line byte-identical): + +```text +git diff --cached -U0 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-15 + +diff <(git show 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-15 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and +``` + +Changelog delta (the existing +`THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry only; every other +line byte-identical): + +```text +git diff --cached -U0 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 -- CHANGELOG.md | grep -o '\[-[^]]*-\]\|{+[^}]*+}' +[-propose-] +{+record+} +{+CTO-approved+} +[-`PROPOSED`:-] +{+`APPROVED`: Javi, CTO approved+} +{+exact+} +[-is not approved.**-] +{+on 2026-10-15.**+} +[-proposal-] +{+decision+} +``` + +Content-approval identifier (the command is shown with the identifier replaced +by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e +decision-register.md 6033bd0743a68d47b9415c5cdaf0e03129400a03 +CHANGELOG.md 5ca616041f46aa2a8a380518a1c803430cad7713 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +CTRL-ADR-AMEND-01 implementation report 7bf7425f0527dbaa72e718df5264e399035a148b +CTRL-ADR-PR-FIRST-01 implementation report d86cf215866750979eafea040426c51374d5155c +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1c98ce0cca1eeb4bf46bbfc526a3826c534f637e +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above; the ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` is the exact +final blob presented for CTO exact-final-blob approval at this stage and is +not yet so approved. This report's own final blob, the approval-state tree +and the approval-state commit SHA depend on this report's bytes and are not +self-pinned; they are verified against the remote head after push and +recorded in the post-publication handoff. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results recorded in the post-publication handoff: + +```text +git rev-list --parents -n 1 HEAD +required: 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 HEAD +required: exit 0; no output + +git diff --name-only 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 HEAD +required: exactly the four authorized paths + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -10052,6 +10633,19 @@ that would become false once it moves into the replacement candidate, in particular any statement naming the approval-state head as the direct parent of the commit containing this report or presenting the first candidate as the current stage, and reframed them. +Amendment-12 approval-state stage: compared the staged ADR-0012 with the +content-approved replacement candidate blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76` +by the exact normalized comparison in section 9 (no difference), and read +the raw delta line by line; compared the staged register and changelog with +the replacement-candidate blobs (only the ADR-0012 row, and only the +post-seal recovery correction entry, change); confirmed the protected +decision and control blobs are unchanged; confirmed by fixed-string search +that the content-approval comment identifier does not occur in this report; +and read the report for statements that would become false once it moves +into the approval-state commit, in particular any statement naming the first +Amendment-12 candidate as the direct parent of the commit containing this +report or describing the replacement candidate as still the current +`PROPOSED` candidate, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -10073,10 +10667,11 @@ replacement candidate `e0fa590e06a00d08df883bc4ddeae40b7660fcb8`, the Amendment- approval-state head `576a860be47aa1ebe39968f4d3297b26ee1d17df` and the content-approved Amendment-11 candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, the Amendment-11 approval-state -head `21e19e81d0acf1aed57952ec00e2653771fb6f47` and the first Amendment-12 -candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`, are historical evidence; -the live PR #960 diff of the replacement-candidate commit exists only after -its publication and is GitHub-owned lifecycle evidence. +head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, the first Amendment-12 +candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` and the content-approved +replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, are historical +evidence; the live PR #960 diff of the approval-state commit exists only +after its publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -10094,9 +10689,10 @@ Amendment-9 approval-state authorization, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the Amendment-11 approval-state authorization, the -Amendment-12 candidate authorization and the Amendment-12 -replacement-candidate authorization, an automatic CI failure, or any -migration execution or Docker publication, is a HOLD condition. +Amendment-12 candidate authorization, the Amendment-12 +replacement-candidate authorization and the Amendment-12 approval-state +authorization, an automatic CI failure, or any migration execution or Docker +publication, is a HOLD condition. ## 12. Rollout and rollback @@ -10135,19 +10731,21 @@ authorization permitted exactly one direct-child approval-state commit, the Amendment-11 candidate authorization permitted exactly one four-path candidate commit, the Amendment-11 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-12 candidate -authorization permitted exactly one four-path candidate commit, and the -Amendment-12 replacement-candidate authorization permits exactly one -direct-child four-path replacement commit; any further source correction -exhausts those authorizations and requires a fresh CTO authorization -(Amendment 3 C10; Amendments 6-12; +authorization permitted exactly one four-path candidate commit, the +Amendment-12 replacement-candidate authorization permitted exactly one +direct-child four-path replacement commit, and the Amendment-12 +approval-state authorization permits exactly one direct-child approval-state +commit; any further source correction exhausts those authorizations and +requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-12; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work -- Historical stage-time evidence: at the Amendment-12 replacement-candidate - stage the implementing agent performed no independent inspection or - review, candidate-content approval, PR #960 title/description reconciliation, +- Historical stage-time evidence: at the Amendment-12 approval-state stage + the implementing agent performed no independent inspection or review, + exact-final-blob approval or confirmation, PR #960 title/description + reconciliation, Draft/Ready change, automated-review request, thread adjudication, exact-head review or merge (section 4.2). The PR #960 title and description describe the historical Amendment-11 source; their @@ -10205,9 +10803,10 @@ Monitoring required: NONE post-seal materialization defect found by the final-head review of that approval-state head only under Specification Amendment 12, its independent review and a separate authorization for one four-path candidate commit, - and the two candidate-source consistency findings found by the independent - review of that candidate only under a separate authorization for this one - four-path replacement commit. + the two candidate-source consistency findings found by the independent + review of that candidate only under a separate authorization for one + four-path replacement commit, and its approval state only under a separate + authorization for this one direct-child approval-state commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -10231,7 +10830,8 @@ Monitoring required: NONE approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction and approval-state commit, the Amendment-11 candidate and approval-state commit, the - Amendment-12 candidate and this replacement candidate. + Amendment-12 candidate, its replacement candidate and this Amendment-12 + approval-state commit. ## 14. Unresolved issues @@ -10436,11 +11036,15 @@ Monitoring required: NONE described as a still-undispositioned route obligation) and `F-A12-CAND-02` (a compatible generation was conditioned on every earlier generation being sealed, blocking it behind an open ineligible one); the replacement - candidate that contains this report corrects both (section 4; section 5, - decisions 145-148). The finding is closed at specification level by - Amendment 12; whether the replacement candidate closes it in source is - decided by the independent review of the replacement candidate, which - GitHub records. + candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` corrected both in source + (section 4; section 5, decisions 145-148); an independent CRITICAL + exact-replacement-candidate source review of that candidate found both + findings satisfied in source and no blocking candidate finding; Javi, CTO + approved its exact architecture content on 2026-10-15; and the + approval-state commit that contains this report records that approval. The + finding is closed at specification level by Amendment 12 and, per that + review, in the replacement candidate's source; its thread adjudication + belongs to the review controls that GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -10454,9 +11058,9 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-12 replacement candidate also -implemented the first Amendment-12 candidate, the Amendment-11 -approval-state commit and candidate, the Amendment-10 +work. The implementing agent of this Amendment-12 approval-state commit also +implemented the Amendment-12 replacement candidate and first candidate, the +Amendment-11 approval-state commit and candidate, the Amendment-10 approval-state commit, the Amendment-10 replacement candidate and first candidate, the Amendment-9 approval-state commit and candidate, the Amendment-8 @@ -10476,8 +11080,23 @@ inspect, review or approve it. Suggested review focus: +- that the approval-state delta from the content-approved Amendment-12 + replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` is limited to + the ADR header approval fields, the two leading section-12 paragraphs, the + ADR-0012 register row and the post-seal recovery correction changelog + entry, that the normalized comparison in section 9 shows no architecture + drift from candidate blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, and that + the approval-state ADR-0012 blob is `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`; +- that the content approval is recorded only through owning issue #958, + approver Javi, CTO, approval date 2026-10-15, candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, that the approval date matches + the CTO candidate-content approval record and is not the historical + 2026-10-12 date, and that no content-approval, exact-final-blob approval, + review or merge identifier is copied into this report (section 5, decision + 152); - that the replacement delta from the first Amendment-12 candidate - `06cc13685f1df5ec41da4f7958e8780a0c63ead9` is limited to the section-3.2 + `06cc13685f1df5ec41da4f7958e8780a0c63ead9` was limited to the section-3.2 backlog sentence, the section-3.5 effect-semantics, liveness and convergence paragraphs, two clauses of invariant 46, validation cases 5, 7, 10, 12 and 16, the ADR-0012 register row and header date, the existing @@ -10747,16 +11366,18 @@ Suggested review focus: fan-out, newly emittable versions and non-binary writers; - that the new invariants 35-40 and the reconciled invariants 3-6 and 8 agree with section 3.2; -- that the header and section 12 carry `PROPOSED` with no current approver or - approval date, bind the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, - 2026-10-08, 2026-10-09 and 2026-10-12 approvals to the earlier exact - versions and keep the repository authority condition and the ADR-0013 - reliance distinction; -- that the register row, the new post-seal recovery correction changelog entry - and this report describe the corrected version as `PROPOSED`, that the - routed-job atomicity correction entry and every other historical changelog - entry are byte-preserved, and that the register header `Last updated` is - `2026-10-15`; +- that the header and section 12 carry `APPROVED` with Javi, CTO and + 2026-10-15, identify the approved content by candidate head + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`, bind the 2026-09-30, + 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09 and 2026-10-12 + approvals to the earlier exact versions and keep the repository authority + condition and the ADR-0013 reliance distinction; +- that the register row, the post-seal recovery correction changelog entry + and this report describe the corrected version as `APPROVED` on + 2026-10-15, that the routed-job atomicity correction entry and every other + historical changelog entry are byte-preserved, and that the register header + `Last updated` is `2026-10-15`; - that ADR-0008, ADR-0010, ADR-0013, the decision README and the other protected control files are byte-identical to the approval-state heads, the clarification head, the recovery head and the content-approved candidates. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index c4ca99ce2..1c98ce0cc 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-15 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -3573,11 +3575,20 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. +Current decision state: **APPROVED**. -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Approved by: Javi, CTO +Approval date: 2026-10-15 + +The CTO approved this exact corrected content on 2026-10-15 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and +candidate ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; this +version differs from that candidate only in this approval-state +representation. That approval does not by itself make this ADR +repository-authoritative: the authority condition below still applies. Any +ADR-0013 programme-local reliance on this exact version is a separate state +that is not effective until its own conditions are satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index efab47f69..6033bd074 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-15 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and its corrected content awaits CTO approval under the repository decision process. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-15 under the repository decision process, as the architecture content represented by Route-B candidate head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only: exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 11:09:04 +0000 Subject: [PATCH 46/48] THOTH-ASYNC-01-ADR-01: propose Amendment-13 mismatch-admission correction (#958) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012Q88PP3G4JMJoxUjDKVpNj --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 1290 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 1032 ++++++++++--- .../decisions/decision-register.md | 4 +- 4 files changed, 1963 insertions(+), 364 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca616041..0833e6d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed + - `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version defines an immutable original-epoch classification of each event for each route and requires every first `(event_id, route_key)` disposition writer - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition - to prove that classification and its own eligibility and authorization inside the transaction that would consume the slot, so that an in-epoch contract mismatch receives a disposition only through compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly. It makes detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also covers the anomaly's first creation when no anomaly row exists: enrollment first creates the anomaly born-terminal `ENROLLED` atomically with its disposition and routed facts, later detection converges without reopening it, divergence resolves only an already committed `OPEN` anomaly and never creates one, and contradictory durable state fails closed as a control HOLD for separately authorized incident recovery. Invariant 47 and thirty active validation cases (`A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`; `A13-T25` retired) are added and invariants 5, 8, 35 and 45 are conformed; tables, locks and SQL remain implementation-specification choices. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`; this content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry, and the CTO approval dated 2026-10-15 that it records, apply only to the historical earlier corrected exact version of ADR-0012 (blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`) that does not contain this correction, and each earlier entry likewise applies only to its own historical exact version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, API or schema change, migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized or follows from it.** - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while an obligation for which no eligible successor can be selected or admitted stays undispositioned, backlogged, age-visible and attention-raising with bounded backoff and bounded generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state rather than the route backlog; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-15.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index e852e4297..7ea63c99d 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -33,15 +33,19 @@ candidate stage of the post-seal materialization correction specified by Specification Amendment 12 after the configured final-head automated review of that Amendment-11 approval-state head, the four-path replacement candidate of that candidate after its independent exact-candidate source -review, and the Route-B approval-state stage of that replacement candidate. +review, the Route-B approval-state stage of that replacement candidate, and +the Route-B candidate stage of the canonical mismatch-anomaly decision order +and universal first route-disposition admission correction specified by +Specification Amendment 13 with its Revisions 1 and 2 after the configured +final-head automated review of that Amendment-12 approval-state head. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-12 Route-B approval-state -commit as the current stage and every earlier candidate, approval-state, +Route-B correction stages, with the Amendment-13 Route-B candidate commit +as the current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and @@ -64,11 +68,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-12 Route-B approval-state commit that contains -this report, an ordinary single-parent additive commit whose direct parent is +Head commit: the Amendment-13 Route-B candidate commit that contains this +report, an ordinary single-parent additive commit whose direct parent is the +historical Amendment-12 approval-state head +`a913232a6623936855d635885d513d4c778c4b25`; its SHA is deliberately not +self-pinned here (section 3). That approval-state head is the direct child of the content-approved Amendment-12 replacement candidate -`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`; its SHA is deliberately not -self-pinned here (section 3). That replacement candidate is the direct child +`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, which is the direct child of the historical first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`, inspected `CHANGES REQUIRED / F-A12-CAND-01, F-A12-CAND-02`, which is the direct child of the historical @@ -106,7 +112,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -125,7 +131,11 @@ Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 replacement-candidate and Amendment-12 approval-state stages - Claude (Fable -5.1), bounded implementing agent. Earlier rounds are recorded in Appendix A. +5.1), bounded implementing agent; Amendment-13 candidate stage - Claude, +bounded implementing agent in a fresh session with no conversation state from +any earlier stage (its exact model identity is disclosed to engineering +control in the stage return rather than committed here; section 5, decision +172). Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -317,10 +327,18 @@ decision-register blob efab47f6928ab23df560a96e6bb2e5e2da34a216 implementation-report blob e3e31754faa0a48ea1e573fefb65975bcb01b4d2 CHANGELOG blob e901096042559985471c49182e404409d218e891 -Amendment-12 Route-B approval-state commit (the commit that contains this report): -ADR-0012 blob 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e (APPROVED; Javi, CTO; 2026-10-15; architecture-equivalent to c4ca99ce2e538ae607496ecf7c89108501b5fe76) +historical Amendment-12 approval-state head / Amendment-13 candidate parent: +a913232a6623936855d635885d513d4c778c4b25 +tree 0fbd637066b1c4a643f62b6da48ec7f796c03358 +ADR-0012 blob 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e (APPROVED; Javi, CTO; 2026-10-15; architecture-equivalent to c4ca99ce2e538ae607496ecf7c89108501b5fe76; independent approval-state inspection and CTO exact-final-blob approval recorded on #958 on 2026-10-16; the exact prior approved blob that the Amendment-13 correction corrects) decision-register blob 6033bd0743a68d47b9415c5cdaf0e03129400a03 CHANGELOG blob 5ca616041f46aa2a8a380518a1c803430cad7713 +implementation-report blob e852e4297d02dec126a85200eb7cd065fbaf8e25 (superseded by the Amendment-13 candidate) + +Amendment-13 Route-B candidate commit (the commit that contains this report): +ADR-0012 blob 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 (PROPOSED; material architectural correction of 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e) +decision-register blob 5611ff3f4f3b1981f30295182efff1f882a24331 +CHANGELOG blob 0833e6d4efcb7451739ea90bbbadb33ff6def37b feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual @@ -328,8 +346,8 @@ clarification, the Amendment-8 candidate and approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction, the Amendment-10 approval-state commit, the Amendment-11 candidate, the Amendment-11 approval-state commit, the -Amendment-12 candidate, its replacement candidate and the Amendment-12 -approval-state commit (unchanged): +Amendment-12 candidate, its replacement candidate, the Amendment-12 +approval-state commit and the Amendment-13 candidate (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -530,7 +548,35 @@ Approved specification: is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and is not copied here; - Amendment-12 Route-B approval-state implementation authorization: #958 - comment `6202232188`. + comment `6202232188`; +- independent CRITICAL exact approval-state source inspection of the + Amendment-12 approval-state head and CTO exact-final-blob approval of + ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` at head + `a913232a6623936855d635885d513d4c778c4b25`: both recorded on #958 on + 2026-10-16; their GitHub comment identifiers are GitHub-owned lifecycle + evidence under `ADR-0005` and Route-B doctrine and are not copied here; +- control adjudication of the configured final-head review of that head, + which accepts finding `R-960-A12-DET-ENR-01` as valid, HIGH-P1 and blocking + and states the required correction scope: #958 comment `6221684005`; +- Specification Amendment 13 / canonical mismatch detection and + historical-enrollment ordering (original proposal): #958 comment + `6221879811`; +- Specification Amendment 13 Revision 1, which accepts the independent + specification-review findings `F-A13-01` and `F-A13-02`: #958 comment + `6237231089`; +- Specification Amendment 13 Revision 2, which accepts the independent + Revision-1 specification-review finding `F-A13R1-01`: #958 comment + `6238676946`; +- fresh independent CRITICAL specification approval of the cumulative + Amendment 13 + Revision 1 + Revision 2 specification, with non-blocking + observations NB-01 to NB-10: #958 comment `6266733934`; +- CTO specification approval of the effective Amendment 13 + Revision 1 + + Revision 2, its `MATERIAL ARCHITECTURAL CORRECTION` classification, the + pre-repository-authority eligibility determination, the Route-B selection + and the approved interpretation of NB-01 to NB-10: #958 comment + `6273281867`; +- Amendment-13 Route-B candidate implementation authorization: #958 comment + `6288171434`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -629,12 +675,25 @@ the job-level state of a committed but held successor and lets a compatible generation be created beside an open but ineligible one; it received independent CRITICAL exact-replacement-candidate source review (`APPROVED`, both findings satisfied in source) and CTO exact candidate-content approval -on 2026-10-15. The commit that contains this report is the one bounded -Route-B approval-state commit that records that approval as durable decision -state: ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and -`Approval date: 2026-10-15`, the ADR-0012 register row and the post-seal -recovery correction changelog entry moved to the approved state, and this -report's approval-state evidence. No architecture content changes. +on 2026-10-15. The one bounded Route-B approval-state commit then recorded +that approval as durable decision state (ADR-0012 `PROPOSED -> APPROVED` with +`Approved by: Javi, CTO` and `Approval date: 2026-10-15`, the ADR-0012 +register row and the post-seal recovery correction changelog entry moved to +the approved state), and, after independent approval-state inspection, the +CTO recorded exact-final-blob approval of that ADR blob. The configured +final-head automated review of that approval-state head exposed a further +architecture defect, `R-960-A12-DET-ENR-01`, specified by Specification +Amendment 13 as revised by its Revisions 1 and 2. The commit that contains +this report is its `PROPOSED` Route-B candidate: one ordinary single-parent +commit on the approval-state head that changes exactly the four authorized +correction paths, returns ADR-0012 to `Status: PROPOSED` with no current +approver or approval date, requires every operation that can create a first +`(event_id, route_key)` disposition to prove immutable original-epoch +classification and its own eligibility and authorization inside the +transaction that would consume that slot, and requires detection, first +historical enrollment and divergence of a mismatch anomaly to share one +database-enforced canonical decision order that also covers the anomaly's +first creation when no anomaly row exists. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -1247,28 +1306,77 @@ report's approval-state evidence. No architecture content changes. ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76` - on 2026-10-15 in a durable record on #958. That stage authorized no PR-state or PR-metadata change. -- **Amendment-12 approval-state stage (the commit that contains this - report).** Authorized by #958 comment `6202232188`. One ordinary - single-parent commit directly on the content-approved replacement candidate - records that approval as durable decision state, changing only the four - authorized paths: ADR-0012 `PROPOSED -> APPROVED` with - `Approved by: Javi, CTO` and `Approval date: 2026-10-15` and the leading - section-12 approval paragraphs, which identify the approved content as the - content represented by candidate head - `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob +- **Amendment-12 approval-state stage (historical approval-state head + `a913232a6623936855d635885d513d4c778c4b25`).** Authorized by #958 comment + `6202232188`. One ordinary single-parent commit directly on the + content-approved replacement candidate recorded that approval as durable + decision state, changing only the four authorized paths: ADR-0012 + `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and + `Approval date: 2026-10-15` and the leading section-12 approval paragraphs, + which identify the approved content as the content represented by candidate + head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; the ADR-0012 register row; the `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` changelog entry; and - this report. No architecture content changes (section 9, "Amendment-12 - approval-state validation"). The content approval is recorded here through - owning issue #958, approver Javi, CTO, approval date 2026-10-15, the - candidate head and the candidate ADR-0012 blob; its GitHub comment - identifier is not copied into repository source, as the + the approval-state report. No architecture content changed (section 9, + "Amendment-12 approval-state validation"). The content approval was + recorded through owning issue #958, approver Javi, CTO, approval date + 2026-10-15, the candidate head and the candidate ADR-0012 blob; its GitHub + comment identifier is not copied into repository source, as the repository-authoritative Route-B rule in `docs/engineering/decisions/README.md` and the approval-state authorization - itself require (section 5, decision 152). PR #960 was already a draft; this - stage authorizes no PR-state or PR-metadata change; the PR #960 title and - description, which describe the historical Amendment-11 source, are - GitHub-owned and their reconciliation is a separate control action. + itself required (section 5, decision 152). PR #960 was already a draft; + that stage authorized no PR-state or PR-metadata change. +- **Exact-final-blob approval, final-head review of the Amendment-12 + approval-state head, Specification Amendment 13 with Revisions 1 and 2, and + the Amendment-13 Route-B candidate (the commit that contains this + report).** Authorized by #958 comment `6288171434`. After the + approval-state head was published, an independent CRITICAL exact + approval-state source inspection returned `APPROVED` and the CTO recorded + exact-final-blob approval of ADR-0012 blob + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` at head + `a913232a6623936855d635885d513d4c778c4b25`, both on 2026-10-16; control then + separately marked PR #960 Ready, which triggered the configured final-head + automated review of that head on 2026-10-16, and PR #960 was returned to + Draft before any candidate source was staged. That review raised one new + finding, recorded by control as `R-960-A12-DET-ENR-01` (HIGH-P1, valid and + blocking; #958 comment `6221684005`): the ADR arbitrated enrollment against + divergence of an already existing anomaly but did not order mismatch + detection against the first authorized historical enrollment while no + anomaly row existed, so an enrollment could commit a disposition and job + before detection and a later detector could leave an open anomaly that + neither truthful terminal outcome could resolve. The original Amendment 13 + proposal (#958 comment `6221879811`) was independently reviewed `CHANGES + REQUIRED` with `F-A13-01` (divergence arriving before detection was + under-specified) and `F-A13-02` (its speculative legacy repair or adoption, + A13-3.4 and test A13-T25, invented an undefined anomaly state and effect + policy). Revision 1 (#958 comment `6237231089`) restricted divergence to an + already committed `OPEN` anomaly, deleted A13-3.4, retired A13-T25 and + added A13-T27 to A13-T30. A fresh independent review of the effective + Revision 1 returned `CHANGES REQUIRED` with `F-A13R1-01` (the exceptional + terminal non-materialized disposition was not required to prove + original-epoch eligibility in the transaction that consumes the route + slot). Revision 2 (#958 comment `6238676946`) added the universal first + route-disposition admission guard, conforming invariants 5 and 8 and test + A13-T31. A fresh independent CRITICAL review of the cumulative + specification returned `APPROVED (SPECIFICATION ONLY)` with non-blocking + observations NB-01 to NB-10 (#958 comment `6266733934`), and the CTO + approved the effective specification, classified it as a + `MATERIAL ARCHITECTURAL CORRECTION`, accepted a read-only ADR-number history + audit as establishing eligibility for the material correction before + repository authority, selected Route B and approved the interpretation of + NB-01 to NB-10 (#958 comment `6273281867`). The candidate is one ordinary + single-parent commit directly on the approval-state head that changes + exactly the four authorized correction paths: ADR-0012 returns to + `Status: PROPOSED` with no current approver or approval date and implements + the effective Amendment 13 (section 4; section 5, decisions 157-172); the + decision-register ADR-0012 row, one new changelog entry and this report + describe the corrected version as `PROPOSED`; and the 2026-09-30, + 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09, 2026-10-12 and + 2026-10-15 CTO approvals remain historical evidence bound to the earlier + exact versions. PR #960 was already a draft; this stage authorizes no + PR-state or PR-metadata change; the PR #960 title and description, which + describe the historical Amendment-12 source, are GitHub-owned and their + reconciliation is a separate control action. Out-of-scope changes made: NONE @@ -1282,10 +1390,16 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e +(Specification Amendment 13 with Revisions 1 and 2; Route-B candidate stage; +candidate status PROPOSED; CTO classification and Route-B selection recorded on #958) + +historical Amendment-12 approval-state at a913232a6623936855d635885d513d4c778c4b25: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-15) of the content-approved Amendment-12 replacement candidate blob c4ca99ce2e538ae607496ecf7c89108501b5fe76 at candidate head 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9; -no architecture change +no architecture change; independent approval-state inspection and CTO exact-final-blob +approval of 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e recorded on #958 on 2026-10-16 historical Amendment-12 replacement candidate at 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9: F-A12-CAND-01 / F-A12-CAND-02 consistency correction of first-candidate blob 678541fe5904ae4ea816cafa46c2e68780b51f4e @@ -1398,6 +1512,16 @@ ADR-0012 version ever being reachable from `develop`. Git objects removed from every reachable ref cannot be proven from current history; no evidence indicates that such a removed repository-authoritative ADR-0012 version existed. +At the Amendment-13 candidate stage `develop` and `feature/worker` were still +`b44303c214498baf76c9d9a0a7cab374377f9cbe` and `master` was still +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`; the same four literal commands +were run again against both commits with identical results (section 9, +"Amendment-13 candidate validation"). The CTO accepted a read-only +ADR-number history audit as establishing that ADR-0012 is eligible for the +material correction before repository authority, as an ADR-number-level +determination, and recorded that a contrary authoritative historical +artifact would require immediate HOLD (#958 comment `6273281867`). + ### 2.2 Stale controls Under the repository material-correction rules, the material correction makes @@ -1616,42 +1740,88 @@ content-approved. The replacement candidate creates a new exact ADR-0012 version, so that review outcome is bound to the first candidate's ADR blob `678541fe5904ae4ea816cafa46c2e68780b51f4e` and head. The replacement candidate received its own independent CRITICAL exact-replacement-candidate -source review and CTO exact candidate-content approval on 2026-10-15, and the -approval-state commit that contains this report records it. For this version, -exact-final-blob approval, independent exact-head review, CTO merge -authorization, guarded merge, programme-local reliance and later migration, -deployment, activation and observation are distinct controlled lifecycle -categories; GitHub, through #958 and PR #960, is authoritative for which of -them is pending, satisfied or blocked for any head. +source review and CTO exact candidate-content approval on 2026-10-15; the +historical approval-state commit `a913232a6623936855d635885d513d4c778c4b25` +recorded that approval in ADR-0012 blob +`1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, and that blob received independent +approval-state inspection and CTO exact-final-blob approval at that head on +2026-10-16. + +The Amendment-13 candidate creates a new exact ADR-0012 version and so makes +stale, for that version, every record bound to the post-seal version as well: + +- the CTO content approval of replacement candidate + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` / ADR blob + `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; +- the 2026-10-15 approval state carried by ADR blob + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, its independent approval-state + inspection and its exact-final-blob approval at head + `a913232a6623936855d635885d513d4c778c4b25`; +- every automated or independent review bound to an earlier source head, + including the final-head automated review of the approval-state head; +- the approval-state-head decision-register row and the + `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` changelog entry that + record the post-seal version as `APPROVED`; the changelog entry is + preserved and remains true only of that historical version, while the + register row now records the candidate as `PROPOSED`. + +They remain historical evidence bound to their exact versions and heads. For +the candidate version, independent exact-candidate source review, CTO exact +candidate-content approval, the approval-state commit, exact-final-blob +approval, independent exact-head review, configured review and thread +adjudication, CTO merge authorization, guarded merge, programme-local +reliance and later migration, deployment, activation and observation are +distinct controlled lifecycle categories; GitHub, through #958 and PR #960, is +authoritative for which of them is pending, satisfied or blocked for any +head. ## 3. Commits -Amendment-12 Route-B approval-state commit (the commit that contains this -report): +Amendment-13 Route-B candidate commit (the commit that contains this report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: record Amendment-12 architecture approval (#958)` - - direct parent: the content-approved Amendment-12 replacement candidate - `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` - - changes only the four authorized paths, and only to record the approval - state (section 4); the approval-state ADR-0012 blob is - `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, the approval-state - decision-register blob `6033bd0743a68d47b9415c5cdaf0e03129400a03` and the - approval-state changelog blob `5ca616041f46aa2a8a380518a1c803430cad7713` + `THOTH-ASYNC-01-ADR-01: propose Amendment-13 mismatch-admission correction (#958)` + - direct parent: the historical Amendment-12 approval-state head + `a913232a6623936855d635885d513d4c778c4b25` + - changes only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, + the candidate decision-register blob + `5611ff3f4f3b1981f30295182efff1f882a24331` and the candidate changelog + blob `0833e6d4efcb7451739ea90bbbadb33ff6def37b` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the approval-state ADR-0012, +here because this report is part of that tree; the candidate ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its replacement-candidate blob +recorded above; every other file keeps its approval-state-head blob (section 9). The commit identities are verified against the remote -task-branch head after push and recorded in the post-publication handoff; -once published, the PR #960 head and head tree are GitHub-owned lifecycle -evidence under `ADR-0005`. The authorized topology is a non-force -fast-forward of the task branch from the content-approved replacement -candidate to the approval-state commit; no merge commit, rebase, squash, -amend, force update or second approval-state commit is authorized, and no -further source commit exists implicitly after it. +task-branch head after push and reported in the stage return; once +published, the PR #960 head and head tree are GitHub-owned lifecycle evidence +under `ADR-0005`. The authorized topology is one non-force fast-forward of +the task branch from the approval-state head to the candidate; no merge +commit, rebase, squash, amend, force update, second candidate commit or +second push is authorized, and no further source commit exists implicitly +after it. + +Amendment-12 Route-B approval-state commit (historical): + +- `a913232a6623936855d635885d513d4c778c4b25` - + `THOTH-ASYNC-01-ADR-01: record Amendment-12 architecture approval (#958)` + - ordinary single-parent additive commit; direct parent: the + content-approved Amendment-12 replacement candidate + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` + - tree `0fbd637066b1c4a643f62b6da48ec7f796c03358` + - changed only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, which received independent + approval-state inspection and CTO exact-final-blob approval at that head + on 2026-10-16; decision-register blob + `6033bd0743a68d47b9415c5cdaf0e03129400a03`, changelog blob + `5ca616041f46aa2a8a380518a1c803430cad7713` and report blob + `e852e4297d02dec126a85200eb7cd065fbaf8e25` + - published as a fast-forward of `feature/async/adr-0012` from the + replacement candidate; at the Amendment-13 candidate stage the remote + task-branch head, tree, single parent and these blobs matched Amendment-12 Route-B replacement candidate commit, the F-A12-CAND-01 and F-A12-CAND-02 consistency correction (content-approved; historical): @@ -1963,11 +2133,21 @@ first-parent order, newest first: ## 4. Files changed -Amendment-12 Route-B approval-state write budget for the commit that contains -this report (authorization #958 comment `6202232188`), identical to the -replacement-candidate write budget; exactly four existing paths, no new file, -no deletion, and every other path byte-identical to the content-approved -replacement candidate +Amendment-13 Route-B candidate write budget for the commit that contains this +report (authorization #958 comment `6288171434`); exactly four existing +paths, no new file, no deletion, rename or file-mode change, and every other +path byte-identical to the historical Amendment-12 approval-state head +`a913232a6623936855d635885d513d4c778c4b25`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-12 Route-B approval-state write budget (authorization +#958 comment `6202232188`), identical to the replacement-candidate write +budget; exactly four existing paths, no new file, no deletion, and every +other path byte-identical to the content-approved replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`: - `CHANGELOG.md` @@ -2144,14 +2324,120 @@ recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 -approval-state, Amendment-12 candidate, Amendment-12 replacement-candidate -and Amendment-12 approval-state stages; all four correction paths already -existed. +approval-state, Amendment-12 candidate, Amendment-12 replacement-candidate, +Amendment-12 approval-state and Amendment-13 candidate stages; all four +correction paths already existed. -Amendment-12 approval-state changes in the commit that contains this report, -relative to the content-approved replacement candidate -`9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` (exactly the four authorized paths, -approval state only): +Amendment-13 candidate changes in the commit that contains this report, +relative to the historical Amendment-12 approval-state head +`a913232a6623936855d635885d513d4c778c4b25` (exactly the four authorized +correction paths): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: implement the CTO-approved effective Specification Amendment 13 + with Revisions 1 and 2 (`R-960-A12-DET-ENR-01`, `F-A13-01`, `F-A13-02`, + `F-A13R1-01`) and the CTO-approved interpretation of NB-01 to NB-10. + - behavioural effect: architecture proposal only. The header returns to + `Status: PROPOSED` and loses `Approved by` and `Approval date`. In section + 3.2: the routed-obligation paragraph names the universal admission guard; + the terminal non-materialized disposition is limited to proven ordinarily + eligible, still-owed obligations, proven inside its own transaction, and + its accepted divergence is distinguished from a mismatch anomaly's + `RECONCILED/DIVERGED` outcome; the route-health list counts only anomalies + committed `OPEN`; the in-epoch mismatch subsection states the two ways an + anomaly comes into existence (detection `OPEN`, or born-terminal + `ENROLLED` by enrollment-first), truthful first-observer provenance, + open-only counting, the canonical identity as a pure function of the + event and route with original epoch and expected revision as audit facts, + and detection convergence; a new subsection "Original-epoch + classification and first route-disposition admission" defines the four + immutable classifications, the universal guard, the exhaustive list of + already-approved first-disposition writers (plans acting only through + them), admission by classification, refusal and concurrency; historical + enrollment classifies first and applies only to outside-epoch and + mismatched events; the subsection "Mutually exclusive terminal resolution + of a mismatch anomaly" is replaced by "Canonical mismatch-anomaly decision + order", covering detection first, enrollment first (born-terminal), + later detection, divergence only from a committed `OPEN` anomaly, + divergence before detection, enrollment versus divergence, losers, aborts, + retries, refused enrollment, fail-closed contradictory state, the meaning + of `ENROLLED` and composition, while retaining the generic-backfill and + compensation paragraphs; and the withdrawal subsection names plan writers, + born-terminal anomalies and the withdrawal-first enrollment refusal and + composes with the new order. In section 3.5: the non-materialized + disposition sentence, the post-seal "Historical enrollment and anomaly + resolution" paragraph, the "Exceptional dispositions unchanged" paragraph + and the atomic-creation boundary carry the new order and guard. Section + 3.14 restates that staff and reconciliation authority remain ADR-0010's, + with no new permission; section 3.15 counts only committed `OPEN` + anomalies and separates accepted-divergence dispositions from + `RECONCILED/DIVERGED` resolutions. Invariants 5, 8, 35 and 45 are + conformed and invariant 47 is added; invariants 1-4, 6-7, 9-34, 36-44 and + 46 are byte-identical. Section 8 item 1 and section 9 rollout proof 3 name + the new order and guard. In section 11, five existing mismatch and + terminal-resolution cases are conformed to be born-terminal-aware, + including the one Amendment-12 historical-enrollment case, whose obligation + is unchanged; the other nineteen Amendment-12 cases are byte-identical; + and thirty active cases `A13-T01` to `A13-T24` and `A13-T26` to + `A13-T31` are added with their common requirements and the retired + `A13-T25` record. Section 12 records the candidate state, extends the + description of this material correction and binds the 2026-10-15 approval + to historical blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`; its + architecture-only, authority-condition, partial-supersession and + amendment paragraphs are unchanged. Candidate blob: + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: describe the corrected ADR-0012 version as `PROPOSED`. + - behavioural effect: the ADR-0012 row only - `APPROVED` becomes + `PROPOSED` and `Satisfied` becomes `Pending`; the 2026-10-15 content + approval is no longer presented as approval of this version and is bound, + with the earlier approvals, to its historical exact version (ADR-0012 blob + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`); the summary gains the + universal admission guard, the canonical decision order, born-terminal + enrollment, divergence-before-`OPEN` refusal, the fail-closed + contradictory-state HOLD and the invariant and validation counts; the + authority-condition and implementation-authorization sentences are + unchanged. The header `Last updated` advances from `2026-10-15` to + `2026-10-20`, the date of this row change (decision 171). Every other row + is unchanged. Candidate blob: `5611ff3f4f3b1981f30295182efff1f882a24331`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the proposed correction under `[Unreleased]`. + - behavioural effect: one new + `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION` entry at the top of + `[Unreleased] -> Changed`, describing the corrected version as + `PROPOSED`, binding the 2026-10-15 approval and every earlier entry to + their historical exact versions and stating that no runtime, API, + schema, migration, provider, deployment, release or activation action + follows; every existing entry is byte-identical. Candidate blob: + `0833e6d4efcb7451739ea90bbbadb33ff6def37b`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-13 candidate stage truthfully. + - behavioural effect: documentation only. Makes the candidate commit the + current stage; records the post-publication approvals of the + Amendment-12 approval-state head, the Amendment-13 specification, + revision, review and CTO records and the candidate authorization; records + the candidate validation, including the documentation-only CI + classification (section 9); and reframes statements that would otherwise + describe the approval-state commit as the commit containing this report, + recording its identities as historical facts. All still-true earlier + evidence is preserved. + - within authorized write budget: YES + +Byte-identical to the historical Amendment-12 approval-state head through the +candidate commit: every path other than the four authorized paths, including +ADR-0008 `622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51`, ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`, the decisions README +`de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7`, the CI classifier +`2cac40c9d1d45e6961cfb4e368c602b75d8f980d` and every workflow file. + +Historical Amendment-12 approval-state changes in the approval-state head +`a913232a6623936855d635885d513d4c778c4b25`, relative to the content-approved +replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` (exactly the +four authorized paths, approval state only): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: record the CTO exact candidate-content approval of 2026-10-15. @@ -2210,14 +2496,15 @@ approval state only): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-12 approval-state stage truthfully. - - behavioural effect: documentation only. Makes the approval-state commit - the current stage; records the independent exact-replacement-candidate - source review, the CTO candidate-content approval and the approval-state - authorization; records the approval-state validation and no-drift proof - (section 9); and reframes statements that would otherwise describe the - replacement candidate as the commit containing this report, recording the - replacement candidate's identities as historical facts. All still-true - earlier evidence is preserved. + - behavioural effect: documentation only. Made the approval-state commit + the then-current stage; recorded the independent + exact-replacement-candidate source review, the CTO candidate-content + approval and the approval-state authorization; recorded the + approval-state validation and no-drift proof (section 9); and reframed + statements that would otherwise have described the replacement candidate + as the commit containing the report, recording the replacement + candidate's identities as historical facts. All still-true earlier + evidence was preserved. - within authorized write budget: YES Byte-identical to the content-approved replacement candidate through the @@ -3571,7 +3858,12 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-12 approval-state commit differs from the content-approved +The Amendment-13 candidate commit differs from the historical Amendment-12 +approval-state head `a913232a6623936855d635885d513d4c778c4b25` in exactly the +four authorized correction paths, with no new file, deletion, rename or mode +change, and from refreshed `feature/worker` in exactly the six cumulative PR +paths (section 9, "Amendment-13 candidate validation"). The Amendment-12 +approval-state commit differed from the content-approved replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` in exactly the four authorized paths and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-12 approval-state validation"). @@ -3668,8 +3960,9 @@ stage (#958 comment `6046895895`), the Amendment-9 approval-state stage `6125285446`), the Amendment-11 approval-state stage (#958 comment `6128858897`), the Amendment-12 candidate stage (#958 comment `6151955721`), the Amendment-12 replacement-candidate stage (#958 comment -`6172224137`) and the Amendment-12 approval-state stage (#958 comment -`6202232188`). This matrix +`6172224137`), the Amendment-12 approval-state stage (#958 comment +`6202232188`) and the Amendment-13 candidate stage (#958 comment +`6288171434`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -4233,8 +4526,8 @@ Amendment-12 approval-state commit: implementing agent; an independent reviewer reviewed the published replacement candidate and returned `APPROVED` (section 2) -Amendment-12 approval-state stage - done up to and including the creation of -the commit that contains this report: +Amendment-12 approval-state stage - historical, completed before the +Amendment-13 candidate: - repository inspection: USED (read-only verification of the exact refs, including `develop` and `feature/worker` at the same base, #957 and #958 @@ -4260,24 +4553,13 @@ the commit that contains this report: verified clean and detached at the content-approved replacement candidate and touching no other worktree, branch or ref, was used for construction, and the existing task branch is the fast-forward target) -- commit: USED (the single-parent approval-state commit that contains this - report) - -Authorized post-commit actions for the approval-state commit - GitHub-owned -lifecycle evidence under `ADR-0005`, not self-recorded here: - -- the non-force push that fast-forwards `feature/async/adr-0012` from - `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` to the approval-state commit; -- post-push read-only verification of the remote head, tree, direct parent, - ADR-0012, decision-register, changelog and report blobs, ADR approval - state, changed path set, cumulative footprint and PR #960 state, and that - no review thread was replied to, resolved or dismissed; -- the automatic PR CI that the push causes, and read-only inspection of any - automated review that runs on the draft head. - -These actions necessarily follow the creation of the commit that contains this -report. Their outcomes are recorded in the post-publication handoff and the -GitHub ledger rather than written into the source tree as completed. +- commit: USED (the single-parent approval-state commit + `a913232a6623936855d635885d513d4c778c4b25`) +- push: USED (one fast-forward of `feature/async/adr-0012` from + `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` to the approval-state commit; its + post-publication verification was recorded in that stage's handoff and the + GitHub ledger, and at the Amendment-13 candidate stage the remote head, + tree, single parent and blobs matched) Not authorized for the implementing agent at the Amendment-12 approval-state stage, each a separate controlled lifecycle category where applicable @@ -4311,6 +4593,89 @@ authoritative for the current state of each): - production activation: NOT USED (not authorized) - other: NONE +Amendment-13 candidate stage - done up to and including the creation of the +commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base and `master`, + #957 and #958 open with the candidate authorization as the latest #958 + comment and no later control record, PR #960 open, draft, unmerged and + mergeable with head `a913232a6623936855d635885d513d4c778c4b25` and target + `feature/worker`, the approval-state head's tree, single parent and + ADR-0012, report, decision-register and changelog blobs, the protected + ADR-0008, ADR-0010 and ADR-0013 blobs, the Route-B doctrine blob, the CI + classifier and workflow blobs, the six-path cumulative footprint, the + absence of any ADR-0012 commit reachable from `develop`, `master` or + `feature/worker`, the job-level outcomes of the four workflow runs on the + approval-state head and, as stage-time evidence, the PR #960 review-thread + state in GitHub; all matched the authorization; current review-thread + state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized correction paths only) +- new file creation, file deletion/move/rename, file-mode change: NOT USED + (not authorized) +- branch creation: NOT USED as a remote action (not authorized). Construction + used the implementing agent's own fresh session clone, in which a local + branch of the same name tracking `origin/feature/async/adr-0012` was + checked out at the approval-state head with a clean worktree; no other + worktree, remote branch or ref was created or touched, and the existing + task branch is the fast-forward target +- commit: USED (the single-parent candidate commit that contains this report) + +Authorized post-commit actions for the candidate commit - GitHub-owned +lifecycle evidence under `ADR-0005`, not self-recorded here: + +- fresh pre-push guards: re-reading #958 and PR #960, confirming that the + task branch still equals `a913232a6623936855d635885d513d4c778c4b25` and that + `feature/worker` and `develop` still equal + `b44303c214498baf76c9d9a0a7cab374377f9cbe`, and re-evaluating the classifier + and automatic workflow behaviour; +- the one normal non-force push that fast-forwards `feature/async/adr-0012` + from `a913232a6623936855d635885d513d4c778c4b25` to the candidate commit; +- post-push read-only verification of the remote head, sole parent, tree, + candidate blobs, `PROPOSED` state, changed path sets, protected blobs and + PR #960 state, and that no review thread was replied to, resolved or + dismissed; +- read-back of the automatic PR CI that the push causes, at job level. + +These actions necessarily follow the creation of the commit that contains this +report. Their outcomes are reported in the stage return and the GitHub ledger +rather than written into the source tree as completed. + +Not authorized for the implementing agent at the Amendment-13 candidate +stage, each a separate controlled lifecycle category where applicable +(stage-time evidence; GitHub, through #958 and PR #960, is authoritative for +the current state of each): + +- any edit to a path other than the four authorized paths: NOT USED (not + authorized) +- any ADR-0012 change beyond the effective Amendment 13 and the approved + NB-01 to NB-10 interpretation, including any new architecture, privilege, + recovery or incident design: NOT USED (not authorized) +- any other branch or ref creation or mutation, PR target change, rebase, + merge, cherry-pick, amend, squash, force push, history rewrite, second + commit or second push: NOT USED (not authorized) +- self-approval, independent inspection or review, content or + exact-final-blob approval: NOT USED (never on the implementing agent's + authority) +- issue/comment mutation: NOT USED (not authorized) +- PR #960 title or description update, Draft -> Ready, reviewer request: NOT + USED (not authorized) +- review-thread reply/resolution/dismissal: NOT USED (not authorized) +- manual CI dispatch/rerun: NOT USED (not authorized) +- manual Codex/app review request: NOT USED (not authorized) +- provider/runtime read or write, credential access: NOT USED (not + authorized) +- migration or data execution: NOT USED (not authorized) +- release/tag/package/image publication: NOT USED (not authorized) +- programme-pin reconciliation: NOT USED (not authorized) +- merge: NOT USED (not authorized; the implementing agent must not merge its + own work) +- deployment: NOT USED (not authorized) +- production activation: NOT USED (not authorized) +- other: NONE + Unauthorized actions performed: NONE ## 4.3 Automatic and manual external effects @@ -4334,24 +4699,34 @@ approval-state authorizations, the Amendment-10 candidate authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the Amendment-11 approval-state authorization, the Amendment-12 candidate -authorization, the Amendment-12 replacement-candidate authorization and the -Amendment-12 approval-state authorization, configured Codex/app review is +authorization, the Amendment-12 replacement-candidate authorization, the +Amendment-12 approval-state authorization and the Amendment-13 candidate +authorization, configured Codex/app review is not expected from a push to a draft PR and is not relied upon at the Amendment-6/7 candidate, approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 approval-state, -Amendment-12 candidate, Amendment-12 replacement-candidate or Amendment-12 -approval-state stage; configured Codex/app review runs on separately +Amendment-12 candidate, Amendment-12 replacement-candidate, Amendment-12 +approval-state or Amendment-13 candidate stage; configured Codex/app review +runs on separately authorized Ready transitions, which are GitHub-owned lifecycle events, and any automated review that nevertheless runs on a draft head is inspected -read-only. The actual job outcomes for the approval-state commit exist only -after its publication; they +read-only. The actual job outcomes for the Amendment-13 candidate commit +exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull -request, the post-publication handoff and the owning issue, not in this file. +request, the stage return and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or -publish an image rather than skip, that is a HOLD condition. +publish an image rather than skip, that is a HOLD condition. Because each +workflow runs its heavy jobs whenever its `classify` job does not succeed, +including a staging-image push, the Amendment-13 candidate stage verified +before push that the workflow and classifier files are unchanged and that +the complete direct and PR-range path sets classify as documentation-only, +including by replaying the classifier's own `pull_request` event path +locally (section 9, "Amendment-13 candidate validation"); a push to +`feature/async/adr-0012` triggers no `push` workflow, which run only for +`master` and `develop`. Historical automatic CI for earlier PR heads, as recorded by earlier stages of this report or read back from GitHub at the transient-state correction, @@ -4359,8 +4734,9 @@ factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 -replacement-candidate or Amendment-12 approval-state stage, followed the same -documentation-only classification, with no manual dispatch or rerun: +replacement-candidate, Amendment-12 approval-state or Amendment-13 candidate +stage, followed the same documentation-only classification, with no manual +dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -4385,6 +4761,7 @@ e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelo 21e19e81d0acf1aed57952ec00e2653771fb6f47: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-11 approval-state head) 06cc13685f1df5ec41da4f7958e8780a0c63ead9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-12 candidate) 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-12 replacement candidate) +a913232a6623936855d635885d513d4c778c4b25: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-12 approval-state head) ``` Manually initiated external actions (anything the implementing agent @@ -5746,7 +6123,8 @@ content-approved replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9 source; that stage was not authorized to change them, and their reconciliation was reported to control as a separate action. -Amendment-12 approval-state stage (the commit that contains this report): +Amendment-12 approval-state stage (historical approval-state head +`a913232a6623936855d635885d513d4c778c4b25`): 149. The approval-state header uses the same field order as the earlier approved ADR-0012 versions (`Status`, `Date`, `Approved by`, @@ -5809,22 +6187,156 @@ Amendment-12 approval-state stage (the commit that contains this report): `F-A12-CAND-01` and `F-A12-CAND-02` corrections, invariants 1-46 and the complete validation matrix with all twenty Amendment-12 cases are unchanged. -156. This report does not self-pin the approval-state commit's SHA, its tree - or its own final blob; it records only non-self-referential evidence and - the approval-state ADR-0012, decision-register and changelog blobs, which - do not depend on its bytes. Because the report now lives in the - approval-state commit, statements that described the replacement - candidate as the commit containing this report are reframed as history, - and the replacement candidate's identities, now the approval-state - parent, are recorded in section 1 as historical facts. The approval-state - ADR blob is recorded as a result; it is not represented as - exact-final-blob approved. Construction used the implementing agent's own +156. The approval-state report did not self-pin the approval-state commit's + SHA, its tree or its own final blob; it recorded only + non-self-referential evidence and the approval-state ADR-0012, + decision-register and changelog blobs, which did not depend on its bytes. + Because the report then lived in the approval-state commit, statements + that described the replacement candidate as the commit containing the + report were reframed as history, and the replacement candidate's + identities were recorded in section 1 as historical facts. The + approval-state ADR blob was recorded as a result and was not represented + as exact-final-blob approved; its later exact-final-blob approval is + recorded in section 2. Construction used the implementing agent's own existing clean session worktree, detached at the content-approved replacement candidate before editing, touching no other worktree, branch, - ref, stash or uncommitted change. The PR #960 title and description - describe the historical Amendment-11 source; this stage is not authorized - to change them, and their reconciliation is reported to control as a - separate action. + ref, stash or uncommitted change. The PR #960 title and description then + described the historical Amendment-11 source; that stage was not + authorized to change them. + +Amendment-13 Route-B candidate (the commit that contains this report): + +157. The original-epoch classification is a new section-3.2 subsection that + derives its four classes - ordinarily eligible, in-epoch contract + mismatch, outside every epoch, unrelated kind - only from the captured + routing generation, the activation epochs and the immutable + route-contract revisions that section 3.2 already defines, and states + that it introduces no second epoch or eligibility definition (NB-07). +158. The universal admission guard lists, exhaustively, the + first-disposition writers the ADR already approved - ordinary routed + materialization (new routed job, coalescing attachment and post-seal + successor), explicitly authorized outside-epoch historical enrollment, + compatible first historical enrollment of an in-epoch mismatch, and the + terminal non-materialized disposition of an ordinarily eligible, + still-owed obligation - and states that an approved migration, + reconciliation or disposition plan acts only through one of them and + gains no mutation authority; each writer keeps its own permission + (NB-08; R2-1.1). +159. The mismatch disposition rule is stated directly, in the section-3.2 + admission list and in invariant 47: no disposition exists for an + in-epoch mismatch except the one created by compatible, explicitly + authorized first historical enrollment with its exact canonical terminal + `ENROLLED` anomaly. Revision 2's double-negative "no accepted terminal + non-materialized mismatch disposition can coexist" wording is not used + (NB-01). +160. The accepted divergence of an ordinarily eligible obligation's terminal + non-materialized disposition and a mismatch anomaly's + `RECONCILED/DIVERGED` terminal outcome are separated wherever either + appears: the section-3.2 accepted-divergence paragraph, the compensation + paragraph of the decision-order subsection, section 3.15's metrics, + invariant 8 and cases `A13-T26` and `A13-T29` (NB-02). +161. Born-terminal awareness is applied to every text that assumed a + committed `OPEN` anomaly: the mismatch definition (two ways an anomaly + comes into existence, first-observer provenance, open-only counting), + "a detector-created anomaly remains `OPEN`", the enrollment check + ("committed `OPEN` or absent"), the post-seal enrollment paragraph and + the atomic-creation rollback ("prior committed state, `OPEN` or absent"), + the route-health and section-3.15 counters and five section-11 cases. + Invariant 38 was reviewed and is unchanged: it already counts only open + anomalies, and the authorization limits invariant changes to 5, 8, 35, 45 + and 47; section 3.2 and section 3.15 carry the born-terminal counting + rule (NB-03). Recorded as an interpretation for independent inspection. +162. Contradictory durable state is handled only as a fail-closed control + HOLD: no operation treats it as healthy, adopts, diverges, repairs or + justifies a new disposition, job or effect for it, and its evidence is + preserved and made visible to engineering control; no third anomaly + state, job hold, automatic adoption or repair, detector bypass, + retroactive authority or provider effect is defined, and the ADR does not + decide what record, if any, a detector writes for such state, which + belongs to the separately authorized incident-recovery design. `A13-T25` + is recorded as retired with its reason and is neither renumbered nor + reused (NB-04; R1-3.3). Recorded as an interpretation for independent + inspection. +163. The canonical decision order is stated as an observable atomicity and + ordering requirement with a non-exhaustive list of qualifying mechanism + families; the deadlock-safe order or equivalent retry discipline must be + documented and proven against the actual PostgreSQL design later. No + lock order is selected, and the reviewer's illustrative ordering is not + adopted. A born-terminal `ENROLLED` anomaly is stated not to be + unresolved work for strand safety, while its revision selection still + participates in the withdrawal/selection order (NB-05; R2-4.2). +164. The thirty cases share one common-requirements paragraph (distinct + sessions or processes, barriers, fault injection, loser conflict, winner + commit and rollback, serializable or uniqueness failure, deadlock and + retry, lost acknowledgment, the R1 common assertions, and the statement + that no documentation change executes them). `A13-T03` is + mechanism-neutral ("blocked by, or aborted against") and requires at + least one explicit serializable-failure-and-retry schedule; `A13-T20` and + `A13-T24` inject a fault after each constituent write; `A13-T31-E` + includes a coalescing attachment and a postcommit lost-acknowledgment + retry; `A13-T31` asserts per-event refusal under a mixed bulk command; and + the three-way absent-row race is recorded as optional additional coverage + (NB-06; R2-4.2). +165. A divergence refused before the anomaly exists grants no authority for + a later decision; a new attempt revalidates authority, reason, evidence + and the existing ADR-0010 section 4.7 and domain command-idempotency + semantics at its own linearization, and no new command identity or + approval kind is mandated (NB-09). +166. Counts are aligned across the four files: invariants 1-46 retained with + 5, 8, 35 and 45 conformed and 47 added; the twenty Amendment-12 cases + retained with one conformed; thirty active `A13-T` cases; `A13-T25` + retired. No transient GitHub review or thread identifier is written into + the ADR; this report records specification and authorization comment + identifiers under its existing practice and records approvals and + reviews of exact versions by outcome and date (NB-10). Recorded as an + interpretation for independent inspection. +167. The canonical anomaly identity is described as a pure function of the + event and the stable `route_key`, which determine the original epoch and + its expected revision because an event's captured generation lies inside + at most one epoch; those are recorded as immutable audit facts and no key + shape is chosen (R2-4.5). +168. Historical enrollment classifies before selecting and applies only to + outside-epoch and mismatched events; an ordinarily eligible event stays + under its original epoch revision and existing owed-route and + materializer rules, and no new recovery or replay authority is conferred + (R1-1.2, R1-1.3). +169. Section 3.14 gains one paragraph restating that the staff and + reconciliation authority section 3.2 relies on is ADR-0010's protected, + audited staff-command authority or another explicitly approved domain + authority, that the ADR creates no new permission and that + classification, a reason or a generic claim never grants permission + (R1-1.5). ADR-0010 itself is unchanged. +170. The subsection "Mutually exclusive terminal resolution of a mismatch + anomaly" is renamed "Canonical mismatch-anomaly decision order" because + it now also governs first creation of the anomaly; its enrollment-versus- + divergence orders, loser rules, generic-backfill paragraph and + compensation paragraph are retained in substance, every in-ADR cross + reference to it is updated, and section 12's history of earlier versions, + which uses the old wording descriptively, is unchanged. Recorded as an + interpretation for independent inspection. +171. Section 12 follows the earlier candidate pattern: "Current decision + state: **PROPOSED**.", no current approver or approval date, an extended + description of this correction, and the 2026-10-15 approval bound to the + historical blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`. The register + header `Last updated` advances to `2026-10-20`, the date of the ADR-0012 + row change, as mechanically consistent register metadata (decisions 25, + 78, 97 and 129 pattern). The changelog gains one entry and changes no + existing entry. +172. This report does not self-pin the candidate commit's SHA, its tree or its + own final blob; it records only non-self-referential evidence and the + candidate ADR-0012, decision-register and changelog blobs, which do not + depend on its bytes. Because the report now lives in the candidate commit, + statements that described the approval-state commit as the commit + containing this report are reframed as history, and the approval-state + head's identities are recorded in section 1 as historical facts. + Construction used the implementing agent's own fresh session clone + (section 4.2). Section 1 names this stage's implementing agent without a + model identifier because the agent's operating policy forbids writing + model identifiers into repository files; the exact identity is disclosed + in the stage return. The PR #960 title and description describe + the historical Amendment-12 source; this stage is not authorized to change + them. + List any deviation from the specification requiring authorization: @@ -5958,8 +6470,19 @@ List any deviation from the specification requiring authorization: - Amendment-12 approval-state stage: NONE. The approval-state transformation followed its separate authorization exactly; no fifth path, new file, deletion, architecture change, second commit, amend, rebase, squash, force - push or history rewrite was needed or performed before the commit that - contains this report was created. + push or history rewrite was needed or performed before the approval-state + commit was created. +- Amendment-13 candidate stage: NONE as to scope. No fifth path, new file, + deletion, rename, mode change, ADR change outside the effective Amendment 13 + and the candidate state, second commit, amend, rebase, squash, force push or + history rewrite was needed or performed before the commit that contains + this report was created. Decisions 161, 162, 166 and 170 are recorded for + independent inspection as interpretations within the authorized scope, not + as deviations. One documentation-convention difference is disclosed rather + than hidden: section 1 records this stage's implementing agent without a + model identifier, because the implementing agent's operating policy + forbids writing model identifiers into repository files; the exact model + identity is disclosed in the stage return (decision 172). ## 6. Database and migration effects @@ -5972,9 +6495,11 @@ ordering of revision withdrawal against new revision selection and of coalescing attachment against a job's first claim/start, its required database-consistent strand-safety decision for revision withdrawal, its required atomic creation of a routed job with its disposition, its -required mutually exclusive terminal resolution of a mismatch anomaly and -its required post-seal materialization path with successor generations for -coalescing kinds, is architecture for a future +required mutually exclusive terminal resolution of a mismatch anomaly, its +required post-seal materialization path with successor generations for +coalescing kinds, its required canonical mismatch-anomaly decision order and +its required universal first route-disposition admission guard, is +architecture for a future separately authorized shared-engine implementation; it creates no migration obligation by itself and names no table, column or index. @@ -5991,8 +6516,9 @@ preserved unchanged in Appendix A; neither the activation-epoch correction, the route-contract-revision correction, the withdrawal/selection ordering correction, the coalescing-seal ordering correction, the withdrawal strand-safety correction, the routed-job atomicity and anomaly -terminal-resolution correction nor the post-seal materialization correction -changes a released contract or adds a consumer. The route, revision, anomaly and materializer-compatibility concepts +terminal-resolution correction, the post-seal materialization correction nor +the mismatch-admission correction changes a released contract or adds a +consumer. The route, revision, anomaly and materializer-compatibility concepts are internal to the future shared engine; any later external read or executor contract derived from them needs its own specification, merged upstream contract and downstream task. @@ -6049,8 +6575,10 @@ validation" records the validation of the historical first candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9`. "Replacement-candidate validation" records the validation of the historical content-approved replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`. "Amendment-12 -approval-state validation" at the end of this section records the validation -of the commit that contains this report. +approval-state validation" records the validation of the historical +approval-state commit `a913232a6623936855d635885d513d4c778c4b25`. "Amendment-13 +candidate validation" at the end of this section records the validation of +the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -10344,17 +10872,23 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e ``` -The approval-state ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above; the ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` is the exact -final blob presented for CTO exact-final-blob approval at this stage and is -not yet so approved. This report's own final blob, the approval-state tree -and the approval-state commit SHA depend on this report's bytes and are not -self-pinned; they are verified against the remote head after push and -recorded in the post-publication handoff. +The approval-state ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the approval-state report's bytes, so +they were recorded above; the ADR-0012 blob +`1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` was the exact final blob presented +for CTO exact-final-blob approval at that stage and received that approval +on 2026-10-16 (section 2). The approval-state report's own final blob, the +approval-state tree and the approval-state commit SHA depended on that +report's bytes and were not self-pinned in it; section 1 now records them as +historical facts: commit `a913232a6623936855d635885d513d4c778c4b25`, tree +`0fbd637066b1c4a643f62b6da48ec7f796c03358`, report blob +`e852e4297d02dec126a85200eb7cd065fbaf8e25`. -Required topology of the approval-state commit, checked after it is created -and before any push, with results recorded in the post-publication handoff: +Required topology of the approval-state commit, checked after it was created +and before push; the published approval-state commit satisfies every +requirement (single parent `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, +`git diff --check` clean, the four authorized paths and the six cumulative +PR paths, re-verified at the Amendment-13 candidate stage): ```text git rev-list --parents -n 1 HEAD @@ -10373,6 +10907,256 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-13 candidate validation + +Environment: the implementing agent's own fresh session clone of +`thoth-pub/thoth`, used by no other process, in which a local branch +`feature/async/adr-0012` tracking `origin/feature/async/adr-0012` was checked +out at the historical Amendment-12 approval-state head +`a913232a6623936855d635885d513d4c778c4b25` (tree +`0fbd637066b1c4a643f62b6da48ec7f796c03358`) with a clean worktree before +editing; no other worktree, remote branch or ref was touched. Immediately +before construction, read-only GitHub and Git inspection showed #957 and #958 +open, the candidate authorization (#958 comment `6288171434`) as the latest +#958 comment with no later control record, `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `master` at +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`, no ADR-0012 commit reachable from +`develop`, `master` or `feature/worker` (section 2.1), +`feature/async/adr-0012` at `a913232a6623936855d635885d513d4c778c4b25` with +single parent `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, 47 commits ahead of +and 0 behind `feature/worker`, ADR-0012 blob +`1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, report blob +`e852e4297d02dec126a85200eb7cd065fbaf8e25`, decision-register blob +`6033bd0743a68d47b9415c5cdaf0e03129400a03` and changelog blob +`5ca616041f46aa2a8a380518a1c803430cad7713` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs, the Route-B doctrine blob, the CI +classifier blob `2cac40c9d1d45e6961cfb4e368c602b75d8f980d` and the workflow +blobs, PR #960 open, draft, unmerged and mergeable with that head and target +`feature/worker`, the cumulative PR footprint exactly the six paths, the four +pull_request workflow runs on that head with every heavy job skipped, and +PR #960 review-thread state that matched the stage authorization (stage-time +evidence; current review-thread state remains GitHub-owned). ADR-0012, the +decision register and the changelog were edited first and their +deterministic blobs recorded; then only this report was edited; all four +were staged, and every check below ran against that exact final staged index +before the candidate commit was created from it; the report's numeric line +counts below were filled in and the complete check set rerun with identical +results. These are documentation checks only: none of them executes, or +substitutes for, the PostgreSQL concurrency and fault-injection validation +that section 11 requires of the later implementation. + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check a913232a6623936855d635885d513d4c778c4b25 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Candidate delta against the approval-state head (exactly the four authorized +paths, all modifications, no mode change, creation, deletion or rename): + +```text +git diff --cached --name-status a913232a6623936855d635885d513d4c778c4b25 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat a913232a6623936855d635885d513d4c778c4b25 +1 0 CHANGELOG.md +1108 182 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +852 180 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +2 2 docs/engineering/decisions/decision-register.md + +git diff --cached --summary a913232a6623936855d635885d513d4c778c4b25 +exit 0; no output (no mode change, creation, deletion or rename) + +git diff --cached --raw a913232a6623936855d635885d513d4c778c4b25 | cut -d' ' -f1-2 | sort -u +:100644 100644 +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths) and unchanged control files: + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-only a913232a6623936855d635885d513d4c778c4b25 -- .github AGENTS.md docs/engineering/AGENTS.md docs/engineering/decisions/README.md docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md docs/engineering/decisions/ADR-0010-staff-operations-console.md docs/engineering/decisions/ADR-0013-programme-integration-decision-reliance.md Cargo.toml Cargo.lock thoth-api +exit 0; no output +``` + +Decision-register delta (the `Last updated` header line and the ADR-0012 row +only): + +```text +git diff --cached -U0 a913232a6623936855d635885d513d4c778c4b25 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -5 +5 @@ Owner: CTO +@@ -20 +20 @@ Last updated: 2026-10-15 + +diff <(git show a913232a6623936855d635885d513d4c778c4b25:docs/engineering/decisions/decision-register.md | sed '5d;20d') <(git show :docs/engineering/decisions/decision-register.md | sed '5d;20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-20 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Dist +``` + +Changelog delta (one added line, the new +`THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION` entry at the top of +`[Unreleased] -> Changed`; every existing entry byte-preserved): + +```text +git diff --cached -U0 a913232a6623936855d635885d513d4c778c4b25 -- CHANGELOG.md | grep '^@@' +@@ -21,0 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show a913232a6623936855d635885d513d4c778c4b25:CHANGELOG.md) <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git show :CHANGELOG.md | sed -n '/^## \[Unreleased\]/,/^## \[\[1.8.0\]\]/p' | grep -c '^### ' +3 +``` + +ADR-0012 candidate state, outline, invariants and validation register: + +```text +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: PROPOSED +4236:Current decision state: **PROPOSED**. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'APPROVED' +every match is in section 12 (lines 4239-4341): the sentence that the version +may carry APPROVED only after CTO approval, the historical-approval paragraph +and the authority condition; no sentence asserts that this version is approved + +diff <(git show a913232a6623936855d635885d513d4c778c4b25:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^#') +11a12 +> ### Original-epoch classification and first route-disposition admission +13c14 +< ### Mutually exclusive terminal resolution of a mismatch anomaly +--- +> ### Canonical mismatch-anomaly decision order +exit 1 (the added subsection and the renamed subsection are the complete outline change) + +invariant comparison (each numbered item of section 7, whitespace-normalized, approval-state head versus staged ADR) +numbering: 1-47 contiguous; changed: [5, 8, 35, 45]; added: [47]; all other invariants byte-identical + +section-11 top-level validation bullets (approval-state head -> staged ADR) +174 -> 204; added: 30, every one an active `A13-T` case +`A13-T01`-`A13-T24` and `A13-T26`-`A13-T31` each head exactly one bullet; `A13-T25` occurs only in the retirement note + +diff <(git show a913232a6623936855d635885d513d4c778c4b25:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk '/^- deterministic post-seal baseline tests/,/^- current-token lease renewal/') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk '/^- deterministic post-seal baseline tests/,/^- current-token lease renewal/') +exit 1; one hunk, in the historical-enrollment and anomaly case only (the other nineteen Amendment-12 cases are byte-identical) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n -i 'remains open until\|still-open state\|leaving the anomaly open\|Mutually exclusive terminal resolution of a mismatch anomaly' +exit 1; no output + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | wc -l +4366 +git diff --cached -U0 a913232a6623936855d635885d513d4c778c4b25 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^+' | grep -v '^+++' | awk 'length>81' | wc -l +0 +``` + +No other line of the ADR changes outside the passages named in section 4; in +particular the claim, lease and attempt-phase text of sections 3.4 and 3.6, +the coalescing-open/coalescing-sealed subsection, the post-seal successor +subsection apart from its historical-enrollment and exceptional-disposition +paragraphs, the atomic-creation subsection apart from its one added +sentence, the anomaly/withdrawal strand-safety orders and the epoch, +revision, producer-coverage and materializer-compatibility rules are +byte-identical to the approval-state head. + +Documentation-only CI classification with the unchanged classifier +(`.github/scripts/classify_ci_changes.py`, blob +`2cac40c9d1d45e6961cfb4e368c602b75d8f980d`): + +```text +python3 .github/scripts/classify_ci_changes.py --paths CHANGELOG.md docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md docs/engineering/decisions/decision-register.md +{"docs_only": "true", "run_build": "false", "run_docker": "false", "run_migrations": "false"} + +python3 .github/scripts/classify_ci_changes.py --paths +{"docs_only": "true", "run_build": "false", "run_docker": "false", "run_migrations": "false"} + +pull_request event path evaluated on the staged tree (the classifier's merge-base comparison of the PR base with the head) +git diff --cached --name-only --no-renames $(git merge-base b44303c214498baf76c9d9a0a7cab374377f9cbe a913232a6623936855d635885d513d4c778c4b25) +the same six paths; classify_paths -> docs_only true, run_build false, run_migrations false, run_docker false + +python3 .github/scripts/classify_ci_changes.py --self-test +PASS all_self_tests: 14 cases +``` + +Under these unchanged workflow definitions, a push to the PR head triggers +the four `pull_request` workflows; each `classify` job is expected to +succeed with `run_build`, `run_migrations` and `run_docker` false, so +`build`, `test`, `lint`, `format_check`, `run_migrations` and +`build_and_push_staging_docker_image` are expected to be skipped and +`check-changelog` to run against the changed `CHANGELOG.md`. The `push` +workflows run only for `master` and `develop`. The actual outcomes are +GitHub-owned. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (PROPOSED candidate) 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 +decision-register.md 5611ff3f4f3b1981f30295182efff1f882a24331 +CHANGELOG.md 0833e6d4efcb7451739ea90bbbadb33ff6def37b +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +.github/scripts/classify_ci_changes.py 2cac40c9d1d45e6961cfb4e368c602b75d8f980d +build_test_and_check.yml 3ae164e8aaf9a1fffe7d1b26f720c599b1e6b7e5 +check_changelog.yml 50e4ddbc62c0cc0e1dae6854a805d4e47b8ae01a +docker_build_and_push_to_dockerhub.yml 1fd32cdbf150e4ce450a32486c45cfb895ffd13d +run_migrations.yml 71fe33a7cf3577697e6e2f62c0c3f2636a5bd8bf +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 +``` + +The candidate ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. This report's own final blob, the candidate tree and the candidate +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and reported in the stage +return. + +Required topology of the candidate commit, checked after it is created and +before any push, with results reported in the stage return: + +```text +git rev-list --parents -n 1 HEAD +required: a913232a6623936855d635885d513d4c778c4b25 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check a913232a6623936855d635885d513d4c778c4b25 HEAD +required: exit 0; no output + +git diff --name-status a913232a6623936855d635885d513d4c778c4b25 HEAD +required: exactly the four candidate paths, each M, with no mode change + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -10646,6 +11430,30 @@ into the approval-state commit, in particular any statement naming the first Amendment-12 candidate as the direct parent of the commit containing this report or describing the replacement candidate as still the current `PROPOSED` candidate, and reframed them. +Amendment-13 candidate stage: read the complete effective Amendment 13, +Revision 1 and Revision 2, the independent specification-review receipt and +the CTO specification approval, then compared the staged ADR-0012 with the +approval-state blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` clause by +clause: every A13-1 to A13-6 provision as superseded by R1-1 to R1-5 and +R2-1 to R2-4, the deletion of A13-3.4, the retirement of A13-T25 and the +approved interpretation of NB-01 to NB-10 is expressed in sections 3.2, 3.5, +3.14 and 3.15, invariants 5, 8, 35, 45 and 47, sections 8, 9, 11 and 12; +every Amendment-12 provision, invariants 1-46 apart from the four conformed +ones, and the twenty Amendment-12 cases apart from the one conformed case are +unchanged (section 9). Searched the staged ADR for any remaining clause that +requires every mismatch anomaly to have been `OPEN`, that limits first +enrollment to an already `OPEN` anomaly, that lets divergence create an +anomaly, or that lets any writer other than compatible first historical +enrollment create a disposition for an in-epoch mismatch, and found none; +confirmed that no sentence claims that the corrected version is approved or +repository-authoritative, or that any future concurrency test ran; compared +the staged register and changelog with the approval-state blobs (only the +`Last updated` line and the ADR-0012 row, and only one added changelog +entry, change); confirmed the protected decision, control, classifier and +workflow blobs are unchanged; and read the report for statements that would +become false once it moves into the candidate commit, in particular any +statement presenting the approval-state commit as the commit containing this +report or the current stage, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -10669,9 +11477,11 @@ content-approved Amendment-11 candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, the Amendment-11 approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, the first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` and the content-approved -replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, are historical -evidence; the live PR #960 diff of the approval-state commit exists only -after its publication and is GitHub-owned lifecycle evidence. +replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and the +Amendment-12 approval-state head `a913232a6623936855d635885d513d4c778c4b25`, +are historical evidence; the live PR #960 diff of the Amendment-13 candidate +commit exists only after its publication and is GitHub-owned lifecycle +evidence. ## 11. CI @@ -10690,9 +11500,10 @@ authorization, the F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the Amendment-11 approval-state authorization, the Amendment-12 candidate authorization, the Amendment-12 -replacement-candidate authorization and the Amendment-12 approval-state -authorization, an automatic CI failure, or any migration execution or Docker -publication, is a HOLD condition. +replacement-candidate authorization, the Amendment-12 approval-state +authorization and the Amendment-13 candidate authorization, an automatic CI +failure, or any migration execution or Docker publication, is a HOLD +condition. ## 12. Rollout and rollback @@ -10733,23 +11544,41 @@ candidate commit, the Amendment-11 approval-state authorization permitted exactly one direct-child approval-state commit, the Amendment-12 candidate authorization permitted exactly one four-path candidate commit, the Amendment-12 replacement-candidate authorization permitted exactly one -direct-child four-path replacement commit, and the Amendment-12 -approval-state authorization permits exactly one direct-child approval-state -commit; any further source correction exhausts those authorizations and -requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-12; -`docs/engineering/decisions/README.md`). +direct-child four-path replacement commit, the Amendment-12 approval-state +authorization permitted exactly one direct-child approval-state commit, and +the Amendment-13 candidate authorization permits exactly one direct-child +four-path candidate commit and one non-force fast-forward push; any further +source correction, rollback, revert or recovery exhausts those +authorizations and requires a fresh CTO authorization (Amendment 3 C10; +Amendments 6-13; `docs/engineering/decisions/README.md`). Monitoring required: NONE ## 13. Known limitations and deferred work +- Stage-time evidence: at the Amendment-13 candidate stage the implementing + agent performed no independent inspection or review, content or + exact-final-blob approval, PR #960 title/description reconciliation, + Draft/Ready change, automated-review request, thread reply, resolution or + adjudication, exact-head review or merge (section 4.2). The PR #960 title + and description describe the historical Amendment-12 source; their + reconciliation is a separate control action. +- The Amendment-13 correction selects no SQL, lock order, isolation level, + schema or key shape: the canonical decision order and the universal + admission guard are observable atomicity and ordering requirements, and + their deadlock-safe mechanism must be specified and proven against the + actual PostgreSQL design by the later implementation specification. The + thirty Amendment-13 cases are future acceptance requirements; none was + executed. Contradictory existing durable state, if ever observed, is a + control HOLD that requires a separately authorized incident-recovery + design, which this task does not define. - Historical stage-time evidence: at the Amendment-12 approval-state stage the implementing agent performed no independent inspection or review, exact-final-blob approval or confirmation, PR #960 title/description reconciliation, Draft/Ready change, automated-review request, thread adjudication, exact-head review or merge (section 4.2). The PR #960 title and - description describe the historical Amendment-11 source; their - reconciliation is a separate control action. + description then described the historical Amendment-11 source; their + reconciliation was a separate control action. Those are separate controlled lifecycle categories; GitHub, through #958 and PR #960, is authoritative for their current state, including whether the PR title and description match the current head. @@ -10806,7 +11635,12 @@ Monitoring required: NONE the two candidate-source consistency findings found by the independent review of that candidate only under a separate authorization for one four-path replacement commit, and its approval state only under a separate - authorization for this one direct-child approval-state commit. + authorization for that one direct-child approval-state commit, and the + canonical mismatch-detection and first-enrollment ordering defect found by + the final-head review of that approval-state head only under Specification + Amendment 13 with Revisions 1 and 2, its independent reviews, the CTO + specification approval and a separate authorization for this one + direct-child four-path candidate commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -10830,8 +11664,8 @@ Monitoring required: NONE approval-state commit, the Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction and approval-state commit, the Amendment-11 candidate and approval-state commit, the - Amendment-12 candidate, its replacement candidate and this Amendment-12 - approval-state commit. + Amendment-12 candidate, its replacement candidate and approval-state + commit, and this Amendment-13 candidate. ## 14. Unresolved issues @@ -11040,11 +11874,37 @@ Monitoring required: NONE (section 4; section 5, decisions 145-148); an independent CRITICAL exact-replacement-candidate source review of that candidate found both findings satisfied in source and no blocking candidate finding; Javi, CTO - approved its exact architecture content on 2026-10-15; and the - approval-state commit that contains this report records that approval. The - finding is closed at specification level by Amendment 12 and, per that - review, in the replacement candidate's source; its thread adjudication - belongs to the review controls that GitHub records. + approved its exact architecture content on 2026-10-15; the historical + approval-state commit `a913232a6623936855d635885d513d4c778c4b25` recorded + that approval; and, after independent approval-state inspection, the CTO + recorded exact-final-blob approval of the resulting ADR-0012 blob + `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` on 2026-10-16. The finding is + closed at specification level by Amendment 12 and, per that review, in the + replacement candidate's source; its thread adjudication belongs to the + review controls that GitHub records. +- The configured final-head automated review of the Amendment-12 + approval-state head `a913232a6623936855d635885d513d4c778c4b25` opened one + further thread, identified here substantively and by its control-assigned + finding name. `R-960-A12-DET-ENR-01`: terminal-resolution arbitration + covered enrollment against divergence of an already existing anomaly, but + nothing ordered mismatch detection against the first authorized historical + enrollment while no anomaly row existed, so an enrollment could commit the + route's only disposition and a job before detection, after which a + detector could leave an open anomaly that `ENROLLED` could no longer + record and `RECONCILED/DIVERGED` would falsely describe. Control accepted + it as valid, HIGH-P1 and blocking; the original Amendment 13 and its + Revisions 1 and 2 closed it together with the independent + specification-review findings `F-A13-01`, `F-A13-02` and `F-A13R1-01`; a + fresh independent CRITICAL specification review of the cumulative + specification returned `APPROVED (SPECIFICATION ONLY)` with non-blocking + observations NB-01 to NB-10; the CTO approved the effective specification, + classified it as a material architectural correction and selected Route + B; and the `PROPOSED` Route-B candidate that contains this report + implements it in source (section 4; section 5, decisions 157-172). The + finding is closed at specification level; its source closure is for the + independent exact-candidate source review and the later review controls + that GitHub records, and its thread adjudication belongs to those + controls. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -11058,8 +11918,19 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-12 approval-state commit also -implemented the Amendment-12 replacement candidate and first candidate, the +work. The implementing agent of this Amendment-13 candidate is a Claude agent +in a fresh session with no conversation state, memory or authored artifact +from any earlier stage, specification, amendment or review of this task; it +authored none of Amendment 13, Revision 1 or Revision 2 and performed none of +their reviews. Earlier source stages are recorded in section 1 as +implemented by Claude agents, so prior involvement of the same underlying +model cannot be excluded at model level; the exact model identity is +disclosed in the stage return. Having implemented this candidate, it is not +eligible to act as an independent inspector or reviewer of it (Amendment 5 +F8; Amendment 6 G12; Amendment 7 H13). + +Historical: the implementing agent of the Amendment-12 approval-state commit +also implemented the Amendment-12 replacement candidate and first candidate, the Amendment-11 approval-state commit and candidate, the Amendment-10 approval-state commit, the Amendment-10 replacement candidate and first candidate, the Amendment-9 approval-state commit and candidate, the @@ -11074,11 +11945,52 @@ eligible to act as an independent inspector or reviewer of this task agent also performed the external independent specification-level review of Amendment 12 whose acceptance is recorded on #958, with its source-transcription history disclosed there; control accepted that review -as independent of the amendment's authorship only. That review is not -approval of this candidate's source, and this agent remains ineligible to +as independent of the amendment's authorship only. That review was not +approval of that candidate's source, and that agent remained ineligible to inspect, review or approve it. -Suggested review focus: +Suggested review focus for the Amendment-13 candidate: + +- that the candidate changes exactly the four authorized paths from the + approval-state head `a913232a6623936855d635885d513d4c778c4b25`, with no new + file, deletion, rename or mode change, that the six-path cumulative + footprint is unchanged, that the candidate ADR-0012 blob is + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, and that ADR-0008, ADR-0010, + ADR-0013, the decisions README, the CI classifier and the workflows keep + their protected blobs; +- that every clause of the effective Amendment 13 + Revision 1 + Revision 2 + is expressed, in particular the universal first route-disposition guard + for every already-approved writer (including plans acting through them), + the direct rule that no disposition exists for an in-epoch mismatch except + through compatible authorized first historical enrollment with its + canonical terminal `ENROLLED` anomaly, the absent-row canonical decision + order, born-terminal `ENROLLED` with truthful provenance, later-detection + convergence, refusal-only divergence before `OPEN`, the deletion of + A13-3.4 and the fail-closed HOLD for contradictory state; +- that no stale clause requires every mismatch anomaly to have been `OPEN`, + limits first enrollment to an already `OPEN` anomaly, lets divergence create + an anomaly or disposition, or lets the terminal non-materialized + disposition consume a mismatched route slot; +- that the NB-01 to NB-10 handling in section 5 (decisions 157-172) stays + within the CTO-approved interpretation and selects no SQL, lock order, + schema, new command identity, recovery state, job hold or permission; +- that invariants 5, 8, 35 and 45 are conformed, invariant 47 is added and + the other forty-two invariants are byte-identical; that the twenty + Amendment-12 cases are preserved with one conformed; that the thirty + active `A13-T` cases match the approved register, including T03, T26, + T27-T31 and the NB-06 hardening; and that `A13-T25` is retired, not + reused and not normative; +- that Amendment-12 successor, coalescing-seal, routed-job atomicity and + effect-fencing semantics are unchanged outside the named conforming + sentences; +- that the ADR, register row, changelog entry and this report describe the + corrected version as `PROPOSED` and bind the 2026-10-15 approval and every + earlier approval to their historical exact versions, with no claim of + approval, repository authority, review completion, merge readiness or + executed concurrency tests. + +Suggested review focus recorded at earlier stages (historical; each item +applies to the exact earlier version or head it names): - that the approval-state delta from the content-approved Amendment-12 replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` is limited to @@ -11394,8 +12306,9 @@ append-only activation-epoch model, and the route-level event kind/version contract and "emits K" emission floor described in earlier rounds are superseded by immutable route-contract revisions and producer-version coverage in the current section 3.2. The 2026-09-30, 2026-10-05, 2026-10-06, -2026-10-07, 2026-10-08, 2026-10-09 and 2026-10-12 approvals and every review -recorded here are bound to the exact historical versions and heads they name. +2026-10-07, 2026-10-08, 2026-10-09, 2026-10-12 and 2026-10-15 approvals and +every review recorded here are bound to the exact historical versions and +heads they name. ### Initial candidate and review round 1 @@ -12194,8 +13107,8 @@ stage: 10. A materially corrected ADR-0012 version may carry `APPROVED` only after CTO approval of that exact corrected content; earlier approvals and reviews, including the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, - 2026-10-08, 2026-10-09 and 2026-10-12 approvals, remain bound to the - earlier exact versions and heads they name. + 2026-10-08, 2026-10-09, 2026-10-12 and 2026-10-15 approvals, remain bound + to the earlier exact versions and heads they name. 11. The route-contract-revision model does not permit silent reinterpretation or loss either: every epoch is bound to one immutable revision, an event outside its epoch revision's accepted versions becomes a visible anomaly @@ -12259,5 +13172,18 @@ stage: and route identity, conditional convergence under finite inputs and fair capacity, and fail-closed activation for a kind without a demonstrated successor contract. +18. The mismatch-admission correction does not permit a disposition for an + in-epoch contract mismatch outside compatible, explicitly authorized + first historical enrollment with its canonical terminal `ENROLLED` + anomaly: every first route-disposition writer proves immutable + original-epoch classification and its own eligibility and authorization + in the transaction that would consume the slot; detection, first + enrollment and divergence share one database-enforced canonical decision + order even when no anomaly row exists; divergence resolves only an + already committed `OPEN` anomaly and never creates one; a born-terminal + `ENROLLED` anomaly is never counted as open; terminal `ENROLLED` is not + evidence of provider success; and contradictory durable state fails + closed as a control HOLD for separately authorized incident recovery, + with no third anomaly state, job hold or automatic repair. This report authorizes none of those later actions. diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 1c98ce0cc..0b5fb2d7e 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,7 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-15 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -372,9 +370,15 @@ and an eligible route obligation remains owed, visible in the route backlog, until it has received that one disposition. The normal disposition materializes a job: a materialized disposition points to exactly one job and remains unique on `(event_id, route_key)` regardless of whether that job is -pending, running, waiting or terminal. The only alternative is the explicit, -authorized and audited terminal non-materialized disposition described below, -which points to no job (section 3.5 and invariant 8). +pending, running, waiting or terminal. The only alternative for an eligible +obligation is the explicit, authorized and audited terminal non-materialized +disposition described below, which points to no job (section 3.5 and +invariant 8). Every operation that could create the first disposition for an +`(event_id, route_key)` pair is subject to the universal admission guard of +"Original-epoch classification and first route-disposition admission" below: +an in-epoch contract mismatch receives a disposition only through compatible, +explicitly authorized first historical enrollment, together with its canonical +terminal `ENROLLED` anomaly (invariant 47). When materializing an obligation requires a **new** logical job, that job is created only together with its disposition: the new routed job, the @@ -418,12 +422,26 @@ disposition remains one durable `(event_id, route_key)` record even when an operator authorizes a bulk action, and records the actor/authority and reason. Where a staff/operator action creates that disposition, it uses ADR-0010's protected, audited staff-command seam; direct production SQL is not the normal -route-disposition interface. +route-disposition interface. This terminal non-materialized disposition exists +only for an obligation proven ordinarily eligible from its immutable original +epoch and still owed: the transaction that would commit it proves that +eligibility, together with the required actor, authority, reason, covering +reconciliation/backfill or expressly accepted divergence and audit, under the +universal admission guard of "Original-epoch classification and first +route-disposition admission" below. It is refused for an in-epoch contract +mismatch, for an event outside every epoch of the route and for an event of a +kind the epoch's revision does not consume, and a bulk authorization applies +that guard to each `(event_id, route_key)` pair individually. A terminal disposition with no covering reconciliation/backfill is not normal successful routing. It records an explicit accepted divergence for the affected domain/effect target and raises durable operator attention; an operator cannot -make required work disappear merely by supplying a reason. +make required work disappear merely by supplying a reason. That accepted +divergence of an ordinarily eligible obligation is a route disposition. It is +distinct from the `RECONCILED/DIVERGED` terminal resolution of an in-epoch +contract-mismatch anomaly (below), which is not a route disposition, creates +none and has its own authorization; the two are recorded, reported and +counted separately. The engine exposes route-level backlog independently of job-level queue metrics, because an undispositioned route obligation may not yet have any job. @@ -439,7 +457,10 @@ route, it must make queryable at least: materializer (section 3.9) is deployed and eligible to perform the materialization; - the count and oldest age of open in-epoch contract-mismatch anomalies, per - epoch and revision. + epoch and revision, counting only anomalies committed `OPEN`; an anomaly + created born-terminal `ENROLLED` by first historical enrollment (below) was + never open, is never counted or aged as open and remains queryable as + terminal history. Route-level totals may aggregate these values, but no aggregate may hide an undispositioned backlog, an open anomaly or an unavailable compatible @@ -566,60 +587,195 @@ payload/schema version is not a member of the revision's accepted-version set, so that it fails the revision's immutable kind/version predicate. Such an event becomes a durable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly, or an equivalently explicit durable routing anomaly whose exact representation is an -implementation-specification detail. The anomaly: - -- durably identifies the event, the `route_key`, the epoch, the expected - immutable revision, and the actual durable event kind and payload/schema - version that failed matching; -- is queryable, counted and age-visible, and raises durable operator attention; +implementation-specification detail. The anomaly comes into existence in only +two ways, both governed by "Canonical mismatch-anomaly decision order" below: +detection records it `OPEN`; or, where a compatible, explicitly authorized +first historical enrollment linearizes before any detector has committed it, +that enrollment's transaction records it born-terminal `ENROLLED`. The +anomaly: + +- durably identifies the event, the `route_key`, the original epoch, the + expected immutable revision, and the actual durable event kind and + payload/schema version that failed matching, together with truthful + first-observer provenance: the detector that recorded it `OPEN`, or the + enrollment that created it born-terminal; +- is durable, queryable and auditable whether it is open or terminal; while it + is `OPEN` it is counted and age-visible and raises durable operator + attention, and an anomaly created born-terminal `ENROLLED` is never counted + or aged as open; - is never classified as successful routing; - creates no route disposition and no job merely because it was detected, and - does not consume the `(event_id, route_key)` disposition uniqueness slot; + detection does not consume the `(event_id, route_key)` disposition + uniqueness slot; - is never reinterpreted automatically under a later revision or any "current" contract. -The anomaly remains open until, under explicit authorization, either: +A detector-created anomaly remains `OPEN` until, under explicit authorization, +either: 1. historical enrollment (below) selects a revision of the same logical route that is validated as compatible with the event and creates the one allowed - `(event_id, route_key)` disposition; or + `(event_id, route_key)` disposition, with terminal outcome `ENROLLED`; or 2. an explicitly approved reconciliation/divergence procedure records why no route disposition will be created and what compensating state or effect - covers the event. + covers the event, with terminal outcome `RECONCILED/DIVERGED`. + +Where path 1 linearizes before any detector has committed the anomaly, the +same enrollment transaction creates it born-terminal `ENROLLED`; it never +commits, and never pretends to have observed, a prior `OPEN` state or a prior +detector run. Path 2 resolves only an already committed `OPEN` anomaly and +never creates one. Those two paths are mutually exclusive for one logical anomaly. Every in-epoch -contract mismatch has one **canonical logical anomaly identity**, a function -of the mismatch facts rather than of the detection, backfill request or -operator procedure that observed it, whose exact composition and storage are +contract mismatch has one **canonical logical anomaly identity**, a pure +function of the immutable mismatch facts of the event and the route: because +an event's captured routing generation lies inside at most one epoch of a +route, the event and the stable `route_key` determine the original epoch and +its expected revision, which are recorded as immutable audit facts. The +identity never depends on the detection, enrollment, backfill request, +compatible revision selected for remediation or operator procedure that +observes or resolves the anomaly, and its exact composition and storage are implementation-specification details; duplicate detection, a generic backfill request or a separately chosen resolution identifier can therefore never mint a parallel anomaly that reaches a second terminal outcome. Each logical anomaly reaches at most one effective **terminal resolution** - `ENROLLED` -through path 1 or `RECONCILED/DIVERGED` through path 2 - arbitrated by a -database-enforced terminal-resolution decision that both paths share. An -application-level "read unresolved, act, then append the outcome" sequence -without that enforcement does not satisfy this rule. "Mutually exclusive -terminal resolution of a mismatch anomaly", in "Historical enrollment, -backfill and replay" below, defines the decision boundary and both commit -orders. +through path 1 or `RECONCILED/DIVERGED` through path 2 - arbitrated by one +database-enforced canonical-anomaly decision order that detection, first +historical enrollment and divergence share and that also arbitrates first +creation of the anomaly when no anomaly row yet exists. An application-level +"read unresolved, act, then append the outcome" or "check absent, then +insert" sequence without that enforcement does not satisfy this rule. +"Canonical mismatch-anomaly decision order", in "Historical enrollment, +backfill and replay" below, defines the decision boundary and every commit +order. Resolution is recorded as append-only, audited evidence linked to the one anomaly, including actor, authority, reason and the resulting disposition or -divergence record. Resolving an anomaly never deletes it or its detection -evidence, and a retry after terminal resolution reads and reports the same -already-effective outcome rather than creating a second terminal decision. +divergence record. Resolving an anomaly never deletes it, its immutable +mismatch evidence or its detection provenance, and a retry after terminal +resolution reads and reports the same already-effective outcome rather than +creating a second terminal decision. Detection records the facts above and raises attention; it need not calculate or persist which resolution paths are available, and any availability-dependent decision about the anomaly is taken from the durable -state current when that decision is made. Detection is never suppressed +state current when that decision is made. Detection that finds the canonical +anomaly already committed, `OPEN` or terminal, converges on it without +reopening, re-aging or duplicating it. Detection is never suppressed because a revision has been withdrawn. The ordering of anomaly creation against a concurrent revision withdrawal is defined in "Revision withdrawal and route retirement" below. Route health exposes open anomalies separately from, and alongside, the undispositioned eligible-event backlog. An in-epoch contract mismatch is -therefore never an invisible fourth eligibility state. +therefore never an invisible fourth eligibility state, and a mismatched event +is never counted in the eligible-event backlog. An anomaly's terminal +`ENROLLED` outcome is a route decision; it is not evidence that a job +executed or that a provider accepted an effect. + +### Original-epoch classification and first route-disposition admission + +For each logical route, every committed event has exactly one immutable +**original-epoch classification**, derived only from facts this section +already fixes - the event's committed durable kind, payload/schema version +and captured routing generation, and the route's activation epochs with the +immutable route-contract revision each references. It introduces no second +epoch or eligibility definition: + +1. **ordinarily eligible** - the captured generation lies inside an epoch and + the event satisfies that epoch revision's kind/version predicate; +2. **in-epoch contract mismatch** - the captured generation lies inside an + epoch, the event's kind is that revision's consumed event kind and its + version is outside the revision's accepted-version set (above); +3. **outside every epoch** - the captured generation lies before the route's + first epoch, in an inactive inter-epoch gap, or after its most recent + closed epoch while no later epoch is open; +4. **unrelated kind** - the captured generation lies inside an epoch whose + revision does not consume the event's kind. + +Because epochs are immutable and pairwise-disjoint and a revision referenced +by an epoch is never rewritten, the classification never changes after the +event commits: closing an epoch, opening a later epoch, a revision transition, +revision withdrawal and route retirement leave it unchanged. It is never +derived from a currently open epoch's revision, a current-contract projection, +a revision selected for remediation, an operator's assertion or an earlier +non-serialized application read. + +**Universal first route-disposition admission guard.** Every operation that +can create the first durable disposition for an `(event_id, route_key)` pair +derives this classification from the committed immutable facts and proves its +own path-specific eligibility and authorization inside the +database-consistent transaction that would consume that pair's uniqueness +slot. A stale application observation, a stated staff reason or covering +evidence is not a substitute for that proof, and an admission whose facts +cannot be verified fails closed. The first-disposition writers this ADR +already approves are, exhaustively: + +- ordinary routed materialization of an ordinarily eligible obligation under + its original epoch revision: creation of a new routed job, attachment to a + coalescing-open job, and the post-seal successor path of section 3.5; +- explicitly authorized historical enrollment of an event outside every epoch + of the route, under the selected-revision validation below; +- compatible, explicitly authorized first historical enrollment of an + in-epoch contract mismatch, under the canonical mismatch-anomaly decision + order below; +- the explicit, authorized and audited terminal non-materialized disposition + of an ordinarily eligible, still-owed obligation (above), through ADR-0010's + protected staff-command seam where staff act. + +An explicitly approved migration, reconciliation or disposition plan under +"Revision withdrawal and route retirement" below creates a first disposition +only through one of those writers, under that writer's guard and authority; +such a plan grants no additional mutation authority. The writers do not share +one permission: each keeps its own authorization. + +Admission by classification: + +- **ordinarily eligible** - ordinary materialization, or the explicit terminal + non-materialized disposition where the obligation is proven still owed and + the existing section-3.2 and ADR-0010 conditions hold. An idempotency + collision, a sealed job, an unavailable materializer or retry exhaustion + never qualifies the obligation for that disposition automatically. Historical + work for the obligation is handled under its original epoch revision and the + existing owed-route and materializer rules; an enrollment or mismatch command + cannot select a different revision for it, change its original + interpretation or bypass its disposition uniqueness. +- **in-epoch contract mismatch** - **no disposition exists for an in-epoch + contract mismatch except the one created by compatible, explicitly + authorized first historical enrollment, atomically with its canonical + anomaly's terminal `ENROLLED` outcome** and with its indispensable newly + created routed job and input facts or eligible open-job attachment. The + terminal non-materialized disposition command and every other + disposition-creating path refuse. The anomaly's `RECONCILED/DIVERGED` + terminal outcome creates no disposition. +- **outside every epoch** - only the existing explicitly authorized historical + enrollment/backfill path, subject to its own first-disposition and + compatibility controls. Staff authority or covering evidence does not make + such an event ordinarily eligible, and the terminal non-materialized + disposition command refuses. +- **unrelated kind** - the event creates neither an obligation nor an anomaly + for the route; the terminal non-materialized disposition command and every + mismatch operation refuse. + +A refused admission creates no disposition, consumes no uniqueness slot and +creates no job, canonical anomaly, terminal anomaly resolution or external +effect; the refusal is audited under the refusing path's existing command +semantics. An existing `OPEN` or terminal anomaly and its immutable evidence +remain unchanged. Where no anomaly exists, the refused operation neither +creates, suppresses nor resolves it, and independent detection remains able to +record it `OPEN` later. + +Admission stays valid against concurrent detection, first enrollment, +divergence, ordinary materialization, revision withdrawal and epoch lifecycle +operations. Ordinary materialization and the terminal non-materialized +disposition of an ordinarily eligible obligation compete through the existing +absolute `(event_id, route_key)` uniqueness boundary, and a loser resolves its +outcome from committed durable state; first mismatch enrollment additionally +participates in the canonical mismatch-anomaly decision order, even when no +anomaly row exists at its first read. A uniqueness conflict alone never means +that work is complete and never permits the event to be reinterpreted. The +guard introduces no new route key, epoch, replay or current-state job and no +external-effect authority. ### Historical enrollment, backfill and replay @@ -640,6 +796,18 @@ event's routing generation, because enrollment is explicit and outside ordinary epoch eligibility, but only when that revision is validated as semantically compatible with the event. +Before selecting a revision, enrollment classifies the event under +"Original-epoch classification and first route-disposition admission" above, +inside the transaction that would create its disposition. Enrollment applies +only to an event outside every epoch +of the route or to an in-epoch contract mismatch. A mismatch-enrollment or +mismatch-divergence command for an ordinarily eligible, outside-epoch or +unrelated-kind event is refused as a mismatch operation, with no anomaly, no +terminal mismatch decision and no change to the event's route disposition or +backlog; explicitly authorized outside-epoch enrollment and ordinary +materialization keep their own semantics and are never relabelled as anomaly +resolution. + Before enrollment creates the first route disposition for an event, the selected revision is validated against the event's durable facts, field by field across its semantic signature: @@ -664,13 +832,16 @@ disposition, never from an earlier separate read. If any check fails, or the revision has been withdrawn, the enrollment is refused before any route disposition or job is created. Where the enrollment -resolves an in-epoch contract-mismatch anomaly, these checks, the anomaly's -still-open state and the terminal-resolution decision are made inside the one -database-enforced boundary defined below. The -`(event_id, route_key)` uniqueness slot remains unused, so a later enrollment -under a compatible revision remains possible. The refused attempt and its reason -are durably audited, and any existing anomaly, backlog or attention state for -the event remains visible. +is the first historical enrollment of an in-epoch contract mismatch - whether +its canonical anomaly is committed `OPEN` or absent at the enrollment's first +read - the immutable classification, these checks, the anomaly's current state +and the terminal decision are made inside the canonical mismatch-anomaly +decision order defined below. A refused enrollment leaves the +`(event_id, route_key)` uniqueness slot unused, so a later enrollment under a +compatible revision remains possible, and it neither creates nor terminally +resolves a previously absent anomaly. The refused attempt and its reason are +durably audited, and any existing anomaly, backlog or attention state for the +event remains visible. Enrollment cannot bypass route deduplication and cannot create a second disposition for an event that already has one for that route, whichever revision @@ -688,57 +859,168 @@ rules of sections 3.3 and 3.5 remain mandatory, so historical enrollment cannot produce an unintended duplicate or regressive effect. A new route identity does not by itself grant permission to duplicate an external effect. -### Mutually exclusive terminal resolution of a mismatch anomaly - -An unresolved in-epoch contract-mismatch anomaly (above) is resolved by -exactly one of two authorized paths: compatible historical enrollment, which -creates the one allowed `(event_id, route_key)` disposition, or an explicitly -approved reconciliation/divergence procedure, which creates none. Because -divergence creates no disposition, `(event_id, route_key)` uniqueness alone -cannot arbitrate between them. Both paths therefore compete through one -database-enforced terminal-resolution decision on the anomaly's canonical -logical identity: a terminal-resolution transition, a uniqueness constraint -over the anomaly's terminal outcome or an equivalent serialized durable -decision, selected by the implementation specification, that admits at most -one effective terminal outcome per logical anomaly. Each path validates its -authorization, the anomaly's still-open state and its applicable current -conditions - for enrollment, the selected revision's field-by-field +### Canonical mismatch-anomaly decision order + +For one event and logical route classified as an in-epoch contract mismatch +from immutable original-epoch facts (above), three kinds of operation compete: +detection, which may create the canonical anomaly `OPEN`; compatible, +explicitly authorized first historical enrollment, which creates the one +allowed `(event_id, route_key)` disposition with terminal outcome `ENROLLED`; +and an explicitly approved reconciliation/divergence procedure, which creates +no disposition and may only resolve an already committed `OPEN` anomaly to +`RECONCILED/DIVERGED`. Because divergence creates no disposition, +`(event_id, route_key)` uniqueness alone cannot arbitrate between enrollment +and divergence; and because the anomaly row may not exist when the contenders +begin, uniqueness enforced only on a later anomaly insert cannot arbitrate +between detection and enrollment either. + +All three therefore share one database-enforced, serially consistent +**canonical mismatch-anomaly decision order** on the anomaly's canonical +logical identity. The order covers first creation of the anomaly as well as +its terminal resolution, including the case in which no anomaly row exists +when several contenders take their first reads; an application-level "check +absent, then act" or "read unresolved, act, then append the outcome" +sequence, with uniqueness enforced only on a later insert, does not satisfy +it. Every outcome is equivalent to one lawful serial order of the contenders. +The mechanism - a claimed canonical identity, a terminal-outcome uniqueness +constraint, an upsert, a canonical lock key, predicate or serializable +conflict detection, or another equivalent database-enforced scheme - is +selected by the implementation specification; the observable atomicity and +order are mandatory. Each operation validates the immutable classification, +its own authorization and its current conditions inside that order, in the +transaction that makes its outcome durable, never from a stale earlier +observation: for enrollment, the selected revision's field-by-field compatibility and its availability under the withdrawal/selection order of "Revision withdrawal and route retirement" below; for divergence, the -reconciliation authority and approved reason - inside that database-enforced -decision boundary, in the transaction that makes the outcome durable, never -from a stale earlier observation. - -Both commit orders are defined: - -- enrollment first: the transaction that durably selects the terminal - `ENROLLED` outcome also creates the allowed `(event_id, route_key)` - disposition and, where the enrollment creates a new routed job rather than - attaching to an existing coalescing-open job, that job and its - indispensable input or membership facts under "Atomic creation of a routed - job" (section 3.5); a concurrent or later divergence attempt for the same - anomaly detects the effective terminal decision and cannot record a - contradictory accepted no-disposition outcome; -- divergence first: the transaction that durably selects the terminal - `RECONCILED/DIVERGED` outcome persists the approved reason, authority and - the durable resolution or compensation reference; it creates no route - disposition and no job merely by resolving the anomaly, and the - `(event_id, route_key)` slot remains unused; a concurrent or later first - historical enrollment for that same anomaly is refused before it can create - a disposition, job or external effect, and the unused slot is not available - to that losing enrollment. - -In either order the losing path detects the durable terminal decision before -committing its own conflicting record or work, aborts or deterministically -refuses, and leaves accurate audit and attention state; no two contradictory -terminal outcomes can both commit, regardless of the absence of a disposition -from divergence. A transaction that aborts or crashes before its terminal -decision commits leaves the anomaly unresolved and independently retryable. -After a terminal decision commits, repeated requests, retries and audit reads -observe the same effective outcome and create no second or contradictory -terminal result. The anomaly's open count, age and attention evidence -transition from open to exactly one terminal outcome without erasing the -detection facts. +committed `OPEN` state, the reconciliation authority, the approved reason and +the covering or compensation evidence. + +The lawful orders are: + +- **detection first:** the first valid detection commits one canonical `OPEN` + anomaly with its immutable mismatch evidence and attention, and no route + disposition or job. A later compatible, authorized first enrollment changes + it to terminal `ENROLLED` in the transaction that also creates the one route + disposition and either a new routed job with its indispensable input or + membership facts under "Atomic creation of a routed job" (section 3.5) or an + eligible attachment to an existing coalescing-open job under the + attachment/seal boundary of section 3.5. A later authorized divergence may + instead change it to terminal `RECONCILED/DIVERGED`, persisting the approved + reason, authority and durable resolution or compensation reference, with no + route disposition and no job. +- **enrollment first:** where a compatible, authorized first enrollment + linearizes before any detector has committed the anomaly, the same + transaction creates the canonical anomaly born-terminal `ENROLLED` - with the + immutable mismatch facts and the enrollment's actor and authority as its + provenance - together with the one route disposition and the indispensable + new routed job and input facts or eligible open-job attachment. These commit + together or none commits. The enrollment never commits its disposition first + and attaches anomaly evidence or resolution later, never commits a transient + `OPEN` state and never records a detector run that did not occur. +- **later detection:** detection after either terminal outcome observes that + outcome and converges on the same canonical anomaly. It may deduplicate and + retain truthful detection provenance, but it never reopens or re-ages the + anomaly, never counts it as open, never creates a second anomaly, job or + disposition and never claims that an earlier detector transaction existed + where only enrollment established the first anomaly evidence. +- **divergence requires a committed `OPEN` anomaly:** a divergence may commit + terminal `RECONCILED/DIVERGED` only by proving, inside the decision order, + that the event is an in-epoch contract mismatch under immutable + original-epoch facts, that its canonical anomaly is already committed and + `OPEN`, that the actor holds explicit reconciliation authority with an + approved reason and durable covering or compensation evidence under + ADR-0010 and this ADR, and that no competing terminal outcome has won. An + absent anomaly, an `ENROLLED` anomaly (born-terminal or resolved), a + `RECONCILED/DIVERGED` anomaly and a non-mismatch event each refuse it. + Divergence never creates the anomaly, even born-terminal, and never creates + a route disposition or job. +- **divergence before detection:** a divergence that linearizes before a + concurrent detector's creation commits is refused, with no canonical + anomaly, terminal record, route disposition, job or external effect, even if + the detector commits immediately afterwards; request arrival and first-read + timing are not the linearization order. The detector may then commit `OPEN`. + The refusal is not authority for a later terminal decision: a new divergence + attempt is decided at its own linearization, revalidating explicit + authority, reason and evidence and the existing command-idempotency + semantics of ADR-0010 section 4.7 and the owning domain. Whether a + still-valid authorization may be re-presented is governed by that existing + policy; this ADR mandates no new command identity or approval kind. +- **enrollment versus divergence on an `OPEN` anomaly:** the first committed + terminal outcome wins. Divergence first prevents any later first enrollment + from creating a disposition, job or effect, notwithstanding the unused route + slot, which is not available to the losing enrollment; enrollment first, + including a born-terminal `ENROLLED` anomaly, prevents any later accepted + no-disposition divergence. There is no born-terminal `RECONCILED/DIVERGED` + outcome. + +In every order the losing operation detects the effective durable decision +before committing its own conflicting record or work, then aborts, re-reads +or deterministically returns the already-effective outcome, and leaves +accurate audit and attention state. No two contradictory terminal outcomes, no +second anomaly and no second disposition can commit; no `ENROLLED` outcome +can commit without its disposition and indispensable job or attachment facts; +and a losing, refused or aborted path leaves no orphan or claimable job and no +provider effect. A zero-row or phantom race, a stale absence read, a +uniqueness collision or a lost lock never bypasses the order. A transaction +that aborts or crashes before its decision commits leaves no partial result: +the anomaly stays in its prior committed state - `OPEN`, or absent - and +independent detection and a later authorized enrollment or divergence remain +possible. After a decision commits, repeated requests, retries after +acknowledgment loss and audit reads observe the same anomaly, terminal +outcome, disposition and job or attachment, and create no second or +contradictory result and no provider replay. A detector-created anomaly's open +count, age and attention evidence transition from open to exactly one terminal +outcome without erasing its detection facts; a born-terminal anomaly was never +open. + +A refused first enrollment - wrong original classification, an invalid or +withdrawn revision, missing explicit authority, an unavailable materializer, +an unsafe seal or successor, a uniqueness or transaction failure, a lost +terminal race or a crash before commit - does not itself create or terminally +resolve a previously absent anomaly. An existing `OPEN` anomaly stays `OPEN` +with truthful age and attention; otherwise independent detection from the +immutable event and epoch facts can still record it `OPEN`. An audited +refusal fabricates no anomaly, job or authority. + +Compliant first-disposition writers make a mismatch disposition without its +canonical terminal `ENROLLED` anomaly and indispensable job or attachment +facts impossible. Should contradictory durable state nevertheless be +observed, for example a disposition for an in-epoch contract mismatch without +that canonical outcome, the engine fails closed for that event and route: no +operation treats it as healthy or successful routing, adopts the existing +disposition as `ENROLLED`, records divergence or repairs it, and no new +disposition, job or provider effect is justified by it; the mandatory +immutable mismatch evidence and the contradictory durable records are +preserved and made visible to engineering control rather than suppressed, +hidden or normalized into success. The condition is a control HOLD pending a +separately authorized incident-recovery design. This ADR defines no third +anomaly lifecycle state, job hold, automatic adoption or repair, detector +bypass, retroactive authority or provider effect for such state. + +Terminal `ENROLLED` completes the mismatch's routing/disposition decision +only. It does not claim that a job executed, that a provider accepted an +effect or that external state is current; the shared job lifecycle and +target-scoped reconciliation continue to decide effect progress +independently. + +The decision order composes with, and introduces no second order beside, the +per-`route_key` epoch lifecycle and retirement serialization, the +withdrawal/selection order and the withdrawal strand-safety decision of +"Revision withdrawal and route retirement" below, `(event_id, route_key)` +uniqueness, atomic routed-job creation, the coalescing attachment/seal +boundary and the post-seal successor path of section 3.5. A terminal +`ENROLLED` decision is atomic with the selected revision's availability and +with its routed-job creation or attachment. A permanently sealed job never +receives the mismatched input, so the mismatch reaches `ENROLLED` only +together with a valid eligible open attachment or a newly created successor +with its disposition and input facts, and otherwise stays unresolved - +`OPEN` where detected, absent pending detection - with truthful attention. A +new job or generation identity grants no duplicate external-effect authority. +The combined locks, constraints and serializable retries follow one +documented, deterministic and deadlock-safe order, or an equivalent +deadlock-safe retry discipline, proven against the actual PostgreSQL design +that the implementation specification selects; this ADR selects no lock +order, SQL or schema. A generic backfill or replay request that targets the event and route of a resolved anomaly is, for that anomaly, a first historical enrollment governed @@ -758,8 +1040,9 @@ durable intent and evidence are what this rule makes consistent, and PostgreSQL is never assumed to commit atomically with a third-party provider write or to grant new external-write authority. This anomaly-resolution divergence is distinct from the separately authorized terminal non-materialized -route disposition of an eligible obligation (section 3.2): under the mismatch -semantics above it creates no disposition. +route disposition of an ordinarily eligible obligation (section 3.2): under +the mismatch semantics above it creates no disposition, and it is recorded and +counted separately from that disposition's accepted divergence. ### Revision withdrawal and route retirement @@ -781,7 +1064,11 @@ compatible materialization path, or while any unresolved in-epoch contract-mismatch anomaly would be left without an approved resolution path (a selectable compatible revision or an approved reconciliation/divergence procedure), unless an explicitly approved migration, reconciliation or -disposition plan closes or transfers that work safely. +disposition plan closes or transfers that work safely. Such a plan creates any +first route disposition only through a writer this ADR already approves, under +the universal admission guard of "Original-epoch classification and first +route-disposition admission" above, and grants no additional mutation +authority. That strand-prevention decision must be **database-consistent and stable through the withdrawal's linearization/commit point**: a withdrawal may commit @@ -803,7 +1090,11 @@ mechanism with equivalent effect. The covered mutations include, at minimum: anomaly identifies the epoch's expected revision; it need not select or reference the separate compatible revision whose availability for historical enrollment makes it resolvable, so the withdrawal/selection order below does - not by itself order it against the withdrawal, and this rule does; + not by itself order it against the withdrawal, and this rule does. Only + detection creates an unresolved (`OPEN`) anomaly: an anomaly that compatible + first historical enrollment creates born-terminal `ENROLLED` is not + unresolved and cannot be stranded by a withdrawal, while that enrollment's + revision selection participates in the withdrawal/selection order below; - any other already-defined durable operation that removes the last approved resolution or materialization path - a selectable compatible revision, an explicitly approved reconciliation/divergence procedure or an explicitly @@ -894,9 +1185,12 @@ Both commit orders are defined: - withdrawal first: when the withdrawal linearizes first, a later epoch opening that would reference the revision is refused before the epoch is created, a later first historical enrollment that selects the revision is refused before - any route disposition or job is created, the `(event_id, route_key)` - uniqueness slot remains unused, and existing audit, anomaly, backlog and - attention state remains visible under the rules above. + any route disposition or job is created - and, for an in-epoch contract + mismatch, before any canonical anomaly creation or terminal `ENROLLED` + decision - the `(event_id, route_key)` uniqueness slot remains unused, and + existing audit, anomaly, backlog and attention state remains visible under + the rules above. Mismatch detection remains able to record or retain the + canonical anomaly after such a withdrawal. A transaction that loses that race may retry only after re-reading current durable state and selecting a revision that is still available, under the @@ -914,9 +1208,10 @@ exclusion constraint is an implementation-specification detail. Whichever mechanism is selected must compose with the serialized open, close, reactivate and retire lifecycle, with database-atomic equivalent-revision convergence, with historical-enrollment validation and `(event_id, route_key)` disposition -uniqueness, with the strand-prevention rule above and its decision-stability -rule, with permanent route retirement and with the engine's normal deadlock -and lock-ordering controls. No +uniqueness, with the canonical mismatch-anomaly decision order above, with the +strand-prevention rule above and its decision-stability rule, with permanent +route retirement and with the engine's normal deadlock and lock-ordering +controls. No implementation may introduce a second route lifecycle order that contradicts the per-`route_key` order. @@ -1293,9 +1588,12 @@ started, and so coalescing-open, only until its first successful execution claim/start commits (below). An explicit, authorized and audited terminal non-materialized disposition points to no job and records the actor, authority, reason and any covering -reconciliation/backfill. Materialized and terminal non-materialized -dispositions share the same `(event_id, route_key)` uniqueness boundary and -cannot coexist for the same route/event pair. Coalescing therefore forms a +reconciliation/backfill; it exists only for an ordinarily eligible, still-owed +obligation under the universal first route-disposition admission guard of +section 3.2, never for an in-epoch contract mismatch. Materialized and +terminal non-materialized dispositions share the same `(event_id, route_key)` +uniqueness boundary and cannot coexist for the same route/event pair. +Coalescing therefore forms a many-route-to-one-job relationship only among materialized dispositions; it never erases the per-event route records needed for audit/completeness. @@ -1541,15 +1839,21 @@ tables, constraints, sequences and locks are implementation-specification details. **Historical enrollment and anomaly resolution.** A first historical -enrollment that uses this path obeys the same rules. Where the enrollment -resolves an in-epoch contract-mismatch anomaly, the terminal-resolution -decision, the enrollment disposition and any newly created successor and its -input facts commit within one consistent terminal-decision transaction -boundary ("Mutually exclusive terminal resolution of a mismatch anomaly", -section 3.2); an enrollment that loses a successor's seal race aborts as a -whole, leaving the anomaly open and retryable. Historical enrollment, anomaly -terminal resolution, revision selection and withdrawal and route retirement -keep their existing orders. +enrollment that uses this path obeys the same rules. Where the enrollment is +the first historical enrollment of an in-epoch contract mismatch, the +canonical-anomaly decision - resolving a committed `OPEN` anomaly to +`ENROLLED`, or creating the anomaly born-terminal `ENROLLED` where none is +committed - the enrollment disposition and the eligible open-successor +attachment or newly created successor and its input facts commit within one +transaction under the canonical mismatch-anomaly decision order of section +3.2. A permanently sealed job never receives the mismatched input. An +enrollment that loses a successor's seal race, or finds no eligible successor +that can lawfully be selected or created, aborts or is refused as a whole, +leaving the anomaly in its prior committed state - `OPEN` and retryable, or +absent and still detectable - with truthful attention; it never creates the +absent anomaly. Historical enrollment, the canonical anomaly decision order, +revision selection and withdrawal and route retirement keep their existing +orders. **Effect semantics, fencing and ambiguity.** A successor executes under the same canonical external effect-target identity, target-scoped concurrency @@ -1625,9 +1929,11 @@ permanent-key collision. **Exceptional dispositions unchanged.** The explicit, authorized and audited terminal non-materialized disposition of section 3.2 remains available only under its existing section-3.2 and ADR-0010 controls, with real covering -reconciliation or expressly accepted divergence. A seal, an idempotency -collision, retry exhaustion or an unavailable materializer never creates it -automatically and is never success. +reconciliation or expressly accepted divergence, and only for an ordinarily +eligible, still-owed obligation under the universal first route-disposition +admission guard; it is refused for an in-epoch contract mismatch. A seal, an +idempotency collision, retry exhaustion or an unavailable materializer never +creates it automatically and is never success. This rule supplements only the post-seal recovery outcome of the claim/start-first order above. It keeps the first successful claim/start as @@ -1652,7 +1958,13 @@ job-input or membership fact indispensable for that disposition's effect semantics under the kind's input or coalescing contract become durable in one PostgreSQL transaction. The job and the disposition become visible together on commit; a failure, a uniqueness conflict or a crash before commit leaves -neither durably created by that attempt. The exact tables, constraints and SQL +neither durably created by that attempt. Where the routed job is created by +the first historical enrollment of an in-epoch contract mismatch, the same +transaction also carries that mismatch's canonical terminal `ENROLLED` +decision under the canonical mismatch-anomaly decision order of section 3.2; +if any indispensable insert fails, every newly created routed fact and the +terminal selection roll back together, leaving the anomaly in its prior +committed state, `OPEN` or absent. The exact tables, constraints and SQL are implementation-specification details; the all-or-nothing boundary is not. The scheduler and the workers must never be able to claim, start or produce @@ -2116,6 +2428,18 @@ to perform internal domain operations merely to impersonate a machine role; internal authorization/execution boundaries must instead be specified explicitly by the owning implementation. +Staff and reconciliation authority used by section 3.2 - for explicit +historical enrollment, mismatch-anomaly divergence, the terminal +non-materialized route disposition and other route or anomaly control +operations - remains ADR-0010's protected, audited staff-command authority +(ADR-0010 sections 4.7 and 4.8) or another explicitly approved domain +authority. This ADR creates no new staff, reconciliation, worker or machine +permission for those operations. Correct original-epoch classification, a +stated reason or a generic command that claims staff or reconciliation +authority never grants permission by itself, and each such operation +revalidates its actual authority and the existing command-idempotency +semantics at its own decision boundary. + ### 3.15 Operability, fairness and operator controls The shared engine must expose durable/queryable operational signals at least by @@ -2142,12 +2466,22 @@ have no job yet: obligations, whether a compatible deployed/eligible materializer exists, for active routes and for temporarily inactive or permanently retired routes with outstanding obligations; -- the count and oldest age of open in-epoch contract-mismatch anomalies; -- terminal route dispositions that represent accepted divergence rather than - covered reconciliation/backfill. +- the count and oldest age of open in-epoch contract-mismatch anomalies, + counting only anomalies committed `OPEN`: an anomaly created born-terminal + `ENROLLED` by first historical enrollment is never counted or aged as open, + and terminal anomalies remain queryable with their original mismatch facts, + provenance, terminal outcome and authority; +- terminal non-materialized route dispositions of ordinarily eligible + obligations that represent accepted divergence rather than covered + reconciliation/backfill, reported separately from `RECONCILED/DIVERGED` + terminal resolutions of mismatch anomalies, which are not route + dispositions. An aggregate route view never hides an unhealthy older revision behind a -healthy current revision. +healthy current revision. A mismatched event is never counted in the +eligible-event backlog, and a committed but held successor or routed job is +reported through job-level signals, not as an open anomaly or an +undispositioned obligation; no metric double-counts or hides one event. Each job kind defines bounded attempts, backoff and concurrency limits. Backoff must prevent tight retry loops; exact algorithms and jitter are implementation @@ -2492,9 +2826,13 @@ execution until a separate approved architecture decision says otherwise. 5. Deactivation, revision transitions and retirement never erase previously eligible work: events eligible through any epoch remain owed, under that epoch's revision, until materialized or given an explicit authorized/audited - route disposition. In-epoch contract-mismatch anomalies remain open and + route disposition. Every in-epoch contract mismatch is recorded as a + durable, visible anomaly. A detector-created anomaly remains `OPEN` and visible until resolved through compatible enrollment or an approved - reconciliation/divergence, recorded append-only. + reconciliation/divergence, recorded append-only; an anomaly that compatible + first historical enrollment creates born-terminal `ENROLLED` was never + open, is not required to have been, and keeps its mismatch evidence + durable and queryable. 6. A newly opened epoch, whether a first activation, a reactivation or a revision transition, does not consume events from before its activation boundary, including inactive-gap events, without explicitly authorized @@ -2515,10 +2853,20 @@ execution until a separate approved architecture decision says otherwise. non-materialized disposition maps to no job, records actor, authority, reason and any covering reconciliation/backfill, and shares the same uniqueness boundary so it cannot coexist with a materialized disposition for - the same event/route pair. The uniqueness boundary spans every activation - epoch and every route-contract revision of the route and excludes any epoch - or revision identifier, so neither reactivation, a revision transition nor - backfill/replay can rematerialize an event that already has a disposition. + the same event/route pair. Only a durable route obligation proven + ordinarily eligible from its immutable original epoch and still owed may + receive that terminal non-materialized disposition: the authorizing + evidence, reconciliation or expressly accepted divergence, protected + staff-command audit and immutable eligibility are validated inside the + disposition's transaction. It is prohibited for an in-epoch contract + mismatch, an event outside every epoch and an event of an unrelated kind; + a mismatched event's first route disposition arises solely from explicitly + authorized compatible historical enrollment under canonical anomaly and + terminal `ENROLLED` arbitration (invariant 47). The uniqueness boundary + spans every activation epoch and every route-contract revision of the + route and excludes any epoch or revision identifier, so neither + reactivation, a revision transition nor backfill/replay can rematerialize + an event that already has a disposition. 9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. A CURRENT_STATE fingerprint covers all canonical inputs determining its effect. A REVISION_BOUND effect must not regress a newer already-applied revision @@ -2585,11 +2933,20 @@ execution until a separate approved architecture decision says otherwise. 34. Merge, migration, deployment, handler activation, provider access and production activation remain separate gates. 35. An event of an epoch revision's consumed event kind, captured inside that - epoch, whose version is outside the revision's accepted-version set is a - durable `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that is queryable, - counted, age-visible and attention-raising. Detection creates no route - disposition or job, consumes no `(event_id, route_key)` slot, is never - successful routing and is never reinterpreted under a later contract. + epoch, whose version is outside the revision's accepted-version set is an + in-epoch contract mismatch, classified solely from immutable event, epoch + and revision kind/version facts, and is recorded as one durable, queryable + `IN_EPOCH_CONTRACT_MISMATCH` routing anomaly that is never successful + routing and is never reinterpreted under a later contract. A pure + detection transaction creates or observes that one canonical anomaly - + created `OPEN`, counted, age-visible and attention-raising - with no route + disposition or job and without consuming the `(event_id, route_key)` + slot. Separately authorized successful first historical enrollment may + create or observe the same anomaly born-terminal `ENROLLED` only in its + all-in-one anomaly, resolution, disposition and job transaction + (invariant 47); that is enrollment, not a detector creating a + disposition. Divergence never creates the anomaly, a route disposition or + a job. Durable attention and audit history are preserved. 36. Route-contract revisions with the same canonical semantic signature converge atomically to one canonical revision through database-enforced uniqueness or serialization, never an application-level check-then-insert; @@ -2694,27 +3051,40 @@ execution until a separate approved architecture decision says otherwise. Attachment to an existing job remains governed by invariant 42; independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. -45. Every in-epoch contract-mismatch anomaly has one canonical logical - identity and at most one effective terminal resolution - compatible - historical enrollment or approved reconciliation/divergence - arbitrated - by a database-enforced terminal-resolution decision shared by both paths; - each path validates authorization, the anomaly's open state and its - current compatibility, availability or reconciliation conditions inside - that boundary, never from a stale observation. Enrollment-first commits - the terminal outcome, the allowed disposition and any new routed job and - input facts in one transaction and excludes a later accepted divergence; - divergence-first commits the approved reason, authority and resolution - reference, creates no disposition or job, leaves the - `(event_id, route_key)` slot unused and refuses the losing enrollment for - that anomaly before any disposition, job or effect. The loser detects the - terminal decision before committing, aborts or deterministically refuses - and preserves audit and attention state; abort or crash before commit - leaves the anomaly open and retryable; retries after commit observe one - outcome. Detection and resolution evidence remain append-only; a generic - backfill cannot bypass a terminal divergence; separately authorized - replay is governed by its own rules and replaces no terminal outcome; and - external compensation remains fenced by the existing effect, ambiguity - and reconciliation controls. +45. Exactly one canonical in-epoch contract-mismatch anomaly, whose canonical + logical identity is a pure function of immutable mismatch facts, and at + most one effective terminal resolution - compatible historical enrollment + (`ENROLLED`) or approved reconciliation/divergence + (`RECONCILED/DIVERGED`) - exist for one logical event/route mismatch. One + database-enforced canonical-anomaly decision order covers both first + creation when the anomaly is absent and resolution when it is `OPEN`; + each path validates its authorization, the immutable classification, the + anomaly's state and its current compatibility, availability or + reconciliation conditions inside that order, never from a stale + observation. Authorized enrollment with a valid original classification + and a selected compatible revision may create the canonical anomaly + born-terminal `ENROLLED` or resolve an existing `OPEN` anomaly to + `ENROLLED`, atomically with its one disposition and indispensable new + routed job and input facts or eligible attachment, and excludes a later + accepted divergence. Authorized divergence may resolve only an already + committed `OPEN` anomaly, with immutable mismatch reclassification and + valid authority, reason and covering evidence; it commits the approved + reason, authority and resolution reference, creates no anomaly, + disposition or job, leaves the `(event_id, route_key)` slot unused and + refuses the losing enrollment for that anomaly before any disposition, + job or effect. Divergence while the anomaly is absent refuses and does + not preclude a later detector-created `OPEN` anomaly followed by + separately authorized resolution. The loser detects the effective + decision before committing, aborts or deterministically refuses and + preserves audit and attention state; abort or crash before commit leaves + the anomaly in its prior state, `OPEN` or absent, and retryable; retries + after commit, including after acknowledgment loss, observe one outcome + and create no competing terminal result or job. Detection, mismatch and + resolution evidence remain append-only; a generic backfill cannot bypass + a terminal divergence; separately authorized replay is governed by its + own rules and replaces no terminal outcome; and external compensation + remains fenced by the existing effect, ambiguity and reconciliation + controls. 46. A coalescing seal closes one logical job, not the route obligations that follow it. An eligible obligation refused because its job is permanently coalescing-sealed remains owed, undispositioned and visible; it is never @@ -2760,6 +3130,45 @@ execution until a separate approved architecture decision says otherwise. coalescing mode and may use only a separately approved safe event-distinct identity; the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. +47. Every operation that can create the first durable disposition for an + `(event_id, route_key)` pair - ordinary routed materialization, explicitly + authorized historical enrollment and the authorized terminal + non-materialized disposition, including when an approved migration, + reconciliation or disposition plan acts through them - classifies the + event from committed immutable original-epoch facts and proves its own + path-specific eligibility and authorization inside the + database-consistent transaction that would consume that slot, failing + closed when those facts cannot be verified. No disposition exists for an + in-epoch contract mismatch except the one created by compatible, + explicitly authorized first historical enrollment together with its exact + canonical terminal `ENROLLED` anomaly; detection and the + eligible-obligation terminal non-materialized disposition can neither + bypass canonical anomaly identity nor consume a mismatched route slot. For + an event and route classified as an in-epoch contract mismatch, detection + and compatible first historical enrollment share one database-enforced + canonical-anomaly creation and decision order even when no anomaly exists + at either transaction's first read, and authorized divergence shares that + order only as the resolution of a previously committed `OPEN` anomaly and + never creates one. Successful first enrollment atomically creates or + observes the canonical anomaly and commits terminal `ENROLLED`, exactly + one `(event_id, route_key)` disposition and every indispensable new-job or + eligible-attachment input fact; detection first may create only `OPEN`, + with no disposition or job; later detection converges with any already + effective terminal state without reopening, re-aging or double-counting it + and without fabricating earlier detection provenance. Divergence before + the anomaly exists refuses, and mismatch divergence and mismatch + enrollment refuse for a non-mismatch event. All contenders use immutable + original-epoch classification, preserve authorization and the revision + withdrawal/selection and strand-safety orders, allow at most one terminal + resolution and never create an orphan job, a duplicate route disposition, + a falsely successful route, an unauthorized provider effect or a hidden + open-anomaly obligation. A refused or failed enrollment neither creates + nor resolves a previously absent anomaly. Terminal `ENROLLED` is a route + decision, not evidence of handler or provider success. Contradictory + durable state outside these compliant transactions fails closed, with its + mismatch evidence preserved and visible to engineering control, and + requires separately approved incident recovery rather than an automatic + third anomaly state, job hold, adoption or repair. ## 8. Implementation impact and decomposition @@ -2782,6 +3191,11 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - atomic creation of a routed job with its materialized disposition and indispensable input facts, and the database-enforced mutually exclusive terminal resolution of a mismatch anomaly; + - the canonical mismatch-anomaly decision order between detection, first + historical enrollment (including a born-terminal `ENROLLED` anomaly) and + divergence of an already `OPEN` anomaly, and the universal + original-epoch first route-disposition admission guard for every + first-disposition writer; - post-seal materialization of obligations refused after a coalescing seal through eligible open successors or distinct successor generations, with database-arbitrated convergence; @@ -2998,7 +3412,10 @@ Rollout should prove in order: of revision withdrawal against epoch opening and first enrollment, and the stability of withdrawal's strand-safety decision against concurrent anomaly creation and resolution-path removal, and the mutually exclusive - terminal resolution of a mismatch anomaly in both commit orders; + terminal resolution of a mismatch anomaly in both commit orders, the + canonical mismatch-anomaly decision order between detection, first + enrollment and divergence, including the absent-anomaly orders, and the + universal first route-disposition admission guard; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing, including the ordering of coalescing attachment against first claim/start, the atomic @@ -3171,9 +3588,11 @@ Before shared implementation can be approved, evidence must include: routing and is never reinterpreted under a later revision or current-contract projection; - mismatch resolution by compatible authorized enrollment that creates the one - allowed disposition, and by approved reconciliation/divergence that creates - none, each recorded append-only without deleting the anomaly or its - detection evidence; + allowed disposition - resolving a committed `OPEN` anomaly or, enrollment + first, creating it born-terminal `ENROLLED` - and by approved + reconciliation/divergence of an already committed `OPEN` anomaly that + creates none, each recorded append-only without deleting the anomaly, its + immutable mismatch evidence or its detection provenance; - aggregate route backlog/health tests proving that neither a mismatch anomaly nor an older revision's backlog or unavailable materializer is hidden behind a healthy current revision; @@ -3363,8 +3782,9 @@ Before shared implementation can be approved, evidence must include: it, obey the atomic attachment/first-claim order above and represent every accepted pre-seal input; - deterministic concurrency tests of simultaneous authorized compatible - enrollment and approved divergence of one mismatch anomaly, enrollment - first: one terminal enrollment resolution, one allowed disposition and job + enrollment and approved divergence of one committed `OPEN` mismatch + anomaly, enrollment first: one terminal enrollment resolution, one allowed + disposition and job where materialized, and no accepted divergence; - the same anomaly-resolution race, divergence first: one terminal divergence with append-only authority and evidence, no disposition or job @@ -3372,16 +3792,18 @@ Before shared implementation can be approved, evidence must include: - proof that a stale "anomaly still open" observation in either competing path cannot bypass the database-enforced winning resolution at commit; - proof that a transaction abort or crash before the resolution commits - leaves the anomaly unresolved and independently retryable, and that after - commit repeated requests and audit retries create no second or - contradictory terminal result; + leaves the anomaly in its prior committed state, `OPEN` or absent, and + independently retryable, and that after commit repeated requests and audit + retries create no second or contradictory terminal result; - proof that a terminal divergence leaves the `(event_id, route_key)` slot unused without permitting the losing enrollment for that same resolved anomaly to spend it, while a later deliberately authorized independent replay remains separately governed; - proof that the anomaly count, open age, attention and append-only audit and evidence transition accurately from open to exactly one terminal outcome - without erasing the mismatch detection facts; + for a detector-created anomaly, that a born-terminal `ENROLLED` anomaly is + never counted or aged as open, and that neither erases the mismatch facts + or their provenance; - proof that the atomic enrollment/job creation and the terminal-outcome selection commit as one coherent database decision where enrollment creates a new routed job; @@ -3453,9 +3875,11 @@ Before shared implementation can be approved, evidence must include: evidence or bypass supersession and replay controls; - historical-enrollment and anomaly tests proving that a compatible first enrollment into a safe successor composes with revision-withdrawal - availability and the one terminal anomaly resolution: divergence-first - creates no successor or disposition, and enrollment-first commits its - allowed terminal resolution and routed facts atomically; + availability and the one terminal anomaly resolution: divergence-first on + an `OPEN` anomaly creates no successor or disposition, and + enrollment-first - resolving an `OPEN` anomaly or creating it + born-terminal `ENROLLED` - commits its allowed terminal resolution and + routed facts atomically; - blocked-admission tests with an unavailable or incompatible materializer, proving accurate backlog count and oldest age that count only obligations without a committed disposition, with committed but held successors @@ -3570,25 +3994,250 @@ Before shared implementation can be approved, evidence must include: - deployment tests with more than one compatible worker task even if production initially runs one. +The canonical mismatch-anomaly decision order and the universal first +route-disposition admission guard of section 3.2 additionally require the +thirty active cases below, identified `A13-T01` to `A13-T24` and `A13-T26` to +`A13-T31` for traceability to the specification that introduced them. They +supplement, and replace none of, the cases above, including the twenty +post-seal materialization cases. Each case is individually falsifiable. Every +concurrency case uses real PostgreSQL transactions in distinct sessions or +processes, driven by deterministic barriers and fault injection, or +equivalently falsifiable transactional instrumentation, and exercises loser +conflict, winner commit and winner rollback, serializable or uniqueness +failure, deadlock handling and retry, and lost acknowledgment where relevant; +a mock or harness that serializes the contenders in advance, or that removes +the absent-anomaly race, does not satisfy it. Each case asserts, as +applicable: the original-epoch classification and epoch/revision identity; +one canonical anomaly identity; the correct `OPEN`, born-terminal or terminal +state; zero or one effective terminal resolution and zero or one route +disposition, as required; committed job-input or membership atomicity and +claimability; actor, authority, reason and provenance fidelity; immutable +retry identity; accurate open-anomaly count and oldest age, eligible-route +backlog and job-level waiting and attention signals; and no hidden terminal +success and no unauthorized provider write. These are acceptance requirements +for the later implementation; no documentation change executes them. + +- **A13-T01 - detection first:** an original-epoch mismatched event is + detected and one canonical `OPEN` anomaly is recorded; a later valid, + authorized enrollment of a compatible selected revision commits exactly one + terminal `ENROLLED` outcome atomically with one disposition and all job + inputs or one valid attachment, with one canonical identity and accurate + open and terminal counts. +- **A13-T02 - enrollment first with no anomaly row:** enrollment commits the + single canonical anomaly born-terminal `ENROLLED`, its disposition and all + job inputs atomically; the first later detector reports the unchanged + terminal state, deduplicates the original mismatch provenance, creates no + second or `OPEN` anomaly and records no fictitious prior detector run; the + born-terminal anomaly stays historically queryable and is reported through + terminal, not open, signals. +- **A13-T03 - absent-anomaly race:** in distinct sessions or processes, a + detector and an enroller both initially find no anomaly, and the loser's + first conflicting write is blocked by, or aborted against, the winner's + uncommitted canonical-identity claim (a row, unique-key, predicate or + serializable conflict, as the selected mechanism dictates). The schedule + covers both commit orders, winner commit and winner rollback with loser + wake-up and retry, and at least one explicit serializable-failure-and-retry + schedule, with deterministic loser reconciliation. Every order converges on + the same unique canonical outcome; none splits terminal `ENROLLED` from its + job and disposition, leaves a half-committed job or leaves a phantom `OPEN` + anomaly after a terminal outcome. +- **A13-T04 - duplicate detectors:** several independent detectors race + before, during and after enrollment-first and detection-first outcomes; one + canonical anomaly and idempotent evidence result, a terminal outcome is + never reopened or re-aged, no second `OPEN` anomaly appears, detection + provenance is neither duplicated nor fabricated, and a born-terminal anomaly + remains historically queryable and separate from open signals. +- **A13-T05 - duplicate identical enrollments:** two authorized enrollments + selecting the same revision race; exactly one terminal decision, one route + disposition and one valid job or attachment survive, the retry result is + stable and the loser leaves deterministic evidence. +- **A13-T06 - competing compatible revisions:** concurrent authorized + enrollments select different, individually compatible revisions; one + selection wins under the revision-availability order and the canonical + anomaly order, and the loser creates no second terminal outcome, + disposition or effect and leaves deterministic evidence. A three-way + absent-anomaly race with two compatible revisions is optional additional + coverage and substitutes for no mandatory case. +- **A13-T07 - divergence first:** an approved divergence of an already + committed `OPEN` anomaly commits terminal `RECONCILED/DIVERGED` with no + disposition, job or provider effect; a later enrollment is refused despite + the unused route slot. The case does not establish that divergence may + create an anomaly. +- **A13-T08 - enrollment first versus divergence:** a compatible enrollment + commits terminal `ENROLLED` with its disposition and job; a later or + concurrent divergence is refused with no contradictory accepted + no-disposition evidence, including against a born-terminal `ENROLLED` + anomaly where the divergence read the anomaly as absent before it + committed. +- **A13-T09 - detection after divergence:** a delayed or repeated detector + records or observes the original mismatch facts while the terminal + `RECONCILED/DIVERGED` outcome is retained, never reopened, and no job or + disposition arises from the divergence. +- **A13-T10 - incompatible enrollment:** refusal for each semantic-signature + field - wrong route, consumed kind, accepted version, owning domain, + materializer contract and compatibility policy - and for an unavailable + materializer and a wrong original classification; no `ENROLLED` outcome, + disposition or job results, mismatch evidence and attention are preserved, + and a refused first enrollment with no anomaly row leaves the anomaly absent + until a detector independently records it `OPEN`. +- **A13-T11 - unauthorized enrollment or divergence:** missing staff or + reconciliation authority cannot mint a terminal decision, disposition, job + or provider effect, including when the anomaly row is absent; a refused + enrollment creates no anomaly. +- **A13-T12 - outside every epoch:** before-first-epoch and inactive-gap + events keep the existing explicitly authorized historical enrollment + without an invented mismatch anomaly, stay outside mismatch semantics, and + repeated attempts remain deduplicated. +- **A13-T13 - ordinarily eligible:** an event whose original epoch revision + accepts its kind and version keeps its ordinary obligation, its original + epoch revision, its backlog accounting and one original-epoch disposition; + a selected historical revision cannot relabel it as mismatched or enrolled, + re-enroll it under another revision or duplicate its disposition. +- **A13-T14 - unrelated event kind:** an event of a kind the epoch's revision + does not consume is not an in-epoch contract mismatch for that route and + obtains no mismatch anomaly, anomalous job or attention. +- **A13-T15 - withdrawal first:** the compatible revision's withdrawal + linearizes before the enrollment's selection; no terminal `ENROLLED` + outcome, disposition, job or enrollment-created anomaly results, and a + detector run after the withdrawal records the mismatch durably and visibly + under the existing withdrawal-first rule. +- **A13-T16 - enrollment before withdrawal:** the terminal admission commits + first; the later withdrawal observes that history rather than revoking it + and continues to protect remaining unresolved work, and a born-terminal + `ENROLLED` anomaly is not unresolved work for strand safety. +- **A13-T17 - detection versus withdrawal:** anomaly-first and withdrawal-first + races against withdrawal of the last compatible revision preserve strand + safety, permit no stale strand scan and never suppress anomaly creation + because a withdrawal already committed. +- **A13-T18 - concurrent last-path removals:** two operations racing to remove + distinct last compatible or approved resolution paths cannot strand a + pre-existing `OPEN` anomaly through write skew; only a safety-preserving + serial result is admitted. +- **A13-T19 - epoch lifecycle and delayed routing:** detection and enrollment + race epoch closure, reactivation, retirement and mixed-version routing; the + event's captured generation and immutable original-epoch classification + never change. +- **A13-T20 - new-job atomic failure:** forcing a missing input, a job + uniqueness or disposition failure, a post-allocation precommit crash and a + fault injected after each constituent write of the enrollment transaction + leaves no committed `ENROLLED` outcome without its valid disposition, job + and inputs, no orphan or claimable job, and mismatch facts that remain + recoverable. +- **A13-T21 - coalescing attachment versus first claim:** with an + already-open eligible shared job, attachment-first attaches and commits + `ENROLLED` atomically before the seal; claim/start-first refuses the + attachment without falsely resolving the anomaly or consuming the + disposition slot. +- **A13-T22 - post-seal successor:** a legitimate approved successor admits + and atomically dispositions a previously unrepresented mismatched input, + while an ineligible or unavailable successor cannot falsely resolve it, even + where an earlier generation is sealed. +- **A13-T23 - ambiguous predecessor or effect:** a successor whose predecessor + holds unresolved `EFFECT_STARTED` evidence may carry a committed terminal + `ENROLLED` route decision but performs no unsafe provider effect, and + job-level blocked and reconciliation visibility remains accurate. +- **A13-T24 - loss and retry:** an abort or crash before commit, including a + fault after each constituent write, leaves no terminal outcome, job or + disposition and remains retryable; a lost acknowledgment after commit makes + the retry observe exactly the committed anomaly, resolution, disposition and + job, with no duplication or provider replay. +- **A13-T26 - coverage and observability:** across detection-first, + born-terminal enrollment-first, failed enrollment with no anomaly, + divergence only from a committed `OPEN` anomaly, terminal history and a + committed but held successor, the open-anomaly count and oldest age, + terminal anomaly history, the distinct eligible-route backlog and job-level + blocked indicators never hide or double-count an obligation; a born-terminal + anomaly is never counted as subsequently open and remains historically + queryable; and accepted-divergence terminal non-materialized dispositions + are reported separately from `RECONCILED/DIVERGED` anomaly resolutions. +- **A13-T27 - divergence first when no anomaly exists:** an authorized + divergence for an immutable mismatched event linearizes before any detector + or anomaly transaction and is refused with no canonical anomaly, terminal + resolution, disposition, new job or external effect. A later independent + detector creates exactly one `OPEN` anomaly, which only a new, valid, + authorized terminal request can resolve. +- **A13-T28 - detector versus divergence in both orders:** in distinct + sessions or processes with real transaction barriers, including the + detector's uncommitted canonical insert and the diverger's stale absence + read: (i) the divergence linearizes before the detector commits and is + refused, and the detector then commits `OPEN`; (ii) the detector commits + `OPEN` before the divergence decision, and the authorized divergence commits + exactly one terminal `RECONCILED/DIVERGED` outcome with no disposition or + job, which a repeated later detector never reopens. The case establishes + database linearization, not wall-clock request arrival. +- **A13-T29 - divergence of a non-mismatch event:** for (i) an ordinarily + eligible event, including one with a still-owed obligation, (ii) an + inactive-gap or before-first-epoch event, (iii) an unrelated kind within an + epoch and (iv) a claimed incompatible or current-only revision, every + mismatch-divergence request is refused without an anomaly, terminal + resolution, route-slot consumption, change to lawful backlog, direct job or + provider effect. A valid, independently authorized terminal + non-materialized disposition of an eligible obligation remains a different + command. +- **A13-T30 - refused first enrollment followed by detection:** with no + anomaly initially, refusals for an invalid signature or authorization, a + withdrawn revision, an unavailable materializer and an unsafe coalescing + successor, including under concurrent detector scheduling, create no + anomaly, terminal resolution, disposition or new job. An independently + committed detector then creates or observes exactly one canonical `OPEN` + anomaly from the original facts, and a later authorized enrollment or + divergence may resolve it under the decision order; a failed attempt + neither consumes the route slot nor disguises an effect. +- **A13-T31 - exceptional terminal non-materialized disposition versus + mismatch classification and concurrent detection:** an event inside route + R's epoch matches the consumed kind with a version outside the original + revision's accepted set; the staff actor otherwise holds valid explicit + authorization, a plausible reason and covering evidence, so each refusal + proves immutable-eligibility refusal rather than a missing staff privilege. + (A) Anomaly absent: the staff command linearizes before mismatch detection + and is refused with no new anomaly, terminal outcome, disposition, route-slot + consumption, job or provider effect; a later independent detector records + one canonical `OPEN` anomaly with the original facts. (B) Concurrent + detector: in distinct sessions or processes, the command and a detector + begin before either observes the other's committed anomaly; both + serialization orders, including conflicting and stale first reads, refuse + the command, and detection produces or retains one `OPEN` canonical anomaly + unless a separate, independently authorized first mismatch enrollment has + lawfully resolved it, with no false terminal outcome or consumed mismatch + slot. (C) Existing `OPEN`: the command is refused, and the anomaly keeps its + immutable evidence, age and attention; a separate valid historical + enrollment can then resolve it `ENROLLED` with the sole disposition and + job, or a separately authorized anomaly divergence can resolve it with no + disposition. (D) Existing terminal: with the canonical anomaly already + `ENROLLED`, and separately already `RECONCILED/DIVERGED`, the command is + refused without reopening, consuming another slot or creating an effect. + (E) Eligible positive control: for a genuinely ordinarily eligible, + still-owed event that meets the existing exceptional non-materialization + requirements, the authorized and audited command commits one unique no-job + disposition with its covering evidence; raced against lawful ordinary + routed materialization, including a coalescing attachment, exactly one + disposition wins the slot, the loser observes the durable outcome, and a + postcommit lost-acknowledgment retry creates no phantom completion, + duplicate job or effect. (F) Outside-epoch and unrelated kind: the command + is refused for a before-first-epoch or inactive-gap event and for an + unrelated event kind, with no anomaly, job, slot or provider effect, while + explicitly authorized outside-epoch historical enrollment keeps working + under its existing semantics. A mixed bulk staff command over eligible, + mismatched, outside-epoch and unrelated-kind events applies the refusal per + event. + +`A13-T25` is retired and is not an active case. It would have tested a +speculative automatic repair or adoption of a pre-existing mismatch +disposition without its canonical anomaly, which this decision does not +adopt; the identifier is not renumbered or reused, and contradictory existing +state is handled only by the fail-closed control HOLD of "Canonical +mismatch-anomaly decision order" (section 3.2). + Provider-specific handlers owe their own acceptance evidence in addition to the shared-engine tests. ## 12. Approval and authority -Current decision state: **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-15 +Current decision state: **PROPOSED**. -The CTO approved this exact corrected content on 2026-10-15 under the -repository decision process, as the architecture content represented by the -Route-B candidate head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and -candidate ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76`; this -version differs from that candidate only in this approval-state -representation. That approval does not by itself make this ADR -repository-authoritative: the authority condition below still applies. Any -ADR-0013 programme-local reliance on this exact version is a separate state -that is not effective until its own conditions are satisfied for this version. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the @@ -3629,7 +4278,25 @@ distinct, stably identified routed successor generation created together with its disposition, with group identity distinct from executable generation identity and database-arbitrated convergence, so that no eligible obligation can remain owed indefinitely against a permanently sealed -idempotency key and no successor can bypass target-scoped effect safety. +idempotency key and no successor can bypass target-scoped effect safety. It +further defines, in section 3.2, the immutable original-epoch classification +of every event for each route and requires every operation that can create +the first `(event_id, route_key)` disposition - ordinary materialization, +explicitly authorized historical enrollment and the authorized terminal +non-materialized disposition - to prove that classification and its own +eligibility and authorization inside the transaction that would consume that +slot, so that no disposition can exist for an in-epoch contract mismatch +except the one created by compatible, explicitly authorized first historical +enrollment. It requires detection, first historical enrollment and divergence +of a mismatch anomaly to share one database-enforced canonical decision order +that also covers the anomaly's first creation when no anomaly row exists: +enrollment first creates the anomaly born-terminal `ENROLLED` atomically with +its disposition and routed facts, later detection converges without +reopening it, and divergence resolves only an already committed `OPEN` +anomaly and never creates one, so that detection and enrollment can never +leave a contradictory open anomaly beside a committed disposition, and +contradictory durable state fails closed for separately authorized incident +recovery. Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO approval dated 2026-09-30. A later corrected version, which introduced the @@ -3651,7 +4318,12 @@ mismatch anomalies, carried `Status: APPROVED` with CTO approval dated 2026-10-09. A further corrected version, which introduced that atomic commit and terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal, carried `Status: APPROVED` -with CTO approval dated 2026-10-12. Those approvals, and +with CTO approval dated 2026-10-12. A further corrected version, ADR-0012 Git +blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, which introduced that +post-seal materialization path without a database-enforced order between +mismatch detection and first historical enrollment or an original-epoch +admission guard for every first route-disposition writer, carried +`Status: APPROVED` with CTO approval dated 2026-10-15. Those approvals, and every review of those earlier versions, remain historical evidence bound to those exact earlier versions and source heads only; they do not approve this version. The exact historical identities are recorded in the task diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 6033bd074..5611ff3f4 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-15 +Last updated: 2026-10-20 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-15 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, and Javi, CTO approved its exact corrected content on 2026-10-15 under the repository decision process, as the architecture content represented by Route-B candidate head `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and candidate ADR-0012 blob `c4ca99ce2e538ae607496ecf7c89108501b5fe76` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only: exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact `PROPOSED` version is a further material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the PR-first staged approval route (Route B); its corrected content is not approved, and only a later approval-state version may carry `APPROVED`, an approver and an approval date. CTO content approval, exact-final-blob approval, independent source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal, and the CTO approval dated 2026-10-15 applies only to the historical earlier corrected exact version, ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, that introduced that post-seal materialization path without a database-enforced order between mismatch detection and first historical enrollment or an original-epoch admission guard for every first route-disposition writer; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic; and every operation that can create the first `(event_id, route_key)` disposition - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition, including when an approved migration, reconciliation or disposition plan acts through them - classifies the event from immutable original-epoch facts and proves its own eligibility and authorization inside the transaction that would consume that slot, so that no disposition exists for an in-epoch contract mismatch except the one created by compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly, while the terminal non-materialized disposition is limited to ordinarily eligible, still-owed obligations and its accepted divergence is reported separately from a mismatch anomaly's `RECONCILED/DIVERGED` outcome; and detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also arbitrates the anomaly's first creation when no anomaly row exists, under which detection first records one `OPEN` anomaly with no disposition or job, enrollment first creates the canonical anomaly born-terminal `ENROLLED` atomically with its one disposition and indispensable routed job and input facts or eligible attachment and is never counted or aged as open, later detection converges without reopening, re-aging or fabricating provenance, divergence resolves only an already committed `OPEN` anomaly, is refused before detection and never creates an anomaly, disposition or job, a refused enrollment neither creates nor resolves an absent anomaly, terminal `ENROLLED` is a route decision rather than evidence of provider success, and contradictory durable state fails closed with its evidence preserved and visible as a control HOLD for separately authorized incident recovery, without a third anomaly state, job hold or automatic repair. Invariant 47 is added; invariants 5, 8, 35 and 45 are conformed; the other preceding invariants and the twenty post-seal validation cases are retained; and thirty active validation cases, `A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`, are added, with `A13-T25` retired and not reused. These are future implementation acceptance requirements, not executed tests. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 2d01255c24f68cd03666cfa05518bb9f4ae76667 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 20 Oct 2026 18:03:13 +0100 Subject: [PATCH 47/48] THOTH-ASYNC-01-ADR-01: record Amendment-13 architecture approval (#958) --- CHANGELOG.md | 2 +- ...H-ASYNC-01-ADR-01-implementation-report.md | 1237 ++++++++++++++--- ...ed-asynchronous-event-and-job-execution.md | 24 +- .../decisions/decision-register.md | 2 +- 4 files changed, 1062 insertions(+), 203 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0833e6d4e..6e8a64078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed - - `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION`: propose a further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version defines an immutable original-epoch classification of each event for each route and requires every first `(event_id, route_key)` disposition writer - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition - to prove that classification and its own eligibility and authorization inside the transaction that would consume the slot, so that an in-epoch contract mismatch receives a disposition only through compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly. It makes detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also covers the anomaly's first creation when no anomaly row exists: enrollment first creates the anomaly born-terminal `ENROLLED` atomically with its disposition and routed facts, later detection converges without reopening it, divergence resolves only an already committed `OPEN` anomaly and never creates one, and contradictory durable state fails closed as a control HOLD for separately authorized incident recovery. Invariant 47 and thirty active validation cases (`A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`; `A13-T25` retired) are added and invariants 5, 8, 35 and 45 are conformed; tables, locks and SQL remain implementation-specification choices. **The corrected ADR-0012 version and its decision-register row are `PROPOSED`; this content is not approved.** The earlier `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry, and the CTO approval dated 2026-10-15 that it records, apply only to the historical earlier corrected exact version of ADR-0012 (blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`) that does not contain this correction, and each earlier entry likewise applies only to its own historical exact version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture proposal only: no runtime implementation, API or schema change, migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized or follows from it.** + - `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version defines an immutable original-epoch classification of each event for each route and requires every first `(event_id, route_key)` disposition writer - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition - to prove that classification and its own eligibility and authorization inside the transaction that would consume the slot, so that an in-epoch contract mismatch receives a disposition only through compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly. It makes detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also covers the anomaly's first creation when no anomaly row exists: enrollment first creates the anomaly born-terminal `ENROLLED` atomically with its disposition and routed facts, later detection converges without reopening it, divergence resolves only an already committed `OPEN` anomaly and never creates one, and contradictory durable state fails closed as a control HOLD for separately authorized incident recovery. Invariant 47 and thirty active validation cases (`A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`; `A13-T25` retired) are added and invariants 5, 8, 35 and 45 are conformed; tables, locks and SQL remain implementation-specification choices. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-20.** The earlier `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry, and the CTO approval dated 2026-10-15 that it records, apply only to the historical earlier corrected exact version of ADR-0012 (blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`) that does not contain this correction, and each earlier entry likewise applies only to its own historical exact version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, API or schema change, migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized or follows from it.** - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while an obligation for which no eligible successor can be selected or admitted stays undispositioned, backlogged, age-visible and attention-raising with bounded backoff and bounded generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state rather than the route backlog; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-15.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering and coalescing-open/coalescing-sealed boundary beneath one stable logical `route_key` and closes a race between revision withdrawal's strand-prevention decision and concurrent durable mutations that are not revision selections: a withdrawal may commit only from a database-consistent strand-safety decision that remains valid through its linearization/commit point, so a stale application-level scan, even inside the withdrawal transaction, is insufficient; every already-defined durable mutation that can change that predicate from safe to unsafe - at minimum creation of an unresolved `IN_EPOCH_CONTRACT_MISMATCH` anomaly whose approved resolution the withdrawal affects, even where the anomaly references only the epoch's expected revision rather than the compatible revision being withdrawn, and removal of the last approved resolution/materialization path on which the decision relies - must share one database-atomic serialization/constraint boundary with the withdrawal or force the withdrawal to detect the change and re-evaluate/abort before commit, while predicate-improving changes need not be serialized for that reason alone; both orders are defined (anomaly-first, the withdrawal includes the anomaly and is refused or held where it would leave no selectable compatible revision and no approved reconciliation/divergence path; withdrawal-first, mismatch detection is never suppressed and always records the anomaly durably and visibly, creating no disposition, job or route slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable); concurrent removals of alternative resolution paths cannot write-skew into stranded work; the rule composes with the withdrawal/selection order as one coherent database order without a second withdrawal lifecycle, invents no new revocability, reconciliation, compatibility or availability semantics, and leaves the concrete lock, constraint or isolation mechanism to the implementation specification. Invariant 43 and nine concurrency validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-09.** The earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the coalescing boundary without this strand-safety rule; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 7ea63c99d..0b700de16 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -33,19 +33,20 @@ candidate stage of the post-seal materialization correction specified by Specification Amendment 12 after the configured final-head automated review of that Amendment-11 approval-state head, the four-path replacement candidate of that candidate after its independent exact-candidate source -review, the Route-B approval-state stage of that replacement candidate, and -the Route-B candidate stage of the canonical mismatch-anomaly decision order +review, the Route-B approval-state stage of that replacement candidate, the +Route-B candidate stage of the canonical mismatch-anomaly decision order and universal first route-disposition admission correction specified by Specification Amendment 13 with its Revisions 1 and 2 after the configured -final-head automated review of that Amendment-12 approval-state head. +final-head automated review of that Amendment-12 approval-state head, and +the Route-B approval-state stage of that Amendment-13 candidate. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-13 Route-B candidate commit -as the current stage and every earlier candidate, approval-state, +Route-B correction stages, with the Amendment-13 Route-B approval-state +commit as the current stage and every earlier candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and @@ -68,11 +69,13 @@ Actual base commit: `b44303c214498baf76c9d9a0a7cab374377f9cbe` PR target: `feature/worker` Programme integration branch: `feature/worker` Task branch: `feature/async/adr-0012` -Head commit: the Amendment-13 Route-B candidate commit that contains this -report, an ordinary single-parent additive commit whose direct parent is the +Head commit: the Amendment-13 Route-B approval-state commit that contains +this report, an ordinary single-parent additive commit whose direct parent is +the content-approved Amendment-13 candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5`; its SHA is deliberately not +self-pinned here (section 3). That candidate is the direct child of the historical Amendment-12 approval-state head -`a913232a6623936855d635885d513d4c778c4b25`; its SHA is deliberately not -self-pinned here (section 3). That approval-state head is the direct child of +`a913232a6623936855d635885d513d4c778c4b25`, which is the direct child of the content-approved Amendment-12 replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, which is the direct child of the historical first Amendment-12 candidate @@ -112,7 +115,7 @@ historical Amendment-4/5 recovery head historical activation-epoch approval-state head `c046e6bf6510f24ee1231f86293a3d499bceed7e`, itself the direct child of the historical content-approved activation-epoch candidate -`95769537cdc9871e42f9f3e5443604a96beb74da`. The candidate commit's +`95769537cdc9871e42f9f3e5443604a96beb74da`. The approval-state commit's publication as the PR #960 head is a post-commit, GitHub-owned lifecycle fact under `ADR-0005`. Pull request: [#960](https://github.com/thoth-pub/thoth/pull/960), targeting @@ -135,7 +138,8 @@ replacement-candidate and Amendment-12 approval-state stages - Claude (Fable bounded implementing agent in a fresh session with no conversation state from any earlier stage (its exact model identity is disclosed to engineering control in the stage return rather than committed here; section 5, decision -172). Earlier rounds are recorded in Appendix A. +172); Amendment-13 approval-state stage - Claude (Opus 5.5), bounded +implementing agent (section 15). Earlier rounds are recorded in Appendix A. Reasoning level: standard Historical and current control identities: @@ -335,11 +339,19 @@ decision-register blob 6033bd0743a68d47b9415c5cdaf0e03129400a03 CHANGELOG blob 5ca616041f46aa2a8a380518a1c803430cad7713 implementation-report blob e852e4297d02dec126a85200eb7cd065fbaf8e25 (superseded by the Amendment-13 candidate) -Amendment-13 Route-B candidate commit (the commit that contains this report): -ADR-0012 blob 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 (PROPOSED; material architectural correction of 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e) +content-approved Amendment-13 candidate / approval-state parent: +18ffbf7d8b39755abfe84b64af29bc47ca7550b5 +tree 4cf1e8f566d85cb84a32a385979d459118ba98a3 +ADR-0012 blob 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 (PROPOSED; content-approved; material architectural correction of 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e; independent exact-candidate source review APPROVED) decision-register blob 5611ff3f4f3b1981f30295182efff1f882a24331 +implementation-report blob 7ea63c99d0a925ef6e666de7b415713b16786996 CHANGELOG blob 0833e6d4efcb7451739ea90bbbadb33ff6def37b +Amendment-13 Route-B approval-state commit (the commit that contains this report): +ADR-0012 blob 69f9ec9017ffe39d865bbe3ae67522c6917dc857 (APPROVED; Javi, CTO; 2026-10-20; architecture-equivalent to 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239) +decision-register blob 776448bae47d6aa03201fc230bc5957d5461db67 +CHANGELOG blob 6e8a64078d0b6a41109e84feec23832d027673d0 + feature/worker at the recovery, the candidates, the approval-state commits, the control-record recovery, the transient-state correction, the factual clarification, the Amendment-8 candidate and approval-state commit, the @@ -347,7 +359,8 @@ Amendment-9 candidate and approval-state commit, the Amendment-10 candidate, its consistency correction, the Amendment-10 approval-state commit, the Amendment-11 candidate, the Amendment-11 approval-state commit, the Amendment-12 candidate, its replacement candidate, the Amendment-12 -approval-state commit and the Amendment-13 candidate (unchanged): +approval-state commit, the Amendment-13 candidate and the Amendment-13 +approval-state commit (unchanged): b44303c214498baf76c9d9a0a7cab374377f9cbe ``` @@ -576,7 +589,19 @@ Approved specification: and the approved interpretation of NB-01 to NB-10: #958 comment `6273281867`; - Amendment-13 Route-B candidate implementation authorization: #958 comment - `6288171434`. + `6288171434`; +- independent CRITICAL exact-candidate source review of the Amendment-13 + candidate, `APPROVED` with no blocking finding: recorded on #958 on + 2026-10-20; its GitHub comment identifier is GitHub-owned lifecycle + evidence under `ADR-0005` and Route-B doctrine and is not copied here; +- CTO exact candidate architecture-content approval of the Amendment-13 + candidate: recorded on #958 on 2026-10-20 by Javi, CTO for candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; its GitHub comment identifier + is GitHub-owned lifecycle evidence under `ADR-0005` and Route-B doctrine and + is not copied here; +- Amendment-13 Route-B approval-state implementation authorization: #958 + comment `6295994615`. Implemented objective: the Route-B material architectural correction of ADR-0012 before repository authority. A first correction (activation epochs) @@ -683,17 +708,24 @@ the approved state), and, after independent approval-state inspection, the CTO recorded exact-final-blob approval of that ADR blob. The configured final-head automated review of that approval-state head exposed a further architecture defect, `R-960-A12-DET-ENR-01`, specified by Specification -Amendment 13 as revised by its Revisions 1 and 2. The commit that contains -this report is its `PROPOSED` Route-B candidate: one ordinary single-parent -commit on the approval-state head that changes exactly the four authorized -correction paths, returns ADR-0012 to `Status: PROPOSED` with no current -approver or approval date, requires every operation that can create a first -`(event_id, route_key)` disposition to prove immutable original-epoch -classification and its own eligibility and authorization inside the -transaction that would consume that slot, and requires detection, first -historical enrollment and divergence of a mismatch anomaly to share one -database-enforced canonical decision order that also covers the anomaly's -first creation when no anomaly row exists. +Amendment 13 as revised by its Revisions 1 and 2. Its `PROPOSED` Route-B +candidate, one ordinary single-parent commit on the approval-state head that +changed exactly the four authorized correction paths, returned ADR-0012 to +`Status: PROPOSED` with no current approver or approval date, requires every +operation that can create a first `(event_id, route_key)` disposition to +prove immutable original-epoch classification and its own eligibility and +authorization inside the transaction that would consume that slot, and +requires detection, first historical enrollment and divergence of a mismatch +anomaly to share one database-enforced canonical decision order that also +covers the anomaly's first creation when no anomaly row exists; it received +independent CRITICAL exact-candidate source review (`APPROVED`, no blocking +finding) and CTO exact candidate architecture-content approval on +2026-10-20. The commit that contains this report is the one bounded Route-B +approval-state commit that records that approval as durable decision state: +ADR-0012 `PROPOSED -> APPROVED` with `Approved by: Javi, CTO` and +`Approval date: 2026-10-20`, the ADR-0012 register row and the +mismatch-admission correction changelog entry moved to the approved state, +and this report's approval-state evidence. No architecture content changes. - **Activation-epoch candidate stage (historical candidate `95769537cdc9871e42f9f3e5443604a96beb74da`).** The candidate implements the @@ -1328,8 +1360,9 @@ first creation when no anomaly row exists. that stage authorized no PR-state or PR-metadata change. - **Exact-final-blob approval, final-head review of the Amendment-12 approval-state head, Specification Amendment 13 with Revisions 1 and 2, and - the Amendment-13 Route-B candidate (the commit that contains this - report).** Authorized by #958 comment `6288171434`. After the + the Amendment-13 Route-B candidate (historical content-approved candidate + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5`).** Authorized by #958 comment + `6288171434`. After the approval-state head was published, an independent CRITICAL exact approval-state source inspection returned `APPROVED` and the CTO recorded exact-final-blob approval of ADR-0012 blob @@ -1364,19 +1397,49 @@ first creation when no anomaly row exists. `MATERIAL ARCHITECTURAL CORRECTION`, accepted a read-only ADR-number history audit as establishing eligibility for the material correction before repository authority, selected Route B and approved the interpretation of - NB-01 to NB-10 (#958 comment `6273281867`). The candidate is one ordinary - single-parent commit directly on the approval-state head that changes - exactly the four authorized correction paths: ADR-0012 returns to + NB-01 to NB-10 (#958 comment `6273281867`). The candidate was one ordinary + single-parent commit directly on the approval-state head that changed + exactly the four authorized correction paths: ADR-0012 returned to `Status: PROPOSED` with no current approver or approval date and implements the effective Amendment 13 (section 4; section 5, decisions 157-172); the - decision-register ADR-0012 row, one new changelog entry and this report - describe the corrected version as `PROPOSED`; and the 2026-09-30, - 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09, 2026-10-12 and - 2026-10-15 CTO approvals remain historical evidence bound to the earlier - exact versions. PR #960 was already a draft; this stage authorizes no - PR-state or PR-metadata change; the PR #960 title and description, which - describe the historical Amendment-12 source, are GitHub-owned and their - reconciliation is a separate control action. + decision-register ADR-0012 row, one new changelog entry and the + candidate-stage report described the corrected version as `PROPOSED`; and + the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09, + 2026-10-12 and 2026-10-15 CTO approvals remain historical evidence bound to + the earlier exact versions. An independent CRITICAL exact-candidate source + review of the published candidate against the actual GitHub source + returned `APPROVED` with no blocking finding, its non-blocking follow-ups + being separately authorized PR-metadata reconciliation and thread + adjudication, preservation of the future PostgreSQL concurrency, + fault-injection and integration tests, and continued separation of the + content-approval, approval-state, exact-final-blob, final-head review and + merge gates; and Javi, CTO approved its exact architecture + content - head `18ffbf7d8b39755abfe84b64af29bc47ca7550b5`, ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239` - on 2026-10-20 in a durable + record on #958. PR #960 was already a draft; that stage authorized no + PR-state or PR-metadata change. +- **Amendment-13 approval-state stage (the commit that contains this + report).** Authorized by #958 comment `6295994615`. One ordinary + single-parent commit directly on the content-approved Amendment-13 + candidate records that approval as durable decision state, changing only + the four authorized paths: ADR-0012 `PROPOSED -> APPROVED` with + `Approved by: Javi, CTO` and `Approval date: 2026-10-20` and the leading + section-12 approval paragraphs, which identify the approved content as the + content represented by candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; the ADR-0012 register row; the + `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION` changelog entry; and + this report. No architecture content changes (section 9, "Amendment-13 + approval-state validation"). The content approval is recorded here through + owning issue #958, approver Javi, CTO, approval date 2026-10-20, the + candidate head and the candidate ADR-0012 blob; its GitHub comment + identifier is not copied into repository source, as the + repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` requires (section 5, decision 176). + PR #960 was already a draft; this stage authorizes no PR-state or + PR-metadata change; the PR #960 title and description, which describe the + historical Amendment-12 source, are GitHub-owned and their reconciliation + is a separate control action. Out-of-scope changes made: NONE @@ -1390,9 +1453,17 @@ selected approval route: Route B - PR-first staged approval (Amendment 2 B2, ratified by Amendment 3) ADR-0012 change in the commit that contains this report: +Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-20) +of the content-approved Amendment-13 candidate blob 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 +at candidate head 18ffbf7d8b39755abfe84b64af29bc47ca7550b5; +no architecture change + +historical Amendment-13 candidate at 18ffbf7d8b39755abfe84b64af29bc47ca7550b5: MATERIAL ARCHITECTURAL CORRECTION of prior approved blob 1c98ce0cca1eeb4bf46bbfc526a3826c534f637e (Specification Amendment 13 with Revisions 1 and 2; Route-B candidate stage; -candidate status PROPOSED; CTO classification and Route-B selection recorded on #958) +candidate status PROPOSED; CTO classification and Route-B selection recorded on #958; +independent CRITICAL exact-candidate source review APPROVED; CTO exact +candidate architecture-content approval on 2026-10-20) historical Amendment-12 approval-state at a913232a6623936855d635885d513d4c778c4b25: Route-B approval-state recording (PROPOSED -> APPROVED; Javi, CTO; 2026-10-15) @@ -1652,7 +1723,8 @@ version as well: `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` changelog entry that record the coalescing-seal ordering version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while - the register row now records the candidate as `PROPOSED`. + the register row recorded the Amendment-10 candidate as `PROPOSED` and then + the approved version. They remain historical evidence bound to their exact versions and heads. For the candidate version, independent exact-candidate inspection, CTO exact @@ -1694,7 +1766,8 @@ well: `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` changelog entry that record the strand-safety version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while the register row - now records the candidate as `PROPOSED`. + recorded the Amendment-11 candidate as `PROPOSED` and then the approved + version. They remain historical evidence bound to their exact versions and heads. The Amendment-11 candidate version received its own independent CRITICAL @@ -1722,7 +1795,8 @@ version as well: `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` changelog entry that record the routed-job atomicity version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while the - register row now records the candidate as `PROPOSED`. + register row recorded the Amendment-12 candidate as `PROPOSED` and then the + approved version. They remain historical evidence bound to their exact versions and heads. For the candidate version, independent exact-candidate inspection, CTO exact @@ -1763,45 +1837,68 @@ stale, for that version, every record bound to the post-seal version as well: `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` changelog entry that record the post-seal version as `APPROVED`; the changelog entry is preserved and remains true only of that historical version, while the - register row now records the candidate as `PROPOSED`. + register row recorded the Amendment-13 candidate as `PROPOSED` and then the + approved version. -They remain historical evidence bound to their exact versions and heads. For -the candidate version, independent exact-candidate source review, CTO exact -candidate-content approval, the approval-state commit, exact-final-blob -approval, independent exact-head review, configured review and thread -adjudication, CTO merge authorization, guarded merge, programme-local -reliance and later migration, deployment, activation and observation are -distinct controlled lifecycle categories; GitHub, through #958 and PR #960, is -authoritative for which of them is pending, satisfied or blocked for any -head. +They remain historical evidence bound to their exact versions and heads. The +Amendment-13 candidate version received its own independent CRITICAL +exact-candidate source review and CTO exact candidate architecture-content +approval on 2026-10-20, and the approval-state commit that contains this +report records that approval. For this version, exact-final-blob approval, +independent exact-head review, configured review and thread adjudication, +CTO merge authorization, guarded merge, programme-local reliance and later +migration, deployment, activation and observation are distinct controlled +lifecycle categories; GitHub, through #958 and PR #960, is authoritative for +which of them is pending, satisfied or blocked for any head. ## 3. Commits -Amendment-13 Route-B candidate commit (the commit that contains this report): +Amendment-13 Route-B approval-state commit (the commit that contains this +report): - exactly one ordinary single-parent additive commit on `feature/async/adr-0012` - - `THOTH-ASYNC-01-ADR-01: propose Amendment-13 mismatch-admission correction (#958)` - - direct parent: the historical Amendment-12 approval-state head - `a913232a6623936855d635885d513d4c778c4b25` - - changes only the four authorized correction paths (section 4); the - candidate ADR-0012 blob is `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, - the candidate decision-register blob - `5611ff3f4f3b1981f30295182efff1f882a24331` and the candidate changelog - blob `0833e6d4efcb7451739ea90bbbadb33ff6def37b` + `THOTH-ASYNC-01-ADR-01: record Amendment-13 architecture approval (#958)` + - direct parent: the content-approved Amendment-13 candidate + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` + - changes only the four authorized paths, and only to record the approval + state (section 4); the approval-state ADR-0012 blob is + `69f9ec9017ffe39d865bbe3ae67522c6917dc857`, the approval-state + decision-register blob `776448bae47d6aa03201fc230bc5957d5461db67` and the + approval-state changelog blob `6e8a64078d0b6a41109e84feec23832d027673d0` Its SHA, its tree and this report's final blob are deliberately not embedded -here because this report is part of that tree; the candidate ADR-0012, +here because this report is part of that tree; the approval-state ADR-0012, decision-register and changelog blobs do not depend on this report and are -recorded above; every other file keeps its approval-state-head blob +recorded above; every other file keeps its content-approved candidate blob (section 9). The commit identities are verified against the remote task-branch head after push and reported in the stage return; once published, the PR #960 head and head tree are GitHub-owned lifecycle evidence under `ADR-0005`. The authorized topology is one non-force fast-forward of -the task branch from the approval-state head to the candidate; no merge -commit, rebase, squash, amend, force update, second candidate commit or -second push is authorized, and no further source commit exists implicitly -after it. +the task branch from the content-approved candidate to the approval-state +commit; no merge commit, rebase, squash, amend, force update, second +approval-state commit or second push is authorized, and no further source +commit exists implicitly after it. + +Amendment-13 Route-B candidate commit (content-approved; historical): + +- `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` - + `THOTH-ASYNC-01-ADR-01: propose Amendment-13 mismatch-admission correction (#958)` + - ordinary single-parent additive commit; direct parent: the historical + Amendment-12 approval-state head + `a913232a6623936855d635885d513d4c778c4b25` + - tree `4cf1e8f566d85cb84a32a385979d459118ba98a3` + - changed only the four authorized correction paths (section 4); the + candidate ADR-0012 blob is `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, + independently reviewed `APPROVED` and content-approved by Javi, CTO on + 2026-10-20; decision-register blob + `5611ff3f4f3b1981f30295182efff1f882a24331`, changelog blob + `0833e6d4efcb7451739ea90bbbadb33ff6def37b` and report blob + `7ea63c99d0a925ef6e666de7b415713b16786996` + - published as a fast-forward of `feature/async/adr-0012` from the + Amendment-12 approval-state head; at the Amendment-13 approval-state + stage the remote task-branch head, tree, single parent and these blobs + matched Amendment-12 Route-B approval-state commit (historical): @@ -2133,10 +2230,22 @@ first-parent order, newest first: ## 4. Files changed -Amendment-13 Route-B candidate write budget for the commit that contains this -report (authorization #958 comment `6288171434`); exactly four existing -paths, no new file, no deletion, rename or file-mode change, and every other -path byte-identical to the historical Amendment-12 approval-state head +Amendment-13 Route-B approval-state write budget for the commit that contains +this report (authorization #958 comment `6295994615`), identical to the +Amendment-13 candidate write budget; exactly four existing paths, no new +file, no deletion, rename or file-mode change, and every other path +byte-identical to the content-approved Amendment-13 candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5`: + +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` + +Historical Amendment-13 Route-B candidate write budget (authorization #958 +comment `6288171434`); exactly four existing paths, no new file, no deletion, +rename or file-mode change, and every other path byte-identical to the +historical Amendment-12 approval-state head `a913232a6623936855d635885d513d4c778c4b25`: - `CHANGELOG.md` @@ -2325,13 +2434,102 @@ candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, Amendment-10 consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 replacement-candidate, -Amendment-12 approval-state and Amendment-13 candidate stages; all four -correction paths already existed. +Amendment-12 approval-state, Amendment-13 candidate and Amendment-13 +approval-state stages; all four correction paths already existed. + +Amendment-13 approval-state changes in the commit that contains this report, +relative to the content-approved Amendment-13 candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5` (exactly the four authorized paths, +approval state only): + +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` + - reason: record the CTO exact candidate architecture-content approval of + 2026-10-20. + - behavioural effect: the header's `Status: PROPOSED` becomes + `Status: APPROVED` and gains `Approved by: Javi, CTO` and + `Approval date: 2026-10-20`; section 12's two leading candidate-state + paragraphs ("Current decision state: **PROPOSED**." and the statement that + the corrected content has not been approved) are replaced by + "Current decision state: **APPROVED**.", the same approver and date, and + a paragraph stating that the CTO approved this exact corrected content on + 2026-10-20 as the architecture content represented by candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, that this version differs + from that candidate only in its approval-state representation, that the + approval is architecture approval only and does not by itself make the + ADR repository-authoritative, and that any ADR-0013 programme-local + reliance on this exact version is a separate state not effective until + its own conditions are satisfied. Every other line, including all + architecture in sections 3.2, 3.5, 3.14 and 3.15, invariants 1-47, the + validation requirements with all twenty Amendment-12 cases, the thirty + active `A13-T` cases and the retired `A13-T25` record, the + historical-approval paragraph, the material-correction description, the + authority condition and the architecture-versus-implementation + separation, is byte-identical to the content-approved candidate + (section 9). Approval-state blob: + `69f9ec9017ffe39d865bbe3ae67522c6917dc857`. + - within authorized write budget: YES +- `docs/engineering/decisions/decision-register.md` + - reason: record the approval state of the ADR-0012 row. + - behavioural effect: the ADR-0012 row only - `PROPOSED` becomes + `APPROVED`, `Pending` becomes `Satisfied`, and the candidate-state + sentences (the version is `PROPOSED`, its corrected content is not + approved and only a later approval-state version may carry `APPROVED`) + become the statement that Javi, CTO approved its exact corrected content + on 2026-10-20, bound to candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239` as historical content-approval + evidence, with the statement that the content approval is + architecture-content approval only and does not itself constitute + exact-final-blob approval, independent final source-head review, merge + authorization or ADR-0013 reliance, which are separate gates recorded in + GitHub, or repository authority, which follows only the row's authority + condition (section 5, decision 177). The historical-approval sentence, + the architecture summary, the invariant and validation counts, the + authority condition and the implementation-authorization sentence are + unchanged. The header `Last updated: 2026-10-20` already carries the date + of this row change and is unchanged. Every other row is unchanged. + Approval-state blob: `776448bae47d6aa03201fc230bc5957d5461db67`. + - within authorized write budget: YES +- `CHANGELOG.md` + - reason: record the approval state of the mismatch-admission correction + entry. + - behavioural effect: the + `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION` entry only - + "propose" becomes "record a CTO-approved", "`PROPOSED`; this content is + not approved" becomes "`APPROVED`: Javi, CTO approved the exact corrected + content on 2026-10-20", and "proposal only" becomes "decision only". Its + architectural description, its statements that the earlier entries and + the 2026-10-15 approval apply only to historical exact versions, and its + no-runtime-authorization boundary are unchanged, and every other entry is + byte-identical. Approval-state blob: + `6e8a64078d0b6a41109e84feec23832d027673d0`. + - within authorized write budget: YES +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + - reason: record the Amendment-13 approval-state stage truthfully. + - behavioural effect: documentation only. Makes the approval-state commit + the current stage; records the independent exact-candidate source + review, the CTO candidate architecture-content approval and the + approval-state authorization; records the approval-state validation, + no-drift proof and documentation-only CI classification (section 9); and + reframes statements that would otherwise describe the candidate as the + commit containing this report or as still `PROPOSED`, recording the + candidate's identities as historical facts. All still-true earlier + evidence is preserved. + - within authorized write budget: YES -Amendment-13 candidate changes in the commit that contains this report, -relative to the historical Amendment-12 approval-state head -`a913232a6623936855d635885d513d4c778c4b25` (exactly the four authorized -correction paths): +Byte-identical to the content-approved Amendment-13 candidate through the +approval-state commit: every path other than the four authorized paths, +including ADR-0008 `622060ad90eec41c792f110c373efffbb11a4b56`, ADR-0010 +`aca2142a3387785e80db908b3a5c1b0afd82ab51`, ADR-0013 +`d928f957bdf775d03e99fc73888ec8e2dec5f80b`, the decisions README +`de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7`, the CI classifier +`2cac40c9d1d45e6961cfb4e368c602b75d8f980d` and every workflow file. + +Historical Amendment-13 candidate changes in the content-approved candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5`, relative to the historical +Amendment-12 approval-state head `a913232a6623936855d635885d513d4c778c4b25` +(exactly the four authorized correction paths): - `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` - reason: implement the CTO-approved effective Specification Amendment 13 @@ -2415,15 +2613,15 @@ correction paths): - within authorized write budget: YES - `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` - reason: record the Amendment-13 candidate stage truthfully. - - behavioural effect: documentation only. Makes the candidate commit the - current stage; records the post-publication approvals of the + - behavioural effect: documentation only. Made the candidate commit the + then-current stage; recorded the post-publication approvals of the Amendment-12 approval-state head, the Amendment-13 specification, - revision, review and CTO records and the candidate authorization; records - the candidate validation, including the documentation-only CI - classification (section 9); and reframes statements that would otherwise - describe the approval-state commit as the commit containing this report, - recording its identities as historical facts. All still-true earlier - evidence is preserved. + revision, review and CTO records and the candidate authorization; + recorded the candidate validation, including the documentation-only CI + classification (section 9); and reframed statements that would otherwise + have described the approval-state commit as the commit containing the + report, recording its identities as historical facts. All still-true + earlier evidence was preserved. - within authorized write budget: YES Byte-identical to the historical Amendment-12 approval-state head through the @@ -3858,9 +4056,14 @@ Files deleted, moved or renamed: NONE PASS -The Amendment-13 candidate commit differs from the historical Amendment-12 -approval-state head `a913232a6623936855d635885d513d4c778c4b25` in exactly the -four authorized correction paths, with no new file, deletion, rename or mode +The Amendment-13 approval-state commit differs from the content-approved +Amendment-13 candidate `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` in exactly +the four authorized paths, with no new file, deletion, rename or mode change, +and from refreshed `feature/worker` in exactly the six cumulative PR paths +(section 9, "Amendment-13 approval-state validation"). The Amendment-13 +candidate commit differed from the historical Amendment-12 approval-state +head `a913232a6623936855d635885d513d4c778c4b25` in exactly the four +authorized correction paths, with no new file, deletion, rename or mode change, and from refreshed `feature/worker` in exactly the six cumulative PR paths (section 9, "Amendment-13 candidate validation"). The Amendment-12 approval-state commit differed from the content-approved @@ -3961,8 +4164,9 @@ stage (#958 comment `6046895895`), the Amendment-9 approval-state stage `6128858897`), the Amendment-12 candidate stage (#958 comment `6151955721`), the Amendment-12 replacement-candidate stage (#958 comment `6172224137`), the Amendment-12 approval-state stage (#958 comment -`6202232188`) and the Amendment-13 candidate stage (#958 comment -`6288171434`). This matrix +`6202232188`), the Amendment-13 candidate stage (#958 comment +`6288171434`) and the Amendment-13 approval-state stage (#958 comment +`6295994615`). This matrix states only what had been done when the commit that contains this report was created; it is historical stage-time evidence and does not describe the current PR #960 lifecycle gate. @@ -4593,8 +4797,8 @@ authoritative for the current state of each): - production activation: NOT USED (not authorized) - other: NONE -Amendment-13 candidate stage - done up to and including the creation of the -commit that contains this report: +Amendment-13 candidate stage - historical, completed before the +Amendment-13 approval-state commit: - repository inspection: USED (read-only verification of the exact refs, including `develop` and `feature/worker` at the same base and `master`, @@ -4611,54 +4815,130 @@ commit that contains this report: state in GitHub; all matched the authorization; current review-thread state remains GitHub-owned) - PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, - title or description change is authorized at this stage) + title or description change was authorized at that stage) - source edit: USED (the four authorized correction paths only) - new file creation, file deletion/move/rename, file-mode change: NOT USED (not authorized) - branch creation: NOT USED as a remote action (not authorized). Construction - used the implementing agent's own fresh session clone, in which a local + used that implementing agent's own fresh session clone, in which a local branch of the same name tracking `origin/feature/async/adr-0012` was checked out at the approval-state head with a clean worktree; no other worktree, remote branch or ref was created or touched, and the existing - task branch is the fast-forward target -- commit: USED (the single-parent candidate commit that contains this report) + task branch was the fast-forward target +- commit: USED (the single-parent candidate commit + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5`) +- push: USED (one fast-forward of `feature/async/adr-0012` from + `a913232a6623936855d635885d513d4c778c4b25` to the candidate; at the + Amendment-13 approval-state stage the remote head, tree, single parent and + ADR-0012, decision-register, changelog and report blobs matched, the four + `pull_request` workflow runs on that head had completed with `classify` + and `check-changelog` succeeding and every heavy job skipped, and no + automated review had run on the draft candidate head) +- independent exact-candidate source review: NOT USED by the implementing + agent; an independent reviewer reviewed the published candidate and + returned `APPROVED` (section 2) -Authorized post-commit actions for the candidate commit - GitHub-owned +Not authorized for the implementing agent at the Amendment-13 candidate +stage, each a separate controlled lifecycle category where applicable +(stage-time evidence; GitHub, through #958 and PR #960, is authoritative for +the current state of each): + +- any edit to a path other than the four authorized paths: NOT USED (not + authorized) +- any ADR-0012 change beyond the effective Amendment 13 and the approved + NB-01 to NB-10 interpretation, including any new architecture, privilege, + recovery or incident design: NOT USED (not authorized) +- any other branch or ref creation or mutation, PR target change, rebase, + merge, cherry-pick, amend, squash, force push, history rewrite, second + commit or second push: NOT USED (not authorized) +- self-approval, independent inspection or review, content or + exact-final-blob approval: NOT USED (never on the implementing agent's + authority) +- issue/comment mutation: NOT USED (not authorized) +- PR #960 title or description update, Draft -> Ready, reviewer request: NOT + USED (not authorized) +- review-thread reply/resolution/dismissal: NOT USED (not authorized) +- manual CI dispatch/rerun: NOT USED (not authorized) +- manual Codex/app review request: NOT USED (not authorized) +- provider/runtime read or write, credential access: NOT USED (not + authorized) +- migration or data execution: NOT USED (not authorized) +- release/tag/package/image publication: NOT USED (not authorized) +- programme-pin reconciliation: NOT USED (not authorized) +- merge: NOT USED (not authorized; the implementing agent must not merge its + own work) +- deployment: NOT USED (not authorized) +- production activation: NOT USED (not authorized) +- other: NONE + +Amendment-13 approval-state stage - done up to and including the creation of +the commit that contains this report: + +- repository inspection: USED (read-only verification of the exact refs, + including `develop` and `feature/worker` at the same base and `master`, + #957 and #958 open with the approval-state authorization as the latest + #958 comment, preceded by the CTO candidate architecture-content approval + and the independent exact-candidate source review with no later control + record, PR #960 open, draft, unmerged and mergeable with head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and target `feature/worker`, no + review submitted on that head, the candidate's tree, single parent and + ADR-0012, report, decision-register and changelog blobs, the protected + ADR-0008, ADR-0010 and ADR-0013 blobs, the Route-B doctrine blob, the CI + classifier and workflow blobs, the six-path cumulative footprint, the + absence of any ADR-0012 commit reachable from `develop`, `master` or + `feature/worker`, the job-level outcomes of the four workflow runs on the + candidate head and, as stage-time evidence, the PR #960 review-thread + state in GitHub; all matched the authorization; current review-thread + state remains GitHub-owned) +- PR creation/update: NOT USED (PR #960 was already a draft; no PR-state, + title or description change is authorized at this stage) +- source edit: USED (the four authorized paths, approval state only) +- new file creation, file deletion/move/rename, file-mode change: NOT USED + (not authorized) +- branch creation: NOT USED (not authorized). Construction used a separate + local Git worktree created by the implementing agent in its own session + scratch area, detached at the content-approved candidate with a clean + status before editing; no branch, remote branch, other worktree, stash or + ref was created or touched, and the existing task branch is the + fast-forward target +- commit: USED (the single-parent approval-state commit that contains this + report) + +Authorized post-commit actions for the approval-state commit - GitHub-owned lifecycle evidence under `ADR-0005`, not self-recorded here: - fresh pre-push guards: re-reading #958 and PR #960, confirming that the - task branch still equals `a913232a6623936855d635885d513d4c778c4b25` and that + task branch still equals `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and that `feature/worker` and `develop` still equal `b44303c214498baf76c9d9a0a7cab374377f9cbe`, and re-evaluating the classifier and automatic workflow behaviour; - the one normal non-force push that fast-forwards `feature/async/adr-0012` - from `a913232a6623936855d635885d513d4c778c4b25` to the candidate commit; + from `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` to the approval-state + commit; - post-push read-only verification of the remote head, sole parent, tree, - candidate blobs, `PROPOSED` state, changed path sets, protected blobs and - PR #960 state, and that no review thread was replied to, resolved or - dismissed; + approval-state blobs, ADR approval state, changed path sets, protected + blobs and PR #960 state, and that no review thread was replied to, + resolved or dismissed; - read-back of the automatic PR CI that the push causes, at job level. These actions necessarily follow the creation of the commit that contains this report. Their outcomes are reported in the stage return and the GitHub ledger rather than written into the source tree as completed. -Not authorized for the implementing agent at the Amendment-13 candidate +Not authorized for the implementing agent at the Amendment-13 approval-state stage, each a separate controlled lifecycle category where applicable (stage-time evidence; GitHub, through #958 and PR #960, is authoritative for the current state of each): - any edit to a path other than the four authorized paths: NOT USED (not authorized) -- any ADR-0012 change beyond the effective Amendment 13 and the approved - NB-01 to NB-10 interpretation, including any new architecture, privilege, - recovery or incident design: NOT USED (not authorized) +- any ADR-0012 change outside the approval-state representation, including + any architecture change: NOT USED (not authorized) - any other branch or ref creation or mutation, PR target change, rebase, merge, cherry-pick, amend, squash, force push, history rewrite, second commit or second push: NOT USED (not authorized) -- self-approval, independent inspection or review, content or - exact-final-blob approval: NOT USED (never on the implementing agent's - authority) +- self-approval, independent inspection or review, exact-final-blob approval + or confirmation: NOT USED (never on the implementing agent's authority) - issue/comment mutation: NOT USED (not authorized) - PR #960 title or description update, Draft -> Ready, reviewer request: NOT USED (not authorized) @@ -4700,8 +4980,9 @@ F-A10-CAND-01 consistency-correction authorization, the Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the Amendment-11 approval-state authorization, the Amendment-12 candidate authorization, the Amendment-12 replacement-candidate authorization, the -Amendment-12 approval-state authorization and the Amendment-13 candidate -authorization, configured Codex/app review is +Amendment-12 approval-state authorization, the Amendment-13 candidate +authorization and the Amendment-13 approval-state authorization, configured +Codex/app review is not expected from a push to a draft PR and is not relied upon at the Amendment-6/7 candidate, approval-state, control-record recovery, transient-state correction, factual-clarification, Amendment-8 candidate, @@ -4709,12 +4990,12 @@ Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 replacement-candidate, Amendment-12 -approval-state or Amendment-13 candidate stage; configured Codex/app review -runs on separately +approval-state, Amendment-13 candidate or Amendment-13 approval-state stage; +configured Codex/app review runs on separately authorized Ready transitions, which are GitHub-owned lifecycle events, and any automated review that nevertheless runs on a draft head is inspected -read-only. The actual job outcomes for the Amendment-13 candidate commit -exist only after its publication; they +read-only. The actual job outcomes for the Amendment-13 approval-state +commit exist only after its publication; they are GitHub-owned lifecycle evidence recorded in the pull request, the stage return and the owning issue, not in this file. If an automatic workflow unexpectedly intends to execute a migration or @@ -4724,7 +5005,10 @@ including a staging-image push, the Amendment-13 candidate stage verified before push that the workflow and classifier files are unchanged and that the complete direct and PR-range path sets classify as documentation-only, including by replaying the classifier's own `pull_request` event path -locally (section 9, "Amendment-13 candidate validation"); a push to +locally (section 9, "Amendment-13 candidate validation"); the Amendment-13 +approval-state stage verified the same before its commit (section 9, +"Amendment-13 approval-state validation"), and its pre-push re-verification +is a post-commit guard reported in the stage return; a push to `feature/async/adr-0012` triggers no `push` workflow, which run only for `master` and `develop`. @@ -4734,9 +5018,9 @@ factual-clarification, Amendment-8 candidate, Amendment-8 approval-state, Amendment-9 candidate, Amendment-9 approval-state, Amendment-10 candidate, consistency-correction, Amendment-10 approval-state, Amendment-11 candidate, Amendment-11 approval-state, Amendment-12 candidate, Amendment-12 -replacement-candidate, Amendment-12 approval-state or Amendment-13 candidate -stage, followed the same documentation-only classification, with no manual -dispatch or rerun: +replacement-candidate, Amendment-12 approval-state, Amendment-13 candidate +or Amendment-13 approval-state stage, followed the same documentation-only +classification, with no manual dispatch or rerun: ```text 7f7e62824eb40308688b46351ffea182e9e52942: 4 success, 6 skipped @@ -4762,6 +5046,7 @@ e0fa590e06a00d08df883bc4ddeae40b7660fcb8: 4 success (classify x3, check-changelo 06cc13685f1df5ec41da4f7958e8780a0c63ead9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (first Amendment-12 candidate) 9db7f7760f22ed2f6e7826b323f5d09c032ed4f9: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-12 replacement candidate) a913232a6623936855d635885d513d4c778c4b25: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (Amendment-12 approval-state head) +18ffbf7d8b39755abfe84b64af29bc47ca7550b5: 4 success (classify x3, check-changelog), 6 skipped (build, test, lint, format_check, run_migrations, build_and_push_staging_docker_image) (content-approved Amendment-13 candidate) ``` Manually initiated external actions (anything the implementing agent @@ -6204,7 +6489,8 @@ Amendment-12 approval-state stage (historical approval-state head described the historical Amendment-11 source; that stage was not authorized to change them. -Amendment-13 Route-B candidate (the commit that contains this report): +Amendment-13 Route-B candidate (historical content-approved candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5`): 157. The original-epoch classification is a new section-3.2 subsection that derives its four classes - ordinarily eligible, in-epoch contract @@ -6322,20 +6608,130 @@ Amendment-13 Route-B candidate (the commit that contains this report): row change, as mechanically consistent register metadata (decisions 25, 78, 97 and 129 pattern). The changelog gains one entry and changes no existing entry. -172. This report does not self-pin the candidate commit's SHA, its tree or its - own final blob; it records only non-self-referential evidence and the - candidate ADR-0012, decision-register and changelog blobs, which do not - depend on its bytes. Because the report now lives in the candidate commit, - statements that described the approval-state commit as the commit - containing this report are reframed as history, and the approval-state - head's identities are recorded in section 1 as historical facts. - Construction used the implementing agent's own fresh session clone - (section 4.2). Section 1 names this stage's implementing agent without a - model identifier because the agent's operating policy forbids writing - model identifiers into repository files; the exact identity is disclosed - in the stage return. The PR #960 title and description describe - the historical Amendment-12 source; this stage is not authorized to change - them. +172. The candidate-stage report did not self-pin the candidate commit's SHA, + its tree or its own final blob; it recorded only non-self-referential + evidence and the candidate ADR-0012, decision-register and changelog + blobs, which did not depend on its bytes. Because the report then lived in + the candidate commit, statements that described the approval-state commit + as the commit containing the report were reframed as history, and the + approval-state head's identities were recorded in section 1 as historical + facts; section 1 now records the candidate's identities as historical + facts because that head is the Amendment-13 approval-state commit's + parent. Construction used that implementing agent's own fresh session + clone (section 4.2). Section 1 names that stage's implementing agent + without a model identifier because that agent's operating policy forbids + writing model identifiers into repository files; the exact identity was + disclosed in its stage return. The PR #960 title and description then + described the historical Amendment-12 source; that stage was not + authorized to change them. + +Amendment-13 approval-state stage (the commit that contains this report): + +173. The approval-state header uses the same field order as the earlier + approved ADR-0012 versions (`Status`, `Date`, `Approved by`, + `Approval date`, `Decision owner`), and section 12 repeats the approver + and date; the original decision date `2026-09-29` is preserved and kept + distinct from the approval date. +174. The approval date carried by the ADR is the date of the CTO exact + candidate architecture-content approval record for the Amendment-13 + candidate, 2026-10-20, as Route B and the approval-state authorization + require; it is neither back-dated nor anticipated, it is not derived from + a commit or push date, and the historical Amendment-12 approval date + 2026-10-15 is not reused. +175. Section 12's approval-state delta is limited to its two leading + candidate-state paragraphs, replaced by "Current decision state: + **APPROVED**.", the approver/date and one paragraph stating that the CTO + approved this exact corrected content on 2026-10-20 as the architecture + content represented by candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, that this version differs + from that candidate only in its approval-state representation, that the + approval is architecture approval only and does not by itself make the + ADR repository-authoritative, and that ADR-0013 programme-local reliance + on this exact version is separate and not effective until its own + conditions are satisfied (decision 151 pattern, with the + architecture-approval-only clause that the approval-state authorization + asks the opening prose to state). Section 12 makes no claim that this + approval-state blob has received exact-final-blob approval or final-head + review. The historical-approval paragraph, which binds the 2026-10-15 + approval to historical blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e` and + states that the earlier approvals do not approve this version, and the + material-correction, architecture-only, authority-condition, + partial-supersession and amendment paragraphs were already true in the + approved state and are unchanged. +176. The content approval is recorded only through owning issue #958, + approver Javi, CTO, approval date 2026-10-20, candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; that is the accurate + attribution of the content-approval receipt that the approval-state + authorization asks for. Its GitHub comment identifier is not copied into + repository source: the repository-authoritative Route-B rule in + `docs/engineering/decisions/README.md` forbids it, the control + adjudication on #958 of the earlier Amendment-6/7 approval-state report + applied that rule to this task (decisions 36-37, 64, 84, 108, 128 and + 152), and the approval-state authorization itself directs that GitHub + transient identifiers not be copied to create redundant committed + lifecycle state. The independent exact-candidate source review receipt is + likewise recorded by outcome and date only. The approval-state + authorization's own comment identifier is recorded, as at every earlier + stage, because it is the stage's governing authorization. +177. The decision-register header `Last updated: 2026-10-20` already carries + the date on which the approval-state commit changes the ADR-0012 row, so + it is unchanged, as the approval-state authorization states; only the + ADR-0012 row changes. The row binds the content approval to the candidate + head and blob as historical content-approval evidence. The + approval-state authorization asks the row to continue to state that + exact-final-blob approval, fresh final-head review, merge authorization, + ADR-0013 reliance and repository authority are separate, unfulfilled + gates. Because `docs/engineering/AGENTS.md` section 1.1 requires committed + status prose to stay true before and after review and merge, the row + records that state in durable form: the content approval "does not + itself constitute" any of those gates, which are "separate gates recorded + in GitHub", and repository authority "follows only the authority + condition" of the row, whose existing sentence is unchanged; a literal + "unfulfilled" would become false as soon as any of those gates is + satisfied. Recorded as an interpretation for independent inspection. +178. Only the `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION` changelog + entry changes, following the earlier approval-state wording pattern + (decisions 66, 86, 110, 130 and 154: "propose" to "record a + CTO-approved", the `PROPOSED` sentence to the `APPROVED` sentence naming + the approver and date, and "proposal only" to "decision only"); its + architecture description and its historical-version and + no-runtime-authorization statements are preserved, every other entry, + including the historical approval entries, is byte-identical, and no + entry is added. +179. Architecture no-drift is proven by the exact normalized comparison in + section 9 ("Amendment-13 approval-state validation"), not by assertion: + outside the header approval fields and the section-12 opening block, the + approval-state ADR is byte-identical to the content-approved candidate + blob, so sections 3.2, 3.5, 3.14 and 3.15, invariants 1-47, the twenty + Amendment-12 validation cases, the thirty active `A13-T` cases, the + retired `A13-T25` record and every other approved byte are unchanged; the + hunk list and the counts in section 9 confirm it independently of the + normalization. +180. This report does not self-pin the approval-state commit's SHA, its tree + or its own final blob; it records only non-self-referential evidence and + the approval-state ADR-0012, decision-register and changelog blobs, which + do not depend on its bytes; the approval-state ADR blob is recorded as a + result and is not represented as exact-final-blob approved. Because the + report now lives in the approval-state commit, statements that described + the candidate as the commit containing this report, or the register row + as currently recording a candidate as `PROPOSED`, are reframed as + history, including the three such section-2.2 sentences written at + earlier candidate stages, and the candidate's identities are recorded in + section 1 as historical facts. The approval-state authorization lists the + report sections to reconcile; section 2's approved-specification list, + implemented objective, stage record, classification block and + stale-control text are reconciled as part of the opening/current-stage + reconciliation it names, because they would otherwise state that the + candidate is the commit containing this report or that the corrected + version is not approved. Recorded as an interpretation for independent + inspection. Construction used a separate local Git worktree that the + implementing agent created in its own session scratch area, detached at + the content-approved candidate with a clean status before editing, + touching no branch, other worktree, stash or ref (section 4.2). The PR + #960 title and description describe the historical Amendment-12 source; + this stage is not authorized to change them. List any deviation from the specification requiring authorization: @@ -6475,14 +6871,22 @@ List any deviation from the specification requiring authorization: - Amendment-13 candidate stage: NONE as to scope. No fifth path, new file, deletion, rename, mode change, ADR change outside the effective Amendment 13 and the candidate state, second commit, amend, rebase, squash, force push or - history rewrite was needed or performed before the commit that contains - this report was created. Decisions 161, 162, 166 and 170 are recorded for - independent inspection as interpretations within the authorized scope, not - as deviations. One documentation-convention difference is disclosed rather - than hidden: section 1 records this stage's implementing agent without a - model identifier, because the implementing agent's operating policy - forbids writing model identifiers into repository files; the exact model - identity is disclosed in the stage return (decision 172). + history rewrite was needed or performed before the candidate commit was + created. Decisions 161, 162, 166 and 170 were recorded for independent + inspection as interpretations within the authorized scope, not as + deviations, and the independent exact-candidate source review raised no + finding against them. One documentation-convention difference was + disclosed rather than hidden: section 1 records that stage's implementing + agent without a model identifier, because that implementing agent's + operating policy forbids writing model identifiers into repository files; + the exact model identity was disclosed in its stage return (decision 172). +- Amendment-13 approval-state stage: NONE as to scope. The approval-state + transformation followed its separate authorization; no fifth path, new + file, deletion, rename, mode change, architecture change, second commit, + amend, rebase, squash, force push or history rewrite was needed or + performed before the commit that contains this report was created. + Decisions 177 and 180 are recorded for independent inspection as + interpretations within the authorized scope, not as deviations. ## 6. Database and migration effects @@ -6577,8 +6981,10 @@ validation" records the validation of the historical content-approved replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`. "Amendment-12 approval-state validation" records the validation of the historical approval-state commit `a913232a6623936855d635885d513d4c778c4b25`. "Amendment-13 -candidate validation" at the end of this section records the validation of -the commit that contains this report. +candidate validation" records the validation of the historical +content-approved candidate `18ffbf7d8b39755abfe84b64af29bc47ca7550b5`. +"Amendment-13 approval-state validation" at the end of this section records +the validation of the commit that contains this report. Validation environment for the activation-epoch candidate: a full local Git worktree of `thoth-pub/thoth`, @@ -11130,15 +11536,21 @@ git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchro 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 ``` -The candidate ADR-0012, decision-register and changelog blobs are -deterministic and do not depend on this report's bytes, so they are recorded -above. This report's own final blob, the candidate tree and the candidate -commit SHA depend on this report's bytes and are not self-pinned; they are -verified against the remote head after push and reported in the stage -return. +The candidate ADR-0012, decision-register and changelog blobs were +deterministic and did not depend on the candidate-stage report's bytes, so +they were recorded above. That report's own final blob, the candidate tree +and the candidate commit SHA depended on its bytes and were not self-pinned +in it; they were verified against the remote head after push and reported in +that stage's return, and section 1 now records them as historical facts: +commit `18ffbf7d8b39755abfe84b64af29bc47ca7550b5`, tree +`4cf1e8f566d85cb84a32a385979d459118ba98a3`, report blob +`7ea63c99d0a925ef6e666de7b415713b16786996`. -Required topology of the candidate commit, checked after it is created and -before any push, with results reported in the stage return: +Required topology of the candidate commit, checked after it was created and +before push; the published candidate satisfies every requirement (single +parent `a913232a6623936855d635885d513d4c778c4b25`, `git diff --check` clean, +exactly the four candidate paths, each `M` with mode `100644`, and the six +cumulative PR paths, re-verified at the Amendment-13 approval-state stage): ```text git rev-list --parents -n 1 HEAD @@ -11157,6 +11569,355 @@ git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD required: exactly the six cumulative PR paths ``` +### Amendment-13 approval-state validation + +Environment: a separate local Git worktree of `thoth-pub/thoth` that the +implementing agent created in its own session scratch area with +`git worktree add --detach` at the content-approved Amendment-13 candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5` (tree +`4cf1e8f566d85cb84a32a385979d459118ba98a3`), verified clean (no tracked or +untracked change) before editing; no branch, other worktree, stash or ref was +created or touched. Immediately before construction, read-only GitHub and Git +inspection showed #957 and #958 open, the approval-state authorization (#958 +comment `6295994615`) as the latest #958 comment, preceded by the CTO +candidate architecture-content approval and the independent exact-candidate +source review with no later control record, `develop` and `feature/worker` at +`b44303c214498baf76c9d9a0a7cab374377f9cbe`, `master` at +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`, `feature/async/adr-0012` at +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5` with single parent +`a913232a6623936855d635885d513d4c778c4b25`, ADR-0012 blob +`0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, report blob +`7ea63c99d0a925ef6e666de7b415713b16786996`, decision-register blob +`5611ff3f4f3b1981f30295182efff1f882a24331` and changelog blob +`0833e6d4efcb7451739ea90bbbadb33ff6def37b` at that head, the protected +ADR-0008, ADR-0010 and ADR-0013 blobs, the Route-B doctrine blob, the CI +classifier and workflow blobs, PR #960 open, draft, unmerged and mergeable +with that head and target `feature/worker` and no review submitted on that +head, the cumulative PR footprint exactly the six paths, the four +`pull_request` workflow runs on that head with `classify` and +`check-changelog` succeeding and every heavy job skipped, and PR #960 +review-thread state that matched the stage authorization (stage-time +evidence; current review-thread state remains GitHub-owned). ADR-0012, the +decision register and the changelog were edited first and their +deterministic blobs recorded; then only this report was edited; all four were +staged, and every check below ran against that exact final staged index +before the approval-state commit was created from it; the report's numeric +line counts below were filled in and the complete check set rerun with +identical results. These are documentation checks only: none of them +executes, or substitutes for, the PostgreSQL concurrency and fault-injection +validation that ADR-0012 section 11 requires of the later implementation. + +Route-B eligibility (the four literal section-2.1 commands, run against +`b44303c214498baf76c9d9a0a7cab374377f9cbe` and +`4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff`): + +```text +git log --format=%h -- ':(glob)**/*ADR-0012*' +result for both refs: no commits + +git log --format=%h -G'^# ADR-0012' +result for both refs: no commits + +git log --format=%h -G'^\| `ADR-0012`' -- docs/engineering/decisions/decision-register.md +result for both refs: no commits + +git grep -l ADR-0012 +result for b44303c214498baf76c9d9a0a7cab374377f9cbe: +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-CTRL-01-implementation-report.md +docs/engineering/decisions/ADR-0013-programme-integration-decision-reliance.md +result for 4fa7eaa9ccb60d39c41ccd8feb257edf28c173ff: no files +``` + +Whitespace: + +```text +git diff --check +exit 0; no output + +git diff --cached --check 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 +exit 0; no output + +git diff --cached --check b44303c214498baf76c9d9a0a7cab374377f9cbe +exit 0; no output +``` + +Approval-state delta against the content-approved candidate (exactly the four +authorized paths, all modifications, no mode change, creation, deletion or +rename): + +```text +git diff --cached --name-status 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 +M CHANGELOG.md +M docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +M docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +M docs/engineering/decisions/decision-register.md + +git diff --cached --numstat 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 +1 1 CHANGELOG.md +1042 195 docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +18 6 docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +1 1 docs/engineering/decisions/decision-register.md + +git diff --cached --summary 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 +exit 0; no output (no mode change, creation, deletion or rename) + +git diff --cached --raw 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 | cut -d' ' -f1-2 | sort -u +:100644 100644 +``` + +Cumulative footprint against refreshed `feature/worker` (exactly the six PR +paths) and unchanged control files: + +```text +git diff --cached --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe +CHANGELOG.md +docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md +docs/engineering/decisions/ADR-0010-staff-operations-console.md +docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +docs/engineering/decisions/decision-register.md + +git diff --cached --name-only 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 -- .github AGENTS.md docs/engineering/AGENTS.md docs/engineering/decisions/README.md docs/engineering/decisions/ADR-0008-machine-roles-and-durable-job-primitives.md docs/engineering/decisions/ADR-0010-staff-operations-console.md docs/engineering/decisions/ADR-0013-programme-integration-decision-reliance.md Cargo.toml Cargo.lock thoth-api +exit 0; no output +``` + +ADR-0012 approval-state delta (three hunks: the header status line, the two +inserted header approval lines and the section-12 opening block; no other +hunk): + +```text +git diff --cached -U0 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^@@' +@@ -3 +3 @@ +@@ -4,0 +5,2 @@ Date: 2026-09-29 +@@ -4236,5 +4238,15 @@ shared-engine tests. + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n '^Status:\|^Approved by:\|^Approval date:\|^Current decision state:' +3:Status: APPROVED +5:Approved by: Javi, CTO +6:Approval date: 2026-10-20 +4238:Current decision state: **APPROVED**. +4240:Approved by: Javi, CTO +4241:Approval date: 2026-10-20 + +git diff --cached 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 -- docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep '^+' | grep -v '^+++' | awk 'length>81' | wc -l +0 +``` + +Architecture no-drift proof. The normalization `N` drops the header +`Status`, `Approved by` and `Approval date` lines before the first `---` +separator and the section-12 lines from the heading up to, but not +including, the line beginning "This version is a material architectural +correction"; it keeps every other byte. The content-approved candidate blob +and the staged approval-state ADR normalize to identical content, and +sections 3, 7 and 11 compare byte-identical without any normalization: + +```text +N='BEGIN{h=1} h&&/^---$/{h=0} h&&/^(Status|Approved by|Approval date): /{next} /^## 12\. Approval and authority$/{print;s=1;next} s&&/^This version is a material architectural correction/{s=0} s{next} {print}' + +diff <(git cat-file -p 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 | awk "$N") <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N") +exit 0; no output (NO DIFFERENCE) + +git cat-file -p 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239 | awk "$N" | shasum -a 256 +b4d44b772399d318843c66936991df299a75d3a4f19c3a66ffd377e40ad1a043 - + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | awk "$N" | shasum -a 256 +b4d44b772399d318843c66936991df299a75d3a4f19c3a66ffd377e40ad1a043 - + +diff <(git show 18ffbf7d8b39755abfe84b64af29bc47ca7550b5:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 3. Decision/,/^## 4\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 3. Decision/,/^## 4\. /p') +exit 0; no output (section 3, including sections 3.2, 3.5, 3.14 and 3.15, byte-identical) + +diff <(git show 18ffbf7d8b39755abfe84b64af29bc47ca7550b5:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^## 8\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7. Invariants/,/^## 8\. /p') +exit 0; no output (invariants 1-47 byte-identical) + +diff <(git show 18ffbf7d8b39755abfe84b64af29bc47ca7550b5:docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 11. Validation requirements/,/^## 12\. /p') <(git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 11. Validation requirements/,/^## 12\. /p') +exit 0; no output (section 11 byte-identical) + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 7\. Invariants/,/^## 8\. /p' | grep -o '^[0-9]\+\. ' | tr -d '. ' | tr '\n' ' ' +1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^## 11\. Validation requirements/,/^## 12\. /p' | grep -c '^- ' +204 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | sed -n '/^- deterministic post-seal baseline tests/,/^- non-coalesced alternative tests/p' | grep -c '^- ' +20 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -o '^- \*\*A13-T[0-9][0-9]' | sed 's/.*T//' | tr '\n' ' ' +01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 26 27 28 29 30 31 + +git show :docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md | grep -n 'A13-T25' | cut -c1-60 +4226:`A13-T25` is retired and is not an active case. It woul +``` + +Lines the normalization removes from each version (trailing blanks stripped +for display), so that the complete raw delta is visible: + +```text +content-approved candidate 0b5fb2d7ef30f156a13bf6f195ef8b34e807d239: +Status: PROPOSED +Current decision state: **PROPOSED**. +The corrected content of this version has not been approved. It may carry +`APPROVED`, an approver and an approval date only after the CTO approves this +exact corrected content under the repository decision process. + +approval-state ADR: +Status: APPROVED +Approved by: Javi, CTO +Approval date: 2026-10-20 +Current decision state: **APPROVED**. +Approved by: Javi, CTO +Approval date: 2026-10-20 +The CTO approved this exact corrected content on 2026-10-20 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and +candidate ADR-0012 blob `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; this +version differs from that candidate only in this approval-state +representation. That approval is architecture approval only and does not by +itself make this ADR repository-authoritative: the authority condition below +still applies. Any ADR-0013 programme-local reliance on this exact version is +a separate state that is not effective until its own conditions are +satisfied for this version. +``` + +Decision-register delta (the ADR-0012 row only; the `Last updated: +2026-10-20` header and every other line byte-identical): + +```text +git diff --cached -U0 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 -- docs/engineering/decisions/decision-register.md | grep '^@@' +@@ -20 +20 @@ Last updated: 2026-10-20 + +diff <(git show 18ffbf7d8b39755abfe84b64af29bc47ca7550b5:docs/engineering/decisions/decision-register.md | sed '20d') <(git show :docs/engineering/decisions/decision-register.md | sed '20d') +exit 0; no output + +git show :docs/engineering/decisions/decision-register.md | sed -n '5p;20p' | cut -c1-200 +Last updated: 2026-10-20 +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Dist +``` + +Changelog delta (the existing +`THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION` entry only; every other +line byte-identical): + +```text +git diff --cached -U0 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 -- CHANGELOG.md | grep '^@@' +@@ -22 +22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +diff <(git show 18ffbf7d8b39755abfe84b64af29bc47ca7550b5:CHANGELOG.md | sed '22d') <(git show :CHANGELOG.md | sed '22d') +exit 0; no output + +git diff --cached --word-diff=plain -U0 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 -- CHANGELOG.md | grep -o '\[-[^]]*-\]\|{+[^}]*+}' +[-propose-] +{+record+} +{+CTO-approved+} +[-`PROPOSED`; this-] +{+`APPROVED`: Javi, CTO approved the exact corrected+} +[-is not approved.**-] +{+on 2026-10-20.**+} +[-proposal-] +{+decision+} +``` + +Review and approval identifiers (each command is shown with the identifier +replaced by a description, so that this report itself does not contain it): + +```text +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 + +git show :docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md | grep -F -c '' +0 +``` + +Documentation-only CI classification with the unchanged classifier +(`.github/scripts/classify_ci_changes.py`, blob +`2cac40c9d1d45e6961cfb4e368c602b75d8f980d`): + +```text +python3 .github/scripts/classify_ci_changes.py --paths CHANGELOG.md docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md docs/engineering/decisions/decision-register.md +{"docs_only": "true", "run_build": "false", "run_docker": "false", "run_migrations": "false"} + +python3 .github/scripts/classify_ci_changes.py --paths +{"docs_only": "true", "run_build": "false", "run_docker": "false", "run_migrations": "false"} + +pull_request event path evaluated on the staged tree (the classifier's merge-base comparison of the PR base with the head) +git merge-base b44303c214498baf76c9d9a0a7cab374377f9cbe 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 +b44303c214498baf76c9d9a0a7cab374377f9cbe +git diff --cached --name-only --no-renames b44303c214498baf76c9d9a0a7cab374377f9cbe +the same six paths; classify_paths -> docs_only true, run_build false, run_migrations false, run_docker false + +python3 .github/scripts/classify_ci_changes.py --self-test +PASS all_self_tests: 14 cases +``` + +Under these unchanged workflow definitions, a push to the PR head triggers +the four `pull_request` workflows; each `classify` job is expected to +succeed with `run_build`, `run_migrations` and `run_docker` false, so +`build`, `test`, `lint`, `format_check`, `run_migrations` and +`build_and_push_staging_docker_image` are expected to be skipped and +`check-changelog` to run against the changed `CHANGELOG.md`. The `push` +workflows run only for `master` and `develop`. The actual outcomes are +GitHub-owned. + +Staged blob identities (condensed `git ls-files -s` output; mode and stage +omitted, paths abbreviated) and the ADR-0012 `git hash-object --no-filters` +result: + +```text +ADR-0012 (APPROVED) 69f9ec9017ffe39d865bbe3ae67522c6917dc857 +decision-register.md 776448bae47d6aa03201fc230bc5957d5461db67 +CHANGELOG.md 6e8a64078d0b6a41109e84feec23832d027673d0 +ADR-0008 622060ad90eec41c792f110c373efffbb11a4b56 +ADR-0010 aca2142a3387785e80db908b3a5c1b0afd82ab51 +ADR-0013 d928f957bdf775d03e99fc73888ec8e2dec5f80b +decisions/README.md de7769a4ee6a50c79c1b57ae6993d7eb4fa673d7 +.github/scripts/classify_ci_changes.py 2cac40c9d1d45e6961cfb4e368c602b75d8f980d +build_test_and_check.yml 3ae164e8aaf9a1fffe7d1b26f720c599b1e6b7e5 +check_changelog.yml 50e4ddbc62c0cc0e1dae6854a805d4e47b8ae01a +docker_build_and_push_to_dockerhub.yml 1fd32cdbf150e4ce450a32486c45cfb895ffd13d +run_migrations.yml 71fe33a7cf3577697e6e2f62c0c3f2636a5bd8bf +ADR-0005 bdaa976e4893b1fc45f994236f9e56d433212d63 +docs/engineering/AGENTS.md e194b14e8c4fbb298db7ca6c38aebeb69547a29a +implementation-report-template.md 0ae39d3892bbca5b0ff90dc7ffa70038662351bc + +git hash-object --no-filters docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +69f9ec9017ffe39d865bbe3ae67522c6917dc857 +``` + +The approval-state ADR-0012, decision-register and changelog blobs are +deterministic and do not depend on this report's bytes, so they are recorded +above. The ADR-0012 blob `69f9ec9017ffe39d865bbe3ae67522c6917dc857` is the +exact final blob that this stage produces for the separate CTO +exact-final-blob gate; this report does not represent it as so approved. +This report's own final blob, the approval-state tree and the approval-state +commit SHA depend on this report's bytes and are not self-pinned; they are +verified against the remote head after push and reported in the stage +return. + +Required topology of the approval-state commit, checked after it is created +and before any push, with results reported in the stage return: + +```text +git rev-list --parents -n 1 HEAD +required: 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 (exactly one parent) + +git rev-parse HEAD^{tree} +required: equal to the git write-tree output of the validated index + +git diff --check 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 HEAD +required: exit 0; no output + +git diff --name-status 18ffbf7d8b39755abfe84b64af29bc47ca7550b5 HEAD +required: exactly the four authorized paths, each M, with no mode change + +git diff --name-only b44303c214498baf76c9d9a0a7cab374377f9cbe HEAD +required: exactly the six cumulative PR paths + +pull_request event replay: the unchanged classifier run with +GITHUB_EVENT_NAME=pull_request and an event whose base SHA is +b44303c214498baf76c9d9a0a7cab374377f9cbe and whose head SHA is the +approval-state commit +required: docs_only true; run_build, run_migrations and run_docker false +``` + ## 10. Manual verification Environment: the local worktrees described in section 9. @@ -11454,6 +12215,23 @@ workflow blobs are unchanged; and read the report for statements that would become false once it moves into the candidate commit, in particular any statement presenting the approval-state commit as the commit containing this report or the current stage, and reframed them. +Amendment-13 approval-state stage: compared the staged ADR-0012 with the +content-approved candidate blob `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239` by +the exact normalized comparison and the hunk list in section 9 (no +architecture difference), and read the raw delta line by line; confirmed that +sections 3, 7 and 11 are byte-identical, that invariants 1-47, the twenty +Amendment-12 cases and the thirty active `A13-T` cases remain and that +`A13-T25` remains retired; compared the staged register and changelog with +the candidate blobs (only the ADR-0012 row, and only the mismatch-admission +correction entry, change); confirmed the protected decision, control, +classifier and workflow blobs are unchanged; confirmed by fixed-string search +that neither the content-approval nor the exact-candidate source-review +comment identifier occurs in this report; and read the report for +statements that would become false once it moves into the approval-state +commit, in particular any statement presenting the candidate as the commit +containing this report, describing the corrected version as not approved or +describing the register row as currently recording a candidate as +`PROPOSED`, and reframed them. Evidence link/screenshot/log reference: the local validation in section 9. Pull-request diffs of earlier heads, including the published activation-epoch candidate `95769537cdc9871e42f9f3e5443604a96beb74da`, its approval-state head @@ -11477,11 +12255,12 @@ content-approved Amendment-11 candidate `528c2db04af5c53b9820b946ddac2ebb936eb4cd`, the Amendment-11 approval-state head `21e19e81d0acf1aed57952ec00e2653771fb6f47`, the first Amendment-12 candidate `06cc13685f1df5ec41da4f7958e8780a0c63ead9` and the content-approved -replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9` and the -Amendment-12 approval-state head `a913232a6623936855d635885d513d4c778c4b25`, -are historical evidence; the live PR #960 diff of the Amendment-13 candidate -commit exists only after its publication and is GitHub-owned lifecycle -evidence. +replacement candidate `9db7f7760f22ed2f6e7826b323f5d09c032ed4f9`, the +Amendment-12 approval-state head `a913232a6623936855d635885d513d4c778c4b25` +and the content-approved Amendment-13 candidate +`18ffbf7d8b39755abfe84b64af29bc47ca7550b5`, are historical evidence; the live +PR #960 diff of the Amendment-13 approval-state commit exists only after its +publication and is GitHub-owned lifecycle evidence. ## 11. CI @@ -11501,9 +12280,9 @@ Amendment-10 approval-state authorization, the Amendment-11 candidate authorization, the Amendment-11 approval-state authorization, the Amendment-12 candidate authorization, the Amendment-12 replacement-candidate authorization, the Amendment-12 approval-state -authorization and the Amendment-13 candidate authorization, an automatic CI -failure, or any migration execution or Docker publication, is a HOLD -condition. +authorization, the Amendment-13 candidate authorization and the Amendment-13 +approval-state authorization, an automatic CI failure, or any migration +execution or Docker publication, is a HOLD condition. ## 12. Rollout and rollback @@ -11545,9 +12324,11 @@ exactly one direct-child approval-state commit, the Amendment-12 candidate authorization permitted exactly one four-path candidate commit, the Amendment-12 replacement-candidate authorization permitted exactly one direct-child four-path replacement commit, the Amendment-12 approval-state -authorization permitted exactly one direct-child approval-state commit, and -the Amendment-13 candidate authorization permits exactly one direct-child -four-path candidate commit and one non-force fast-forward push; any further +authorization permitted exactly one direct-child approval-state commit, the +Amendment-13 candidate authorization permitted exactly one direct-child +four-path candidate commit and one non-force fast-forward push, and the +Amendment-13 approval-state authorization permits exactly one direct-child +approval-state commit and one non-force fast-forward push; any further source correction, rollback, revert or recovery exhausts those authorizations and requires a fresh CTO authorization (Amendment 3 C10; Amendments 6-13; `docs/engineering/decisions/README.md`). @@ -11555,13 +12336,18 @@ Monitoring required: NONE ## 13. Known limitations and deferred work -- Stage-time evidence: at the Amendment-13 candidate stage the implementing - agent performed no independent inspection or review, content or - exact-final-blob approval, PR #960 title/description reconciliation, +- Stage-time evidence: at the Amendment-13 approval-state stage the + implementing agent performed no independent inspection or review, + exact-final-blob approval or confirmation, PR #960 title/description + reconciliation, Draft/Ready change, automated-review request, thread + reply, resolution or adjudication, exact-head review or merge (section + 4.2). The PR #960 title and description describe the historical + Amendment-12 source; their reconciliation is a separate control action. +- Historical stage-time evidence: at the Amendment-13 candidate stage the + implementing agent performed no independent inspection or review, content + or exact-final-blob approval, PR #960 title/description reconciliation, Draft/Ready change, automated-review request, thread reply, resolution or - adjudication, exact-head review or merge (section 4.2). The PR #960 title - and description describe the historical Amendment-12 source; their - reconciliation is a separate control action. + adjudication, exact-head review or merge (section 4.2). - The Amendment-13 correction selects no SQL, lock order, isolation level, schema or key shape: the canonical decision order and the universal admission guard are observable atomicity and ordering requirements, and @@ -11639,8 +12425,9 @@ Monitoring required: NONE canonical mismatch-detection and first-enrollment ordering defect found by the final-head review of that approval-state head only under Specification Amendment 13 with Revisions 1 and 2, its independent reviews, the CTO - specification approval and a separate authorization for this one - direct-child four-path candidate commit. + specification approval and a separate authorization for one direct-child + four-path candidate commit, and its approval state only under a separate + authorization for this one direct-child approval-state commit. - The strand-check timing question that decision 53 left outside the Amendment-8 correction is now determined by Specification Amendment 10 and implemented by the Amendment-10 candidate as corrected by this consistency @@ -11665,7 +12452,8 @@ Monitoring required: NONE the Amendment-10 candidate, its consistency correction and approval-state commit, the Amendment-11 candidate and approval-state commit, the Amendment-12 candidate, its replacement candidate and approval-state - commit, and this Amendment-13 candidate. + commit, the Amendment-13 candidate and this Amendment-13 approval-state + commit. ## 14. Unresolved issues @@ -11899,12 +12687,16 @@ Monitoring required: NONE specification returned `APPROVED (SPECIFICATION ONLY)` with non-blocking observations NB-01 to NB-10; the CTO approved the effective specification, classified it as a material architectural correction and selected Route - B; and the `PROPOSED` Route-B candidate that contains this report - implements it in source (section 4; section 5, decisions 157-172). The - finding is closed at specification level; its source closure is for the - independent exact-candidate source review and the later review controls - that GitHub records, and its thread adjudication belongs to those - controls. + B; the `PROPOSED` Route-B candidate + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` implemented it in source + (section 4; section 5, decisions 157-172); an independent CRITICAL + exact-candidate source review of that candidate found no blocking + candidate-source finding; Javi, CTO approved its exact architecture content + on 2026-10-20; and the approval-state commit that contains this report + records that approval. The finding is closed at specification level by + Amendment 13 with its Revisions 1 and 2 and, per that review, in the + candidate's source; its thread adjudication belongs to the review controls + that GitHub records. PR #960 holds the historical and current review threads covering the findings summarized above. GitHub is authoritative for their current number, @@ -11918,16 +12710,29 @@ merge, not to this report, which neither resolves nor dismisses any thread. The agent may identify risks but may not approve the task. The implementing agent does not approve, mark merge-ready or merge its own -work. The implementing agent of this Amendment-13 candidate is a Claude agent -in a fresh session with no conversation state, memory or authored artifact -from any earlier stage, specification, amendment or review of this task; it -authored none of Amendment 13, Revision 1 or Revision 2 and performed none of -their reviews. Earlier source stages are recorded in section 1 as -implemented by Claude agents, so prior involvement of the same underlying -model cannot be excluded at model level; the exact model identity is -disclosed in the stage return. Having implemented this candidate, it is not -eligible to act as an independent inspector or reviewer of it (Amendment 5 -F8; Amendment 6 G12; Amendment 7 H13). +work. The implementing agent of this Amendment-13 approval-state commit is +Claude (Opus 5.5), in a session whose persistent memory records that the +same agent line implemented the earlier Route-B source stages of this task +through the Amendment-12 approval-state commit and performed the earlier +reviews described in the historical paragraph below. It did not implement +the Amendment-13 candidate, which the candidate-stage report records as +implemented by a fresh session without that state, and it authored none of +Amendment 13, Revision 1 or Revision 2 and performed none of their reviews +or of the candidate's review. Having implemented this approval-state commit +and earlier source stages of this task, it is not eligible to act as an +independent inspector or reviewer of this commit or of this task +(Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). + +Historical: the implementing agent of the Amendment-13 candidate is a Claude +agent in a fresh session with no conversation state, memory or authored +artifact from any earlier stage, specification, amendment or review of this +task; it authored none of Amendment 13, Revision 1 or Revision 2 and +performed none of their reviews. Earlier source stages are recorded in +section 1 as implemented by Claude agents, so prior involvement of the same +underlying model cannot be excluded at model level; its exact model identity +was disclosed in its stage return. Having implemented that candidate, it is +not eligible to act as an independent inspector or reviewer of it +(Amendment 5 F8; Amendment 6 G12; Amendment 7 H13). Historical: the implementing agent of the Amendment-12 approval-state commit also implemented the Amendment-12 replacement candidate and first candidate, the @@ -11949,7 +12754,49 @@ as independent of the amendment's authorship only. That review was not approval of that candidate's source, and that agent remained ineligible to inspect, review or approve it. -Suggested review focus for the Amendment-13 candidate: +Suggested review focus for the Amendment-13 approval-state commit: + +- that the approval-state delta from the content-approved Amendment-13 + candidate `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` is limited to the ADR + header approval fields, the two leading section-12 paragraphs, the + ADR-0012 register row, the mismatch-admission correction changelog entry + and this report; that the normalized comparison and hunk list in section 9 + show no architecture drift from candidate blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; that sections 3, 7 and 11 are + byte-identical, with invariants 1-47, the twenty Amendment-12 cases, the + thirty active `A13-T` cases and the retired `A13-T25` record intact; and + that the approval-state ADR-0012 blob is + `69f9ec9017ffe39d865bbe3ae67522c6917dc857`; +- that the content approval is recorded only through owning issue #958, + approver Javi, CTO, approval date 2026-10-20, candidate head + `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob + `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`, that the approval date matches + the CTO candidate architecture-content approval record and is not the + historical 2026-10-15 date, and that no content-approval, exact-final-blob + approval, review or merge identifier is copied into this report (section + 5, decision 176); +- that the header and section 12 carry `APPROVED` with Javi, CTO and + 2026-10-20, identify the approved content by candidate head and blob, + state that the approval is architecture approval only, claim no + exact-final-blob approval, final-head review or repository authority, bind + the 2026-09-30, 2026-10-05, 2026-10-06, 2026-10-07, 2026-10-08, 2026-10-09, + 2026-10-12 and 2026-10-15 approvals to the earlier exact versions and keep + the repository authority condition and the ADR-0013 reliance distinction; +- that the register row, the mismatch-admission correction changelog entry + and this report describe the corrected version as `APPROVED` on + 2026-10-20, that the row's durable statement of the separate gates + (section 5, decision 177) meets the approval-state authorization, that + every other register row and changelog entry is byte-preserved, and that + the register header `Last updated` is `2026-10-20`; +- that the section-2 reconciliation and the three reframed section-2.2 + sentences (section 5, decision 180) are limited to statements that would + otherwise be false; +- that ADR-0008, ADR-0010, ADR-0013, the decisions README, the CI classifier + and the workflows keep their protected blobs and the six-path cumulative + footprint is unchanged. + +Suggested review focus recorded at the Amendment-13 candidate stage +(historical; applies to candidate `18ffbf7d8b39755abfe84b64af29bc47ca7550b5`): - that the candidate changes exactly the four authorized paths from the approval-state head `a913232a6623936855d635885d513d4c778c4b25`, with no new @@ -11983,11 +12830,11 @@ Suggested review focus for the Amendment-13 candidate: - that Amendment-12 successor, coalescing-seal, routed-job atomicity and effect-fencing semantics are unchanged outside the named conforming sentences; -- that the ADR, register row, changelog entry and this report describe the - corrected version as `PROPOSED` and bind the 2026-10-15 approval and every - earlier approval to their historical exact versions, with no claim of - approval, repository authority, review completion, merge readiness or - executed concurrency tests. +- that the ADR, register row, changelog entry and the candidate-stage report + describe the corrected version as `PROPOSED` and bind the 2026-10-15 + approval and every earlier approval to their historical exact versions, + with no claim of approval, repository authority, review completion, merge + readiness or executed concurrency tests. Suggested review focus recorded at earlier stages (historical; each item applies to the exact earlier version or head it names): diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 0b5fb2d7e..69f9ec901 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,7 +1,9 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: PROPOSED +Status: APPROVED Date: 2026-09-29 +Approved by: Javi, CTO +Approval date: 2026-10-20 Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -4233,11 +4235,21 @@ shared-engine tests. ## 12. Approval and authority -Current decision state: **PROPOSED**. - -The corrected content of this version has not been approved. It may carry -`APPROVED`, an approver and an approval date only after the CTO approves this -exact corrected content under the repository decision process. +Current decision state: **APPROVED**. + +Approved by: Javi, CTO +Approval date: 2026-10-20 + +The CTO approved this exact corrected content on 2026-10-20 under the +repository decision process, as the architecture content represented by the +Route-B candidate head `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and +candidate ADR-0012 blob `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; this +version differs from that candidate only in this approval-state +representation. That approval is architecture approval only and does not by +itself make this ADR repository-authoritative: the authority condition below +still applies. Any ADR-0013 programme-local reliance on this exact version is +a separate state that is not effective until its own conditions are +satisfied for this version. This version is a material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 5611ff3f4..776448bae 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -17,7 +17,7 @@ Last updated: 2026-10-20 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Pending - this exact `PROPOSED` version is a further material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the PR-first staged approval route (Route B); its corrected content is not approved, and only a later approval-state version may carry `APPROVED`, an approver and an approval date. CTO content approval, exact-final-blob approval, independent source-head review, merge authorization and ADR-0013 reliance are separate gates recorded in GitHub. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal, and the CTO approval dated 2026-10-15 applies only to the historical earlier corrected exact version, ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, that introduced that post-seal materialization path without a database-enforced order between mismatch detection and first historical enrollment or an original-epoch admission guard for every first route-disposition writer; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic; and every operation that can create the first `(event_id, route_key)` disposition - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition, including when an approved migration, reconciliation or disposition plan acts through them - classifies the event from immutable original-epoch facts and proves its own eligibility and authorization inside the transaction that would consume that slot, so that no disposition exists for an in-epoch contract mismatch except the one created by compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly, while the terminal non-materialized disposition is limited to ordinarily eligible, still-owed obligations and its accepted divergence is reported separately from a mismatch anomaly's `RECONCILED/DIVERGED` outcome; and detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also arbitrates the anomaly's first creation when no anomaly row exists, under which detection first records one `OPEN` anomaly with no disposition or job, enrollment first creates the canonical anomaly born-terminal `ENROLLED` atomically with its one disposition and indispensable routed job and input facts or eligible attachment and is never counted or aged as open, later detection converges without reopening, re-aging or fabricating provenance, divergence resolves only an already committed `OPEN` anomaly, is refused before detection and never creates an anomaly, disposition or job, a refused enrollment neither creates nor resolves an absent anomaly, terminal `ENROLLED` is a route decision rather than evidence of provider success, and contradictory durable state fails closed with its evidence preserved and visible as a control HOLD for separately authorized incident recovery, without a third anomaly state, job hold or automatic repair. Invariant 47 is added; invariants 5, 8, 35 and 45 are conformed; the other preceding invariants and the twenty post-seal validation cases are retained; and thirty active validation cases, `A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`, are added, with `A13-T25` retired and not reused. These are future implementation acceptance requirements, not executed tests. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a further material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the PR-first staged approval route (Route B), and Javi, CTO approved its exact corrected content on 2026-10-20 under the repository decision process, as the architecture content represented by Route-B candidate head `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only and does not itself constitute exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization or ADR-0013 reliance, which are separate gates recorded in GitHub, or repository authority, which follows only the authority condition stated later in this row. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal, and the CTO approval dated 2026-10-15 applies only to the historical earlier corrected exact version, ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, that introduced that post-seal materialization path without a database-enforced order between mismatch detection and first historical enrollment or an original-epoch admission guard for every first route-disposition writer; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic; and every operation that can create the first `(event_id, route_key)` disposition - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition, including when an approved migration, reconciliation or disposition plan acts through them - classifies the event from immutable original-epoch facts and proves its own eligibility and authorization inside the transaction that would consume that slot, so that no disposition exists for an in-epoch contract mismatch except the one created by compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly, while the terminal non-materialized disposition is limited to ordinarily eligible, still-owed obligations and its accepted divergence is reported separately from a mismatch anomaly's `RECONCILED/DIVERGED` outcome; and detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also arbitrates the anomaly's first creation when no anomaly row exists, under which detection first records one `OPEN` anomaly with no disposition or job, enrollment first creates the canonical anomaly born-terminal `ENROLLED` atomically with its one disposition and indispensable routed job and input facts or eligible attachment and is never counted or aged as open, later detection converges without reopening, re-aging or fabricating provenance, divergence resolves only an already committed `OPEN` anomaly, is refused before detection and never creates an anomaly, disposition or job, a refused enrollment neither creates nor resolves an absent anomaly, terminal `ENROLLED` is a route decision rather than evidence of provider success, and contradictory durable state fails closed with its evidence preserved and visible as a control HOLD for separately authorized incident recovery, without a third anomaly state, job hold or automatic repair. Invariant 47 is added; invariants 5, 8, 35 and 45 are conformed; the other preceding invariants and the twenty post-seal validation cases are retained; and thirty active validation cases, `A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`, are added, with `A13-T25` retired and not reused. These are future implementation acceptance requirements, not executed tests. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules From 5660e82e2ddd67c52369699de4589945d82e8508 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Sat, 24 Oct 2026 15:49:04 +0100 Subject: [PATCH 48/48] THOTH-ASYNC-01-ADR-01: propose Amendment 14 terminal-admission correction (#958) --- CHANGELOG.md | 1 + ...H-ASYNC-01-ADR-01-implementation-report.md | 142 +- ...ed-asynchronous-event-and-job-execution.md | 1151 ++++++++++------- .../decisions/decision-register.md | 4 +- 4 files changed, 837 insertions(+), 461 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e8a64078..0136e4936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MIG-01`: bounded administrative audit/backfill tooling for the canonical publisher package and distribution-platform configuration (issue [828](https://github.com/thoth-pub/thoth/issues/828), parent programme [765](https://github.com/thoth-pub/thoth/issues/765)). A new workspace-visible `thoth-api` administrative facade (`publisher_service_configuration::migration_backfill`) reads an immutable, raw-byte-`SHA-256`-identified input manifest, resolves each publisher against canonical Thoth identity, normalises the desired linked-platform set, and emits a deterministic, canonical, raw-byte-hashed dry-run plan plus a bounded reconciliation report; a separately supplied reviewed plan is then applied — pending entries only, after fail-closed resume classification (`REVIEWED_NOOP`/`PENDING`/`ALREADY_APPLIED_BY_THIS_PLAN`/`DRIFT`) — exclusively through the existing canonical service-configuration coordinator with a fixed `MIGRATION_BACKFILL` source and a plan-derived audit actor. A thin `thoth publisher-services migration-backfill dry-run|apply` CLI passes only paths and operational arguments. The tooling is job-free by design (it creates no `distribution_job`, `distribution_job_target` or `distribution_job_attempt`, with automatic job creation `OFF` or `ON`), audits and reports unsupported licence values and their reviewed dispositions without any licence-normalisation write, reports the per-publisher work-freshness lock footprint, and encodes the strict production job-state preflight. No database migration, `schema.rs`, GraphQL/API contract, authorization, worker/job schema or workflow change; no production or provider data is read or written and nothing is executed against production; automatic distribution-job creation remains `OFF`. ### Changed +- `THOTH-ASYNC-01-ADR-01-AMENDMENT-14-CANDIDATE`: propose a **material** Amendment 14 + Revision 1 [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) correction through Route B with `Status: PROPOSED`, preserving the historical CTO-approved A13 exact Git blob separately. Permanently close coalescing admission at first committed claim/start **or** an independently lawful terminal preclaim transition or durable approved kind-policy closure; serialize attachment/claim/terminal/policy facts; ensure safe successor generation for **new undispositioned** routes while never reopening an already attached route disposition. Require truthful all-attached-obligation pre-effect terminal consequences and per-kind/per-route-revision counts/oldest-age for **all routed jobs**, with F-01–F-08 and A14-T01–T34 future implementation acceptance. Documentation only, with no Rust, API, migrations, authorization/provider, worker deployment, image publication, merge, release or production activation authorized. - `THOTH-ASYNC-01-ADR-01-MISMATCH-ADMISSION-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version defines an immutable original-epoch classification of each event for each route and requires every first `(event_id, route_key)` disposition writer - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition - to prove that classification and its own eligibility and authorization inside the transaction that would consume the slot, so that an in-epoch contract mismatch receives a disposition only through compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly. It makes detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also covers the anomaly's first creation when no anomaly row exists: enrollment first creates the anomaly born-terminal `ENROLLED` atomically with its disposition and routed facts, later detection converges without reopening it, divergence resolves only an already committed `OPEN` anomaly and never creates one, and contradictory durable state fails closed as a control HOLD for separately authorized incident recovery. Invariant 47 and thirty active validation cases (`A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`; `A13-T25` retired) are added and invariants 5, 8, 35 and 45 are conformed; tables, locks and SQL remain implementation-specification choices. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-20.** The earlier `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION` entry, and the CTO approval dated 2026-10-15 that it records, apply only to the historical earlier corrected exact version of ADR-0012 (blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`) that does not contain this correction, and each earlier entry likewise applies only to its own historical exact version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, API or schema change, migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized or follows from it.** - `THOTH-ASYNC-01-ADR-01-POST-SEAL-RECOVERY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary, withdrawal strand-safety decision, atomic routed-job creation and mutually exclusive anomaly resolution beneath one stable logical `route_key` and closes a liveness and identity gap after the permanent coalescing seal: an eligible event-route obligation refused because the job it would have joined is permanently coalescing-sealed remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer; every coalescing kind must have an approved deterministic ordinary post-seal materialization contract under which, after rereading canonical durable state, the obligation attaches to a different eligible coalescing-open successor under the existing attachment/seal boundary or is represented by a distinct legitimate routed successor created together with its `(event_id, route_key)` disposition and indispensable input facts in one transaction, the sealed job never being such a path; coalescing group/window identity is distinguished from executable generation identity, a successor has a unique and stable idempotency key for its intended successor effect, the predecessor's key, seal, attempts and history are unchanged, predecessor retry or reclaim is never successor creation, a successor is neither a replay nor a forced resend, a new generation is created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, and an already represented obligation receives no successor on any retry; successor selection and creation are database-arbitrated with deterministic recovery after races, uniqueness losses, rollback and acknowledgment loss, without duplicate dispositions, duplicate jobs for one obligation, orphan successors, write skew or deadlock, and compose with the terminal anomaly resolution in one decision boundary; successors keep the canonical target identity, target-scoped concurrency, applied-fingerprint/revision evidence, claim fencing, `PRE_WRITE`/`EFFECT_STARTED`/`EFFECT_CONFIRMED`, `WAITING` and `RECONCILIATION_REQUIRED` semantics, an ambiguous predecessor effect is never bypassed by a new key, `CURRENT_STATE` and `REVISION_BOUND` semantics still prevent duplicate or regressive effects, and no `route_key`, epoch, revision, source event, eligibility or provider authorization is redefined; under finite owed inputs, a compatible available materializer, fair capacity and no legitimate blocker the path converges on one disposition per obligation and never reselects a sealed identity indefinitely, while an obligation for which no eligible successor can be selected or admitted stays undispositioned, backlogged, age-visible and attention-raising with bounded backoff and bounded generations and a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state rather than the route backlog; a kind without a demonstrated safe successor contract may not activate that coalescing mode and may use only a separately approved safe event-distinct identity; and the exceptional terminal non-materialized disposition stays under its existing controls and is never automatic. The concrete tables, constraints, sequences, locks and SQL are left to the implementation specification. Invariant 46, a clarified invariant 42 and twenty deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-15.** The earlier `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION` entry, and the CTO approval dated 2026-10-12 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the atomic routed-job commit and mutually exclusive anomaly resolution without this post-seal rule; the earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal strand-safety decision; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** - `THOTH-ASYNC-01-ADR-01-ROUTED-JOB-ATOMICITY-CORRECTION`: record a CTO-approved further material architectural correction of [ADR-0012](docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md) (issue [958](https://github.com/thoth-pub/thoth/issues/958), pull request [960](https://github.com/thoth-pub/thoth/pull/960), programme [957](https://github.com/thoth-pub/thoth/issues/957)) through the PR-first staged approval route (Route B) of [`docs/engineering/decisions/README.md`](docs/engineering/decisions/README.md). The corrected version keeps the append-only activation epochs, immutable route-contract revisions, withdrawal/selection ordering, coalescing-open/coalescing-sealed boundary and withdrawal strand-safety decision beneath one stable logical `route_key` and closes two further concurrency gaps. First, a brand-new logical job created to satisfy an event-route obligation - through event-route materialization or a first historical enrollment - becomes durable only together with its materialized `(event_id, route_key)` disposition and every indispensable durable job-input or membership fact, in one PostgreSQL transaction: the job is never claimable or effectful before that commit; a failure, uniqueness conflict or crash before commit leaves no orphan or claimable routed job, no disposition, no consumed slot and no phantom route satisfaction, with the obligation still owed and visible; an idempotency or disposition collision is reconciled with the transaction, never accepted alone as satisfaction; a lost acknowledgment after commit is recovered as the same job and disposition without a duplicate job, disposition or effect; attachment to an existing job keeps the coalescing boundary; and independently authorized direct jobs keep their own semantics and cannot be used to disguise a routed job. Second, every in-epoch contract-mismatch anomaly has one canonical logical identity and at most one effective terminal resolution - compatible historical enrollment or approved reconciliation/divergence - arbitrated by a database-enforced terminal-resolution decision shared by both paths, each validating authorization, open state and current compatibility, availability or reconciliation conditions inside that boundary: enrollment-first commits the outcome, the allowed disposition and any new routed job and input facts in one transaction and excludes a later accepted divergence; divergence-first commits the approved reason, authority and resolution reference, creates no disposition or job, leaves the `(event_id, route_key)` slot unused and refuses the losing enrollment for that anomaly before any disposition, job or effect; the loser always detects the terminal decision before committing; abort or crash before commit leaves the anomaly open and retryable; retries after commit observe one outcome; detection and resolution evidence stay append-only; a generic backfill cannot bypass a terminal divergence; separately authorized replay replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls, with no claim that PostgreSQL commits atomically with a third-party provider. Both rules compose with the withdrawal/selection order, the first-claim coalescing seal and the strand-safety decision as one coherent database ordering, and the concrete tables, constraints, locks and SQL are left to the implementation specification. Invariants 44 and 45 and fourteen deterministic validation cases are added. **The corrected ADR-0012 version and its decision-register row are `APPROVED`: Javi, CTO approved the exact corrected content on 2026-10-12.** The earlier `THOTH-ASYNC-01-ADR-01-STRAND-SAFETY-CORRECTION` entry, and the CTO approval dated 2026-10-09 that it records, apply only to the historical earlier corrected exact version of ADR-0012 that introduced the withdrawal strand-safety decision without these two rules; the earlier `THOTH-ASYNC-01-ADR-01-COALESCING-SEAL-CORRECTION` entry, and the CTO approval dated 2026-10-08 that it records, apply only to the historical earlier corrected exact version that introduced the coalescing boundary; the earlier `THOTH-ASYNC-01-ADR-01-WITHDRAWAL-ORDER-CORRECTION` entry, and the CTO approval dated 2026-10-07 that it records, apply only to the historical earlier corrected exact version that introduced the withdrawal/selection ordering; the earlier `THOTH-ASYNC-01-ADR-01-ROUTE-CONTRACT-CORRECTION` entry, and the CTO approval dated 2026-10-06 that it records, apply only to the historical earlier corrected exact version that introduced immutable route-contract revisions and to its factual clarification; the earlier `THOTH-ASYNC-01-ADR-01-CORRECTION` entry, and the CTO approval dated 2026-10-05 that it records, apply only to the historical earlier corrected exact version that introduced activation epochs; the earlier `THOTH-ASYNC-01-ADR-01-APPROVAL` and `THOTH-ASYNC-01-ADR-01-CLARIFICATION` entries, and the CTO approval dated 2026-09-30 that they record, apply only to the historical pre-correction exact versions; none of them approves this corrected version. ADR-0008, ADR-0010 and ADR-0013 are unchanged. **Documentation/architecture decision only: no runtime implementation, schema/data migration, provider/IAM/runtime action, external write, deployment, release or production activation is authorized.** diff --git a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md index 0b700de16..0f8876485 100644 --- a/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md +++ b/docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md @@ -38,21 +38,155 @@ Route-B candidate stage of the canonical mismatch-anomaly decision order and universal first route-disposition admission correction specified by Specification Amendment 13 with its Revisions 1 and 2 after the configured final-head automated review of that Amendment-12 approval-state head, and -the Route-B approval-state stage of that Amendment-13 candidate. +the Route-B approval-state stage of that Amendment-13 candidate, and the +Route-B PROPOSED candidate stage of Amendment 14 + Revision 1. It records durable task evidence only. Live head, CI, review, authorization and merge state is GitHub-owned lifecycle evidence under `ADR-0005` and is not copied here. -Sections 1-15 follow the repository implementation-report template for the -Route-B correction stages, with the Amendment-13 Route-B approval-state -commit as the current stage and every earlier candidate, approval-state, +Sections 1-15 preserve the earlier Amendment-13 approval-state and prior +historical Route-B evidence; the current Amendment-14 stage is recorded +below. Those historical candidate, approval-state, recovery, report-only correction and clarification stage recorded as history. Appendix A preserves the earlier authoring, review, approval-state, reconciliation and validation record. Appendix B records the durable control conditions. +## Amendment 14 + Revision 1 — current PROPOSED candidate stage + +**Identity:** `THOTH-ASYNC-01-ADR-01`, +`thoth-pub/thoth#958` / parent `#957`, CRITICAL. +**Implementer:** OpenAI ChatGPT GPT-6, +not independent specification reviewer +Claude Fable 5.1; the implementer cannot +independently approve its own source. +Programme-integration task uses the +existing transitional `feature/async/adr-0012` +branch and PR #960 targeting `feature/worker`. + +**Authorized source:** historical A13 head +`2d01255c24f68cd03666cfa05518bb9f4ae76667`, +exact base `feature/worker` and `develop` +`b44303c214498baf76c9d9a0a7cab374377f9cbe` +at preflight. One new additive single-parent +A14 candidate commit, one guarded non-force +fast-forward push. The final candidate +SHA, tree and this report blob remain in +GitHub lifecycle evidence and are not +self-pinned inside the committing report. + +**Authority:** the CTO approved the exact +Amendment 14 original plus Revision 1 +SPECIFICATION, including F-01–F-08 and +application to **all** routed jobs, but +has NOT yet approved this candidate ADR +blob or head. ADR-0012 and the register +are `PROPOSED`. The CTO-approved 2026-10-20 +Amendment-13 ADR blob +`69f9ec9017ffe39d865bbe3ae67522c6917dc857` +and all earlier source reviews/approvals +remain **historical only**. + +**Write budget — EXACT four existing paths:** +- `docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md` +- `docs/engineering/decisions/decision-register.md` +- `CHANGELOG.md` +- `docs/engineering/ai-delivery/implementation-reports/THOTH-ASYNC-01-ADR-01-implementation-report.md` + +No new/deleted/renamed files. The pre-existing +PR cumulative diff includes earlier ADR-0008 +and ADR-0010 changes, but this A14 stage +changes neither. Other ADRs, runtime source, +migration, generated contracts, CI workflow, +policy, infrastructure and repositories stay +byte-unchanged. + +**Source correction and F-01–F-08:** +one `coalescing-admission-closed` predicate +covers first committed claim/start, lawful +preclaim terminal and applicable durable +kind-policy closure, with a single +composable atomic order. Only a **new +undispositioned** obligation may use a +safe deterministic successor; already +materialized dispositions remain tied +to the terminal predecessor with +authorized job-level remediation. +All routed kinds must truthfully account +for each pre-effect attached obligation +across cancellation, retry exhaustion, +reconciliation, kind retirement/drain, +poison jobs and non-coalesced jobs. +F-01 terminology; F-02 legal pathways; +F-03 queryable terminalized-obligation +count/oldest-age; F-04 all routed jobs; +F-05 HOLD/refusal semantics; F-06 +job-level replay; F-07 six additional +cases; and F-08 durable kind-policy +closure are included. All earlier +47 invariants and thirty active +A13 cases remain, with amended 8, +42, 46, new 48 and A14-T01–T34. + +**Validation:** connector-driven +documentation assertions verify +candidate status, all four changed paths, +48 consecutive invariants, unique +A14-T01–T34 case identifiers, retention +of active A13 case identifiers, Markdown +whitespace, no conflict markers and +documentation-only full-PR changed-path +classification. The actual local +`git diff --check` is **not claimed** +without a checked-out repository; +linewise whitespace checks are an +explicitly reported substitute, +not a misleading test-pass claim. +No actual Rust, database concurrency, +migration, auth or provider tests are +performed by this architecture-only task. +Exact CI outcomes belong to GitHub. +A documentation-only push ordinarily +skips migration and staging GHCR +publication; any unexpected image +publication or heavy classified CI +is a STOP/report exception, not +release authorization. + +**Compatibility/operations/rollback:** +documentation-only, no runtime, +schema/data, GraphQL, auth, provider +or deployment effect. Future owning +repositories/consumers include +`thoth`, `thoth-dissemination`, +`thoth-app`, `infrastructure` and +Hosting. Their bounded tasks and +pinned upstream contracts remain +separately gated; no cross-repo +implementation is authorized. +Rollback or a second source commit +requires a **new** authorization +and no force push. + +**Next independent gates:** exact +PROPOSED candidate CRITICAL source +review of F-01–F-08; separate CTO +candidate architecture-content approval; +one separately authorized direct-child +APPROVED-state commit; CTO exact-final-blob +approval; fresh independent exact-final-head +review; configured GitHub review and +actual Codex P1 source correction/ +thread adjudication; exact-head CTO +merge authorization, merge, +programme integration and independent +runtime/activation controls. +The implementer does not self-approve. + +### Preserved historical task-stage record + ## 1. Repository state Owning GitHub issue: [#958](https://github.com/thoth-pub/thoth/issues/958) diff --git a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md index 69f9ec901..142ef922c 100644 --- a/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md +++ b/docs/engineering/decisions/ADR-0012-shared-asynchronous-event-and-job-execution.md @@ -1,9 +1,10 @@ # ADR-0012 - Shared asynchronous event and job execution architecture -Status: APPROVED +Status: PROPOSED Date: 2026-09-29 -Approved by: Javi, CTO -Approval date: 2026-10-20 +Decision owner: Javi, CTO +Material correction: Amendment 14 + Revision 1; Route B candidate +Historical approved Amendment-13 ADR Git blob: 69f9ec9017ffe39d865bbe3ae67522c6917dc857 (CTO approval 2026-10-20; not approval of these A14 bytes) Decision owner: CTO Programmes affected: Shared Backend Architecture (owning programme); Publisher Services and Distribution Configuration; Thoth Hosting; future Thoth programmes requiring asynchronous work Repositories affected: `thoth-pub/thoth` (shared durable engine and default worker runtime); `thoth-pub/thoth-dissemination` (dissemination executor/consumer); `thoth-pub/thoth-app` (released BE-04 read-surface consumer that must migrate before retirement); `thoth-pub/infrastructure` (worker runtime and IAM substrate) @@ -391,17 +392,28 @@ or none of them does. "Atomic creation of a routed job" in section 3.5 defines that boundary, its failure, crash and acknowledgment-loss behaviour and its interaction with coalescing into an existing job. -A coalescing seal closes one logical job, never the obligations that follow -it. When an eligible obligation cannot attach to a job of a coalescing kind -because that job is permanently coalescing-sealed (section 3.5), the -obligation remains owed, undispositioned and visible in the route backlog -until a different eligible coalescing-open successor accepts it or a distinct -legitimate routed successor is created for it under the same all-or-nothing -boundary; "Post-seal materialization and successor generations" in section -3.5 defines that ordinary path. The sealed job, the permanent collision with -its idempotency key, retry exhaustion and an unavailable materializer are -never themselves a disposition and never convert the obligation into the -exceptional terminal non-materialized disposition below. +A job's **permanent coalescing-admission closure** prevents later +attachments to **that job**, but does not dispose of any later eligible +event-route obligation. Closure arises from a first successful committed +claim/start **or** a separately lawful terminal-before-first-claim +transition **or** an applicable approved durable kind-policy closure +(section 3.5). A cancelled never-claimed predecessor is no more attachable +than a first-claimed predecessor. Every newly eligible, still-undispositioned +obligation refused after closure preserves its unused `(event_id, route_key)` +slot, original epoch/revision and route backlog with reason and oldest age; +after a fresh durable reread it uses a different compatible genuinely +admission-open successor or a distinct executable successor generation +created atomically with its first disposition and input facts. +The closed predecessor keeps its absolute idempotency reservation. + +An **already materialized** obligation retains its one immutable +disposition and its original terminal predecessor linkage: cancellation +does not make it an undispositioned route or authorize an automatic +second disposition, job/route reparenting or provider resend. Any +outstanding effect and safe authorized job-level replay/replacement +or reconciliation is operational state under section 3.4, with +unchanged canonical target-scoped fencing. Neither terminal job +state nor key collision is evidence of effect success. Disposition uniqueness is per logical route, not per epoch or per revision: `(event_id, route_key)` is the complete uniqueness boundary across every epoch @@ -480,7 +492,7 @@ unavailable for any revision or version with outstanding obligations, those obligations remain owed and the unavailable-materializer and route-backlog signals raise attention, whether the route is active, temporarily inactive or permanently retired. The same holds for an obligation refused after a -coalescing seal while no eligible successor can be selected or admitted for +coalescing admission closure while no eligible successor can be selected or admitted for it (section 3.5): until its materialized disposition commits, it stays in that backlog with its reason, age-visible and attention-raising. Once a successor and the obligation's disposition have committed together under @@ -715,7 +727,7 @@ already approves are, exhaustively: - ordinary routed materialization of an ordinarily eligible obligation under its original epoch revision: creation of a new routed job, attachment to a - coalescing-open job, and the post-seal successor path of section 3.5; + coalescing-admission-open job, and the post-closure successor path of section 3.5; - explicitly authorized historical enrollment of an event outside every epoch of the route, under the selected-revision validation below; - compatible, explicitly authorized first historical enrollment of an @@ -736,7 +748,7 @@ Admission by classification: - **ordinarily eligible** - ordinary materialization, or the explicit terminal non-materialized disposition where the obligation is proven still owed and the existing section-3.2 and ADR-0010 conditions hold. An idempotency - collision, a sealed job, an unavailable materializer or retry exhaustion + collision, an admission-closed job, an unavailable materializer or retry exhaustion never qualifies the obligation for that disposition automatically. Historical work for the obligation is handled under its original epoch revision and the existing owed-route and materializer rules; an enrollment or mismatch command @@ -905,8 +917,8 @@ The lawful orders are: it to terminal `ENROLLED` in the transaction that also creates the one route disposition and either a new routed job with its indispensable input or membership facts under "Atomic creation of a routed job" (section 3.5) or an - eligible attachment to an existing coalescing-open job under the - attachment/seal boundary of section 3.5. A later authorized divergence may + eligible attachment to an existing coalescing-admission-open job under the + attachment/admission-closure boundary of section 3.5. A later authorized divergence may instead change it to terminal `RECONCILED/DIVERGED`, persisting the approved reason, authority and durable resolution or compensation reference, with no route disposition and no job. @@ -977,7 +989,7 @@ open. A refused first enrollment - wrong original classification, an invalid or withdrawn revision, missing explicit authority, an unavailable materializer, -an unsafe seal or successor, a uniqueness or transaction failure, a lost +an unsafe admission closure or successor, a uniqueness or transaction failure, a lost terminal race or a crash before commit - does not itself create or terminally resolve a previously absent anomaly. An existing `OPEN` anomaly stays `OPEN` with truthful age and attention; otherwise independent detection from the @@ -1009,10 +1021,10 @@ The decision order composes with, and introduces no second order beside, the per-`route_key` epoch lifecycle and retirement serialization, the withdrawal/selection order and the withdrawal strand-safety decision of "Revision withdrawal and route retirement" below, `(event_id, route_key)` -uniqueness, atomic routed-job creation, the coalescing attachment/seal -boundary and the post-seal successor path of section 3.5. A terminal +uniqueness, atomic routed-job creation, the coalescing attachment/admission closure +boundary and the post-closure successor path of section 3.5. A terminal `ENROLLED` decision is atomic with the selected revision's availability and -with its routed-job creation or attachment. A permanently sealed job never +with its routed-job creation or attachment. A permanently admission-closed job never receives the mismatched input, so the mismatch reaches `ENROLLED` only together with a valid eligible open attachment or a newly created successor with its disposition and input facts, and otherwise stays unresolved - @@ -1532,17 +1544,111 @@ concurrency key by default. A kind may release it only under an explicit, reviewed rule proving that overlapping work cannot create conflicting external effects. -Cancellation invalidates the current claim. Cancelling pending/pre-write work -may produce terminal `CANCELLED`. Cancelling an effectful job at or after -`EFFECT_STARTED` cannot assert that no external effect happened and therefore -produces `RECONCILIATION_REQUIRED` unless provider/domain evidence proves a -stronger safe terminal outcome. - -Resolving `RECONCILIATION_REQUIRED` is itself a claimed, claim-token-fenced, -kind-authorized action recorded as a durable attempt. A domain reconciler or -protected staff command may establish `SUCCEEDED`, `FAILED`, `CANCELLED`, -a safe `WAITING` checkpoint, or a new explicitly authorized effectful attempt -only from evidence permitted by that kind's reconciliation contract. +Cancellation invalidates the current claim in the original +database-enforced token/attempt-phase order. A kind-authorized +pending or `PRE_WRITE` cancellation may produce `CANCELLED` +**only** where the kind/domain policy allows the transition +and the complete routed-obligation consequence requirement +below is met. `RETRY_SCHEDULED` cancellation, other +pre-effect transitions, and any terminal `FAILED` before +first claim are allowed **if and only if** independently +specified in the kind's legal transition table. Ordinary +retry exhaustion after attempts have occurred does not +authorize `PENDING -> FAILED` without a first claim. +These examples grant **no new** terminal transitions +or staff/handler authority. + +An at-or-after-`EFFECT_STARTED` attempt cannot be +terminalized as bare `CANCELLED` or `FAILED`: +it requires `RECONCILIATION_REQUIRED` unless the +kind's current provider/domain evidence establishes +a safe stronger outcome. Cancellation-first invalidates +the current claim so a stale worker cannot durably +cross `EFFECT_STARTED` or begin a new effect; +`EFFECT_STARTED`-first requires evidence or +reconciliation. A rolled-back cancellation neither +invalidates a committed claim nor closes admission. +First-claim-first cancellation during `PRE_WRITE` +remains post-start, with the first-claim admission +closure permanent. + +A reconciler or protected staff command may establish +`SUCCEEDED`, `FAILED` or `CANCELLED` from +`RECONCILIATION_REQUIRED` only under existing +claimed/token-fenced kind authorization and the +kind's permitted evidence, with truthful outcome +of all attached obligations. `SUCCEEDED` requires +normal effect/no-op evidence; an effect ambiguity +never silently becomes no-effect. A later permitted +`WAITING` checkpoint or effectful attempt retains +the original target/idempotency/claim controls. + +**Mandatory truthful pre-effect terminal outcomes +for ALL routed jobs.** Before **any** job with +one or more committed materialized route dispositions +- coalescing or non-coalescing - may commit **any** +independently lawful terminal state before sufficiently +proven intended effects, it must account for **every** +attached immutable `(event_id, route_key)` obligation. +This includes preclaim terminal `CANCELLED`, +claim-first `PRE_WRITE` cancellation, cancellation +from `RETRY_SCHEDULED` or another approved pre-effect +state, genuine pre-write retry-exhaustion `FAILED`, +evidence-based no-effect `CANCELLED`/`FAILED` +from `RECONCILIATION_REQUIRED`, authorized kind +retirement/drain, poison-job `FAILED` and +approved supersession or accepted divergence. +The enumeration creates **no** otherwise unlawful +terminal or operator transition. + +Each attachment must have independently kind-approved, +durable and auditable **truthful** consequence evidence: +proven provider effect/no-op under target-scoped applied +state; proven source/current-state supersession removing +the duty; lawful terminal failure with visible attention; +an expressly authorized and audited cancellation/accepted +divergence with reason, authority and required follow-up; +or **separately authorized** job-level replacement, +replay, reconciliation or compensation that safely +covers the still-required effect. One shared proof +may cover several attachments only if it proves each. +`CANCELLED`/`FAILED`, an operator reason, a permanent +idempotency conflict or a promise to repair is NOT +proof of provider success or permission to silently +abandon an owed effect. + +**All-or-nothing terminalization order.** The +terminal transition serializes against attachment +and evaluates the **whole committed membership** +at its decision point, atomically persisting legal +job terminal state, current-claim fencing/phase +where relevant, domain/operator authority and +audit, and sufficient queryable per-disposition +terminal consequence/failure/attention proof. +Referenced earlier proof must remain valid in +that same transaction. No best-effort later +audit suffices. If even one attached obligation +has no safe approved terminal treatment, +the proposed terminal command **refuses/HOLD**: +no terminal job state mutation commits, +the original membership and route slots remain, +and attention is explicit. HOLD is not +a new job lifecycle state; coalescing +mode OFF/HOLD is a distinct activation control. +`RECONCILIATION_REQUIRED` is used only for its +existing ambiguity predicate. + +An already attached obligation needing repair +remains a **job-level** issue and never returns +to route backlog. Its independently authorized +replay/replacement is a new **correlated logical job** +under ADR-0010 section 4.6 and this ADR's +existing replay semantics, with **no** new +route disposition or routed successor generation. +The original immutable disposition is the +historical route-materialization record; original +target-scoped effect, `EFFECT_STARTED`, +claim-token and provider safety remain binding. The full legal transition table, including claim-token requirements and attempt budget/deadline effects, is fixed by the shared-engine implementation @@ -1566,7 +1672,7 @@ meaningfully new, for example: - an explicit command/request id; - an approved coalescing/debounce group or window together with the executable generation that represents it, wherever successive generations - of one group may be needed ("Post-seal materialization and successor + of one group may be needed ("Post-closure materialization and successor generations" below). A completed Crossref sync, search-index update, invalidation or reconciliation @@ -1584,10 +1690,12 @@ eligible route obligation eventually receives exactly one. A successfully materialized disposition points to exactly one materialized job, and a job newly created for a route obligation becomes durable only together with that disposition ("Atomic creation of a routed job" below). Several -materialized route dispositions may point to one **not-yet-started** job only -when the kind defines an explicit durable coalescing rule; a job is not yet -started, and so coalescing-open, only until its first successful execution -claim/start commits (below). An explicit, +materialized route dispositions may point to one job only under an explicit +durable kind coalescing contract and while that job is genuinely +**coalescing-admission-open at the attachment commit**: executable/nonterminal, +without a committed first claim, lawful preclaim terminal transition or +kind-policy closure. A job lacking a first claim is **not** automatically +admission-open (below). An explicit, authorized and audited terminal non-materialized disposition points to no job and records the actor, authority, reason and any covering reconciliation/backfill; it exists only for an ordinarily eligible, still-owed @@ -1603,12 +1711,13 @@ An idempotency conflict never silently means "route satisfied", and it is never a reason to retry the same permanently unusable identity indefinitely. It either: -1. attaches the route to an existing not-yet-started job when the kind's - coalescing rule explicitly permits it, inside the database-atomic - coalescing boundary below; -2. for a coalescing kind whose existing job is permanently coalescing-sealed, - follows the ordinary post-seal materialization path below, which selects a - different eligible coalescing-open successor or creates a distinct +1. attaches the route to an existing **coalescing-admission-open** job only + when its approved kind, current executable/nonterminal state, first-claim, + terminal and kind-policy facts permit it at the **database-atomic** + attachment commit below; +2. for a coalescing kind whose existing job is permanently coalescing-admission-closed, + follows the ordinary post-closure materialization path below, which selects a + different eligible coalescing-admission-open successor or creates a distinct legitimate routed successor after rereading durable state; or 3. produces a deterministic routing/materialization error surfaced for attention/recovery. @@ -1633,135 +1742,209 @@ The shared scheduler supports per-kind concurrency limits. Provider-specific rate limits remain owned by handlers, but a single failing or rate-limited job kind must not consume all worker capacity indefinitely. -### Coalescing-open and coalescing-sealed jobs - -For every job kind that permits many-route-to-one-job coalescing, each logical -job has one durable semantic boundary. The logical job is **coalescing-open** -only before its first successful execution claim/start transition linearizes; -while it is open, a new materialized route disposition may still be attached to -it under the kind's explicit coalescing rule. The first successful committed -claim/start transition makes the logical job **coalescing-sealed**, -permanently: no new route disposition may be attached to it afterwards. The -"not-yet-started job" that coalescing may attach to and the "first successful -claim/start" that seals it refer to the same durable transition. The seal is a -property of the logical job, not of one attempt or one route disposition: a -later retry after a pre-write failure, lease expiry and reclaim, WAITING and a -later resume, RECONCILIATION_REQUIRED and its reconciliation attempts, -cancellation or recovery transitions, and every later claim or attempt on the -same logical job never reopen coalescing. The exact persisted column or state -encoding is an implementation-specification detail; a separate sealed flag is -not required where the canonical job state, attempt or claim transition already -enforces the same property. - -A coalescing attachment and the logical job's first successful claim/start -transition participate in one database-atomic serialization, lock or -compare-and-set boundary, or another database-enforced mechanism with -equivalent atomic ordering, so that the two operations have one durable -linearized order. A router or materializer may not observe a job as not yet -started, give up that protection and later commit a disposition attachment from -that observation: the attachment write itself must atomically prove that the -logical job is still coalescing-open. Likewise, the first claim/start -transition participates in the boundary, so that no concurrent attachment can -commit after it. An application-level pre-check without database-enforced -ordering between the check and the attachment does not satisfy this rule. The -exact SQL, lock or constraint is an implementation-specification detail. - -Both commit orders are defined: - -- attachment first: when a valid attachment linearizes before the first - claim/start, the new materialized disposition commits and points to the - existing job; the job remains a valid representative of that route and event - under the kind's explicit coalescing contract; the later first claim/start - may proceed; the job's execution represents every disposition and durable - input contribution that committed before the seal, under the kind's approved - coalescing semantics; and starting the job never rewrites or detaches a - previously attached valid disposition; -- claim/start first: when the first successful claim/start linearizes first, - the logical job is sealed before execution proceeds; a later attempted - attachment is refused; no materialized route disposition is committed for - the losing route and event; the `(event_id, route_key)` uniqueness slot - remains unused; the eligible route obligation remains owed and visible in - the route backlog; and the route is never treated as represented by the - started job. The losing stale attachment transaction attaches nothing, - invents no logical-job identity and introduces no new replay or - current-state rule. The losing router or materializer may retry only after - re-reading current durable state and under the kind's existing authorized - materialization and recovery semantics; that recovery is the ordinary - post-seal materialization path of "Post-seal materialization and successor - generations" below - a separate, database-arbitrated materialization - decision taken from the reread state, never a continuation of the losing - transaction - and it is the normal path, with attention raised only while - recovery is pending or blocked. - -Sealing is bound to the durable committed transition, not to an attempted -claim. A worker attempt whose first claim/start transaction acquires transient -locks but rolls back before its sealing transition commits has not sealed the -job; after that rollback an attachment may still succeed when every ordinary -coalescing, idempotency and materialization requirement remains satisfied. - -A kind that permits coalescing defines, in its approved coalescing contract, -how the one surviving job represents every attachment that committed before -the seal, so that execution never uses an input or membership snapshot that -omits a disposition whose attachment linearized first. The contract stays -mechanism-neutral: a `CURRENT_STATE` kind may represent accepted pre-seal -attachments through its canonical current-state and effect-fingerprint -semantics (section 3.3); another approved kind may require an explicit durable -retained input set or an equivalent durable representation. A worker handler -is not required to enumerate route dispositions. This boundary does not change -`CURRENT_STATE` or `REVISION_BOUND` semantics, effect fingerprints, -target-scoped concurrency or applied-revision evidence. - -Every operation that durably attaches a route disposition to an existing -coalescible logical job uses this same boundary, including an attachment made -through historical enrollment, backfill or replay, or to a directly created -logical job, where this ADR already permits that attachment; none of those -paths bypasses it. The boundary does not itself decide whether any path may -coalesce: the kind's explicit durable coalescing rule and the rules above -decide that. - -The coalescing seal is earlier than, and distinct from, `EFFECT_STARTED`: -attachment closes at the first successful execution claim/start because -coalescing is permitted only into a not-yet-started job, while -`EFFECT_STARTED` fences the later external-write boundary (sections 3.4 and -3.6). The boundary composes with database-enforced claims, leases and current -claim tokens, `FOR UPDATE SKIP LOCKED` or the equivalent claim mechanism the -implementation selects, `(event_id, route_key)` disposition uniqueness, -idempotency-key uniqueness, explicit durable coalescing rules, `CURRENT_STATE` -and `REVISION_BOUND` semantics, target-scoped concurrency and -applied-revision/fingerprint evidence, the attempt phases, WAITING and -RECONCILIATION_REQUIRED, retry and reconciliation transitions and the -scheduler's deadlock and lock-ordering controls. It introduces no second -identity for one logical job, no second job lifecycle, route lifecycle or -claim-lock mode, and no second order for claim/start against attachment; a -successor generation (below) is a distinct logical job, not a second identity -of the sealed one. - -### Post-seal materialization and successor generations - -A coalescing seal closes one logical job, not the route obligations that -arrive after it. An eligible event-route obligation whose attachment is +### Coalescing admission, closure and attachment races + +Use one canonical predicate: **`coalescing-admission-open`** +is an **executable nonterminal** job eligible for new +attachments under all approved kind-policy, +route/epoch/revision/version, input and authority +checks, **without** a committed first successful +claim/start, independently lawful terminal +pre-first-claim transition or durable +kind-specific admission-closing fact. +Once any of those permitted durable closing events +commits, the job is **`coalescing-admission-closed`**. +The historical A13 labels "coalescing-sealed", +"sealed job" and "post-seal" meant first-claim +closure only; their **operative** attachment +and successor meaning in this corrected +version is the canonical permanent closure +predicate. First successful claim/start remains +a separate **execution** fact; `EFFECT_STARTED` +is a distinct later external-write boundary. + +**One composable database-atomic order** includes +each coalescing attachment, first successful +claim/start, any separately authorized terminal +transition before first claim and **every** +durable kind-policy/window fact that closes +admission. A lock/CAS/constraint or equivalent +database-serialization mechanism must +prove admission is open at the attachment +commit point and atomically close it +on any permitted closing commit. +It composes with the universal +first route-disposition guard, the +canonical mismatch-anomaly order, +revision withdrawal/selection and +strand safety, atomic routed-job/ +disposition/input creation, lease/ +claim tokens, target effect fencing +and deadlock-safe lock order. +A stale application-level `PENDING` +or no-attempt precheck is insufficient. + +**Terminal preclaim first:** the +job's terminal state and admission +closure commit together with lawful +audit and consequences for **every** +already attached disposition. Any +later attempted attachment refuses +with no route slot, job input or +membership. A historical mismatch +first enrollment that loses the race +rolls back **the entire** proposed +born-terminal `ENROLLED` anomaly, +slot and input set, retaining +detectable/open anomaly as appropriate. +The unused eligible new obligation +stays owed and after durable reread +may use only a genuinely distinct +compatible executable successor. +The predecessor's key remains reserved; +a later first claim cannot start it. + +**Attachment first:** one valid +materialized route disposition +and indispensable membership/input +facts commit before closure. +A later attempted cancellation +must see the complete set, including +this attachment, and satisfy the +independently approved section-3.4 +per-attached-obligation evidence. +If any still-required effect lacks +safe terminal treatment, cancellation +refuses/HOLD and cannot discard, +reparent or falsely finish it. + +**First claim/start first:** this +commit permanently closes admission, +then the handler reads all previously +committed membership and effect inputs. +A stale later attachment is refused +without consuming the route slot; +a new eligible undispositioned +route follows the ordinary +post-closure successor after fresh +durable reread. Subsequent cancellation +uses legal **post-start** `PRE_WRITE` +versus `EFFECT_STARTED` semantics, +claim-token fencing, reconciler +and all-attachments truth, not +preclaim terminal shortcuts. + +**Claim vs terminal, effect vs +cancellation, rollback and crash:** +terminal-first prevents new claim +and external effects; claim-first +retains permanent admission closure +and applies only lawful post-start +terminal rules. Cancellation-first +invalidates the current worker +claim; `EFFECT_STARTED`-first +requires provider/domain proof or +reconciliation. Rollbacks never +commit phantom closure or +membership. After a lost +acknowledgment, the retry +rereads the single committed +job, disposition, generation, +terminal outcome and authority +without a second disposition, +terminal effect or provider write. + +A first claim must genuinely +represent **every** accepted +preclaim attachment; a +`CURRENT_STATE` kind may use +canonical current-state/fingerprint +semantics, another approved kind +a durable membership/input set. +A handler need not enumerate +route dispositions, but cannot +execute with an input snapshot +that omits one whose attachment +committed first. This same +boundary governs historical +enrollment, backfill, replay +attachment and direct-created +coalescible jobs where **already +independently permitted**; it +grants no new attachment, +cancel or provider authority. + +**Single-generation admission +proof before activation:** +a kind may disclaim executable +successor generations only +where an approved durable, +concurrency-stable domain/activation +contract proves that no new +eligible route may arrive +**or be first historically +enrolled after ANY kind-permitted +permanent admission closure**. +The proof includes first claim, +lawful terminal preclaim +transition, durable kind-policy +window close, ordinary routing, +prior epochs/revisions, +event versions, direct-created +attachments, activation/ +withdrawal/retirement and +compatible backfill. A mutable +current queue scan is not proof. +If later eligible input is +possible, the kind needs +a deterministic, stable, +database-arbitrated distinct +executable successor generation +**before activation**, or must +independently forbid/refuse +the conflicting terminal +preclaim transition under +its approved kind policy. +A terminal command proves +its kind-specific eligibility +and successor exemption at +the same commit point. +Otherwise the coalescing +mode stays **OFF/HOLD**; +only an existing separately +approved safe event-distinct +non-coalesced contract may run. +No new terminal state, route +identity, automatic replay +or provider effect is granted. + +### Post-closure materialization and successor generations + +A coalescing admission closure closes one logical job, not the route obligations that +arrive after it. A **new, still-undispositioned** eligible event-route obligation whose attachment is refused because the logical job it would have joined is permanently -coalescing-sealed - whether it lost the claim/start-first order above or -arrived after the seal - remains undispositioned, owed and visible in the -route backlog. It is never retroactively attached to the sealed job, never +coalescing-admission-closed - whether claim-first, lawful terminal-before-claim +or a durable kind-policy closure committed first, or the obligation arrived +after any such closure - remains undispositioned, owed and visible in the +route backlog. It is never retroactively attached to the admission-closed job, never treated as complete because that job exists or has run, and never silently represented by that job's effect. The permanent idempotency-key collision -with the sealed job is not proof of route completion and is not a reason to +with the admission-closed job is not proof of route completion and is not a reason to keep retrying that permanently unusable identity: because key uniqueness is -absolute across job states and the seal never reopens, a reread that could +absolute across job states and the admission closure never reopens, a reread that could resolve only to that identity would leave the obligation owed forever. Every job kind that permits many-route-to-one-job coalescing therefore has, as part of its approved coalescing contract, a deterministic **ordinary -post-seal materialization contract** under which each such obligation +post-closure materialization contract** under which each such obligation eventually receives its one materialized disposition whenever compatible materialization is available and no genuine external reconciliation or authority blocker prevents progress. After re-reading canonical current durable state, the materializer takes exactly one of two normal paths, and the contract guarantees that at least one of them is always available: -- it attaches the obligation to a **different eligible coalescing-open - successor job** of the same group under the attachment/seal boundary +- it attaches the obligation to a **different eligible coalescing-admission-open + successor job** of the same group under the attachment/admission-closure boundary above; a successor is eligible only where the kind's coalescing contract admits it for the obligation's route, epoch, route-contract revision and event version under the compatibility rules of sections 3.2 and 3.9, and an @@ -1772,7 +1955,10 @@ the contract guarantees that at least one of them is always available: disposition and its indispensable input or membership facts under "Atomic creation of a routed job" below. -The sealed job is not such a path. A new generation is considered only after +The admission-closed job is not such a path. A previously attached +obligation retains its original one disposition; its unmet work is +job-level terminal consequence/replacement under section 3.4, +not a new route-disposition or ordinary successor. A new generation is considered only after the reread confirms that the obligation is still owed and that no eligible open successor exists; an unchanged retry that merely encounters the same conflict manufactures no identity, and an obligation already represented by a @@ -1780,18 +1966,19 @@ committed disposition receives no successor on any retry. **Successor identity.** For any coalescing kind in which more than one executable generation of one group may be needed - the default for every -coalescing kind, which a kind may disclaim only by demonstrating that no -eligible obligation can arrive after a seal - the coalescing group or window +coalescing kind, which can disclaim it only on the preceding +approved proof that no eligible new obligation may arrive +or first enroll after ANY permitted admission closure - the coalescing group or window identity and the individual executable logical-job effect/idempotency identity are distinguishable: an approved group or window never irreversibly reserves the only executable identity when later eligible events of that same -group can arrive after the first job seals. A successor is a distinct logical +group can arrive after the first job closes admission. A successor is a distinct logical job representing a previously unrepresented, still-owed event-route input, with its own deterministic idempotency key that is unique and stable for that intended successor effect across retries and terminal states - for example a durably selected successor generation of the group, or another explicitly -approved event-derived identity. The sealed predecessor's idempotency key, -seal, dispositions, attempts and effect history are unchanged: a retry, lease +approved event-derived identity. The admission-closed predecessor's idempotency key, +admission closure, dispositions, attempts and effect history are unchanged: a retry, lease reclaim, WAITING resume or reconciliation of the predecessor remains work on the predecessor and never becomes successor creation, and a successor created for a new owed obligation is not a covert retry, replay or forced external @@ -1806,10 +1993,10 @@ rewrite predecessor dispositions or job or effect history. or creation of the next executable generation of a group is database-arbitrated, with a deterministic observation and recovery result after competing materializers, uniqueness losses, transaction rollback or -acknowledgment loss. A valid attachment to an open successor and that -successor's first successful claim/start participate in the same -database-enforced linearized boundary defined above, so an attachment after a -successor's seal is refused without consuming the route slot and the +acknowledgment loss. A valid attachment to an open successor, its first successful claim/start, +any lawful preclaim terminal transition and any durable kind-policy +admission-close fact share the same database-enforced order defined above, +so an attachment after **any** admission closure is refused without consuming the route slot and the materializer rereads and follows this path again. Two simultaneous materializers for the same obligation cannot create two dispositions, two jobs representing the same required work or a phantom satisfied route: @@ -1817,14 +2004,14 @@ jobs representing the same required work or a phantom satisfied route: the atomic creation boundary arbitrate them, and the loser rereads committed state. Concurrent different owed obligations may attach to the same eligible open successor under the kind's coalescing contract; when that successor -seals first, later obligations follow the next valid generation or path. +closes admission first, later obligations follow the next valid generation or path. Because a new successor is created only together with the disposition it represents, every generation represents at least one obligation. -Materializers converge on an existing eligible coalescing-open successor +Materializers converge on an existing eligible coalescing-admission-open successor whenever one exists, attaching under the approved coalescing rule rather than bypassing it to create another generation, and create a subsequent generation only when no eligible open successor can represent the -obligation: because every earlier generation is sealed, or because an +obligation: because every earlier generation is admission-closed, or because an earlier generation that remains open is ineligible for the obligation's route, epoch, route-contract revision or event version under the kind's contract. An open ineligible generation therefore never blocks a compatible @@ -1835,7 +2022,7 @@ committed route-disposition, job and generation state; after a creation commits but its acknowledgment is lost, the retry observes the same job and disposition and creates no further generation, disposition or effect. The group, generation and `(event_id, route_key)` uniqueness boundaries compose -with the attachment/seal boundary, the atomic creation boundary and the +with the attachment/admission-closure boundary, the atomic creation boundary and the scheduler's lock-ordering controls without write skew or deadlock; the exact tables, constraints, sequences and locks are implementation-specification details. @@ -1848,8 +2035,8 @@ canonical-anomaly decision - resolving a committed `OPEN` anomaly to committed - the enrollment disposition and the eligible open-successor attachment or newly created successor and its input facts commit within one transaction under the canonical mismatch-anomaly decision order of section -3.2. A permanently sealed job never receives the mismatched input. An -enrollment that loses a successor's seal race, or finds no eligible successor +3.2. A permanently admission-closed job never receives the mismatched input. An +enrollment that loses a successor's admission closure race, or finds no eligible successor that can lawfully be selected or created, aborts or is refused as a whole, leaving the anomaly in its prior committed state - `OPEN` and retryable, or absent and still detectable - with truthful attention; it never creates the @@ -1894,7 +2081,7 @@ job-materialization state, not route history. compatible and available materializer, fair progress capacity and no outstanding legitimate dependency, authority or effect-ambiguity blocker, this path converges on one durable disposition for every eligible obligation; -the engine never indefinitely reselects a permanently sealed job or group +the engine never indefinitely reselects a permanently admission-closed job or group identity, and that collision is not itself a legitimate blocker. Continuous input, rate limits and unavailable workers remain governed by the bounded scheduler and backpressure requirements of section 3.15, not by a promise of @@ -1933,20 +2120,21 @@ terminal non-materialized disposition of section 3.2 remains available only under its existing section-3.2 and ADR-0010 controls, with real covering reconciliation or expressly accepted divergence, and only for an ordinarily eligible, still-owed obligation under the universal first route-disposition -admission guard; it is refused for an in-epoch contract mismatch. A seal, an +admission guard; it is refused for an in-epoch contract mismatch. An admission closure, an idempotency collision, retry exhaustion or an unavailable materializer never creates it automatically and is never success. -This rule supplements only the post-seal recovery outcome of the -claim/start-first order above. It keeps the first successful claim/start as -the permanent seal and both attachment/claim commit orders, the atomic +This rule applies to admission closure from first claim **and** +independently lawful terminal preclaim or kind-policy facts; +it retains first claim as one closing trigger and all the +composed attachment/claim/terminal/policy commit orders, the atomic creation boundary below, the mutually exclusive anomaly resolution, one `(event_id, route_key)` namespace across epochs and revisions, the withdrawal/selection and strand-safety orders, event and route immutability, the job-attempt lifecycle and target-scoped effect ordering. It creates no generic route retry bypass, no free-form external-effect authority, no hidden terminal non-materialized disposition, no second claim lifecycle, no -reopening of sealed jobs, no duplicate route effects and no new `route_key` +reopening of admission-closed jobs, no duplicate route effects and no new `route_key` for another generation of the same logical route. ### Atomic creation of a routed job @@ -1991,14 +2179,14 @@ job and disposition identity and create no second job, second disposition or duplicate effect. When a concurrent router wants to use an **existing** logical job for the -obligation, the coalescing-open/coalescing-sealed boundary above and the +obligation, the corrected coalescing-admission-open/closed boundary above and the kind's explicit coalescing semantics apply unchanged: an attachment to an -open job commits a disposition under that boundary; a sealed job acquires no +open job commits a disposition under that boundary; an admission-closed job acquires no new disposition; a losing attempt leaves its obligation owed and follows the -post-seal materialization path above, under which a successor created for +post-closure materialization path above, under which a successor created for that obligation is a brand-new routed job governed by this boundary. Two routes may share one -eligible not-yet-started job only under the already-approved coalescing rule, +eligible **coalescing-admission-open** job only under the approved kind rule, and the creation of a new routed job and a concurrent other-route attachment to it obey that one linearized order. @@ -2006,8 +2194,13 @@ This boundary applies to event-route materialization and to first historical enrollment that creates a routed job. An independently authorized scheduled, operator, reconciliation, replay or current-state job that is not created to satisfy a route disposition keeps its existing direct-job semantics; the rule -invents no route disposition for such a job, and such a job may not be used -to satisfy a route obligation without the disposition this boundary requires. +invents no route disposition for such a job, and such a job may not serve as a **first** event-route materialization without +the required atomic disposition/input boundary. A separately authorized job-level replay +covering an **already dispositioned** obligation is a new linked +logical job whose earlier immutable disposition supplies the +historical route-materialization record; it creates no new route +slot, consumes no ordinary successor generation and remains +subject to original target/effect fencing. Whether a job is routed is decided by the obligation it is created to satisfy and the authority under which it is created, not by a label. @@ -2234,7 +2427,11 @@ declares: - effect classification and expected effect window; - timeout/lease/waiting-deadline requirements; - retry/reconciliation policy; -- idempotency/coalescing/concurrency rules; +- idempotency/coalescing/concurrency rules, including safe successor + generation or no-later-obligation proof for every kind-permitted closure; +- per-attached-route truthful, authorized pre-effect terminal-consequence + policy for **every routed kind**, coalescing or not; without approved + safe cancellation, an attempted cancellation is refused/HOLD; - kind-retirement compatibility for outstanding jobs, including WAITING and RECONCILIATION_REQUIRED work; - for high-volume families, retention/coalescing/admission/backpressure rules @@ -2278,7 +2475,10 @@ A job kind/version cannot be removed from every compatible executor while non-terminal durable jobs of that kind/version remain. Retirement first stops new admission, then drains, migrates, reconciles or explicitly dispositions existing PENDING/RUNNING/WAITING/RETRY_SCHEDULED/RECONCILIATION_REQUIRED work under its -approved domain rules. A compatible executor remains available until that +approved domain rules. Retirement/drain terminalization of a job with routed dispositions +obeys the section-3.4 all-attached-obligation evidence and +section-3.5 closure order, never silent abandonment or false success. +A compatible executor remains available until that obligation is closed; retirement must not orphan a retained concurrency key or make a durable job uninterpretable. Likewise, compatible materialization for a route-contract revision and event version cannot be removed from every worker @@ -2454,7 +2654,16 @@ job kind: - `RECONCILIATION_REQUIRED` count and age; - terminal failure count/rate; - execution latency/attempt distribution; -- worker liveness and last successful claim/heartbeat activity. +- worker liveness and last successful claim/heartbeat activity; +- **per job kind and route key/revision**, queryable count and oldest age + of previously materialized route dispositions whose job reached a + terminal pre-effect outcome, grouped by truthful consequence + (source supersession/no-op, lawful failure, accepted divergence, + pending authorized replacement or other approved class). These + job-level signals and evidence/attention are separate from + undispositioned route backlog, terminal non-materialized route + dispositions and `RECONCILED/DIVERGED` anomaly resolutions; + do not double-count or conceal older unhealthy route revisions. It also exposes route-materialization health at least by route key and by route-contract revision, without relying on job metrics for obligations that @@ -2493,7 +2702,9 @@ handler's responsibility on top of engine-level concurrency limits. A poison job terminates as `FAILED` or `RECONCILIATION_REQUIRED` according to whether an external effect may be ambiguous; it is surfaced as attention rather -than retried forever. The architecture does not require a second dead-letter +than retried forever. With materialized routes its lawful `FAILED` outcome +preserves each attached obligation's failure/attention proof and +never recasts an ambiguous effect as cancelled/no-effect. The architecture does not require a second dead-letter queue. Operator controls such as cancel, safe manual retry, replay and mark/reconcile @@ -2513,12 +2724,15 @@ silently delete historical attempts. Coalescing/debouncing may be defined by individual job kinds, but it must be durable and auditable: it may reduce executable jobs only under an explicit domain rule and must retain enough correlation to explain which events or -requests were represented by the surviving job. A surviving job accepts -attachments only while it is coalescing-open and represents every attachment -that committed before its first successful claim/start sealed it (section -3.5); an obligation refused after that seal follows the post-seal -materialization path of section 3.5 and stays in the route backlog, with its -reason and age visible, until it is dispositioned. +requests were represented by the surviving job. A surviving job accepts attachments only while genuinely +**coalescing-admission-open** under section 3.5's database-atomic boundary. +Its first successful claim/start must represent all previously committed +attachments; independently lawful terminal preclaim or durable kind-policy +closure equally refuses later attachments, without treating earlier ones as +executed. A newly owed undispositioned route refused after closure follows +the post-closure successor path and remains age-visible in the route backlog +until lawfully dispositioned. Previously attached obligations keep their +original job/route linkage and truthful job-level terminal consequences. --- @@ -2846,29 +3060,27 @@ execution until a separate approved architecture decision says otherwise. `(event_id, route_key)` unused. 7. Job idempotency identifies a specific intended effect; a resource-only key must not suppress later revisions/schedule slots/commands. -8. At most one durable disposition may exist for a given - `(event_id, route_key)`; an eligible route obligation remains owed until it - has received exactly one. A successfully materialized disposition maps to - exactly one job; many materialized dispositions may map to one - not-yet-started, coalescing-open job only under an explicit durable - coalescing rule (invariant 42). An explicit authorized/audited terminal - non-materialized disposition maps to no job, records actor, authority, - reason and any covering reconciliation/backfill, and shares the same - uniqueness boundary so it cannot coexist with a materialized disposition for - the same event/route pair. Only a durable route obligation proven - ordinarily eligible from its immutable original epoch and still owed may - receive that terminal non-materialized disposition: the authorizing - evidence, reconciliation or expressly accepted divergence, protected - staff-command audit and immutable eligibility are validated inside the - disposition's transaction. It is prohibited for an in-epoch contract - mismatch, an event outside every epoch and an event of an unrelated kind; - a mismatched event's first route disposition arises solely from explicitly - authorized compatible historical enrollment under canonical anomaly and - terminal `ENROLLED` arbitration (invariant 47). The uniqueness boundary - spans every activation epoch and every route-contract revision of the - route and excludes any epoch or revision identifier, so neither - reactivation, a revision transition nor backfill/replay can rematerialize - an event that already has a disposition. +8. Exactly one durable `(event_id, route_key)` disposition + may exist across all epochs/revisions, and eligible + obligations remain owed until lawfully dispositioned. + Materialized dispositions each reference one job; + several may share only a **genuinely coalescing-admission-open** + job under kind rules and invariant 42. A never-claimed + but lawfully terminal job is never admission-open. + The explicit authorized and audited terminal + non-materialized disposition references no job + and may cover only a still-owed, **ordinarily eligible** + original-epoch obligation with actor/authority, + reason and lawful reconciliation or accepted-divergence + evidence. It occupies the same unique slot and + is barred for contract mismatches, out-of-epoch + events and unrelated kinds. Mismatched first + disposition arises solely from compatible + authorized historical enrollment with its + canonical terminal `ENROLLED` anomaly. + No job cancellation, replay, reactivation + or route revision resets this uniqueness. + 9. Every job kind declares `CURRENT_STATE` or `REVISION_BOUND` semantics. A CURRENT_STATE fingerprint covers all canonical inputs determining its effect. A REVISION_BOUND effect must not regress a newer already-applied revision @@ -2989,25 +3201,36 @@ execution until a separate approved architecture decision says otherwise. without database-enforced ordering does not satisfy this. The mechanism composes with the per-`route_key` lifecycle serialization and introduces no second contradictory lifecycle order. -42. For every logical job of a kind that permits many-route-to-one-job - coalescing, the job is coalescing-open only before its first successful - execution claim/start transition commits and is permanently - coalescing-sealed by that transition; the seal belongs to the logical job, - and no retry, lease reclaim, WAITING resume, reconciliation, cancellation, - recovery or later claim reopens it. A coalescing attachment and that first - claim/start share one database-atomic serialization, lock or - compare-and-set boundary with one durable linearized order: an attachment - that linearizes first commits a disposition that the started job genuinely - represents and that is never rewritten or detached; a claim/start that - linearizes first seals the job before execution, and the later attachment - is refused with no disposition committed, the `(event_id, route_key)` slot - unused, the obligation still owed and visible and its recovery governed - by invariant 46 after the losing materializer rereads durable state; the - losing stale attachment transaction invents no logical-job identity. A - stale pre-claim observation never - commits an attachment after sealing; an application-level pre-check does - not satisfy this; a rolled-back first claim does not seal. The seal is - earlier than and distinct from `EFFECT_STARTED`. +42. One logical job is **coalescing-admission-open** + only while executable/nonterminal, without any + committed first successful claim/start, independently + lawful terminal-before-first-claim outcome or + approved durable kind-policy admission-close fact, + and while all compatible admission/authorization + conditions hold. A successful first claim/start + **or** lawful terminal preclaim commit permanently + closes admission, with policy closure also + forbidding later attachment. Attachment, first + claim, every lawful preclaim terminal transition + and policy close share **one** database-enforced, + deadlock-safe serialization, composing with route + and canonical anomaly order. Attachment-first + commits immutable disposition and complete inputs, + which later execution must genuinely represent. + Terminal-first prevents a later claim and refuses + attachment without route slot or born-terminal + anomaly. Claim-first refuses attachment and any + later cancellation must obey the post-start effect + phase and all-attached-obligation invariant 48. + Rolled-back claims/cancellations do not close + admission, and retry/lease/recovery cannot + reopen committed closure. A stale app-level + precheck is insufficient. First claim remains + distinct from `EFFECT_STARTED`. Historical + enrollment/direct-created coalescing attachment + has the same order; no new route, job-state + or cancellation authority is inferred. + 43. A revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through the withdrawal's linearization/commit point. Every already-defined durable mutation that @@ -3087,51 +3310,40 @@ execution until a separate approved architecture decision says otherwise. own rules and replaces no terminal outcome; and external compensation remains fenced by the existing effect, ambiguity and reconciliation controls. -46. A coalescing seal closes one logical job, not the route obligations that - follow it. An eligible obligation refused because its job is permanently - coalescing-sealed remains owed, undispositioned and visible; it is never - attached to the sealed job, never counted complete because that job - exists or ran, and never dispositioned by the permanent idempotency-key - collision, a seal, retry exhaustion or an unavailable materializer. Every - coalescing kind has an approved deterministic post-seal materialization - contract under which, after rereading canonical durable state, the - obligation attaches to a different eligible coalescing-open successor - under invariant 42 or is represented by a distinct legitimate routed - successor created under invariant 44 together with its disposition; the - sealed job is never such a path, a new generation is created only when - the reread shows the obligation still owed and no eligible open - successor - whether every earlier generation is sealed or an earlier open - generation is ineligible for the obligation - and an already represented - obligation receives no successor on any retry. Group or window identity - is distinct from executable generation identity wherever successive - generations may be needed; a - successor has a unique, stable idempotency key for its intended successor - effect; the predecessor's key, seal, attempts and history are unchanged; - predecessor retry or reclaim is never successor creation; and a successor - is not a replay or forced resend. Successor selection and creation are - database-arbitrated with deterministic recovery after races, uniqueness - losses, rollback and acknowledgment loss: no duplicate disposition, no - two jobs for one obligation, no orphan or unrepresented successor, no - write skew and no deadlock. Successors keep the canonical target - identity, target-scoped concurrency, applied-fingerprint/revision - evidence, claim fencing, effect phases, `WAITING` and - `RECONCILIATION_REQUIRED` semantics; an ambiguous predecessor effect is - never bypassed by a new key; `CURRENT_STATE` and `REVISION_BOUND` - semantics prevent duplicate or regressive effects; and no `route_key`, - epoch, revision, event, eligibility or provider authorization is - redefined. Under finite owed inputs, a compatible available materializer, - fair capacity and no legitimate blocker the path converges on one - disposition per obligation and never reselects a sealed identity - indefinitely; an obligation for which no eligible successor can be - selected or admitted stays undispositioned, backlogged, age-visible and - attention-raising, while a committed successor whose execution is held - is a dispositioned job visible through job-level waiting, reconciliation - and blocked-successor state, in both cases with bounded backoff and - bounded generations. A kind - without a demonstrated safe successor contract may not activate that - coalescing mode and may use only a separately approved safe - event-distinct identity; the exceptional terminal non-materialized - disposition stays under its existing controls and is never automatic. +46. Permanent coalescing-admission closure from first + committed claim/start, independently lawful terminal + preclaim outcome or approved durable policy fact + closes **one job**, never the eligible **new** + event-route obligations arriving later. A new + still-undispositioned obligation keeps its slot + unused, remains owed/backlogged/age-visible and + after fresh durable reread follows one approved + compatible **different** admission-open successor + or new deterministic executable successor + generation committed atomically with its + one disposition and inputs (invariant 44). + The closed predecessor and reserved idempotency + key are never execution evidence; collisions + cannot hot-loop or invent extra generations. + A kind may use one generation only on durable + preactivation proof that no new eligible + obligation can arrive or first enroll after + **ANY** permitted closure; otherwise it + must provide a valid distinct successor + or forbid the conflicting terminal transition, + else coalescing activation remains OFF/HOLD. + A previously attached obligation keeps + its original immutable disposition and + needs invariant 48's truthful job-level + terminal consequence and separately + authorized replacement, never another + route disposition. Target-scoped + effect/claim fencing, ambiguity, + `CURRENT_STATE`/`REVISION_BOUND`, + original epoch/revision and immutable + route key remain authoritative, + with no unsafe second provider write. + 47. Every operation that can create the first durable disposition for an `(event_id, route_key)` pair - ordinary routed materialization, explicitly authorized historical enrollment and the authorized terminal @@ -3172,6 +3384,36 @@ execution until a separate approved architecture decision says otherwise. requires separately approved incident recovery rather than an automatic third anomaly state, job hold, adoption or repair. +48. **All-routed-jobs terminal consequence.** + Any independently lawful terminal pre-effect + outcome of a job with materialized route + dispositions, **coalescing or not**, + must atomically evaluate its entire + committed attached set and durably + account for each immutable route disposition + with authorized effect/no-op, supersession, + lawful failure/attention, accepted divergence + or separately approved job-level + replay/reconciliation/compensation evidence. + Bare `CANCELLED`/`FAILED`, operator reason + or job-key collision is not proof of + provider delivery. An unsupported + terminal transition or one without + complete lawful proof refuses/HOLD + without terminal mutation; neither + route reparenting, duplicate disposition, + automatic effect replay nor effect-phase + ambiguity bypass is permitted. + Queryable per-kind and per-route/revision + terminalized-obligation counts, truthful + consequence classes, oldest age and + attention remain separate from + undispositioned route backlog, + terminal non-materialized dispositions + and mismatch-anomaly resolution. + Durable kind-policy admission-close + facts share invariant 42's atomic order. + ## 8. Implementation impact and decomposition Approval of this ADR should lead to separate bounded tasks, at minimum: @@ -3188,8 +3430,9 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: - job/attempt/checkpoint lifecycle; - claim/renewal/wait/reconciliation primitives; - effect-scoped idempotency and explicit coalescing, with the - database-atomic coalescing-open/coalescing-sealed boundary between - attachment and first claim/start; + database-atomic coalescing-admission-open/closed boundary among + attachment, first claim/start, every lawful preclaim terminal + transition and every approved durable kind-policy closure fact; - atomic creation of a routed job with its materialized disposition and indispensable input facts, and the database-enforced mutually exclusive terminal resolution of a mismatch anomaly; @@ -3198,7 +3441,7 @@ Approval of this ADR should lead to separate bounded tasks, at minimum: divergence of an already `OPEN` anomaly, and the universal original-epoch first route-disposition admission guard for every first-disposition writer; - - post-seal materialization of obligations refused after a coalescing seal + - post-closure materialization of obligations refused after a coalescing admission closure through eligible open successors or distinct successor generations, with database-arbitrated convergence; - revision semantics plus domain-owned canonical effect-target identity and @@ -3401,7 +3644,7 @@ separately authorized. Effectful handler activation is prohibited until its High-volume handlers such as indexing/revalidation additionally require kind-specific retention, coalescing and admission/backpressure limits before activation; any coalescing mode additionally requires the documented and -demonstrated post-seal successor contract of section 3.5, failing closed +demonstrated post-closure successor contract of section 3.5, failing closed without it. Rollout should prove in order: @@ -3420,9 +3663,10 @@ Rollout should prove in order: universal first route-disposition admission guard; 4. crash/lease renewal/WAITING/reconciliation recovery; 5. effect-scoped idempotency, revision ordering and coalescing, including the - ordering of coalescing attachment against first claim/start, the atomic - creation of a routed job with its disposition and the post-seal - materialization of an obligation refused after a coalescing seal through + ordering of coalescing attachment against first claim/start, lawful + terminal preclaim transitions and durable kind-policy admission closure, + and the atomic creation of a routed job with its disposition and the post-closure + materialization of an obligation refused after a coalescing admission closure through successor generations; 6. priority/fairness/per-kind concurrency and bounded retry/wait deadlines; 7. one or more non-destructive representative handlers; @@ -3744,19 +3988,20 @@ Before shared implementation can be approved, evidence must include: taken, including when the attachment attempt arrives through the idempotency-conflict path; - proof that a stale pre-claim observation cannot commit an attachment after - the sealing transition linearizes; + the closing admission transition linearizes; - proof that several concurrent attachments from several router or materializer instances racing the first claim/start produce one total - order, every attachment that linearized before the seal being accepted and + order, every attachment that linearized before the admission closure being accepted and represented and every attachment after it refused; - proof that a first claim/start transaction that rolls back before its - sealing transition commits does not durably seal the job, and that a later + closing admission transition commits does not durably close admission for the job, and that a later attachment may still succeed when every ordinary coalescing, idempotency and materialization condition holds; -- proof that once a first claim/start has committed, retry scheduling, lease +- proof that once first claim/start **or any independently lawful permanent + preclaim terminal/policy closure** has committed, retry scheduling, lease expiry and reclaim, WAITING and resume, RECONCILIATION_REQUIRED and reconciliation, cancellation and recovery transitions and later claims never - reopen coalescing, and that the seal persists across worker crash and + reopen coalescing, and that the admission closure persists across worker crash and restart; - proof that the selected locking or compare-and-set order for attachment and first claim/start composes with `(event_id, route_key)` disposition @@ -3776,13 +4021,13 @@ Before shared implementation can be approved, evidence must include: disposition-uniqueness races leave exactly one successful commit, no orphan job and no false route completion; - proof that a disposition-insert error or constraint failure rolls back the - new job, that a job-idempotency collision with an existing coalescing-open - job uses only the approved coalescing path, and that an existing sealed job + new job, that a job-idempotency collision with an existing coalescing-admission-open + job uses only the approved coalescing path, and that an existing admission-closed job rejects attachment without consuming the losing event's slot; - proof that concurrent first-claim and coalescing attempts, including a simultaneously created initial routed job and an other-route attachment to it, obey the atomic attachment/first-claim order above and represent every - accepted pre-seal input; + accepted pre-admission closure input; - deterministic concurrency tests of simultaneous authorized compatible enrollment and approved divergence of one committed `OPEN` mismatch anomaly, enrollment first: one terminal enrollment resolution, one allowed @@ -3815,24 +4060,24 @@ Before shared implementation can be approved, evidence must include: claim/attachment races above, uniqueness and idempotency constraints and deterministic lock and deadlock handling, proving no split ordering, no stale strand decision and no permitted effect after a losing terminal path; -- deterministic post-seal baseline tests in which an original coalescing job - `J0` seals before an eligible `E2` can attach, proving `E2` remains owed and +- deterministic post-closure baseline tests in which an original coalescing job + `J0` closes admission before an eligible `E2` can attach, proving `E2` remains owed and visible, its `(event_id, route_key)` slot unused, and that it is never retroactively attached to `J0`; - proof that the same `E2` is materialized through one different eligible - coalescing-open successor or one newly created successor `J1` with a + coalescing-admission-open successor or one newly created successor `J1` with a genuinely distinct approved effect identity, and that eventually exactly one `(E2, route_key)` disposition exists; -- proof that retries encountering the same sealed `J0` idempotency key +- proof that retries encountering the same admission-closed `J0` idempotency key neither loop indefinitely nor declare success, and that the supported successor-selection path advances; - deterministic concurrency tests of two materializers racing the same owed - `E2` after the seal, proving one disposition, no duplicate successor or + `E2` after the admission closure, proving one disposition, no duplicate successor or external effect for the obligation, and deterministic loser recovery from committed state; - deterministic concurrency tests of different owed obligations `E2` and `E3` - racing after `J0` seals, proving they converge on an eligible `J1` while it - is open or, when it seals first, follow correctly ordered later + racing after `J0` closes admission, proving they converge on an eligible `J1` while it + is open or, when it closes admission first, follow correctly ordered later generations, and that where `J1` is open but ineligible for `E3`, `E3` proceeds to a compatible generation while `J1` remains open, with no obligation dropped or represented by a job whose input omits it; @@ -3840,7 +4085,7 @@ Before shared implementation can be approved, evidence must include: its first claim/start in both orders, proving one durable order and that claim/start-first leaves the refused obligation owed and safely eligible for a later successor, never stale-attached; -- successive-seal tests in which `J0`, `J1` and `J2` are needed within one +- successive-admission closure tests in which `J0`, `J1` and `J2` are needed within one approved group or window, including a generation created while an earlier ineligible generation is still open, proving distinct stable identities, no permanently stuck eligible obligation and no false route completion; @@ -3889,7 +4134,7 @@ Before shared implementation can be approved, evidence must include: retry and backoff, no fabricated success, no uncontrolled successor growth, and resumed safe progress when an authorized compatible path returns; - escape-path negative tests proving that no staff exception is synthesized - from an ordinary sealed-key collision, that direct-job or replay identities + from an ordinary admission-closed-key collision, that direct-job or replay identities cannot evade a route obligation, and that no new route key or epoch is invented as a fallback; - cross-target and cross-kind ordering tests proving that multiple generation @@ -3897,7 +4142,7 @@ Before shared implementation can be approved, evidence must include: lock and evidence namespace, including ambiguous provider outcomes and multiple workers; - finite-workload liveness tests under an adversarial deterministic schedule - of late obligations and successively sealing jobs with a healthy compatible + of late obligations and successively closing admission jobs with a healthy compatible materializer and no legitimate blocker, proving one disposition for every eligible event-route pair within bounded fair recovery rather than an indefinitely repeating permanent collision; @@ -4001,7 +4246,7 @@ route-disposition admission guard of section 3.2 additionally require the thirty active cases below, identified `A13-T01` to `A13-T24` and `A13-T26` to `A13-T31` for traceability to the specification that introduced them. They supplement, and replace none of, the cases above, including the twenty -post-seal materialization cases. Each case is individually falsifiable. Every +post-closure materialization cases. Each case is individually falsifiable. Every concurrency case uses real PostgreSQL transactions in distinct sessions or processes, driven by deterministic barriers and fault injection, or equivalently falsifiable transactional instrumentation, and exercises loser @@ -4127,13 +4372,13 @@ for the later implementation; no documentation change executes them. recoverable. - **A13-T21 - coalescing attachment versus first claim:** with an already-open eligible shared job, attachment-first attaches and commits - `ENROLLED` atomically before the seal; claim/start-first refuses the + `ENROLLED` atomically before the admission closure; claim/start-first refuses the attachment without falsely resolving the anomaly or consuming the disposition slot. -- **A13-T22 - post-seal successor:** a legitimate approved successor admits +- **A13-T22 - post-closure successor:** a legitimate approved successor admits and atomically dispositions a previously unrepresented mismatched input, while an ineligible or unavailable successor cannot falsely resolve it, even - where an earlier generation is sealed. + where an earlier generation is admission-closed. - **A13-T23 - ambiguous predecessor or effect:** a successor whose predecessor holds unresolved `EFFECT_STARTED` evidence may carry a committed terminal `ENROLLED` route decision but performs no unsafe provider effect, and @@ -4230,149 +4475,145 @@ adopt; the identifier is not renumbered or reused, and contradictory existing state is handled only by the fail-closed control HOLD of "Canonical mismatch-anomaly decision order" (section 3.2). +### Amendment 14 + Revision 1: mandatory future falsification vectors + +The sixteen original A14 cases and twelve Revision-1 cases are +retained below; Revision 1 governs conflicts. A14-T29–T34 +implement independent review F-07's six additional requirements. +**All 30 active A13 cases** (A13-T25 remains retired), earlier +post-closure cases and their original safety predicates remain +binding. A13-T21/T22/T30 and predecessor/successor tests additionally +cover **both** first-claim closure and lawful preclaim terminal closure. +No A14 tests are claimed executed by documenting them. + +| Test | Scenario | Required durable outcome | +| --- | --- | --- | +| A14-T01 | Pending coalescing job, no prior first claim; pre-start cancel commits then new eligible route arrives | Terminal predecessor rejects attachment, leaves slot unused; distinct eligible open/new-generation successor handles owed route under existing atomic boundary | +| A14-T02 | Router reads open job; cancellation commits; router attempts stale attachment | Atomic refusal; no ghost disposition/input; backlog remains owed and observed | +| A14-T03 | New valid attachment commits before cancellation | Cancellation sees newly committed disposition and applies approved domain treatment or HOLD; does not lose or reparent it | +| A14-T04 | Cancellation race with first claim; cancellation-first | Claim refused; no execution from terminal job | +| A14-T05 | First claim race with cancellation; claim-first | First-claim seal retained; cancellation uses post-start/pre-write-or-effect-start rules, not pre-start terminal shortcut | +| A14-T06 | Cancel transaction rolls back while attachment waits | No durable closure; ordinary valid attachment remains possible | +| A14-T07 | Two routers after cancelled predecessor compete for new generation | Exactly one disposition per event-route; deterministic generation/idempotency convergence; no duplicate effect | +| A14-T08 | Cancelled predecessor retains absolute idempotency key | Repeated materialization cannot loop or attach to it; successor has different legitimate executable identity | +| A14-T09 | Pre-start `FAILED` or other enabled terminal transition without prior claim | Same permanent admission closure and safe successor eligibility; unsupported transitions fail closed | +| A14-T10 | Cancellation would abandon an already attached still-required effect | Cancel refused/HOLD unless an independently approved safe domain treatment exists; no false success or silent loss | +| A14-T11 | Authorized current-state supersession cancels pending coalesced job with several attached obligations | Each attached obligation remains auditable with correct domain-approved cancellation evidence, no provider-success assertion and no duplicate route slot | +| A14-T12 | Historical mismatch enrollment races cancellation/terminal closure | No partial born-terminal `ENROLLED` anomaly, disposition or job input on failed attachment; successful alternative successor path commits its full canonical decision atomically | +| A14-T13 | First claim after terminal before external write; repeated cancellation; crash/ack loss | No post-terminal start or resurrection; repeat operations recover the committed result, not an extra effect/job | +| A14-T14 | Cancellation and successor execution at same canonical target with ambiguous predecessor effect | Target-scoped fence/reconciliation remains binding; a new generation does not license an unsafe write | +| A14-T15 | Direct-created not-yet-started coalescible job is terminalized before later route attachment | Later attachment rejected; same explicit coalescing and route disposition rules as routed predecessor | +| A14-T16 | Attachment, claim, and cancellation concurrently across separate database clients | One composable commit order, bounded retries and deadlock-safe lock acquisition; every durable outcome matches A14-R02/R03 | +| `A14-T17` | Kind disclaims multi-generation identity because no arrivals can follow *first claim*; pre-first-claim cancel attempted; later ordinary eligible obligation possible | Cancellation refused/HOLD unless the kind has an independently approved distinct successor contract; no terminal reserved-key dead end | +| `A14-T18` | Legitimate one-generation kind proves no new obligations after *any permitted closure* including authorized pre-claim cancellation, across historical enrollment and route lifecycles | Exemption admitted only with durable/approved proof; terminal closure cannot strand eligible route input | +| `A14-T19` | `J0` has two materialized dispositions, first claim commits, attempt still `PRE_WRITE`; cancellation requested while both effects still required | No silent terminal `CANCELLED`; reject/HOLD unless authorized per-attachment coverage is established, with immutable slots and accurate job-level attention | +| `A14-T20` | Claim-first `PRE_WRITE` cancellation, all attached `CURRENT_STATE` obligations proven superseded under an approved kind policy | Lawful terminalization only when every obligation has durable correlated evidence; no false provider success and no second route slot | +| `A14-T21` | Mixed attached set: one proven superseded, one still required with no approved cancellation coverage | All-or-nothing refusal/HOLD of terminal cancellation; cannot terminalize using evidence for only a subset; no partial/late audit | +| `A14-T22` | Unclaimed `PENDING` job receives direct terminal `FAILED` command with no approved first-claim-free failure transition | Command rejected; no automatic consumption of retry budget, invented failure, claim or route disposition | +| `A14-T23` | Kind explicitly permits a terminal transition before first claim, with committed existing dispositions | Terminal admission closes atomically; every represented input gets truthful durable authorized outcome or the transition aborts; late attachments fail and remain owed | +| `A14-T24` | First claim commits, worker begins `PRE_WRITE`; cancellation and `EFFECT_STARTED` race with current/stale claim token | Cancellation-first fences later effect start; `EFFECT_STARTED`-first requires reconciliation or evidence-backed stronger outcome; no `CANCELLED` used as ambiguity bypass | +| `A14-T25` | Terminal `FAILED` after genuine pre-write retry exhaustion with two attached routes and no provider effect | Failure remains visible with truthful per-attachment failure/attention (not provider success); no phantom post-closure rerouting; no unapproved cancellation-policy shortcut | +| `A14-T26` | Terminal outcome committed with audit/coverage record but worker/router loses acknowledgment, then replayed transition and fresh routing occur | Single terminalization, stable immutable audit/route linkage, no resurrection or duplicate effect; fresh eligible undispositioned obligation follows authorized successor only | +| `A14-T27` | First compatible historical mismatch enrollment races a pending-job terminal transition and loses attachment; predecessor's key remains reserved | No partial born-terminal `ENROLLED` anomaly, slot or input; after reread, only an approved distinct eligible successor can receive first disposition under canonical anomaly order | +| `A14-T28` | Per-attachment proof/audit insert fails during terminalization while a competing router attempts attachment | Whole terminal transaction rolls back, admission remains open if not otherwise sealed; router/worker state converges in one order, with no falsely terminal job or missing route membership | +| `A14-T29` | Reconciler establishes legal no-effect `CANCELLED`/`FAILED` from `RECONCILIATION_REQUIRED` with attached obligations | Token-fenced valid per-obligation proof/attention or whole terminal command aborts | +| `A14-T30` | `RETRY_SCHEDULED` and other pre-effect cancellation allowed/forbidden by kind | Only legal transitions commit; closure and complete attachment evidence cannot be skipped | +| `A14-T31` | Terminal close races first historical mismatch enrollment, revision withdrawal/selection, strand safety and kind retirement/drain | One canonical atomic ordering, no ghost born-terminal anomaly or abandoned older attachment | +| `A14-T32` | Mixed superseded, lawful failure, accepted-divergence and pending replacement for coalescing/non-coalescing terminalized jobs | Per-kind/per-route/revision consequence counts/oldest ages, separately from backlog/anomaly, without hidden or double-counted attached work | +| `A14-T33` | Two cancellation commands race; duplicate arrives after postcommit lost acknowledgment | Exactly one lawful terminal status/audit/effect consequence; no duplicate provider effect or route slot | +| `A14-T34` | Durable kind-policy window-close fact races attachment, claim, cancellation and rollback | Same database admission order, stale late attachment refused, accepted membership accounted and rolled-back close not durable | + +Implementation must use real distinct PostgreSQL sessions/processes, +deterministic concurrency barriers and fault injection, including +both commit orders, stale attachment, claim-first and cancellation-first, +EFFECT_STARTED-first, rollback/deadlock, crash/restart, +lost acknowledgment and durable kind-policy closure. Each case +asserts one route slot, no phantom born-terminal anomaly, truthful +per-attached-obligation consequence/attention, kind authorization, +target-scoped effect safety and age/count observability. + Provider-specific handlers owe their own acceptance evidence in addition to the shared-engine tests. ## 12. Approval and authority -Current decision state: **APPROVED**. - -Approved by: Javi, CTO -Approval date: 2026-10-20 - -The CTO approved this exact corrected content on 2026-10-20 under the -repository decision process, as the architecture content represented by the -Route-B candidate head `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and -candidate ADR-0012 blob `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239`; this -version differs from that candidate only in this approval-state -representation. That approval is architecture approval only and does not by -itself make this ADR repository-authoritative: the authority condition below -still applies. Any ADR-0013 programme-local reliance on this exact version is -a separate state that is not effective until its own conditions are -satisfied for this version. - -This version is a material architectural correction of ADR-0012, made before -any approved version of ADR-0012 was repository-authoritative, under the -material-correction rules in `docs/engineering/decisions/README.md`. It -replaces the single activation interval per route with the append-only -activation-epoch model in section 3.2 and binds every activation epoch to an -immutable route-contract revision, so that no mutable route-level contract can -reinterpret historical obligations. It also defines producer-version coverage, -in-epoch contract-mismatch anomalies, revision-validated historical enrollment, -atomic revision equivalence, deterministic materializer compatibility, -per-revision route health and safe revision withdrawal, and it requires revision -withdrawal and every new durable selection of a revision to share one -database-atomic linearized order, so that a stale availability observation can -never commit a new selection of a withdrawn revision. It further defines the -coalescing-open/coalescing-sealed boundary of a logical job in section 3.5 and -requires a coalescing attachment and the job's first successful claim/start to -share one database-atomic linearized order, so that no route disposition can be -attached from a stale observation to a job that has already started. It -further requires a revision withdrawal to commit only from a -database-consistent strand-safety decision that remains valid through the -withdrawal's commit, with every already-defined durable mutation that can make that decision -unsafe - at minimum concurrent creation of an unresolved in-epoch -contract-mismatch anomaly and removal of the last approved resolution path - -sharing one database-atomic boundary with the withdrawal or forcing its -re-evaluation, so that a stale strand-prevention scan can never commit a -withdrawal that strands existing work. It further requires a new routed job -to become durable only together with its materialized disposition and -indispensable input facts in one transaction, so that no orphan or claimable -routed job and no phantom route satisfaction can survive a failure, race or -crash, and requires compatible historical enrollment and approved -reconciliation/divergence of one mismatch anomaly to compete through one -database-enforced terminal-resolution decision, so that no two contradictory -terminal outcomes can both commit. It further defines the ordinary post-seal -materialization path in section 3.5, under which an eligible obligation -refused after a permanent coalescing seal remains owed and visible and is -materialized through a different eligible coalescing-open successor or a -distinct, stably identified routed successor generation created together -with its disposition, with group identity distinct from executable -generation identity and database-arbitrated convergence, so that no eligible -obligation can remain owed indefinitely against a permanently sealed -idempotency key and no successor can bypass target-scoped effect safety. It -further defines, in section 3.2, the immutable original-epoch classification -of every event for each route and requires every operation that can create -the first `(event_id, route_key)` disposition - ordinary materialization, -explicitly authorized historical enrollment and the authorized terminal -non-materialized disposition - to prove that classification and its own -eligibility and authorization inside the transaction that would consume that -slot, so that no disposition can exist for an in-epoch contract mismatch -except the one created by compatible, explicitly authorized first historical -enrollment. It requires detection, first historical enrollment and divergence -of a mismatch anomaly to share one database-enforced canonical decision order -that also covers the anomaly's first creation when no anomaly row exists: -enrollment first creates the anomaly born-terminal `ENROLLED` atomically with -its disposition and routed facts, later detection converges without -reopening it, and divergence resolves only an already committed `OPEN` -anomaly and never creates one, so that detection and enrollment can never -leave a contradictory open anomaly beside a committed disposition, and -contradictory durable state fails closed for separately authorized incident -recovery. - -Earlier pre-correction versions of this ADR carried `Status: APPROVED` with CTO -approval dated 2026-09-30. A later corrected version, which introduced the -activation-epoch model without immutable route-contract revisions, carried -`Status: APPROVED` with CTO approval dated 2026-10-05. A further corrected -version, which introduced immutable route-contract revisions without a -database-atomic order between revision withdrawal and new revision selection, -carried `Status: APPROVED` with CTO approval dated 2026-10-06, retained through -a factual clarification of that version. A further corrected version, which -introduced that withdrawal/selection order without a database-atomic order -between coalescing attachment and a job's first claim/start, carried -`Status: APPROVED` with CTO approval dated 2026-10-07. A further corrected -version, which introduced that coalescing boundary without a -database-consistent strand-safety decision for revision withdrawal, carried -`Status: APPROVED` with CTO approval dated 2026-10-08. A further corrected -version, which introduced that strand-safety decision without an atomic -routed-job/disposition commit or a mutually exclusive terminal resolution of -mismatch anomalies, carried `Status: APPROVED` with CTO approval dated -2026-10-09. A further corrected version, which introduced that atomic commit -and terminal resolution without a defined post-seal materialization path for -an obligation refused after a coalescing seal, carried `Status: APPROVED` -with CTO approval dated 2026-10-12. A further corrected version, ADR-0012 Git -blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, which introduced that -post-seal materialization path without a database-enforced order between -mismatch detection and first historical enrollment or an original-epoch -admission guard for every first route-disposition writer, carried -`Status: APPROVED` with CTO approval dated 2026-10-15. Those approvals, and -every review of those earlier versions, remain historical evidence bound to -those exact earlier versions and source heads only; they do not approve this -version. The exact historical identities are recorded in the task -implementation report and the owning issue. - -CTO approval of this ADR adopts the architecture decision and invariants -recorded here. It is architecture approval only: it does not authorize -implementation, schema or data migration, worker deployment, IAM/provider -changes, credentials, external writes, BE-04/DIS-02 activation, Hosting -implementation, release or production activation. - -Authority condition: this record becomes repository-authoritative only when its -exact `APPROVED` approval-state content has received independent exact-head -review and is reachable from the repository's authoritative integration branch -(`develop`). A branch carrying `APPROVED` is not repository-authoritative before -merge. Programme-integration reliance under ADR-0013, where a programme uses -it, is a narrower exact-version state that does not make this ADR -repository-authoritative. Live review, CI, merge-authorization and merge -evidence belongs in GitHub and is not duplicated into this ADR as transient -lifecycle metadata. - -The partial supersession of ADR-0008 and ADR-0010 is limited exactly to the -clauses identified in this ADR's header. All unaffected architecture in those -records remains binding. - -Amendments to this ADR follow the Amendments section of -`docs/engineering/decisions/README.md`. After approval, the CTO classifies a -proposed update as a factual clarification or a material architectural -correction. A factual clarification may update this ADR in place only when it -does not alter the selected architecture decision. A material architectural -correction may update this ADR in place only when that README's -material-correction-before-repository-authority rule permits it, including -explicit CTO authorization and the requirement that no approved version of -ADR-0012 has ever been repository-authoritative; otherwise a material -architectural change requires a new ADR that supersedes this one. Every update -follows normal review and changelog requirements. - -BE-04/DIS-02 deployment/activation and Hosting implementation that depends on -the async architecture remain separately controlled and are not authorized by -this ADR or its approval. +Current decision state: **PROPOSED - Amendment 14 + Revision 1 +Route-B material correction candidate**. CTO approval of +the **exact effective original A14 + Revision-1 specification**, +including F-01–F-08 mandatory candidate acceptance and +per-attached-obligation safeguards for **ALL routed jobs**, +is **SPECIFICATION ONLY**. The corrected ADR source bytes, +candidate Git blob/head and later exact final blob have +**not** yet received their separate CTO approvals. + +The historical **Amendment-13** version was `APPROVED` +by Javi, CTO on 2026-10-20 as ADR-0012 Git blob +`69f9ec9017ffe39d865bbe3ae67522c6917dc857`, +with its historical exact-head review. Its content/blob +approval is **not** approval of A14 source. Earlier +exact versions were approved on 2026-09-30, +2026-10-05, 2026-10-06, 2026-10-07, +2026-10-08, 2026-10-09, 2026-10-12 and +2026-10-15; each approval/review remains +historical for its exact old blob/head only. +The owning GitHub issue and PR retain +complete earlier approvals and evidence. + +This A14 correction is **material**, eligible for +in-place ADR-0012 amendment under +`docs/engineering/decisions/README.md` +only while **no previously approved version +has ever been repository-authoritative**. +It replaces first-claim-only coalescing admission +with first claim **or** independently lawful +terminal preclaim and approved durable kind-policy +admission closure; attachment/claim/terminal/ +policy facts share one database order. +It strengthens executable successor proof +and adds truthful all-routed-jobs terminal +consequences and per-kind/route/revision +operational visibility. Original route/ +epoch/revision classification, unique +disposition and canonical mismatch anomaly +order, claim and target-effect fencing, +ADR-0010 staff authority and ADR-0013 +pinned reliance remain binding. Original +invariants 1–47 and thirty active A13 +cases remain, with conformed 8/42/46, +new invariant 48 and additional +A14-T01–T34 future acceptance vectors. + +Under **Route B**, the CTO must separately +approve the exact candidate PROPOSED ADR +blob/head's architecture content; only then +can a separately authorized single direct-child +approval-state commit mark the new version +`APPROVED` with fresh approver/date. +The CTO must next approve the exact final +ADR Git blob, and a fresh independent +final-head source review and distinct +SHA-bound CTO merge authorization +must precede guarded merge. +Repository authority requires that exact +independently reviewed `APPROVED` version +to be reachable from `develop`; +programme-local ADR-0013 version +reliance is narrower, separately opted-in +and does not grant repository authority. +A new commit invalidates exact-head +reviews and affected SHA/blob permissions. + +ADR-0008 and ADR-0010 are superseded +only to the exact boundaries in this +ADR's header; unaffected machine-role, +least-privilege, Staff Operations and +staff-command controls remain binding. +This proposed architecture candidate +authorizes no runtime implementation, +SQL/data migration, provider/IAM action, +external writes, worker deployment, +BE-04/DIS-02 or Hosting activation, +release, merge, production activation +or deployment. Live lifecycle, +CI, review, authorization and merge +evidence belongs to GitHub. diff --git a/docs/engineering/decisions/decision-register.md b/docs/engineering/decisions/decision-register.md index 776448bae..cebf3e8d7 100644 --- a/docs/engineering/decisions/decision-register.md +++ b/docs/engineering/decisions/decision-register.md @@ -2,7 +2,7 @@ Status: ACTIVE Owner: CTO -Last updated: 2026-10-20 +Last updated: 2026-10-24 | ADR | Decision | Status | Programmes | Approval blocker | |---|---|---|---|---| @@ -17,7 +17,7 @@ Last updated: 2026-10-20 | `ADR-0009` | [Programme-integration branch namespace](ADR-0009-programme-integration-branch-namespace.md) | APPROVED | Shared Engineering Control; Thoth Metrics; any programme adopting the shared programme-integration workflow | CTO decision approved 2026-08-26 under [issue #837](https://github.com/thoth-pub/thoth/issues/837) (comment `5426997804`), following independent review round 1 `CHANGES REQUIRED` (`5426849829`) and the fresh amended review `APPROVED` (`5426855358`). Programme integration branches remain `feature/`; `PROGRAMME_INTEGRATION` slice branches are **siblings** spelled `feature/--`; `--` is the reserved programme/slice separator and may not appear inside a governed identifier; governed programme, area, slice and task identifiers are non-empty single Git path segments; `STANDARD` task branches remain `feature//`; and a symmetric fail-closed live namespace preflight is required before any governed ref is created. The descendant form `feature//` is rejected because Git cannot hold a ref and a ref namespace at the same path, which is the exact HTTP 422 failure that blocked #836; the single-hyphen form `feature/-` is rejected because `-` is not reserved and recreates the live `metrics` / `metrics-control` collision class. Existing branches are not renamed, deleted or moved, and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings or production effect. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `MET-WP1-01` [#836](https://github.com/thoth-pub/thoth/issues/836) remains HOLD and does not resume from this approval or from merge alone — it requires its own task-specific amendment to `feature/metrics--wp1-registry-foundation`, fresh HOLD-sensitive verification, fresh required review and fresh bounded implementation authorization | | `ADR-0010` | [Staff Operations Console and canonical publisher-service operations](ADR-0010-staff-operations-console.md) | APPROVED | Staff Operations Console; Publisher Services and Distribution Configuration; OAI-PMH; Thoth Metrics; shared backend/authorization architecture | Satisfied - Javi, CTO approved the exact written decision on 2026-08-28, and the independently reviewed decision was merged into `develop` through PR [#847](https://github.com/thoth-pub/thoth/pull/847) under programme issue [#846](https://github.com/thoth-pub/thoth/issues/846). Implementation remains separately gated: the decision authorizes no implementation, migration, provider/runtime action, external write, replay, deployment, release or production activation, and every adopting task still requires its own specification, independent review and explicit authorization. | | `ADR-0011` | [Preserve established repository release branch names](ADR-0011-preserve-established-release-branch-names.md) | APPROVED | Shared Engineering Control; Thoth Metrics; Publisher Services and Distribution Configuration; any programme using the shared branch-topology controls | CTO decision approved 2026-09-09 under [issue #897](https://github.com/thoth-pub/thoth/issues/897) (comment `5604021690`), following Specification Amendment 1 (`5603738964`) and independent specification review `APPROVED` (`5603985920`); bounded implementation authorization is comment `5604141046`. Shared doctrine standardizes the **role** of the release branch, written ``, not whether it is spelled `main` or `master`. The conceptual flows become `feature// -> -> ` and `feature/-- -> feature/ -> -> `. Each repository's `` is its own verified established release/default branch: a repository established on `main` remains on `main`, one established on `master` remains on `master`, and neither name may be created, renamed, moved or substituted merely because another repository uses it. `thoth`'s own `develop -> master` flow is unchanged. Changing an established release branch requires a separately scoped repository-local decision justified by something other than naming consistency; development-branch normalization remains independent; and protections, CI, release automation, deployment and publication configuration bind to the verified repository release branch rather than a globally assumed spelling. Removing a rename from a `BR-` readiness task does **not** lower that task's risk, which must be reassessed from its remaining CI, provider, deployment, publishing and external-write effects. `ADR-0009` is unchanged in substance and historical lifecycle evidence is not rewritten. Documentation and control only: no runtime, schema, migration, API, auth, workflow, settings, provider or production effect, and no branch is created, deleted, renamed or moved. Under the repository's existing process controls the decision is repository-authoritative only when this `APPROVED` content receives independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is **not** authoritative before it merges. Decision approval is not merge authorization and is not implementation authorization for any consumer: `BR-SPHINX-01` is the immediate consumer and must be re-specified against the authoritative policy, with its own review and authorization | -| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | APPROVED | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | Satisfied - this exact version is a further material architectural correction of ADR-0012, made before any approved version of ADR-0012 was repository-authoritative, under the PR-first staged approval route (Route B), and Javi, CTO approved its exact corrected content on 2026-10-20 under the repository decision process, as the architecture content represented by Route-B candidate head `18ffbf7d8b39755abfe84b64af29bc47ca7550b5` and candidate ADR-0012 blob `0b5fb2d7ef30f156a13bf6f195ef8b34e807d239` (historical content-approval evidence; this version differs from that candidate only in its approval-state representation). That content approval is architecture-content approval only and does not itself constitute exact-final-blob approval of the resulting ADR blob, independent final source-head review, merge authorization or ADR-0013 reliance, which are separate gates recorded in GitHub, or repository authority, which follows only the authority condition stated later in this row. The CTO approval dated 2026-09-30 applies only to the historical pre-correction exact versions of ADR-0012, the CTO approval dated 2026-10-05 applies only to the historical earlier corrected exact version that introduced activation epochs without immutable route-contract revisions, the CTO approval dated 2026-10-06 applies only to the historical earlier corrected exact version that introduced immutable route-contract revisions without a database-atomic order between revision withdrawal and new revision selection, the CTO approval dated 2026-10-07 applies only to the historical earlier corrected exact version that introduced that withdrawal/selection order without a database-atomic order between coalescing attachment and a job's first claim/start, the CTO approval dated 2026-10-08 applies only to the historical earlier corrected exact version that introduced that coalescing boundary without a database-consistent strand-safety decision for revision withdrawal, the CTO approval dated 2026-10-09 applies only to the historical earlier corrected exact version that introduced that strand-safety decision without an atomic routed-job/disposition commit or a mutually exclusive terminal resolution of mismatch anomalies, and the CTO approval dated 2026-10-12 applies only to the historical earlier corrected exact version that introduced that atomic routed-job/disposition commit and mutually exclusive terminal resolution without a defined post-seal materialization path for an obligation refused after a coalescing seal, and the CTO approval dated 2026-10-15 applies only to the historical earlier corrected exact version, ADR-0012 blob `1c98ce0cca1eeb4bf46bbfc526a3826c534f637e`, that introduced that post-seal materialization path without a database-enforced order between mismatch detection and first historical enrollment or an original-epoch admission guard for every first route-disposition writer; none of them approves this corrected version. The correction adopts append-only activation epochs beneath one stable logical `route_key`: at most one open epoch; pairwise-disjoint half-open routing-generation intervals totally ordered by activation generation; serialized opening, write-once closing and permanent retirement; historical eligibility through any open or closed epoch, with inactive inter-epoch gaps covered only by explicitly authorized backfill/replay; and `(event_id, route_key)` disposition uniqueness across every epoch, where a materialized disposition points to exactly one job and a terminal non-materialized disposition points to no job. It binds every epoch to exactly one immutable route-contract revision whose canonical semantic signature covers the route, consumed event kind, closed accepted-version set, route-to-materializer/job contract version, semantic consumer identity and any compatibility policy, so that no mutable route-level contract can reinterpret historical obligations: eligibility requires both epoch membership and a match to that epoch's revision; the producer emission set must stay within the accepted-version set of every affected open epoch, with expand/contract version transitions; an in-epoch contract mismatch becomes a durable, visible routing anomaly that creates no disposition or job and consumes no `(event_id, route_key)` slot; historical enrollment validates the selected revision field by field before creating a disposition; equivalent revisions converge atomically at the database; materializer compatibility derives from immutable revision content and declared capabilities; route health is visible per revision; revision withdrawal or route retirement never strands obligations or anomalies; and revision withdrawal and every operation that creates a new durable selection of a revision - first activation, reactivation or revision-transition epoch opening and first historical enrollment at minimum - share one database-atomic linearized order, so that a selection that linearizes first remains valid historical state while a withdrawal that linearizes first refuses later epoch opening before the epoch exists and later first historical enrollment before any disposition or job exists, leaving the `(event_id, route_key)` slot unused; and a logical job of a kind that permits many-route-to-one-job coalescing is coalescing-open only until its first successful claim/start commits and is permanently sealed by it, with a coalescing attachment and that first claim/start sharing one database-atomic linearized order, so that an attachment that linearizes first is genuinely represented by the started job while a claim/start that linearizes first refuses the later attachment, commits no disposition and leaves the `(event_id, route_key)` slot unused and the obligation owed and visible; and a revision withdrawal commits only from a database-consistent strand-safety decision that remains valid through its commit, with every already-defined durable mutation that can make that decision unsafe - at minimum concurrent creation of an unresolved in-epoch contract-mismatch anomaly, even one referencing only the epoch's expected revision, and removal of the last approved resolution path - sharing one database-atomic boundary with the withdrawal or forcing its re-evaluation, so that an anomaly-first withdrawal is refused or held where it would strand the anomaly, a withdrawal-first detection always records the anomaly durably with no disposition, job or slot and without being conditioned on computing or persisting resolution-path availability, while any availability-dependent computation or decision, if performed, uses current post-withdrawal durable state and never treats the withdrawn revision as selectable, and concurrent removals of alternative resolution paths cannot write-skew into stranded work; a new routed job becomes durable only together with its materialized `(event_id, route_key)` disposition and indispensable input facts in one PostgreSQL transaction, never claimable or effectful before commit, with failure, uniqueness races and crashes leaving neither and a lost acknowledgment recovering the same identities without duplicates, while attachment to an existing job keeps the coalescing boundary and independent direct jobs keep their own semantics; and every mismatch anomaly has one canonical logical identity and at most one database-enforced terminal resolution between compatible historical enrollment and approved reconciliation/divergence, defined in both commit orders, with the loser refused before any disposition, job or effect, the slot unused after divergence and unavailable to the losing enrollment, detection and resolution evidence append-only and external compensation separately fenced; and an eligible obligation refused after a permanent coalescing seal remains owed, undispositioned and visible, is never attached to the sealed job, never counted complete because that job exists or ran, and never dispositioned by the permanent idempotency-key collision, the seal, retry exhaustion or an unavailable materializer, and is materialized through the kind's approved deterministic post-seal path - attachment to a different eligible coalescing-open successor under the attachment/seal boundary, or creation of a distinct, stably identified routed successor generation together with its disposition and indispensable input facts in one transaction, the sealed job never being such a path - with coalescing group/window identity distinct from executable generation identity, a new generation created only when the reread shows the obligation still owed with no eligible open successor, whether earlier generations are sealed or an open one is ineligible for it, the predecessor's key, seal, attempts and history unchanged, database-arbitrated convergence without duplicate dispositions, duplicate jobs for one obligation, orphan successors, fresh identities per retry, write skew or deadlock, unchanged target-scoped fencing, applied-evidence, effect-ambiguity, `CURRENT_STATE` and `REVISION_BOUND` protection, unchanged route, epoch, revision, event and authorization identity, conditional convergence under finite inputs, a compatible available materializer, fair capacity and no legitimate blocker, visible bounded backoff for an obligation for which no eligible successor can be selected or admitted while a committed successor whose execution is held is a dispositioned job visible through job-level waiting and reconciliation state, fail-closed activation for a kind without a demonstrated successor contract, and the exceptional terminal non-materialized disposition never automatic; and every operation that can create the first `(event_id, route_key)` disposition - ordinary materialization, explicitly authorized historical enrollment and the authorized terminal non-materialized disposition, including when an approved migration, reconciliation or disposition plan acts through them - classifies the event from immutable original-epoch facts and proves its own eligibility and authorization inside the transaction that would consume that slot, so that no disposition exists for an in-epoch contract mismatch except the one created by compatible, explicitly authorized first historical enrollment with its canonical terminal `ENROLLED` anomaly, while the terminal non-materialized disposition is limited to ordinarily eligible, still-owed obligations and its accepted divergence is reported separately from a mismatch anomaly's `RECONCILED/DIVERGED` outcome; and detection, first historical enrollment and divergence share one database-enforced canonical mismatch-anomaly decision order that also arbitrates the anomaly's first creation when no anomaly row exists, under which detection first records one `OPEN` anomaly with no disposition or job, enrollment first creates the canonical anomaly born-terminal `ENROLLED` atomically with its one disposition and indispensable routed job and input facts or eligible attachment and is never counted or aged as open, later detection converges without reopening, re-aging or fabricating provenance, divergence resolves only an already committed `OPEN` anomaly, is refused before detection and never creates an anomaly, disposition or job, a refused enrollment neither creates nor resolves an absent anomaly, terminal `ENROLLED` is a route decision rather than evidence of provider success, and contradictory durable state fails closed with its evidence preserved and visible as a control HOLD for separately authorized incident recovery, without a third anomaly state, job hold or automatic repair. Invariant 47 is added; invariants 5, 8, 35 and 45 are conformed; the other preceding invariants and the twenty post-seal validation cases are retained; and thirty active validation cases, `A13-T01` to `A13-T24` and `A13-T26` to `A13-T31`, are added, with `A13-T25` retired and not reused. These are future implementation acceptance requirements, not executed tests. ADR-0012's partial supersession of ADR-0008 and ADR-0010 applies only under its authority condition and only within the explicit boundaries recorded in those ADRs and in ADR-0012; all unaffected machine-role, least-privilege, SUPERUSER-separation, Staff Operations, `ServiceOperation`, desired/execution/observed-state, attention/reconciliation and staff-command rules remain binding. Authority condition: ADR-0012 is repository-authoritative only when an exact `APPROVED` approval-state version has received independent exact-head review and is reachable from `develop`; a branch carrying `APPROVED` is not repository-authoritative before merge. ADR-0013 programme-integration reliance, where a programme uses it, is a narrower exact-version state that does not make ADR-0012 repository-authoritative. Architecture approval is not implementation authorization: BE-04/DIS-02 activation, Hosting implementation, migrations, provider/IAM/runtime changes, deployment and production activation remain separately controlled. | +| `ADR-0012` | [Shared asynchronous event and job execution architecture](ADR-0012-shared-asynchronous-event-and-job-execution.md) | PROPOSED (Amendment 14 candidate) | Shared Backend Architecture; Publisher Services and Distribution Configuration; Thoth Hosting; future asynchronous-work consumers | **Current A14 source is PROPOSED, not candidate-content approved or repository-authoritative.** The CTO classified Amendment 14 a MATERIAL pre-authority correction, approved the exact original + Revision-1 specification only, and made independent findings F-01–F-08 mandatory acceptance; F-04 explicitly covers **all routed jobs**, not just coalescing kinds. Coalescing admission closes at committed first claim/start **or** an independently lawful terminal-before-first-claim transition or approved durable kind-policy closure, under one database-atomic attachment/claim/terminal/policy order. New undispositioned routes use only distinct legitimate executable successors; already attached immutable route dispositions require authorized truthful per-effect terminal accounting, separately authorized job-level replay/replacement and per-kind/per-route-revision count/oldest-age attention. Invariants 1–47 and all active A13 cases remain binding, with reconciled 8/42/46, new 48 and A14-T01–T34 future tests. The prior Amendment-13 ADR blob `69f9ec9017ffe39d865bbe3ae67522c6917dc857` was CTO-approved on **2026-10-20**; that and all earlier approval/review records are **historical exact-version evidence**, not approval of these A14 source bytes. Separate Route-B gates remain CTO exact candidate-content approval, authorized approval-state commit, CTO exact-final-blob approval, fresh independent exact-head review, configured PR review and SHA-specific merge authority; repository authority requires the reviewed `APPROVED` version to be reachable from `develop`, with ADR-0013 pinned programme reliance distinct. No runtime, migration, provider/IAM, downstream source, deployment, image publication, release or production activation is authorized. | | `ADR-0013` | [Programme-integration reliance on approved decisions](ADR-0013-programme-integration-decision-reliance.md) | APPROVED | Shared Engineering Control; THOTH-ASYNC-01 as first intended opt-in consumer; future explicitly adopting programmes | CTO-approved decision dated 2026-09-30. Repository authority remains unchanged: an ADR is repository-authoritative only under its normal authority condition, including reachability from `develop`. ADR-0013 defines a narrower, opt-in programme-integration reliance state for one exact `APPROVED` and independently reviewed ADR version only after that exact version is merged into an explicitly designated `feature/` integration branch. The mechanism is available only when ADR-0013 itself is `APPROVED`, independently reviewed and repository-authoritative, and the programme has explicitly opted in; existing programmes do not adopt it implicitly or retroactively; every relying task still needs its own specification, exact base, explicit authorization and independent review; cross-repository consumers require pinned preview/dependency evidence; and no migration execution, provider/IAM/runtime action, deployment, release or production activation is authorized by programme-local reliance. | ## Merge and implementation rules