Repository navigation
Description
THOTH-IMPORT-CONTRIB-RECOVERY-01 - capability-bound orphan-safe Contributor recovery
Status
SPECIFICATION RECORDED - IMPLEMENTATION NOT AUTHORIZED
Current gate: CRITICAL specification/setup recorded; no source/new-file change, migration, branch, commit, push, PR, CI dispatch, merge, deployment, production activation or data operation is authorized by this issue.
Identity
- Programme:
BULK-IMPORT-RELIABILITY/ BULK-IMPORT-RELIABILITY: Contract-complete, self-service bibliographic bulk ingest #885 - Programme architecture decision: BULK-IMPORT-RELIABILITY: Contract-complete, self-service bibliographic bulk ingest #885 comment
5935294840 - Downstream consumer:
thoth-pub/thoth-app#187/APP-IMPORT-ONIX-EXEC-01 - Downstream dependency record: thoth-app#187 comment
5935295886 - Repository:
thoth-pub/thoth - Task ID:
THOTH-IMPORT-CONTRIB-RECOVERY-01 - Risk: CRITICAL
- Proposed task branch:
feature/thoth-import-contrib-recovery-01 - Proposed PR target:
develop
Candidate preparation baseline - not implementation authorization
Verified live at task creation on 2026-10-01:
develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462- tree
98e64f0a6a49975f52d3dd14e7855395de2f34e6
This is a preparation baseline only. A later implementation authorization must freshly reverify develop, CI, branch topology, migration namespace and the exact write envelope.
Objective
Provide a narrow backend recovery contract that allows thoth-app#187 to compensate a Contributor genuinely created by one bulk-import execution attempt after the failed new top-level Work has been deleted and verified, without ever turning ordinary Contributor deletion into a destructive race.
The contract must be sufficient for the app to distinguish:
- cleanup completed safely;
- cleanup was already completed safely;
- cleanup refused because the Contributor is now in legitimate use;
- cleanup/provenance/authorization failed or is unknown.
This task does not introduce file-level transactions or a general import-command backend.
Why backend work is required
The current authoritative contract has these properties:
- ordinary
createContributorrequires authentication but carries no import-recovery provenance; - ordinary
deleteContributorderives authorization from publishers reachable through the Contributor's current Contributions; - a Contributor left orphaned after a failed Work has no such publisher linkage;
- the database FK from
contribution.contributor_idto Contributor isON DELETE CASCADE; - therefore app-side "check zero Contributions, then call deleteContributor" is race-unsafe: a concurrent legitimate Contribution can appear between the check and delete and would then be cascaded.
The approved programme recovery invariant requires a failed Work's independently-owned reusable entities either to be deterministically reusable or to have safe compensation. A newly-created Contributor without ORCID identity satisfies neither condition under the current contract.
Approved observable contract
The backend implementation must satisfy all of the following.
1. Import-specific creation authority
The recovery-capable creation path must be distinct in semantics from ordinary Contributor creation.
When bulk-import execution genuinely creates a new Contributor, the backend must return both:
- the created Contributor identity; and
- server-verifiable recovery provenance / an opaque cleanup capability bound to that exact creation and the authenticated creation context.
A bare Contributor ID is not cleanup authority.
Existing/reused Contributors must never receive recovery authority.
2. Atomic creation + recovery provenance
The recovery-capable creation operation must be atomic with its recovery evidence.
There must be no committed state in which:
- a new import-created Contributor exists; but
- the backend cannot later verify the corresponding recovery authority.
If durable persistence is required to satisfy this, the implementation must use a migration and that migration becomes part of the later CRITICAL implementation package. If an alternative design satisfies the same atomicity without schema change, it must prove that explicitly.
3. Creation idempotency / uncertain-response safety
A network/client uncertainty after creation must not make retry create a second Contributor.
The import-specific creation contract must therefore have a stable client-supplied operation/idempotency key, or an equivalent server mechanism, such that:
- first successful execution creates at most one Contributor and its recovery authority;
- repeating the same creation request under the same authenticated context returns/reconciles the same creation result rather than creating another Contributor;
- reusing that key with materially different Contributor creation payload is rejected, never silently treated as the same operation.
The operation key is an execution transport identity only. It must not become bibliographic Contributor identity and must not survive into normal name/ORCID matching semantics.
4. Atomic conditional cleanup
The recovery operation must consume server-verifiable recovery authority, not merely a Contributor ID.
At one transactional decision point it must establish that:
- the recovery authority is valid for this exact Contributor creation;
- the caller is authorized under the approved recovery context;
- the Contributor currently has zero Contributions.
Only then may it delete the Contributor.
If any Contribution exists at that decision point, cleanup must refuse non-destructively. It must never cascade-delete a Contributor that has become referenced.
5. Cleanup idempotency / reconciliation
Repeating the same cleanup request after an uncertain response must be safe.
The backend result must make it possible to distinguish at least:
CLEANED- this operation deleted the capability-bound orphan;ALREADY_CLEANED- the same recovery authority had already completed cleanup;IN_USE- the Contributor exists and currently has one or more Contributions, so no deletion occurred;- invalid/unauthorized recovery authority;
- genuine backend failure.
Exact GraphQL type/enum names may differ, but these states and their semantics must remain observable.
A missing Contributor must not be treated as ALREADY_CLEANED unless the backend can verify that the same recovery authority previously completed the cleanup.
6. Authorization / capability rules
The recovery path must not weaken ordinary Contributor deletion.
At minimum:
- authentication is required;
- recovery authority is unguessable or otherwise cryptographically/server-verifiably bound to the exact creation;
- it is bound to the authenticated creation context defined by this task;
- an arbitrary authenticated user who learns a Contributor UUID must not gain recovery delete authority;
- existing/reused Contributors can never be enrolled retroactively in this recovery mechanism;
- superuser behaviour, if different, must be explicit and tested rather than accidental.
The backend child implementation must choose and document the exact caller-binding policy before coding (for example creator user identity, or an equally strong server-verifiable creation context). It may not fall back to the current empty-publisher-set behaviour as implicit authorization.
7. No name-based identity change
This task must not:
- make full name exact Contributor identity;
- auto-merge Contributors;
- change ORCID uniqueness/normalization semantics;
- make contributor-name suggestions authoritative.
The downstream app continues to treat same-name Contributors as suggestions only.
Downstream #187 contract
After this backend contract is merged and generated into thoth-app, #187 may:
- use the recovery-capable create path only when the immutable plan requires creation of a new Contributor;
- track the returned recovery authority only for Contributors actually created during the current top-level Work's in-flight execution;
- delete and positively verify the exact failed new Work first;
- then invoke Contributor recovery for the capability-bound Contributors created for that failed Work;
- declare
Rolled back - safe to retry the complete fileonly after every required cleanup result is terminal-safe; - report
Cleanup failed/unknown - manual reconciliation requiredforIN_USE, invalid/unauthorized, transport-unknown or backend-failure outcomes; - never clean a Contributor created for an earlier successful Work merely because the failed Work also reused it.
Downstream app code must not guess mutation names or response shapes before this backend contract is merged.
Series boundary
No Series backend recovery change belongs in this task.
The approved programme decision records that a surviving Series is deterministically rediscovered by normal ONIX preflight using native Series identity where applicable, declared ISSN, then exact/normalised name within the Work's imprint. Ambiguity blocks rather than creating another Series.
Security and concurrency invariants
Tests must falsify at least:
- arbitrary Contributor ID + valid login cannot invoke recovery;
- recovery authority for Contributor A cannot clean Contributor B;
- recovery authority for an existing/reused Contributor cannot exist through this path;
- simultaneous cleanup vs new Contribution creation cannot cascade-delete the newly referenced Contributor;
- simultaneous duplicate create requests with the same operation key produce one Contributor;
- same operation key + different payload fails closed;
- two different operation keys may create two Contributors only when the caller genuinely requested two creations;
- replay after successful cleanup returns a reconciled safe state and performs no second destructive action;
- invalid/tampered/expired authority, if expiry exists, fails non-destructively.
Concurrency evidence must exercise the database transaction/locking behaviour, not only mocked GraphQL calls.
Data / migration requirements
No migration is pre-authorized by this specification.
During implementation preparation:
- determine whether durable recovery/idempotency state is required;
- if yes, allocate exactly one reviewed migration slot and specify up/down semantics;
- migration must be additive and backwards-compatible;
- ordinary Contributor rows and existing clients must remain valid without backfill;
- no production migration execution is part of source implementation authorization.
No historical Contributor is to be enrolled into recovery provenance.
Compatibility
The change must be additive:
- existing
createContributor,deleteContributor, Contributor queries and ordinary editor flows retain existing behaviour unless an independently justified security correction is separately approved; - existing GraphQL clients remain schema-compatible;
- generated schema/contract changes are expected only for the new recovery surface;
thoth-app#187remains blocked until the merged generated contract is available.
Expected implementation surface
The exact authorized write budget is intentionally deferred to a later read-only implementation-preparation gate.
Likely areas requiring inspection include:
- Contributor model / recovery model if added;
- GraphQL mutation/result types;
- Contributor/recovery policy;
- database CRUD/transaction logic;
- schema/migration only if required;
- GraphQL/model/concurrency tests;
- generated GraphQL schema artifact(s).
This list is not source-write authorization and is not an approved path budget.
Non-goals
This task does not authorize or implement:
- file-level import transactions;
- Work cleanup itself;
- Publication/Location/relation execution;
- ONIX parsing/planning/UI;
- Contributor merge/deduplication by name;
- a general "delete contributor if orphaned" public mutation;
- changes to ordinary Contributor identity semantics;
- Series recovery;
- production cleanup of any existing Contributor;
- publisher-data inspection;
- production migrations;
- deployment/release/activation.
Acceptance
Source acceptance requires evidence that:
- recovery-capable creation is atomic with server-verifiable recovery authority;
- creation is idempotent under uncertain response/retry;
- cleanup is capability-bound and atomically conditional on zero Contributions;
- a concurrent legitimate Contribution prevents deletion;
- cleanup is idempotent and returns explicit reconciliation outcomes;
- ordinary Contributor create/delete behaviour remains compatible;
- no existing/reused Contributor can be recovery-deleted through the new path;
- no name-based identity semantic changes;
- generated GraphQL contract is consistent;
- all repository CI/test/build gates pass.
Integration acceptance remains separate: thoth-app#187 must later prove it consumes the merged contract correctly.
HOLD / STOP conditions
HOLD if:
developmoves before later exact-SHA implementation authorization;- safe authorization cannot be established for an orphan without broadening ordinary delete rights;
- zero-Contribution cleanup cannot be made atomic against concurrent Contribution creation;
- creation cannot be made idempotent under an uncertain response;
- the implementation requires a wider shared async/job/import architecture;
- more than one migration or a destructive/backfill migration appears necessary;
- the exact source footprint cannot be bounded.
STOP if the proposed implementation:
- exposes a bare Contributor-ID orphan delete;
- relies on app-side check-then-delete;
- cascade-deletes any Contributor with a current Contribution;
- infers identity from a name;
- retrofits recovery authority onto arbitrary existing Contributors;
- turns this into a whole-file transaction/import-command redesign.
Required gates
specification/setup (this issue)
-> bounded read-only implementation-preparation
-> independent CRITICAL specification review
-> CTO final specification approval
-> exact-SHA bounded implementation authorization
-> DRAFT PR + natural CI
-> independent CRITICAL exact-head source review
-> explicit exact-head CTO merge authorization
-> merge + post-merge CI/schema reconciliation
-> thoth-app #187 re-preparation against merged generated contract
Implementation, review, merge, deployment and production activation remain separate permissions.
Current gate
THOTH-IMPORT-CONTRIB-RECOVERY-01
ISSUE / SPECIFICATION RECORD: CREATED
RISK: CRITICAL
IMPLEMENTATION: HOLD / NOT AUTHORIZED
NEXT: bounded read-only implementation-preparation + independent specification review
Activity
ja573 commentedon Oct 1, 2026
Read-only implementation preparation - COMPLETE / SPECIFICATION AMENDMENT REQUIRED
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Decision:
PREPARATION COMPLETE - CHANGES REQUIRED BEFORE CRITICAL SPECIFICATION APPROVALNo source/new-file mutation, migration creation/execution, branch, commit, push, PR, CI dispatch, provider/runtime action, deployment, release or production action was performed.
Live preparation baseline
thoth-pub/thothdevelop345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6feature/thoth-import-contrib-recovery-01: ABSENTThe current base is merge commit #962. GitHub exposes no workflow run bound to exact merge SHA
345a7a....That is recorded as an evidence gap, not silently called GREEN.
Its runtime parent
923545d5c9028bc04c40e38efeb7de674efed3fdhas:build-test-and-checkrun35636185017: SUCCESSrun-migrationsrun35636185185: SUCCESSThe complete delta
923545d5... -> 345a7a...is six documentation/control paths only; no Rust, SQL, schema, GraphQL, dependency or runtime file changed. Therefore current runtime source is byte-equivalent to the tested runtime parent for the surfaces relevant to #963, while exact-base CI remains honestly absent.Migration conclusion
Durable recovery/idempotency state is required.
Without durable state the backend cannot simultaneously provide:
ALREADY_CLEANED;IN_USEreconciliation;Repository
make migrationon current version1.8.0resolves today's path to:thoth-api/migrations/20261001_v1.9.0Collision checks found:
develop;Candidate migration slot reserved for #963:
20261001_v1.9.0.This reservation is SHA/date-sensitive. Recheck immediately before implementation authorization; if occupied, HOLD and rebind rather than invent a second same-date path.
Prepared database contract
One additive table plus one closed enum is sufficient.
Candidate table:
contributor_import_recoveryRequired durable fields:
operation_id UUID PRIMARY KEY- client-stable creation idempotency key;contributor_id UUID NOT NULL UNIQUE- exact Contributor created by this operation;recovery_token UUID NOT NULL UNIQUE DEFAULT uuid_generate_v4()- server-generated recovery capability;actor_user_id TEXT NOT NULL- exact authenticated creator principal;request_sha256 TEXT NOT NULL- canonical fingerprint of the requested Contributor creation payload;status contributor_import_recovery_status NOT NULL DEFAULT 'ACTIVE';resolved_at TIMESTAMPTZ NULL;created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP.Closed status values:
ACTIVECLEANEDIN_USERequired checks:
actor_user_id;ACTIVE <=> resolved_at IS NULL;CLEANED|IN_USE <=> resolved_at IS NOT NULL.No foreign key from the recovery row to Contributor. This is intentional and required: the recovery row is the durable idempotency/reconciliation tombstone and must survive deletion of the Contributor it records. Creation transaction tests must prove an ACTIVE row cannot commit without its Contributor.
Migration is additive, creates zero rows and needs no backfill. It does not alter a populated existing table. The down migration may drop the new table/enum for disposable/pre-activation validation; after activation and real recovery rows exist, rollback is forward-repair/compatibility work rather than destructive production down-migration execution.
Prepared creation transaction
Candidate GraphQL operation: additive import-specific Contributor creation; exact public names remain reviewable but semantics are frozen.
Input must contain:
operationId;NewContributorpayload.Coordinator semantics:
actor_user_idfrom request context - never caller input;IMPORT_CONTRIBUTOR_RECOVERY|<operationId>;NO_ACCESS, disclosing no row details;CLEANED-> stable resolved-operation error; never recreate;ACTIVEorIN_USE-> load and return the exact existing Contributor plus the same stored recovery token; missing Contributor here is an internal inconsistency, never recreated;The
operationIdis transport idempotency only. It is never bibliographic identity and never enters normal Contributor lookup semantics.Prepared cleanup transaction
Cleanup input is the server-generated
recoveryToken, not Contributor ID and not operation ID.Coordinator semantics:
NO_ACCESS;CLEANED-> returnALREADY_CLEANED;IN_USE-> returnIN_USEwithout re-evaluating or re-enabling deletion;FOR UPDATE;ALREADY_CLEANED;IN_USE+resolved_at, returnIN_USE, delete nothing;CLEANED+resolved_at, returnCLEANED.IN_USEis deliberately terminal. Once legitimate reuse wins, the old recovery capability is permanently revoked; it must never become destructive later merely because those legitimate Contributions are subsequently removed.Contributor row locking is required to race safely with FK-backed Contribution creation:
IN_USE;No check-then-delete outside one transaction is permitted.
Server capability pattern
Use a server-generated random UUID recovery token, stored durably and never exposed through a query/list surface.
This follows the repository's existing durable claim-token principle:
No new cryptography dependency is required.
Required specification amendment - ordinary orphan delete policy
Preparation found a pre-existing authorization edge in current source:
ContributorPolicy::can_delete -> require_publishers_for(current)and
require_role_for_publishersauthenticates, iterates the derived publisher set, then returns success.
For an orphan Contributor,
Contributor::publisher_ids()returns an empty set because it derives publishers only through Contributions. The loop therefore performs no role check and any authenticated caller who knows the Contributor UUID passes ordinarydeleteContributorauthorization.Current tests cover:
but do not cover the orphan case.
Known downstream consumer check:
thoth-appcarries the schema field but has no Contributor-delete service/hook;deleteContributorcleanup capability.This does not justify silently changing the API. It is nevertheless a material authorization condition for a CRITICAL recovery task.
Recommended Specification Amendment 1:
deleteContributoron a Contributor with one or more publisher-linked Contributions retains existing publisher-scoped behaviour;deleteContributoron a Contributor with zero Contributions is denied to ordinary authenticated users;deleteContributor;This closes the existing vacuous-authorization path without changing Contributor identity or creating a general orphan-delete API.
Because this is an authorization semantic change, it requires explicit CTO adoption before #963 can receive final CRITICAL specification approval.
Candidate exact write envelope after Amendment 1 approval
Maximum 12 paths, no others:
Existing files:
CHANGELOG.mdthoth-api/src/schema.rsthoth-api/src/model/contributor/mod.rsthoth-api/src/model/contributor/policy.rsthoth-api/src/model/contributor/tests.rsthoth-api/src/graphql/mutation.rsthoth-api/src/graphql/tests.rsthoth-errors/src/lib.rsAuthorized-new-file candidates:
thoth-api/src/model/contributor/recovery.rsthoth-api/migrations/20261001_v1.9.0/up.sqlthoth-api/migrations/20261001_v1.9.0/down.sqldocs/engineering/ai-delivery/implementation-reports/THOTH-IMPORT-CONTRIB-RECOVERY-01-implementation-report.mdNo Cargo/dependency change is required. No
thoth-clientsource/query change is required by the backend task. No GraphQL query surface is added for the recovery ledger.Live overlap check
Open PRs targeting
developinclude:CHANGELOG.md,thoth-api/src/graphql/mutation.rs, andthoth-errors/src/lib.rs;CHANGELOG.mdonly under the prepared envelope.Neither is a dependency and neither contains the #963 migration path.
Before implementation authorization, re-check whether either merged or materially changed. Do not incorporate unmerged PR bytes into #963 and do not silently resolve an overlap by broadening scope.
Required implementation validation
At minimum:
operationIdcreate proof -> one Contributor;CLEANED->ALREADY_CLEANED;IN_USE->IN_USE;deleteContributorauthorization regression if Amendment 1 is approved;No production migration execution belongs to source implementation.
Current gate
ja573 commentedon Oct 1, 2026
Specification Amendment 1 - ordinary orphan Contributor delete authorization
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Decision: CTO APPROVED SPECIFICATION AMENDMENT
This amendment is adopted in response to the read-only implementation-preparation finding recorded in comment
5935744657.The preparation baseline remains unchanged at the time of adoption:
develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6This amendment changes the approved specification only. It does not authorize implementation.
A1. Ordinary
deleteContributorauthorizationFor a Contributor with one or more Contributions:
For a Contributor with zero Contributions:
This closes the current vacuous-authorization edge where
require_publishers_forsucceeds after authentication when the derived publisher set is empty.A2. Import-recovery deletion is separate
The #963 recovery coordinator must not use ordinary
deleteContributorauthorization as its compensation authority.Import recovery remains governed exclusively by the capability-bound contract already specified:
IN_USE;CLEANED/ALREADY_CLEANEDreconciliation;Ordinary Contributor deletion and import recovery are distinct authorization surfaces and must remain distinct in source and tests.
A3. Required authorization regression matrix
The later implementation must prove at minimum:
deleteContributordenied;deleteContributor;A4. Compatibility statement
This is an intentional security/authorization correction.
Known downstream inspection found:
thoth-appincludesdeleteContributoronly in its checked-in schema contract;No downstream UI behaviour change is therefore currently known to be required by this amendment. Any later discovered consumer relying on ordinary non-superuser orphan deletion is a compatibility HOLD and must be assessed rather than silently preserved.
A5. Prepared implementation envelope
Subject to independent CRITICAL specification review and fresh pre-authorization verification, Amendment 1 fits inside the prepared 12-path envelope from comment
5935744657; no extra path is required.Current gate
No source/new-file mutation, migration creation/execution, branch, commit, push, PR, CI dispatch, merge, provider/runtime access, deployment, release, production activation or production-data operation is authorized by this amendment.
ja573 commentedon Oct 1, 2026
Independent CRITICAL specification review - CHANGES REQUIRED
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Reviewed specification: issue body + preparation comment
5935744657+ CTO-approved Amendment 1 comment5935809908Reviewed repository baseline:
develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462/ tree98e64f0a6a49975f52d3dd14e7855395de2f34e6Decision:
CHANGES REQUIREDThis review re-read the actual specification and amendments against live repository doctrine and source. It does not rely on the prior preparation summary as authority.
No source/new-file mutation, migration creation/execution, branch, commit, push, PR, CI dispatch, provider/runtime action, merge, deployment, release or production action was performed.
Accepted design conclusions
The following prepared decisions are sound and do not block specification approval:
FOR UPDATE/delete cannot both commit as though the other did not exist;IN_USEmust be terminal once legitimate reuse wins;deleteContributormust be corrected exactly as Amendment 1 specifies;CR-SPEC-1 - BLOCKING - recovery authority has no success-path revocation or bounded lifetime
The prepared state machine is:
ACTIVE -> CLEANED | IN_USEA recovery token is returned to the authenticated creator and remains
ACTIVEuntil cleanup is attempted.For a successful top-level Work, #187 does not call cleanup. The recovery row therefore remains ACTIVE indefinitely.
That creates authority beyond the stated compensation purpose:
This contradicts the specification's own boundary that recovery authority exists to compensate an attempt-created Contributor for the in-flight failed Work, and it bypasses Amendment 1's intentional rule that ordinary orphan deletion is superuser-only.
Client-side discarding of the token is not a security control.
Required Amendment 2A
The server-side recovery lifecycle must include both:
The exact lifecycle and expiry must be specified before approval.
A bounded candidate shape is:
All four terminal states are irreversible.
Required semantics:
The CTO must choose/approve the concrete maximum lifetime; it may not be left to the implementation agent.
CR-SPEC-2 - BLOCKING - proposed task branch violates repository STANDARD branch doctrine
The issue proposes:
feature/thoth-import-contrib-recovery-01Live repository doctrine in root
AGENTS.mdrequires STANDARD work to use:feature/<area>/<task>and requires a symmetric namespace preflight before branch creation.
#963 targets
developdirectly and is not a PROGRAMME_INTEGRATION slice, so it is STANDARD work.The proposed flat branch is therefore not conformant.
Live branch search found no existing
feature/import...branch occupancy, so a conforming candidate is available.Required Amendment 2B
Replace the proposed task branch with:
feature/import/thoth-import-contrib-recovery-01Keep PR target:
developBefore later branch creation, freshly verify:
feature/importdoes not occupy the namespace;Do not create either branch form until exact-SHA implementation authorization.
CR-SPEC-3 - BLOCKING - idempotency request fingerprint is not byte-defined
Preparation requires:
request_sha256over a "canonical field-labelled representation" of the Contributor payload.
That is not sufficiently deterministic for a CRITICAL idempotency contract.
Without an exact byte contract, two implementations or later refactors may disagree about:
The consequence is not merely cosmetic: the hash decides whether a repeated
operationIdis a safe replay or a conflicting mutation.Required Amendment 2C
Freeze a versioned fingerprint contract.
A bounded candidate requiring no dependency is:
ContributorImportCreateFingerprintV1serialization shape;1firstNamelastNamefullNameorcidwebsitenull, never omission;Orcidvalue produced by GraphQL parsing;Equivalent byte-exact encoding is acceptable only if it is written into the specification before approval.
CR-SPEC-4 - NON-BLOCKING CONTROL NOTE - exact-base CI absent
GitHub exposes no workflow run for exact merge SHA
345a7a....The runtime parent
923545d5c9028bc04c40e38efeb7de674efed3fdhas green:35636185017;35636185185.The complete delta to
345a7a...is documentation/control-only, so the runtime surfaces relevant to #963 are unchanged.This is sufficient for specification review but must remain explicit during implementation authorization. Do not describe exact base CI as GREEN unless a run actually exists.
Required consistency correction
The original acceptance item saying ordinary Contributor create/delete behaviour remains compatible must be read/amended to reflect Amendment 1:
This is documentation consistency, but it should be folded into Amendment 2 so the final approved specification has no internally stale acceptance statement.
Review decision
Next gate
CTO adoption of Specification Amendment 2 resolving CR-SPEC-1 through CR-SPEC-3 and the acceptance consistency note.
After Amendment 2 is durable, repeat the independent CRITICAL specification review against the full issue + Amendments 1 and 2.
No implementation action is authorized by this review.
ja573 commentedon Oct 1, 2026
Specification Amendment 2 - recovery lifecycle, branch binding and fingerprint bytes
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Decision: CTO APPROVED SPECIFICATION AMENDMENT
This amendment resolves all blocking findings from the independent CRITICAL specification review in comment
5935983779.The live preparation baseline remains unchanged at adoption:
develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6This is a specification amendment only. It does not authorize implementation.
A2.1 Recovery capability lifecycle and expiry
The prepared three-state lifecycle is replaced by the following closed state machine:
Every state except
ACTIVEis terminal and irreversible.The durable recovery row therefore additionally requires:
expires_at TIMESTAMPTZ NOT NULL;ACTIVE | CLEANED | IN_USE | RELEASED | EXPIRED;resolved_at IS NULLiff status isACTIVE;resolved_at IS NOT NULLfor every terminal status.Maximum lifetime
The server-enforced recovery capability lifetime is exactly:
24 hours from the committed creation timestampThe authoritative expiry is derived by the backend from server/database time:
expires_at = created_at + INTERVAL '24 hours'It is never caller-supplied and never extended or refreshed by replay.
The 24-hour window is a recovery window, not a lease renewal mechanism.
Expiry semantics
Every recovery-capable mutation that loads an ACTIVE row must evaluate expiry inside its transaction.
If:
CURRENT_TIMESTAMP >= expires_atthe backend must atomically transition the row:
ACTIVE -> EXPIREDset
resolved_at, perform no Contributor deletion, and return the terminalEXPIREDstate.An EXPIRED capability:
Successful-attempt release
Add a capability-bound, actor-bound release/finalization operation.
Input authority is the same server-generated recovery token used by cleanup.
Release semantics:
RELEASED, setresolved_at, delete nothing;RELEASED;CLEANED;IN_USE;EXPIRED;Release is idempotent and never destructive.
Downstream success rule
For
thoth-app#187, every recovery capability created while one top-level Work is in flight must reach a terminal non-destructive success-finalization state before that Work is reported as fully complete.Normal successful path:
ACTIVE -> RELEASEDIf release transport is uncertain, the app must retry/reconcile the release result. It may not silently discard the token and report final success.
If release cannot be reconciled, the Work may already exist successfully, but the import execution state must report a recovery-finalization problem requiring reconciliation; it must not claim clean terminal completion.
A capability that reaches EXPIRED before successful release is terminal and non-destructive, but that is an abnormal reconciliation outcome, not the normal success path.
A2.2 Creation replay after terminal recovery state
For the same authenticated actor + same operation ID + same request fingerprint:
ACTIVEand unexpired -> return the same Contributor identity, same recovery token and ACTIVE state;ACTIVEbut expired by server time -> transition to EXPIRED, then return the same Contributor identity and EXPIRED state;RELEASED-> return the same Contributor identity and RELEASED state;IN_USE-> return the same Contributor identity and IN_USE state;CLEANED-> return CLEANED reconciliation and never create another Contributor;EXPIRED-> return the same Contributor identity if it still exists plus EXPIRED state; if the Contributor is unexpectedly absent, return an inconsistency error rather than reinterpret absence as cleanup.A terminal operation ID is never reusable for a second creation.
Recovery-token disclosure on terminal replay is not required. If returned for reconciliation, terminal status is authoritative and the token confers no active recovery authority.
A2.3 Correct STANDARD task branch
The original proposed branch:
feature/thoth-import-contrib-recovery-01is superseded.
The governed STANDARD branch is:
feature/import/thoth-import-contrib-recovery-01PR target remains:
developBefore later branch creation, implementation preflight must freshly verify:
feature/importdoes not occupy that ref namespace;developbase SHA is unchanged;No branch is authorized or created by this amendment.
A2.4 Versioned byte-exact request fingerprint
The phrase "canonical field-labelled representation" is replaced by this byte contract.
The recovery row additionally stores:
request_fingerprint_version SMALLINT NOT NULL;request_sha256 TEXT NOT NULL.Version 1 is the only permitted initial value:
request_fingerprint_version = 1with a database check constraint enforcing that value for this task.
Fingerprint V1 logical fields
The fingerprint covers exactly these values, in this exact order:
1firstNamelastNamefullNameorcidwebsiteNo operation ID, actor ID, recovery token, timestamps or database-generated values are included.
Fingerprint V1 exact bytes
Hash the UTF-8 bytes of this exact compact JSON object shape, with keys in this exact order and no insignificant whitespace:
{"v":1,"firstName":...,"lastName":...,"fullName":...,"orcid":...,"website":...}Implementation must serialize a dedicated fixed-field Rust structure in that field order using the repository's pinned
serde_jsonserializer and hash the resulting UTF-8 bytes with SHA-256.Rules:
null, never omitted;firstNameandwebsiteuse their GraphQL-accepted string bytes exactly;lastNameandfullNameuse their GraphQL-accepted string bytes exactly;orcid, when present, uses the canonical Thoth ORCID representation already produced by the GraphQL/domain parser, including thehttps://orcid.org/prefix;serde_jsonimplementation for those input strings.The stored digest is lowercase hexadecimal SHA-256 over those exact bytes.
Required fingerprint fixture
Implementation tests must pin at least one complete V1 fixture containing:
nulloptional;The test must assert:
Changing field order, omission rules, normalization or encoding later requires a new fingerprint version and an explicit compatibility decision; existing V1 rows may never be silently reinterpreted.
A2.5 Prepared durable table correction
The prepared table now has the following minimum durable fields:
operation_id UUID PRIMARY KEYcontributor_id UUID NOT NULL UNIQUErecovery_token UUID NOT NULL UNIQUE DEFAULT uuid_generate_v4()actor_user_id TEXT NOT NULLrequest_fingerprint_version SMALLINT NOT NULLrequest_sha256 TEXT NOT NULLstatus contributor_import_recovery_status NOT NULL DEFAULT 'ACTIVE'expires_at TIMESTAMPTZ NOT NULLresolved_at TIMESTAMPTZ NULLcreated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMPStatus enum:
ACTIVE | CLEANED | IN_USE | RELEASED | EXPIREDNo FK to Contributor remains intentional so terminal reconciliation survives Contributor deletion.
This remains one additive table + one enum and still fits the prepared single migration.
A2.6 Acceptance consistency correction
The original acceptance item saying ordinary Contributor create/delete behaviour remains compatible is superseded by:
A2.7 Additional required lifecycle tests
In addition to the existing #963 test matrix, implementation must prove:
expires_at;Current gate
No source/new-file mutation, migration creation/execution, branch, commit, push, PR, CI dispatch, merge, provider/runtime access, deployment, release, production activation or production-data operation is authorized by this amendment.
ja573 commentedon Oct 1, 2026
Fresh independent CRITICAL specification review - CHANGES REQUIRED
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Reviewed specification: issue body + preparation
5935744657+ Amendment 15935809908+ first CRITICAL review5935983779+ Amendment 25936089788Reviewed repository baseline:
develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a462/ tree98e64f0a6a49975f52d3dd14e7855395de2f34e6Decision:
CHANGES REQUIREDThe live baseline remains unchanged. This review inspected the amended specification against current PostgreSQL/concurrency conventions and the cross-repository contract boundary.
No source/new-file mutation, migration creation/execution, branch, commit, push, PR, CI dispatch, provider/runtime action, merge, deployment, release or production action was performed.
Findings from the first review that are now closed
Amendment 2 correctly closes:
feature/import/thoth-import-contrib-recovery-01;Those findings are accepted.
CR-SPEC-5 - BLOCKING - the specified 24-hour boundary uses a transaction timestamp, not decision-time wall clock
Amendment 2 says both:
CURRENT_TIMESTAMP >= expires_at;created_atremainsDEFAULT CURRENT_TIMESTAMP;These statements are not mutually implementable as written.
The repository itself explicitly documents that PostgreSQL
CURRENT_TIMESTAMPis transaction-scoped, not statement/wall-clock scoped.Consequences:
created_at DEFAULT CURRENT_TIMESTAMPrecords transaction start, not commit time;CURRENT_TIMESTAMP;For a CRITICAL destructive capability, the expiry decision must be made using server wall clock after all locks required for the destructive decision have been acquired.
Required Amendment 3A
Freeze these semantics:
clock_timestamp()value and persists it ascreated_at;expires_at = created_at + INTERVAL '24 hours'from that same sampled value;clock_timestamp()immediately before the expiry/zero-Contribution decision;clock_timestamp()immediately before deciding whether ACTIVE must become EXPIRED;resolved_atfor the transition uses that same decision-time sample;>= expires_at.Using
CURRENT_TIMESTAMPfor this authority boundary is prohibited.CR-SPEC-6 - BLOCKING - shared GraphQL contract is still delegated to implementation
The issue and preparation still say that exact mutation/type/enum names may differ or remain reviewable.
That is too open for this task.
#963 is an upstream shared API contract required by
thoth-app#187. Project/repository doctrine requires the downstream repository not to guess an unmerged upstream contract, and an implementation agent must not become the architect of that shared contract.The specification already fixes behaviour tightly enough to freeze the public schema now.
Required Amendment 3B
Freeze an exact additive GraphQL contract before implementation.
A bounded contract that fits the prepared envelope is:
Required semantics:
contributorId, not a non-null Contributor object, so CLEANED and later legitimate deletion remain representable;NO_ACCESS, with no existence disclosure;CONTRIBUTOR_IMPORT_OPERATION_CONFLICT;CONTRIBUTOR_IMPORT_RECOVERY_INCONSISTENT;Equivalent naming is not left to implementation after this amendment: if the CTO wants different public names, those exact names must be written into Amendment 3 before approval.
CR-SPEC-7 - BLOCKING - EXPIRED replay needs a fail-closed downstream retry rule
Amendment 2 correctly makes EXPIRED terminal and says creation replay may return the stored Contributor identity.
But the cross-repository retry consequence is not yet explicit.
Consider:
contributorIdwith EXPIRED;If the app simply allocates a fresh operation ID and "tries again", the backend is allowed to create a second Contributor because name is deliberately not identity.
That would recreate the exact duplicate ambiguity #963 exists to prevent.
Required Amendment 3C
Bind the downstream rule:
thoth-app#187must not automatically substitute a fresh operation ID for the same planned source Contributor after EXPIRED;contributorIdas a fresh recovery-capable creation;This does not turn operation ID into bibliographic identity. It is a fail-closed retry rule for one already-recorded transport attempt.
Non-blocking controls retained
345a7a...still has no workflow run; runtime-equivalent parent CI remains evidence, not exact-base GREEN;20261001_v1.9.0and 12-path envelope must be collision/rebase checked again immediately before implementation authorization;Review decision
Next gate
CTO adoption of Specification Amendment 3 resolving CR-SPEC-5 through CR-SPEC-7.
After Amendment 3 is durable, perform one final independent CRITICAL specification review of the complete issue + Amendments 1-3.
No implementation action is authorized by this review.
ja573 commentedon Oct 1, 2026
Specification Amendment 3 - decision-time expiry, frozen GraphQL contract and EXPIRED retry boundary
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Decision: CTO APPROVED SPECIFICATION AMENDMENT
This amendment resolves CR-SPEC-5 through CR-SPEC-7 from independent review comment
5937234051.The live specification baseline remains unchanged at adoption:
develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6This is specification-only. It does not authorize implementation.
A3.1 Exact 24-hour wall-clock authority boundary
Amendment 2's use of
CURRENT_TIMESTAMPfor the destructive capability boundary is superseded.PostgreSQL
CURRENT_TIMESTAMPis transaction-scoped and must not decide recovery expiry.Creation timestamp
The recovery-capable create coordinator must:
clock_timestamp();created_at;expires_at = created_at + INTERVAL '24 hours';created_atandexpires_atare therefore coordinator-written values, not caller-supplied and not derived from aCURRENT_TIMESTAMPcolumn default.The prepared table definition is amended accordingly:
created_at TIMESTAMPTZ NOT NULL- no default;expires_at TIMESTAMPTZ NOT NULL- no default.Replay never changes either value.
The 24-hour lifetime is exactly 24 hours from this server-side creation sample. It is not described as commit time.
Cleanup decision time
For ACTIVE cleanup:
FOR UPDATE;clock_timestamp()after both required locks are held;>= expires_at:resolved_atto that same sampled value;No destructive cleanup may occur after the decision-time wall clock has reached expiry, even if the transaction began earlier.
Release and create-replay decision time
Release and creation replay:
clock_timestamp()immediately before the lifecycle decision;resolved_at;No lifecycle path may use transaction-start
CURRENT_TIMESTAMPas the capability-expiry authority.A3.2 Frozen additive GraphQL contract
The shared backend contract consumed later by
thoth-app#187is now frozen.Implementation may add descriptions/documentation but may not rename, remove, repurpose or change nullability of these fields/types without a new approved specification amendment.
Payload semantics
ContributorImportRecoveryis deliberately tombstone-safe:contributorId, not a non-null Contributor object;recoveryTokenis returned only through these mutation results. No query/list field for recovery records or tokens is added.The token is a capability/secret:
NO_ACCESS.Exact result semantics
createContributorForImport
New operation:
ACTIVECREATEDSame actor + same operation ID + same V1 fingerprint, ACTIVE and unexpired:
ACTIVEREPLAYEDIf an ACTIVE row has expired at decision time:
EXPIREDEXPIREDTerminal replay:
CLEANED-> status CLEANED, outcome ALREADY_CLEANEDIN_USE-> status IN_USE, outcome IN_USERELEASED-> status RELEASED, outcome RELEASEDEXPIRED-> status EXPIRED, outcome EXPIREDNo terminal replay creates another Contributor or reactivates authority.
cleanupContributorImport
ACTIVE + unexpired + zero Contributions:
Already CLEANED:
ACTIVE + unexpired + one or more Contributions:
IN_USE / RELEASED / EXPIRED replay:
releaseContributorImport
ACTIVE + unexpired:
Terminal replay:
Release never deletes and never reactivates.
Exact error classifications
Add stable machine-readable GraphQL error types through
ThothError::IntoFieldError:CONTRIBUTOR_IMPORT_OPERATION_CONFLICTCONTRIBUTOR_IMPORT_RECOVERY_INCONSISTENTInvalid token or actor mismatch uses existing:
NO_ACCESSThese errors must not disclose stored token, actor identity, request fingerprint, SQL details or whether an invalid token ever existed.
Normal lifecycle states ACTIVE/CLEANED/IN_USE/RELEASED/EXPIRED are payload states, not GraphQL errors.
A3.3 Durable model consistency
The durable recovery record defined by Amendments 2 and 3 is the sole source of lifecycle reconciliation.
Minimum fields remain:
operation_id UUID PRIMARY KEYcontributor_id UUID NOT NULL UNIQUErecovery_token UUID NOT NULL UNIQUE DEFAULT uuid_generate_v4()actor_user_id TEXT NOT NULLrequest_fingerprint_version SMALLINT NOT NULLrequest_sha256 TEXT NOT NULLstatus contributor_import_recovery_status NOT NULL DEFAULT 'ACTIVE'expires_at TIMESTAMPTZ NOT NULLresolved_at TIMESTAMPTZ NULLcreated_at TIMESTAMPTZ NOT NULLNo FK to Contributor.
The server-generated recovery token remains stable for the durable operation record. Terminal tokens have no active destructive authority even if returned during authorized same-actor reconciliation.
A3.4 EXPIRED and unreconciled-attempt downstream rule
An EXPIRED operation is a fail-closed terminal state, not permission to mint a replacement Contributor automatically.
For
thoth-app#187:operationIdmust be materialised into the immutable confirmed execution plan before the corresponding Contributor creation mutation is attempted;For an EXPIRED no-ORCID Contributor attempt:
createContributorForImportwith a fresh operation ID for the same planned source Contributor;contributorIdas a fresh recovery-capable creation;If the recorded Contributor no longer exists for a terminal state that did not itself clean it, surface an inconsistency/manual-reconciliation condition; do not silently replace it.
The same fail-closed rule applies to any terminal/inconsistent recovery state where automatic fresh creation could duplicate a no-ORCID Contributor.
This does not make operation ID bibliographic identity. It is the durable transport/reconciliation identity of one already-recorded creation attempt.
A3.5 Downstream success/failure reporting clarification
For one top-level Work:
Normal success requires every capability created while that Work was in flight to reach
RELEASEDbefore the app reports clean Work completion.Failed-Work retry-safe rollback requires:
IN_USE,EXPIRED, authorization failure, operation conflict, inconsistent state or transport-unknown result is not "safe to retry the complete file"; it requires reconciliation.A successfully created Work whose release/finalization cannot be reconciled may remain a successful data write, but the import session must report recovery-finalization incomplete rather than clean terminal success.
A3.6 Prepared envelope impact
Amendment 3 remains within the prepared maximum 12-path implementation envelope in comment
5935744657.No additional dependency/Cargo path is required:
clock_timestamp()is PostgreSQL built-in;sha2,hex,serde,serde_json,uuidare already present;mutation.rs;thoth-errors/src/lib.rs.If implementation proves an additional path is materially required, HOLD and amend the write budget before editing it.
A3.7 Additional mandatory review tests
In addition to the existing test matrix, implementation must prove:
created_atandexpires_atare derived from one serverclock_timestamp()sample;NO_ACCESS;Current gate
No source/new-file mutation, migration creation/execution, branch, commit, push, PR, CI dispatch, merge, provider/runtime access, deployment, release, production activation or production-data operation is authorized by this amendment.
ja573 commentedon Oct 1, 2026
Final independent CRITICAL specification review - APPROVED
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Decision:
APPROVEDReviewed authority
This approval is bound to the complete durable specification chain:
5935744657;5935809908;5935983779;5936089788;5937234051;5937358301.Where text conflicts, later approved amendments supersede earlier preparation/body language exactly as stated.
Reviewed repository baseline:
thoth/develop @ 345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6Live re-verification at final review:
developunchanged;feature/import/thoth-import-contrib-recovery-01: absent;feature/importnamespace occupancy: none found;thoth-api/migrations/20261001_v1.9.0: absent;CHANGELOG.md,thoth-api/src/graphql/mutation.rs,thoth-errors/src/lib.rs;CHANGELOG.md.Those overlaps are not dependencies and do not block specification approval; they must be freshly reconciled before exact-SHA implementation authorization.
Final reviewed architecture
The specification now provides a complete, bounded and internally consistent recovery contract.
Creation
operationIdis transport idempotency, not bibliographic identity;Durable state
One additive recovery table and one closed enum are sufficient.
Durable states:
ACTIVE | CLEANED | IN_USE | RELEASED | EXPIREDTerminal states are irreversible.
No FK to Contributor is correct because the row is a reconciliation tombstone that must survive cleanup.
Capability lifetime
clock_timestamp()creation sample;created_atandexpires_atare coordinator-written;clock_timestamp()after all required locks;CURRENT_TIMESTAMPis prohibited as the destructive authority clock.This closes the lock-wait boundary correctly.
Cleanup concurrency
FOR UPDATE;This is compatible with PostgreSQL FK locking: concurrent legitimate Contribution creation either wins first and blocks deletion, or loses to deletion and cannot commit a dangling FK.
No application-side check-then-delete remains.
Success-path authority revocation
releaseContributorImport;This prevents stale success-path recovery authority.
Expiry/retry safety
An EXPIRED or otherwise unreconciled no-ORCID creation attempt is manual-reconciliation state.
Downstream
thoth-app#187must carry the exact operation ID in the immutable plan, reuse it on retry, and may not automatically mint a new operation ID to recreate the same planned Contributor after EXPIRED/inconsistent terminal state.Name remains non-binding identity evidence.
Ordinary delete authorization
Amendment 1 correctly separates ordinary Contributor deletion from recovery:
This closes the pre-existing empty-publisher-set authorization gap.
Frozen GraphQL contract
The Amendment 3 contract is approved exactly:
ContributorImportRecoveryStatusContributorImportRecoveryOutcomeCreateContributorForImportInputContributorImportRecoveryContributorImportRecoveryResultcreateContributorForImportcleanupContributorImportreleaseContributorImportand stable error classifications:
CONTRIBUTOR_IMPORT_OPERATION_CONFLICTCONTRIBUTOR_IMPORT_RECOVERY_INCONSISTENTNO_ACCESSfor invalid token / actor mismatch.The explicit outcome layer correctly preserves first
CLEANEDvsALREADY_CLEANEDreplay while durable status remains tombstone-safe.No recovery query/list surface is approved.
Fingerprint contract
V1 is sufficiently byte-defined for a CRITICAL idempotency boundary:
No implementation discretion remains that could silently reinterpret existing idempotency rows.
Migration / compatibility
The reviewed migration design is additive:
Production migration execution remains a separate gate.
Reviewed implementation envelope
Subject to fresh exact-SHA implementation authorization, the prepared maximum remains 12 paths:
CHANGELOG.mdthoth-api/src/schema.rsthoth-api/src/model/contributor/mod.rsthoth-api/src/model/contributor/policy.rsthoth-api/src/model/contributor/tests.rsthoth-api/src/graphql/mutation.rsthoth-api/src/graphql/tests.rsthoth-errors/src/lib.rsthoth-api/src/model/contributor/recovery.rsthoth-api/migrations/20261001_v1.9.0/up.sqlthoth-api/migrations/20261001_v1.9.0/down.sqldocs/engineering/ai-delivery/implementation-reports/THOTH-IMPORT-CONTRIB-RECOVERY-01-implementation-report.mdNo Cargo/dependency change is approved.
If implementation needs any 13th path, differently named migration, extra dependency, wider async/import framework, or different public GraphQL contract, HOLD for specification amendment.
Required validation accepted
The complete test/validation matrix across the issue and Amendments 1-3 is sufficient, including:
Non-blocking control note
Exact base SHA
345a7a...still has no GitHub Actions run.The runtime-equivalent parent
923545d5c9028bc04c40e38efeb7de674efed3fdhas successful build/test/check and migration runs, and the delta to the reviewed base is documentation/control-only.That is sufficient for specification approval only.
A later implementation authorization must not describe
345a7a...as exact-base CI GREEN unless new evidence exists.Final review decision
This approval is specification approval evidence only. It does not authorize source/new-file mutation, migration creation/execution, branch creation, commit, push, PR mutation, manual CI, provider/runtime access, merge, deployment, release, production activation or production-data operations.
Next gate
CTO final specification approval bound to this exact specification chain and current reviewed baseline.
After that, perform a fresh implementation-authorization preflight for live base SHA, branch namespace, migration slot, PR overlap and exact 12-path action matrix before granting implementation authority.
ja573 commentedon Oct 1, 2026
Final CTO specification approval - APPROVED
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Decision:
FINAL SPECIFICATION APPROVEDCTO final specification approval is granted, bound exclusively to the following durable specification/review chain:
5935744657;5935809908;5935983779;5936089788;5937234051;5937358301;5937482656.Where earlier text conflicts with later approved amendments, the later approved amendment governs.
Reviewed baseline at approval
Live
thoth/developremains:345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6This approval is bound to that reviewed specification state and baseline evidence only.
Final approved contract
The approved implementation must preserve all of the following:
operationIdper planned creation attempt, used only as transport/reconciliation identity;ACTIVE | CLEANED | IN_USE | RELEASED | EXPIRED;clock_timestamp();CURRENT_TIMESTAMP;RELEASED;IN_USEwhen legitimate reuse wins;deleteContributordenied to ordinary authenticated users and allowed only to superuser;operationIdreuse;Candidate implementation controls
Subject to a fresh implementation-authorization preflight:
feature/import/thoth-import-contrib-recovery-01developthoth-api/migrations/20261001_v1.9.05937482656.These are not yet implementation authorization.
Before implementation authority may be granted, reverify:
developSHA/tree;Gate state
Next gate
Perform a fresh exact-SHA implementation-authorization preflight and present the bounded implementation action matrix for explicit CTO authorization.
This approval does not authorize source/new-file mutation, migration creation/execution, branch creation, commit, push, PR mutation, manual CI dispatch, provider/runtime access, merge, deployment, release, production activation or production-data operations.
ja573 commentedon Oct 1, 2026
Exact-SHA implementation-authorization preflight - READY SUBJECT TO AGENT BINDING
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Specification authority: final CTO approval comment
5937502955, incorporating Amendments 1-3 and final CRITICAL review5937482656Decision:
IMPLEMENTATION PACKAGE READY - IMPLEMENTATION NOT YET AUTHORIZEDNo source/new-file mutation, migration creation/execution, branch, commit, push, PR mutation, manual CI, provider/runtime action, merge, deployment, release or production action was performed.
1. Exact live base
Fresh live verification:
thoth-pub/thothdevelop345a7a04131e7c0539f7518c6ea457fa5cb6a46298e64f0a6a49975f52d3dd14e7855395de2f34e6This exactly matches the final approved specification baseline.
2. CI evidence
GitHub exposes:
345a7a...;This is not described as GREEN.
The previously reviewed runtime-equivalent parent
923545d5c9028bc04c40e38efeb7de674efed3fdhad successful build/test/check and migration runs, and the delta to345a7a...is documentation/control-only. That remains specification/preflight evidence only.Natural PR CI must provide the implementation-head evidence.
3. Governed branch namespace
Approved STANDARD branch:
feature/import/thoth-import-contrib-recovery-01Fresh checks:
feature/importrefs: none;feature/import: none found;Namespace is currently clear.
At implementation start, before branch creation, re-read live
develop. If it is no longer exactly345a7a..., HOLD before any mutation and return for reauthorization.4. Migration slot
Approved candidate:
thoth-api/migrations/20261001_v1.9.0Fresh checks:
Slot is currently clear.
If the slot becomes occupied before migration-file creation, HOLD; do not invent another migration path.
5. Exact 12-path write envelope
Existing authorized paths - all verified present on exact base:
CHANGELOG.mdthoth-api/src/schema.rsthoth-api/src/model/contributor/mod.rsthoth-api/src/model/contributor/policy.rsthoth-api/src/model/contributor/tests.rsthoth-api/src/graphql/mutation.rsthoth-api/src/graphql/tests.rsthoth-errors/src/lib.rsAuthorized new files - all verified absent:
thoth-api/src/model/contributor/recovery.rsthoth-api/migrations/20261001_v1.9.0/up.sqlthoth-api/migrations/20261001_v1.9.0/down.sqldocs/engineering/ai-delivery/implementation-reports/THOTH-IMPORT-CONTRIB-RECOVERY-01-implementation-report.mdMaximum: 12 changed paths.
No file deletion, move or rename is permitted.
Any 13th path, Cargo/dependency change, different migration path, workflow change, client change or documentation path outside the implementation report requires HOLD and a new specification/write-budget amendment before editing.
6. Fresh open-PR overlap
Every currently open PR targeting
developwas checked against the 12-path envelope.Overlaps:
CHANGELOG.mdonly;CHANGELOG.md,thoth-api/src/graphql/mutation.rs,thoth-errors/src/lib.rs;CHANGELOG.mdonly;CHANGELOG.mdonly.No open PR overlaps:
schema.rs;These PRs are not dependencies and their unmerged bytes are not part of #963's base.
Implementation must not copy, merge, cherry-pick or otherwise incorporate their unmerged changes.
If any overlapping PR merges before the implementation branch is created, live
developnecessarily changes and the exact-base HOLD rule above applies.7. Binding implementation content
The implementation is limited to the final approved contract. In particular it must implement:
contributor_import_recoverytable + enum;clock_timestamp()authority boundary;No broader Contributor identity/deduplication, import-command framework, async architecture, Work cleanup, Series recovery or downstream app work belongs here.
8. Required local validation
Implementation authorization, when granted, should require at minimum:
cargo test -p thoth-api --features backend;cargo test --workspace;cargo check --workspace;cargo clippy --all --all-targets --all-features -- -D warnings;cargo fmt --all -- --check;git diff --check.No validation command may target production services/databases or require production credentials.
9. Candidate action-authorization matrix
The following actions are prepared for the next explicit CTO implementation authorization, not granted by this comment.
Candidate authorized actions
feature/import/thoth-import-contrib-recovery-01from exact SHA345a7a04131e7c0539f7518c6ea457fa5cb6a462;develop;Candidate explicitly denied actions
master;thoth-appor any other repository.10. Automatic side effects
The later authorized branch push / draft PR may automatically trigger repository CI. That natural CI side effect is part of the candidate implementation package.
No manual workflow dispatch is included.
11. Agent separation control
Repository doctrine requires the implementation agent/model and independent reviewer/model to be durably identified before source mutation.
They are not yet bound in this ledger.
Therefore the implementation package is technically complete but source mutation remains unavailable until the next authorization explicitly records:
This identity binding may be included in the same CTO comment that grants the exact implementation action matrix; no additional specification amendment is required.
12. Implementation HOLD/STOP conditions
Before first mutation, HOLD if:
develop!= authorized SHA345a7a...;STOP if implementation would require any already-approved STOP condition, including bare-ID recovery delete, app-side check-then-delete, destructive referenced-Contributor cascade, name identity inference, retroactive recovery authority or whole-file import redesign.
Preflight decision
Next gate: explicit CTO implementation authorization binding the implementing agent/model, independent reviewer/model, exact base, branch, migration, 12-path envelope and action matrix above.
ja573 commentedon Oct 1, 2026
STOP - task premise superseded by programme architecture amendment
Date: 2026-10-01
Task:
THOTH-IMPORT-CONTRIB-RECOVERY-01/ #963Risk: CRITICAL
Programme authority:
thoth-pub/thoth#885comment5937857546Downstream amendment:
thoth-pub/thoth-app#187comment5937864828Decision:
STOP - NO LONGER REQUIRED BY BULK-IMPORT-RELIABILITYThe CTO has changed the bulk-import recovery boundary.
Attempt-created Contributors that become orphaned after a failed new Work is deleted are now an accepted residual state. Their cleanup is no longer required before
thoth-app#187may treat that Work as rolled back and retryable.Accordingly, this task's core premise - that a new backend Contributor recovery contract is required to unblock #187 - no longer holds.
Superseded implementation package
The following previously approved/prepared #963 work must not be implemented under this task:
contributor_import_recoverydurable table/enum;20261001_v1.9.0;createContributorForImport;cleanupContributorImport;releaseContributorImport;feature/import/thoth-import-contrib-recovery-01;No branch, migration, source file, commit or PR was ever created for #963.
Historical findings retained
The prior specification, Amendments 1-3 and CRITICAL reviews remain durable historical evidence. They do not constitute implementation authority after this STOP.
Preparation also identified a pre-existing ordinary
deleteContributorauthorization edge for orphan Contributors. That finding is not fixed by this STOP and is no longer part of the bulk-import blocker. If the CTO elects to address it, it requires its own independently specified task; it must not be smuggled into #187.Downstream effect
thoth-app#187no longer waits for any GraphQL contract from #963.#187 is separately amended so failed-Work compensation leaves attempt-created Contributors untouched and accepts orphan residue.
Final task state
No backend mutation, migration, branch, commit, push, PR, CI, merge, release, deployment or production action is authorized or required by this task.