Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
19e471e
feat(integration): persist exclusive outbox delivery leases
seonghobae Aug 13, 2026
dd9f602
test(integration): expose schema-scoped lease migration bug
seonghobae Aug 13, 2026
994a050
fix(integration): install schema-scoped outbox lease constraints
seonghobae Aug 13, 2026
8b12fb9
fix(coverage): cover outbox lease database error paths
seonghobae Aug 14, 2026
3bf4336
test(integration): serialize remaining lease database cases
seonghobae Aug 14, 2026
ff8a582
merge(main): reconcile persist PR #60 after #64
seonghobae Aug 14, 2026
ea9a1ec
test(integration): isolate missing-outbox database paths
seonghobae Aug 14, 2026
3d9b792
merge(main): reconcile persist PR #60 after #62
seonghobae Aug 14, 2026
b2d2dc5
test(outbox): prove lease fencing generation integrity
seonghobae Aug 14, 2026
06c9ca9
fix(outbox): constrain active fence to lease generation
seonghobae Aug 14, 2026
b38f0c2
merge(main): reconcile persist PR #60 after #63
seonghobae Aug 14, 2026
50e4534
test(outbox): cover zero expiry claim timestamps
seonghobae Aug 14, 2026
c02eed2
fix(integration): cover zero-expiry claims and negative lease tokens
seonghobae Aug 14, 2026
7c60e38
Merge protected main into outbox-lease slice
seonghobae Aug 14, 2026
8ecacf3
Merge protected main into outbox delivery lease slice
seonghobae Aug 14, 2026
1d395c4
Merge branch 'main' into feat/outbox-delivery-lease-20260814
opencode-agent[bot] Aug 14, 2026
147fcee
test(integration): expose lease authority regressions
seonghobae Aug 14, 2026
f082125
style(test): rustfmt lease authority regressions
seonghobae Aug 14, 2026
55aa094
test(integration): isolate lease fencing schema
seonghobae Aug 14, 2026
7c463cc
test(integration): align lease tests with database clock
seonghobae Aug 14, 2026
43f2cb7
fix(integration): make lease authority database-clock fenced
seonghobae Aug 14, 2026
75f29b1
docs(architecture): map active outbox lease schema
seonghobae Aug 14, 2026
969610c
test(integration): cover corrupt lease shape fail-closed path
seonghobae Aug 14, 2026
9d9aa71
test(integration): prove concurrent outbox claim exclusivity
seonghobae Aug 14, 2026
a98687a
test(integration): serialize migration isolation schemas
seonghobae Aug 14, 2026
09a0db3
fix(ci): format outbox lease concurrency acceptance
seonghobae Aug 14, 2026
b175c70
fix(outbox): borrow claim race barrier
seonghobae Aug 14, 2026
d59c029
test(outbox): cover lease replay and database-clock failure edges
seonghobae Aug 14, 2026
fbda49b
test(integration): require repairable lease column constraints
seonghobae Aug 15, 2026
a0bd2af
fix(integration): make lease constraints independently repairable
seonghobae Aug 15, 2026
de7db2e
test(outbox): cover claimed-at overflow, claim query failure, and neg…
seonghobae Aug 15, 2026
db965a6
test(integration): prove blocked concurrent lease claim
seonghobae Aug 15, 2026
db60451
docs(architecture): reconcile lease evidence tests
seonghobae Aug 15, 2026
0a309db
fix(ci): rustfmt concurrent lease acceptance
seonghobae Aug 15, 2026
4fc5302
test(outbox): verify lease constraint reapplication definitions
seonghobae Aug 15, 2026
51f3f1f
style(outbox): apply rustfmt to concurrency acceptance
seonghobae Aug 16, 2026
4fc3334
fix(outbox): cover lease error displays and satisfy clippy
seonghobae Aug 16, 2026
dbeea2b
merge(main): reconcile persist PR #60 after #85
seonghobae Aug 16, 2026
69d0fa5
fix(outbox): classify lease expiry from the database clock
cursoragent Aug 16, 2026
078e082
merge(main): reconcile protected anonymous credential lifecycle
seonghobae Aug 16, 2026
b5a0449
Merge branch 'main' into cursor/bc-d00f59be-1bb0-4e1d-9e7a-08b0c394b9…
seonghobae Aug 17, 2026
c2541b9
Merge branch 'main' into cursor/bc-d00f59be-1bb0-4e1d-9e7a-08b0c394b9…
opencode-agent[bot] Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ All notable product and architecture changes are recorded here. Releases use imm

### Fixed

- Outbox delivery-lease expiry recovery now classifies liveness from the PostgreSQL clock, so a future caller timestamp cannot steal a still-live exclusive lease.
- Scoring results now reject non-canonical engine-artifact digests and accept only `sha256:` followed by 64 lowercase hexadecimal characters as immutable provenance.
- Exact replay of an already accepted response event remains idempotent after collection pauses or closes, while conflicting replay evidence still fails closed and genuinely new responses remain restricted to active sessions.
- Documentation status drift that still described protected-main `item_delivery`, participant linking, authorization, and integration domain primitives as Target after their merge.
Expand Down
2 changes: 1 addition & 1 deletion docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Still-Target logical modules/adapters include remaining product aggregate persis

### Active implementation work that is not protected-main truth

**Active PR** #76 data-rights processing-start persistence is not protected-main truth until an unchanged reviewed/check-clean head is integrated. Identity-verified requests persist an immutable operation identity and processing-start time under `FOR UPDATE` so later lifecycle composition cannot race the classified row. Dependent-system execution remains outside this slice.
**Active PR** #60 exclusive outbox delivery-lease persistence is not protected-main truth until an unchanged reviewed/check-clean head is integrated. Pending outbox rows accept one fenced worker lease, recover expiry from the database clock without transferring the fence, reject a future caller timestamp that would steal a still-live lease, and reject stale or zero-window claims. Live side-effect execution remains outside this slice.

## 5. ADR traceability by concern

Expand Down
26 changes: 21 additions & 5 deletions docs/architecture/AS_BUILT_SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

- Status: Normative evidence map
- Date: 2026-08-14
- Protected-main baseline: `4b828134f4d597ca1add3d6dbf02bebd72bfb0b2`
- Protected-main baseline: `cc5850a0d1eacbbf16d03075534fce460a8286e6`

This document records which portions of the logical ERD have executable PostgreSQL migrations and adapters. It does **not** promote active-PR DDL or target entities to protected-main truth. `ERD.md` remains the normative logical model; this file is the physical/as-built maturity companion required once migrations exist. Status terms follow `docs/TRACEABILITY.md`: **Implemented** means evidence exists on the named protected-main baseline, **Active PR** means evidence exists only on an open PR, and **Target** means required behavior not yet implemented on that baseline.

Expand All @@ -12,18 +12,34 @@ Protected main contains executable PostgreSQL 18 persistence subsets for integra

| Physical object | Logical ownership | Protected-main maturity |
|---|---|---|
| `integration_outbox` | integration | Implemented subset |
| `integration_outbox` | integration | Implemented subset; exclusive delivery-lease extension is **Active PR** #60 |
| `integration_delivery_attempt` | integration | Implemented subset |
| `integration_inbox` | integration | Implemented subset |
| `scoring_job_state` | scoring | Implemented subset |
| `instrument_release` | instrument publication | Implemented subset |
| `integration_consumption` | integration | **Active PR** #58 (not protected-main truth) |
| `integration_consumption` | integration | Implemented subset |

The protected-main integration identity is source- and tenant-scoped. A physical implementation must continue to preserve the stronger logical tenant/resource, replay, and crash-safety invariants in ADR-0014 and ADR-0015.

## Active PR inbox-consumption physical schema
## Active PR outbox delivery-lease physical schema

PR #58 (`feat/inbox-consumption-persistence-20260814`) `migrations/0012_integration_consumption.sql` and `src/postgres_inbox_consumption.rs` adapter persist one consumption work item for an existing `integration_inbox` receipt. The slice is **Active PR**, not protected-main truth. It stores pending/processing/completed/quarantined evidence, a monotonically increasing fencing token, a time-bounded processing claim, a durable `side_effect_ref`, and optional completion or quarantine evidence. Receipt-only inbox rows remain uncompleted. A processing claim cannot be stolen by another worker. Expire-and-reclaim returns an expired claim to pending without transferring the crashed worker's fence.
PR #60 (`feat/outbox-delivery-lease-20260814`) extends the protected-main `integration_outbox` relation through `migrations/0013_outbox_delivery_lease.sql` and `src/postgres_integration.rs`. The extension is **Active PR**, not protected-main truth.

The active slice adds:

- nullable `lease_worker_ref` and `lease_ref` opaque ownership references;
- nullable positive `lease_fencing_token` and `lease_expires_at_unix_ms` values that are either all present or all absent for the current lease;
- `delivery_lease_generation BIGINT NOT NULL DEFAULT 0 CHECK (delivery_lease_generation >= 0)` as the persisted monotonic generation;
- `integration_outbox_fencing_generation_check`, requiring any live `lease_fencing_token` to equal the current persisted generation;
- exclusive pending-row claims, explicit expired-lease recovery, and fenced attempt recording that clears the current lease after an accepted attempt;
- database-clock authority for both worker-side lease-expiry classification and exclusive-lease recovery, while caller-supplied attempt timestamps remain immutable delivery-attempt evidence and a future caller observation cannot steal a still-live lease;
- fail-closed stale fencing before replay classification whenever a current lease exists, while exact replay after a completed attempt has cleared its lease remains idempotent.

Real PostgreSQL evidence on the active PR is carried by `tests/postgres_outbox_delivery_lease.rs`, `tests/postgres_outbox_delivery_lease_fencing_integrity.rs`, `tests/postgres_outbox_delivery_lease_authority.rs`, `tests/postgres_outbox_delivery_lease_concurrency.rs`, `tests/postgres_outbox_delivery_lease_coverage_edges.rs`, and `tests/postgres_outbox_delivery_lease_migration_isolation.rs`. These tests cover exclusive claim/recovery, monotonic fencing, invalid physical state rejection, database-authoritative expiry, rejection of a future caller timestamp against a still-live lease, stale-fence replay precedence, blocking-proven concurrent claims, schema isolation, and persistence failure paths. The slice must remain **Active PR** until the exact reviewed/check-clean head is merged and protected main is refetched.

## Protected-main inbox-consumption physical schema

`migrations/0012_integration_consumption.sql` and `src/postgres_inbox_consumption.rs` persist one consumption work item for an existing `integration_inbox` receipt. This is an **Implemented subset** on protected main after #58. It stores pending/processing/completed/quarantined evidence, a monotonically increasing fencing token, a time-bounded processing claim, a durable `side_effect_ref`, and optional completion or quarantine evidence. Receipt-only inbox rows remain uncompleted. A processing claim cannot be stolen by another worker. Expire-and-reclaim returns an expired claim to pending without transferring the crashed worker's fence. Subsequent claim-deadline columns from later inbox-expiry slices remain documented with those slices.

## Protected-main scoring-job physical schema

Expand Down
7 changes: 6 additions & 1 deletion docs/architecture/ERD.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ erDiagram
string payload_digest
timestamp occurred_at
timestamp published_at
string lease_worker_ref
string lease_ref
int lease_fencing_token
timestamp lease_expires_at
int delivery_lease_generation
}

integration_delivery_attempt {
Expand Down Expand Up @@ -428,7 +433,7 @@ The target ERD deliberately includes several logical entities that are not yet p
- `consent_ledger` and `consent_event` persist the already-merged `src/consent.rs` append-only ledger. Physical persistence is carried by Active PR #49 (`migrations/0005_consent_lifecycle.sql`); HTTP consent transport and derived snapshot tables remain Target.
- `participant_identity_link` is the persistence target accepted by ADR-0020. The current `src/participant.rs` `keyverse_subject_ref` field is an application-domain first-link projection, not the future mutable persistence source of truth.
- `longitudinal_enrollment`, `longitudinal_observation_record`, and `temporal_analysis_submission` make the ADR-0008 Commons-owned Gyeot/TEPP orchestration boundary explicit. No TEPP analytical kernel is duplicated here.
- `integration_outbox`, `integration_delivery_attempt`, `integration_inbox`, and `integration_consumption` reflect `src/integration.rs` domain semantics. Outbox/inbox/delivery-attempt tables are on protected main; `integration_consumption` pending/processing/completed/quarantined persistence and expire-and-reclaim of a crashed processing claim exist only on this Active PR until merged.
- `integration_outbox`, `integration_delivery_attempt`, `integration_inbox`, and `integration_consumption` reflect `src/integration.rs` domain semantics. Outbox/inbox/delivery-attempt tables are on protected main. Exclusive outbox delivery-lease columns (`lease_worker_ref`, `lease_ref`, `lease_fencing_token`, `lease_expires_at`, `delivery_lease_generation`) and database-clock expiry recovery exist only on Active PR #60 until merged. `integration_consumption` pending/processing/completed/quarantined persistence is already on protected main.

This section is a maturity guard: a logical entity may be architecture-complete without being as-built database evidence.

Expand Down
157 changes: 157 additions & 0 deletions migrations/0013_outbox_delivery_lease.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
ALTER TABLE integration_outbox
ADD COLUMN IF NOT EXISTS lease_worker_ref TEXT;

ALTER TABLE integration_outbox
ADD COLUMN IF NOT EXISTS lease_ref TEXT;

ALTER TABLE integration_outbox
ADD COLUMN IF NOT EXISTS lease_fencing_token BIGINT;

ALTER TABLE integration_outbox
ADD COLUMN IF NOT EXISTS lease_expires_at_unix_ms BIGINT;

ALTER TABLE integration_outbox
ADD COLUMN IF NOT EXISTS delivery_lease_generation BIGINT NOT NULL DEFAULT 0;

DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_lease_worker_ref_format_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_lease_worker_ref_format_check
CHECK (
lease_worker_ref IS NULL
OR (
lease_worker_ref = btrim(lease_worker_ref)
AND lease_worker_ref <> ''
AND NOT (
lease_worker_ref ~ '[[:digit:]]'
AND lease_worker_ref ~ '^[[:digit:]+,.eE-]+$'
)
)
);
END IF;

IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_lease_ref_format_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_lease_ref_format_check
CHECK (
lease_ref IS NULL
OR (
lease_ref = btrim(lease_ref)
AND lease_ref <> ''
AND NOT (
lease_ref ~ '[[:digit:]]'
AND lease_ref ~ '^[[:digit:]+,.eE-]+$'
)
)
);
END IF;

IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_lease_fencing_token_positive_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_lease_fencing_token_positive_check
CHECK (lease_fencing_token IS NULL OR lease_fencing_token > 0);
END IF;

IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_lease_expiry_positive_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_lease_expiry_positive_check
CHECK (lease_expires_at_unix_ms IS NULL OR lease_expires_at_unix_ms > 0);
END IF;

IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_delivery_lease_generation_nonnegative_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_delivery_lease_generation_nonnegative_check
CHECK (delivery_lease_generation >= 0);
END IF;

IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_lease_presence_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_lease_presence_check
CHECK (
(lease_worker_ref IS NULL) = (lease_ref IS NULL)
AND (lease_worker_ref IS NULL) = (lease_fencing_token IS NULL)
AND (lease_worker_ref IS NULL) = (lease_expires_at_unix_ms IS NULL)
);
END IF;

IF NOT EXISTS (
SELECT 1
FROM pg_constraint AS constraint_row
JOIN pg_class AS relation_row
ON relation_row.oid = constraint_row.conrelid
JOIN pg_namespace AS namespace_row
ON namespace_row.oid = relation_row.relnamespace
WHERE constraint_row.conname = 'integration_outbox_fencing_generation_check'
AND relation_row.relname = 'integration_outbox'
AND namespace_row.nspname = current_schema()
) THEN
ALTER TABLE integration_outbox
ADD CONSTRAINT integration_outbox_fencing_generation_check
CHECK (
lease_fencing_token IS NULL
OR lease_fencing_token = delivery_lease_generation
);
END IF;
END
$$;
Loading
Loading