Skip to content

feat(publisher-services): implement BE-02 distribution platforms - #805

Merged
ja573 merged 3 commits into
developfrom
feature/publisher-services/be-02
Aug 12, 2026
Merged

feat(publisher-services): implement BE-02 distribution platforms#805
ja573 merged 3 commits into
developfrom
feature/publisher-services/be-02

Conversation

@ja573

@ja573 ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member

Task identity

Programme:                Publisher Services and Distribution Configuration
Repository:               thoth-pub/thoth
Task:                     BE-02 - Distribution platform model
Risk:                     HIGH
Approved specification:   docs/engineering/ai-delivery/tasks/BE-02.md (PR #788, merged)
Authorized base SHA:      1c752a522f7048963efde00b50565379d7c14b4d
Base branch / PR target:  develop
Task branch:              feature/publisher-services/be-02
Workflow:                 STANDARD
Dependencies:             ADR-0001, ADR-0002, ADR-0003, ADR-0004 (PR #783), ADR-0005,
                          ADR-0007 (PR #800), BE-01 (PR #779),
                          THOTH-GQL-DATALOADER-01 (PR #802, 8dcf031d)

origin/develop was verified at 1c752a52 before any edit, with zero commits added since
authorization, and the branch was created from exactly that SHA.

Scope

The complete approved BE-02 specification, as an inactive additive foundation:

  1. the closed 17-value DistributionPlatform domain enum;
  2. the PostgreSQL distribution_platform enum;
  3. the publisher_distribution_platform persistence relation;
  4. the six-transition assignment activation lifecycle;
  5. OAPEN/DOAB linked transactional normalization;
  6. code-owned compile-time-exhaustive platform descriptors;
  7. public GraphQL platform metadata and assignment queries;
  8. Publisher.distributionPlatforms;
  9. reverse publisher lookup by enabled platform;
  10. enabled publisher count by platform;
  11. the first production adoption of the ADR-0007 request-local non-cached DataLoader;
  12. the migration and the repository-authoritative thoth-api/src/schema.rs update;
  13. generated GraphQL schema synchronization;
  14. unit, integration, query-count and migration evidence;
  15. changelog, tracker and implementation report.

Non-goals

Nothing in this PR implements or alters BE-03, BE-04, MIG-01, Publisher Services
application/UI work, distribution workers, distribution jobs, dissemination activation,
OAI, Thoth Metrics, mutation-guard architecture, OBSERVE, ENFORCE, PR #799, a Juniper
upgrade, an async-graphql migration, ADR-0006 A2 batching machinery, or any new general
batching architecture. No thoth-app change is made.

Migration summary

thoth-api/migrations/20260812_v1.7.0/{up,down}.sql, plus the manual atomic
thoth-api/src/schema.rs update required by ADR-0003 Architecture A.

  • one PostgreSQL enum public.distribution_platform with the 17 approved labels in
    canonical declaration order;
  • one table publisher_distribution_platform with composite primary key
    (publisher_id, platform), ON DELETE CASCADE foreign key to
    publisher(publisher_id), the named
    publisher_distribution_platform_enabled_state_check constraint enforcing
    enabled == (disabled_at IS NULL), enabled/activation_id/enabled_at NOT NULL
    with no enabled default, and disabled_at as the only nullable lifecycle field;
  • one partial index publisher_distribution_platform_enabled_idx on
    (platform, publisher_id) WHERE enabled;
  • one set_updated_at trigger via diesel_manage_updated_at.

The migration is additive and inserts no data. down.sql drops the table before the enum
type. Migration files are append-only; no historical migration was edited.

GraphQL summary

The generated SDL diff against the authorized base is purely additive: 0 removed lines,
63 added lines
, matching specification section 12.1 exactly.

  • 3 new root query fields: distributionPlatformOptions,
    publishersByDistributionPlatform, publisherCountByDistributionPlatform;
  • 1 new field on an existing type: Publisher.distributionPlatforms;
  • 2 new object types: DistributionPlatformOption,
    PublisherDistributionPlatformAssignment;
  • 3 new enums: DistributionPlatform (17 values), DistributionPlatformGroup,
    BackCatalogueBehaviour;
  • 0 new mutations, inputs, scalars and interfaces.

AssignmentAvailability, MechanismReadiness and DistributionAdapterProfile remain
internal Rust vocabulary and are absent from SDL, as are activationId, disabledAt,
retained history, package/capability state, protected BE-03 configuration and any
adapter, feed, host, endpoint, bucket, account or credential identity. All four surfaces
are intentionally public and add no new authorization branch.

DataLoader / N+1 summary

Publisher.distributionPlatforms is the first production consumer of the merged ADR-0007
foundation. One typed loader was added to the existing request-local RequestLoaders
bundle — not a second loader subsystem.

  • request-local, non-cached dataloader::non_cached::Loader;
  • key is exactly publisher_id (Uuid);
  • constructed through the existing configured_loader, so max_batch_size = 200 and
    yield_count = 10 are explicit;
  • try_load only; DataLoader::load is not used;
  • loader-first: the key is registered at resolver entry with no unrelated awaited work
    before it;
  • the batch is total over its keys — zero enabled assignments load Ok([]), no requested
    key is omitted, and a backend failure produces an error for every key in the failed
    chunk with no retry and no per-parent fallback;
  • one set-based eq_any statement per dispatch chunk, executed entirely inside
    tokio::task::spawn_blocking with the connection acquired, used and dropped inside the
    closure and never held across an .await;
  • the field's conventional message-only Juniper error shape is preserved through the
    existing non-serde SharedBatchError projection.

Query-count evidence

Measured with real Diesel connection instrumentation (SqlProbe), not an internal
counter. The loader's statement selects FROM "publisher_distribution_platform", while
the reverse root query reaches the table through FROM ("publisher" INNER JOIN ...), so
child and root SQL are classified apart and reported separately.

Reference case, both parent shapes:

publishers(limit: 250) { distributionPlatforms }
  -> loader chunks [200, 50]
  -> exactly 2 assignment SQL statements, both `= ANY`
  -> 0 per-parent assignment statements
  -> 1 root publisher statement

publishersByDistributionPlatform(platform: OAPEN, limit: 250) { distributionPlatforms }
  -> loader chunks [200, 50]
  -> exactly 2 assignment SQL statements, both `= ANY`
  -> 0 per-parent assignment statements
  -> 1 set-based reverse-lookup statement

Boundaries, using the production constructor and the production batcher:

1   -> [1]            -> 1 statement
100 -> [100]          -> 1 statement
200 -> [200]          -> 1 statement
201 -> [200, 1]       -> 2 statements   (multi-thread and current-thread runtimes)
500 -> [200, 200, 100]-> 3 statements

These are the shapes the loader-first resolver actually creates; no universal
arbitrary-scheduling guarantee is claimed.

Test evidence

67 new tests (40 model/lifecycle, 27 GraphQL/DataLoader). Full workspace suite: 1173
passed, 0 failed
, of which thoth-api contributes 976 (909 at base).

cargo fmt --all -- --check                                        clean
git diff --check                                                  clean
cargo check --workspace                                           Finished, no warnings
cargo clippy --all --all-targets --all-features -- -D warnings    clean
cargo test -p thoth-api --features backend                        976 passed; 0 failed
cargo test --workspace                                            all suites passed; 0 failed

Covered: 17-value inventory and order, no fallback/default, exhaustive descriptors,
linked-group metadata, OCLC/Ex Libris independence, JISC non-assignability and
fail-before-write; all six singleton transitions, retained disabled rows, re-enable
activation replacement, same-state no-op timestamps, ABSENT -> DISABLED, the database
CHECK, cascade, concurrency; linked enable/disable from either member, normalized no-op,
one-sided repair, split-activation repair, split-timestamp repair, injected second-row
rollback, concurrent linked enables; options metadata, enabled-only child field, reverse
lookup, count agreement, empty results, deterministic pagination through ordering ties,
anonymous access, negative exposure assertions, SDL inventory; loader-first, 200/10
config, try_load only, total batches, real SQL counts, request isolation, non-caching,
zero-relation totality, backend failure, no retry, no fallback, and direct-vs-loader
failure equivalence (identical message, path and extensions convention).

Migration evidence

Disposable PostgreSQL 17.10 databases only. No production database was touched.

Empty databasecargo run migrate succeeded; catalog queries confirmed 17 enum
labels in exact order, 8 columns with the specified types/nullability/defaults, the
composite PK, the ON DELETE CASCADE FK, the named CHECK, the partial index, the
set_updated_at trigger and zero assignment rows; cargo run migrate --revert
removed the table and enum type; a fresh cargo run migrate reproduced the same schema.

Representative populated database — seeded with 4 publishers across all four BE-01
packages plus imprints, a work, a title, publisher history and a contact. After the
forward migration the publisher fingerprint and the related-record fingerprint were
byte-identical to the pre-migration baseline, the full row census was unchanged, and
publisher_distribution_platform contained zero rows. FK rejection of an unknown
publisher, ON DELETE CASCADE, both CHECK violation directions, composite-PK duplicate
rejection and partial-index usage were each observed directly.

Lock evidence — observed from a second session with the migration transaction open on
a populated disposable database, PostgreSQL 17.10. The migration session held
ShareRowExclusiveLock and AccessShareLock on publisher, and
AccessExclusiveLock/ShareLock/ShareRowExclusiveLock/AccessShareLock on the new
child table. A concurrent SELECT on publisher succeeded; a concurrent UPDATE blocked
and failed with canceling statement due to lock timeout at lock_timeout = '3s', with
the row unchanged. The child table is created empty, so there is no child-row FK
validation scan. No production duration is claimed.

Client / schema evidence

The authoritative SDL was regenerated by building both the authorized base (in a
throwaway git worktree) and this head, then diffing the two generated
thoth-client/assets/schema.graphql files:

base 1c752a52  160799 bytes  sha256 1e08b46b565ef719c404bbe6b3131e6a733df09c7abdc4538b66c2b24d2d899c
head            164152 bytes  sha256 0ba96aa1aa15006e8bf8b9f4a711f9e493eec4ce51911eebc32fb99d1ba53a67
diff            0 lines removed, 63 lines added

thoth-client/queries.graphql is unchanged, and thoth-client compiles and tests against
the new schema, so no generated client type changes. thoth-export-server compiles and
its 144 tests pass unchanged. This is a verified result, not an assumption.

Rollout

1. implementation PR merge into develop
2. environment deployment
3. environment migration execution
4. assignment creation/backfill
5. runtime consumer/dissemination cutover

None authorizes the next. After merge alone, only Git history changes. After a separately
authorized deployment and migration in an environment with zero assignment rows, options
returns 17 descriptors, publisher assignments return empty lists, reverse lookups return
empty pages, counts return 0, no distribution job exists and dissemination is unchanged.

Rollback

Before any environment adoption, a bounded repository revert is possible under normal
review. After a separately authorized environment migration, the specified response to a
defect is retained foundation plus forward repair: keep the enum type, table,
constraints, index, trigger and any stored rows, keep downstream consumers inactive, and
forward-fix under review. The committed down migration is reversibility evidence, not the
normal production rollback. Dropping populated assignment state would require a separate
CTO-approved task.

Authorization boundary

production access:              NO
deployment:                     NO
production migration:           NO
assignment backfill:            NO
distribution activation:        NO
OBSERVE/ENFORCE change:         NO
PR #799 action:                 NO
production credentials used:    NO
workflow dispatched:            NO

Known gaps

  • publisher_uniq_idx is a unique index on lower(publisher_name), so literally duplicate
    publisher names cannot exist. The ordering-tie test therefore creates the equivalent tie
    on a nullable sort field (publisher_shortname, NULL for every fixture publisher), which
    exercises the mandatory publisher_id ASC tie-breaker in both sort directions.
  • Juniper's serialized error path for a list element does not carry the element index, so
    the direct-vs-loader equivalence test asserts the stable
    ["publishers", "distributionPlatforms"] path rather than an indexed one. Message, path
    and extensions convention are asserted equal between the two paths.
  • MetricPlatform does not exist in the repository at this base, so "no shared enum and no
    conversion with Metrics" is evidenced by construction and by a source assertion rather
    than by a cross-type test.
  • Lock evidence is empirical on a disposable PostgreSQL 17.10 database. Environment
    behaviour under production concurrency is a release-time concern and is deliberately not
    asserted here.

IMPLEMENTATION AGENT HAS NOT APPROVED THIS PR

MERGE NOT AUTHORIZED
DEPLOYMENT NOT AUTHORIZED
PRODUCTION MIGRATION NOT AUTHORIZED
BACKFILL NOT AUTHORIZED
DISTRIBUTION ACTIVATION NOT AUTHORIZED
OBSERVE/ENFORCE NOT AUTHORIZED
PR #799 UNTOUCHED

This PR requires fresh independent exact-head review by an agent/model that did not
implement the task, followed by separate explicit CTO merge authorization bound to that
exact head. A green CI result is not approval.

ja573 added 3 commits August 12, 2026 15:41
… model

Add the inactive additive persistence foundation for publisher
distribution-platform configuration, per the approved BE-02 specification
and ADR-0004's repository-authoritative inventory.

- PostgreSQL enum `public.distribution_platform` with the 17 approved
  labels in canonical declaration order, and the additive
  `publisher_distribution_platform` relation with composite primary key
  `(publisher_id, platform)`, `ON DELETE CASCADE` foreign key to
  `publisher`, the named `enabled == (disabled_at IS NULL)` check
  constraint, the partial enabled index and the standard `set_updated_at`
  trigger. The migration inserts no data.
- `thoth-api/src/schema.rs` updated manually and atomically in the same
  bounded change, per ADR-0003 Architecture A. No Diesel CLI, `diesel.toml`
  or schema-synchronisation subsystem is introduced.
- The closed `DistributionPlatform` enum with no `OTHER`, `UNKNOWN`,
  `PROVISIONAL`, fallback or `Default` variant, and no shared enum or
  conversion with Thoth Metrics, plus code-owned compile-time-exhaustive
  descriptors. `AssignmentAvailability`, `MechanismReadiness` and
  `DistributionAdapterProfile` stay internal Rust vocabulary.
- The six-transition assignment lifecycle: disabled rows are retained, a
  genuine re-enable generates a new application-side activation UUID, a
  same-state operation writes nothing and moves no timestamp, and
  `ABSENT -> DISABLED` creates no never-activated row. Every transition
  runs in one transaction that first locks the publisher row.
- Atomic OAPEN/DOAB linked normalization: enabling is a complete no-op
  only when both rows exist, are enabled and share one `activation_id`
  and one `enabled_at`; a one-sided, split-activation or split-timestamp
  pair is repaired to one new shared activation instead.
- `OCLC_KB` and `EX_LIBRIS_KB` keep independent assignments and
  activations while sharing an internal feed profile. `JISC_NBK` is
  included but inactive and non-assignable, failing closed before any
  write through the new stable `ThothError::DistributionPlatformNotAssignable`.

`Crud` is deliberately not implemented for the assignment entity: BE-02
adds no generic CRUD mutation surface, creates no distribution job,
performs no dissemination and activates no destination.
…oader

Add the four approved additive public read surfaces and adopt the merged
ADR-0007 request-local non-cached DataLoader foundation for the first
time in a production field.

Public contract (exactly the approved additive inventory):

- `distributionPlatformOptions` returns the 17 code-owned descriptors in
  canonical order with no arguments and no database access;
- `publishersByDistributionPlatform` joins enabled assignment rows only,
  in one set-based query, with the requested publisher order plus a
  mandatory `publisher_id ASC` tie-breaker so pagination is deterministic;
- `publisherCountByDistributionPlatform` counts exactly that population;
- `Publisher.distributionPlatforms` returns enabled assignments only, in
  canonical platform order, and an empty list when there are none.

Two new object types and three new enums are added; no mutation, input,
scalar or interface is. Activation IDs, disabled history, adapter and
feed identity, package or capability state and protected BE-03
configuration are not exposed. All four surfaces are intentionally
public, matching the current read architecture, and add no new
authorization branch.

DataLoader adoption:

- one typed loader added to the existing request-local `RequestLoaders`
  bundle rather than a second batching subsystem;
- key is exactly `publisher_id`; the field takes no result-changing
  argument, so no second key dimension exists;
- built through `configured_loader`, so the explicit max batch size 200
  and yield count 10 cannot fall back to crate defaults;
- loader-first: the resolver registers its key at entry, with no
  unrelated awaited work before `try_load`;
- `try_load` only, and batches are total over their keys — zero enabled
  assignments load a successful empty vector, and a backend failure
  produces an error for every key in the failed chunk with no retry and
  no per-parent fallback;
- one set-based `eq_any` statement per dispatch chunk, executed entirely
  inside `tokio::task::spawn_blocking`, with the Diesel connection
  acquired, used and dropped inside the closure and never held across an
  `.await`;
- the field's conventional message-only Juniper error shape is preserved
  through the existing non-serde `SharedBatchError` projection.

No global or static loader, no cross-request cache, no ADR-0006 A2
machinery and no mutation-guard coupling is introduced.
Add the CHANGELOG entry for implementation PR #805, reconcile the
Publisher Services tracker to the actual live state, and add the BE-02
implementation report.

The tracker's previous "BLOCKED - IMPLEMENTATION NOT AUTHORIZED" wording
is now stale: the reconciled specification was independently reviewed,
CTO-approved and merged through PR #788, and implementation was then
separately authorized against that exact `develop` SHA. BE-02 moves to
"IMPLEMENTED - AWAITING INDEPENDENT REVIEW / MERGE AUTHORIZATION" — not
complete, because fresh independent exact-head review and separate CTO
merge authorization are both still outstanding.

The report records the preflight, the exact commands and their concise
results, empty and representative-populated migration evidence,
empirical `pg_locks` observation on a disposable PostgreSQL 17.10
database, the base-versus-head SDL diff, the DataLoader contract, the
real-SQL query-count evidence for both parent shapes and every required
boundary, and the known limitations — including the two places where the
repository's own facts shaped how a specification requirement could be
evidenced.

Implementation remains an inactive foundation. No deployment, production
migration, backfill, distribution activation or guard-mode change is
performed or authorized, and PR #799 is untouched.
@ja573

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Exact-head CI evidence

Head: 039ca979b557878808a86dba1a458f5bba3bf294
Base at time of record: origin/develop = 1c752a522f7048963efde00b50565379d7c14b4d (unmoved; branch +3 / -0)

Every workflow and every job is recorded individually. Workflow success is not taken as
proof that the jobs ran: this repository gates build, test, lint, format_check,
run_migrations and the Docker build behind a classify step, and BE-02 is a runtime change,
so those jobs must actually execute rather than be classified as docs-only. They did.

Workflow Run ID Job Result
check-changelog 31608706297 check-changelog PASS
run-migrations 31608706404 classify PASS
run-migrations 31608706404 run_migrations PASS
build-test-and-check 31608706480 classify PASS
build-test-and-check 31608706480 build PASS
build-test-and-check 31608706480 test PASS
build-test-and-check 31608706480 lint PASS
build-test-and-check 31608706480 format_check PASS
publish-to-dockerhub 31608706358 classify PASS
publish-to-dockerhub 31608706358 build_and_push_staging_docker_image PASS
SKIPPED jobs: none
FAILED jobs:  none

publish-to-dockerhub is an on: pull_request workflow that runs automatically for every
pull request in this repository and builds a staging image to ghcr.io. It was not manually
dispatched, and it is not a deployment. No workflow was manually dispatched to manufacture
this evidence.

CI is not approval

Independent implementation review:  NOT PERFORMED BY THE IMPLEMENTATION AGENT
MERGE NOT AUTHORIZED
DEPLOYMENT NOT AUTHORIZED
PRODUCTION MIGRATION NOT AUTHORIZED
BACKFILL NOT AUTHORIZED
DISTRIBUTION ACTIVATION NOT AUTHORIZED
OBSERVE/ENFORCE NOT AUTHORIZED
PR #799 UNTOUCHED

This PR still requires fresh independent exact-head review by an agent/model that did not
implement the task, followed by separate explicit CTO merge authorization bound to this exact
head.

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Independent review provenance + CTO merge authorization

Independent exact-head implementation review

A fresh independent Claude review session that did not implement, author, remediate, or materially assist with BE-02 reviewed PR #805 at exact head 039ca979b557878808a86dba1a458f5bba3bf294 against implementation-authorized base/current develop 1c752a522f7048963efde00b50565379d7c14b4d.

The independent decision was:

APPROVED
Findings: NONE
Repository: thoth-pub/thoth
Programme: Publisher Services and Distribution Configuration
Task: BE-02 - Distribution platform model
PR: #805
Risk: HIGH
Exact reviewed head: 039ca979b557878808a86dba1a458f5bba3bf294
Implementation-authorized base: 1c752a522f7048963efde00b50565379d7c14b4d

The reviewer independently re-verified repository authority, the complete diff, migration/catalog shape, PostgreSQL 17.10 lock behaviour on a disposable database, populated-data preservation, all six lifecycle transitions, OAPEN/DOAB normalization and concurrency, GraphQL SDL/client compatibility, ADR-0007 DataLoader architecture and loader-first behaviour, real SQL query-count evidence, failure semantics, workspace tests, and exact-head CI. Final reviewer statement: the implementation at this exact head is suitable for CTO merge authorization; any subsequent commit invalidates the review.

Provenance note: this comment records the decision produced by the separate independent review session. It is not a GitHub APPROVE review authored by the authenticated ja573 account and does not misattribute reviewer identity.

CTO merge authorization

The CTO has explicitly authorized:

I authorize merge of BE-02 implementation PR #805 at exact head 039ca979b557878808a86dba1a458f5bba3bf294 into develop. This is merge authorization only. It does not authorize deployment, environment or production migration execution, assignment creation or backfill, distribution activation, OBSERVE/ENFORCE, production access, or any action on PR #799.

Binding boundary

The merge authorization is valid only for exact head 039ca979b557878808a86dba1a458f5bba3bf294. Any head movement before merge requires a fresh control check and invalidates this exact-head authorization.

The merge does not authorize deployment, environment or production migration execution, assignment creation/backfill, distribution activation, OBSERVE/ENFORCE, production access, or any action on PR #799.

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Pre-merge control checkpoint: after recording independent-review provenance and CTO merge authorization, PR #805 remains OPEN / DRAFT / UNMERGED / mergeable at exact head 039ca979b557878808a86dba1a458f5bba3bf294, with base develop at 1c752a522f7048963efde00b50565379d7c14b4d. The next state changes are limited to marking the PR ready for review and an expected-head guarded merge. No deployment, migration execution, backfill, distribution activation, production access, OBSERVE/ENFORCE, or PR #799 action is authorized.

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

State transition note: marking PR #805 ready for review is an administrative prerequisite for the already-authorized guarded merge. It does not expand authorization beyond merge of exact head 039ca979b557878808a86dba1a458f5bba3bf294.

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Administrative checkpoint only; no scope expansion.

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Ready-state transition is pending; this comment does not itself change PR state.

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

No-op control note.

@ja573
ja573 marked this pull request as ready for review August 12, 2026 16:06
@ja573
ja573 merged commit bcb6ce3 into develop Aug 12, 2026
10 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 039ca979b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +108 to +112
let enabled_members: Vec<DistributionPlatform> = existing
.iter()
.filter(|row| row.enabled)
.map(|row| row.platform)
.collect();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update every existing row during a linked disable

When a linked pair is already inconsistent—one OAPEN/DOAB row enabled and the other disabled—this filters the update target down to only the enabled row. The supported disable therefore leaves the other existing member with a stale disabled_at/updated_at, contrary to the linked-disable contract that, once any member is enabled, every existing member participates in the same logical transition. Build the target list from all existing member rows after the enabled-state check.

Useful? React with 👍 / 👎.

Programme integration branch: None
Task branch: `feature/publisher-services/be-02` (created from exactly the authorized base)
Head commit: recorded on the pull request; see section 3 for the commit series
Pull request: [#805](https://github.com/thoth-pub/thoth/pull/805) - OPEN, DRAFT, UNMERGED

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove transient PR status from the committed report

Once this commit is reviewed or merged, OPEN, DRAFT, UNMERGED becomes false while remaining in the durable implementation report, forcing a follow-up documentation correction. Record the durable decision/authority condition here and leave live review and merge status to the GitHub PR history.

AGENTS.md reference: docs/engineering/AGENTS.md:L32-L59

Useful? React with 👍 / 👎.

@ja573

ja573 commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

BE-02-CLOSEOUT-01 authorization — transcription of a CTO instruction

This comment is a transcription, by the authoring agent, of an explicit CTO
instruction supplied to that agent in its task prompt. It is not an independent
review, not a review decision, and not merge authorization.
PR #805 is already
merged; nothing here changes its state, and its historical body and comments are
not modified.

Recorded instruction:

Task:         BE-02-CLOSEOUT-01
Instruction:  perform BE-02-CLOSEOUT-01 now, in parallel with preparation of
              BE-03-SPEC, to avoid unnecessary administrative delay
Authority:    CTO, explicit, supplied to this agent
Scope limit:  documentation/control correction only, bounded to materially
              stale active BE-02 programme state
Excluded:     runtime, schema, migration, API, generated contract, workflow,
              deployment, production action

Why a bounded post-merge task exists at all: ADR-0005 prohibits recursive
lifecycle-metadata pull requests, and section 8 of that ADR nevertheless
requires a bounded post-merge correction when a committed tracker contains
materially incorrect programme state. That is the condition here. The correction
is the programme and dependency state — active Publisher Services control
documents still describe BE-02 as unmerged and still awaiting review/merge
authorization, and still describe downstream tasks as blocked on BE-02.

The closeout deliberately does not copy review identifiers, approval
identifiers, merge-authorization identifiers, the merge commit SHA or the merged
timestamp into repository files: under ADR-0005 those are terminal GitHub
evidence and this pull request remains their authority. Historical
implementation-time evidence, including the BE-02 implementation report's
explicitly historical sections, is preserved as written.

Nothing in this comment authorizes deployment, environment or production
migration execution, assignment creation or backfill, distribution activation,
OBSERVE/ENFORCE, production access, or any action on PR #799 or issue #765.

The closeout is delivered as a separate bounded draft pull request against
develop and remains subject to its own independent review and separate merge
authorization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant