Repository navigation
DDD(organization_core): establish canonical Position ownership before service extraction #274
Description
Verified protected-truth gap
Fresh protected truth develop@eb9757f8649aaad026a9865508d9aad50c1a7a4f is internally inconsistent at the executable bounded-context boundary.
ARCHITECTURE.mdanddocs/TRD.mdassign Position truth toorganization_core, with a distinctorganization_coreservice/schema/role.people_coreowns Person/Employment/Assignment truth.- The protected
services/tree contains onlyjob-analysis-apiandpeople-api; there is no executableorganization-core-apiowner yet. services/people-api/src/orgmetra_people_api/mutations.pyexplicitly describes governed People employment, position, and assignment writes, definesPositionMutationCommand/PositionMutationResult, and exposesPeopleMutationPort.create_position(...)as the authoritative persistence port.
This is not a naming-only concern. Position status/version, Assignment seat allocation, and the ADR 0005 rule that Assignment days remain under an active|open Position currently participate in one PostgreSQL mutation boundary. Simply moving files or tables would either create cross-service SQL, break those invariants, or introduce distributed checked-versus-used gaps.
Canonical owner / stack discipline
Do not compete with active owner lanes:
- fix(people): protect governed mutation runtime types #64 remains the canonical hardening owner for the currently shipped People mutation boundary, including Position runtime/idempotency/authorization integrity. Preserve that valid delta through normal protected integration; do not delete or copy it into a new branch while mutable.
- feat(core): add governed Organization hierarchy-change review #96/feat(core): apply reviewed Organization hierarchy changes #119 retain valid Organization hierarchy review/application delta. Repair/restack them rather than closing them or treating an old base as canonical Organization truth.
- docs(architecture): propose fenced Position capacity ownership #275 is the dedicated Proposed-ADR owner lane for this decision. It does not authorize source/schema extraction.
docs/product-technical-gap-baseline.mdremains docs: maintain durable product-technical gap baseline #100 single-writer; hand off durable invariants there rather than editing the file from another lane.
Required ADR before source extraction
Record the Position/Assignment consistency boundary with problem, constraints, alternatives, selection/rejection rationale, failure modes, migration/recovery and exact executable evidence. At minimum compare:
A. Position and Assignment in one aggregate/service
Move Assignment ownership with Position into organization_core (or a renamed canonical structural-work bounded context). Strong local transactions are simple, but this contradicts the current people_core ownership of worker Assignment history and would move substantial HR employment truth.
B. Position in organization_core, Assignment in people_core, synchronous availability/status checks only
Keep current context map and call a released Position API/ACL before Assignment persistence. Reject as insufficient by itself: an availability/status read followed by a separate Assignment write has a TOCTOU window. Capacity can be consumed or Position eligibility can change again after the read and before People commits.
C. Position in organization_core, Assignment in people_core, Organization-owned capacity/eligibility reservation plus People-owned terminal attempt fence
Provisional selection for implementation. Make organization_core the sole writer for Position status/version and a versioned, idempotent PositionCapacityReservation. people_core owns Assignment history and one terminal outcome for each opaque assignment_attempt_id authorized by a fenced reservation.
Reservation lifecycle:
held: capacity-consuming, bounded, autonomously expirable only before a commit fence;commit_fenced: capacity-consuming, non-expiring unresolved debit that also fences theactive|openPosition eligibility needed by its effective interval;confirmed: capacity-consuming debit bound to terminal Peoplecommittedevidence and retaining required Position eligibility while effective;released/expired/cancelled: non-consuming states under their governed transition rules.
People attempt lifecycle is monotonic and terminal:
committed: Assignment and its exact terminal attempt evidence are written atomically in one People transaction;aborted: durable tombstone for the exact attempt/reservation/version/digest that prevents every delayed/retried create for that attempt from later committing.
A post-fence not found/absence observation is not release authority. It does not prove that a delayed request or in-flight transaction cannot commit later. Reconciliation must serialize create_assignment and terminalize_attempt by assignment_attempt_id: if create wins, return committed and Organization must confirm; if terminalization wins, record aborted and the late create cannot commit. Only that terminal abort receipt, or later governed Assignment correction/end evidence, may return post-fence capacity.
A second fence is required for Position eligibility. arm_commit_fence must validate the exact Position version and active|open coverage for the reservation interval under the same Position-root serialization used for capacity. While an effective reservation is commit_fenced or confirmed, an Organization status/version mutation that would make the interval ineligible cannot commit independently. It must fail closed or run an explicit governed coordination that first resolves/corrects the affected Assignment attempt/Assignment and reservation. People must not repair this with a second mutable Position read before write; that would recreate the same TOCTOU race.
This preserves current bounded-context truth while making the cross-context invariants explicit protocols rather than implicit SQL reads. Reservation/attempt receipts are versioned, digest-bound, idempotent and PII-minimized; no service queries the other's application tables.
D. Two-phase Assignment visibility
People persists a non-active pending_capacity Assignment, Organization confirms capacity, then People activates it. This avoids non-expiring unresolved Organization debits but adds another authoritative Assignment lifecycle plus activation/reconciliation failure windows. Keep it as the principal alternative while option C is Proposed; executable evidence may still overturn the provisional choice.
Implementation acceptance
- Add a canonical
organization_coreexecutable owner surface only after the ADR and protected prerequisite adoption; do not source-copy mutable fix(people): protect governed mutation runtime types #64/feat(core): add governed Organization hierarchy-change review #96/feat(core): apply reviewed Organization hierarchy changes #119 branches. - Move Position command/write authority, schema/role/migration ownership and generated API contract to that owner through ordinary forward migration preserving existing identities/history.
people_coremust not retain a second Position writer. Position/capacity interaction uses released/versioned ACL/API/event contracts; no cross-service application SQL.- Cross-context Assignment creation must be race-safe under real PostgreSQL/concurrent service execution. A read-only precheck is not sufficient evidence.
- A fenced reservation must bind one opaque
assignment_attempt_id; People must commit Assignment + terminalcommittedoutcome atomically, and terminalabortedmust fence every later create/retry for that attempt. - Explicitly execute the delayed-create-vs-terminalization race: exactly one People terminal outcome wins. Current absence/not-found, timeout, elapsed time, missing event, or stale read must never authorize release.
- Explicitly execute the fenced-create-vs-Position-status race: after a commit fence is issued from
active|openevidence, a concurrent Organization mutation may not make the reservation interval ineligible while People can still commit from that authentic receipt. Both reservation and eligibility-changing Position mutations serialize at the same Position root. - Preserve tenant isolation, purpose-bound PII/authorization, exact idempotency authority, bitemporal history, audit/outbox atomicity, correction/recovery and rollback.
- Preserve protected TRD retroactive-correction semantics: cutover must project every currently recorded-visible Assignment across the complete effective-time domain, including wholly past intervals. Superseded recorded-history versions remain People/audit provenance and must not be duplicated as simultaneously live capacity debits. Post-cutover retroactive writes must still see historical migrated capacity/eligibility and may not exceed
1.0000on a past slice. - Add contract/integration tests proving concurrent seat claims, pre-fence expiry, post-fence terminal outcome fencing, Position eligibility fencing, retry/replay, partial failure, message loss/duplication/reordering, correction/end, retroactive past-effective correction, and connection cleanup. Synthetic fixtures are integration evidence only; buyer-realistic claims require provenance-backed right-cleared data.
- Cutover must fence new capacity mutations, project complete currently authoritative Assignment occupancy and Position eligibility across past/present/future effective slices into the Organization reservation ledger with immutable manifest/digest evidence, prove equivalence, switch one writer at a time, and rehearse rollback without any dual-writer interval.
- Keep OpenAPI/AsyncAPI, ARCHITECTURE/TRD/DATA_MODEL/ERD/UML/TEST_STRATEGY/OPERABILITY/TRACEABILITY/CHANGELOG and the canonical baseline code-current through their single-writer owner paths.
- Final protected candidate requires 100% owned statement/branch/doc coverage where tooling exposes it, isolated PostgreSQL evidence, security gates, required review governance, and normal non-bypass integration.
Do not solve this by renaming people-api, adding a compatibility copy/view, cross-service SQL, broad database privileges, shared mutable branch dependency, treating timeout/retry/absence/current-status reads as correctness, automatically expiring a post-fence capacity debit, allowing Position eligibility to change independently underneath an authentic fence, or dropping past-effective currently authoritative Assignment occupancy merely because cutover happens after its effective interval.
Activity
seonghobae commentedon Sep 7, 2026
Protected ADR reconciliation evidence: #274 must preserve, not replace, the semantic contracts already Accepted in ADR 0004/0005.
active|openPosition versions and visible allocation for one Position seat to remain ≤1.0000; it also records the current same-context People mutation contract for POST employment/position/assignment.The new ADR therefore has a narrower decision: which bounded context owns Position write/capacity authority once the accepted modular-service architecture is made executable, and how Assignment preserves ADR 0004/0005 invariants across that context boundary without cross-service SQL or a TOCTOU read-check-write. Keep the new ADR Proposed until #64 and the Organization prerequisite stack are protected truth and the chosen protocol has executable concurrency/failure evidence. Do not mark ADR 0004/0005 Superseded merely because ownership is extracted; amend/supersede only the portions that explicitly imply a co-located mutation boundary.
seonghobae commentedon Sep 7, 2026
Prerequisite progress: Organization hierarchy-review owner #96 is no longer on its stale/conflicted parent. It ordinary-merged protected
develop@eb9757f8649aaad026a9865508d9aad50c1a7a4finto the existing feature history and is now exact54dd31bc240624a3fdb05f6df217eeaad1f92709, open · Draft · mechanically mergeable. Protected #161 workflow consolidation was adopted rather than reversed: the obsolete per-feature leaf is absent, and the package's exact installed-artifact contract now executes from canonical Foundation CI with a regression against leaf resurrection.This advances #274's Organization prerequisite but does not authorize Position extraction yet. #96 still needs fresh exact-head gates and normal protected integration; #119 remains a stale-base Draft descendant until that happens. Keep the Position/capacity-reservation ADR Proposed and continue to preserve ADR 0004/0005 semantics without introducing a second Position writer or cross-service SQL.
seonghobae commentedon Sep 7, 2026
Fresh DDD/transaction-design refinement against protected
develop@eb9757f8649aaad026a9865508d9aad50c1a7a4fand the current #64 successor. This advances #274's owner decision without starting a competing extraction branch before prerequisites are protected truth.Protected schema currently makes
position_recordthe stable Position identity,position_record_versionthe bitemporal status history, andassignment_recordcarryposition_record_id, an effective interval andallocation_ratio; the row-level DB check only constrains one ratio to(0, 1]. Current #64 People mutation code therefore locks the Position stable root, reads overlapping Employment/Position/Assignment evidence, and lets the HRIS kernel enforce the cross-row allocation invariant before inserting Assignment. In other words, the shipped invariant is not safely extractable asGET position availability -> INSERT assignment: that would split checked capacity from used capacity and reintroduce TOCTOU.For ADR work, narrow provisional option C to this executable contract:
organization_coreownsPositionand aPositionCapacityReservationaggregate. A reservation is a capacity debit, not an advisory availability result.position_record_id, requestedallocation_ratio, effective interval, assignment-intent/reference, exact Position version/evidence, idempotency key + semantic command digest, lifecycle state and immutable audit/outbox evidence. No Person/Employment payload is copied into this aggregate.organization_core, then validating all capacity-debit reservations whose effective intervals overlap the candidate interval. The invariant issum(allocation_ratio) <= 1.0000for every overlapping valid-time slice.heldandconfirmedreservations consume capacity;released/expired/cancelledreservations do not.people_coremay create Assignment only from a released/versioned reservation receipt bound to the same tenant, Position, allocation ratio and effective interval. It must not query organization tables or infer capacity from a stale read model.Cutover also needs a writer fence, not dual ownership. Proposed migration acceptance: temporarily fence new Assignment capacity mutations; project current and future-effective Assignment occupancy into the new reservation ledger with an immutable migration manifest/digest; prove the projected sums and tenant/Position/effective-interval bindings; switch Position/capacity write authority to
organization_core; switchpeople_coreto the released reservation contract; then remove the fence. Rollback must be defined before un-fencing and must not re-enable two Position/capacity writers.Test-first RED/GREEN for the successor should include at least: two concurrent 0.6 reservations for the same Position/overlapping interval (exactly one accepted); non-overlapping future intervals; exact replay vs same-key/different-digest; crash after reserve; crash after Assignment commit before confirm; release/expiry/reconciliation; Position version/status change while a reservation is in flight; migration projection equivalence; and a real two-service/PostgreSQL acceptance proving no cross-service SQL and no capacity >1.0000. Apply the repository buyer-path p95<=20ms rule to reserve/confirm/release with real concurrency rather than warm-cache-only measurements.
Prerequisite order remains unchanged: #64 protected integration first, then #96/#119 Organization truth in their canonical order, then ADR/source extraction. Do not copy mutable #64/#96 code into a new service while those owners are still Draft.
seonghobae commentedon Sep 7, 2026
Fresh failure-mode review tightens provisional option C before any source extraction.
The previously sketched
reserve -> People Assignment commit -> confirmflow is unsafe if the reservation remains an ordinary expirable hold. A crash after the People transaction commits but before Organization receivesconfirmcan let that hold expire while the durable Assignment remains visible; a later reservation can then consume the released capacity and overbook the Position. Timeout/TTL is therefore not a correctness mechanism after durable Assignment commit.Proposed ADR must distinguish at least these states/authorities:
held: bounded/expirable capacity hold; no Assignment may yet become durable-visible from this state alone.commit_fenced(name provisional): Organization has durably converted the hold into a non-expiring unresolved capacity debit for one tenant/Position/allocation/effective interval + semantic command digest. This transition is versioned/idempotent and serialized at the Position capacity aggregate.confirmed: the debit is bound to an authoritative People Assignment receipt/reference.released: only an uncommitted/aborted hold or a commit-fenced debit whose absence/abort has been authoritatively established may return capacity.Safe choreography is therefore
reserve -> arm commit fence -> People Assignment transaction -> confirm, not a TTL-only saga. Once commit-fenced, autonomous expiry is forbidden because absence of a confirm message does not prove absence of a committed Assignment. Recovery must use a released/versioned People outcome API or immutable Assignment committed/aborted event/outbox receipt to decide confirm vs release. A timeout may schedule reconciliation; it may not release capacity by itself.The main alternative worth recording in ADR is a two-phase Assignment visibility model: People persists
pending_capacityfirst, Organization confirms capacity, then People activates the Assignment. That avoids non-expiring unresolved debits but adds Assignment lifecycle/state semantics and another failure/reconciliation edge. The ADR should compare this explicitly against the commit-fence design before moving from Proposed.New executable acceptance for the eventual owner stack:
heldbut before commit fence -> expiry may reclaim capacity.1.0000for overlapping valid-time intervals.This is consistent with the original Saga literature's requirement for explicit compensating transactions rather than treating retries/timeouts as atomicity, and with PostgreSQL 18
READ COMMITTED, where successive statements may observe different committed snapshots and explicit conflict control remains necessary for application invariants. Design inputs: Garcia-Molina & Salem (1987), Sagas, ACM SIGMOD Record, 16(3), 249–259, DOI 10.1145/38713.38742 (ACM page also resolves DOI 10.1145/38714.38742); PostgreSQL 18, §13.2 Transaction Isolation. These are architectural inputs, not evidence that the distributed protocol is correct; correctness still requires the executable interleavings above.Prerequisite order remains unchanged: preserve #64 current People truth through normal integration, then #96/#119 Organization truth, then Proposed ADR/source extraction. No cross-service SQL, dual Position writer, mutable branch source copy, timeout-as-correctness, or automatic expiry of a post-fence capacity debit.
seonghobae commentedon Sep 7, 2026
Reference correction for the preceding architecture note: cite the conference paper as Garcia-Molina, H., & Salem, K. (1987). Sagas. Proceedings of the 1987 ACM SIGMOD International Conference on Management of Data, 249–259. https://doi.org/10.1145/38713.38742.
10.1145/38714is the SIGMOD Record issue DOI, not the conference-paper DOI. No protocol/acceptance semantics change.docs(architecture): propose fenced Position capacity ownership
seonghobae commentedon Sep 7, 2026
#275 now carries the required Proposed ADR as a dedicated docs-only owner lane, current exact head
7556b40e8e1fb66e4ed2ef5e9420b1edf4546f0a, direct from protecteddevelop@eb9757f8649aaad026a9865508d9aad50c1a7a4f.The ADR folds the latest failure-mode refinement into the durable decision record rather than leaving the older TTL sketch as implementation authority:
heldconsumes capacity and alone may expire autonomously;commit_fencedis a non-expiring unresolved debit;confirmedbinds authoritative People Assignment evidence. Safe choreography isreserve -> arm_commit_fence -> People Assignment transaction -> confirm. Timeout, missing delivery, elapsed time, or one stale read can schedule reconciliation but cannot release a post-fence debit. A post-fence release requires authoritative People abort/absence or governed correction/end evidence.Ordinary forward
7556b40e...also removes a Ubiquitous Language ambiguity: reservation lifecyclereleasedis now kept distinct from a published/versioned cross-context contract. There is no protocol-semantic change.#275 keeps the two-phase
pending_capacityAssignment model as the principal alternative, preserves ADR 0004/0005 semantics, defines the no-dual-writer cutover/rollback fence, and adds APA 7 doctoring plus explicit traceability/evidence classifications. It intentionally contains no service/schema/source extraction and does not close #274. Causal prerequisite order remains #64 -> #96 -> #119 protected truth before executable extraction.seonghobae commentedon Sep 7, 2026
Fresh #275 design review found one more causal boundary that is now repaired in ordinary-forward docs commits: do not perform People/network I/O while the Organization Position-root PostgreSQL transaction/row lock is open. PostgreSQL row locks persist until transaction end, so
change_position_status_or_versionmust not hold the Position lock while asking People to terminalize/correct an Assignment attempt. The local Organization transaction now only evaluates authoritative local Position/reservation state; an incompatible status/version change fails closed with durable conflict/audit evidence and ends. A separate governed workflow resolves/terminalizes People state outside any Position lock, then applies the resulting durable terminal receipt in a new idempotent Organization transaction and retries the exact status/version command against current version/digest evidence. Crash during external coordination leaves the original Position eligibility + reservation fence authoritative. Add executable acceptance that injects remote delay/failure and proves it cannot extend Position row-lock lifetime or block unrelated local work beyond the bounded local transaction. This supplements the canonical issue body; it does not authorize source extraction or ADR Accepted.seonghobae commentedon Sep 7, 2026
#275 advanced by ordinary-forward commits to exact
30c8c84f368996e7b07c2dc6a276913a915381a9; #274 remains open and ADR 0274 remains Proposed.Fresh review produced two material migration findings and one repository-specific self-review finding, all repaired in the documentation owner before runtime extraction:
pg_stat_activityobservation are not drain evidence.assignment_record_idto a versioned opaque migrationassignment_attempt_id, reservation identity, terminal Peoplecommittedevidence, andconfirmedreservation. Replaying the same authoritative snapshot must reproduce identical identifiers/digests.validate_assignment_position_coverageaccepts a union of multiple visible staffable Position versions. ADR 0274 therefore now modelsPositionEligibilityCoverageas a normalized ordered set of exact version/interval slices whose union covers the entire Assignment interval; migration/runtime receipts cannot substitute one arbitrary Position version.Executable acceptance now explicitly requires a held pre-fence legacy transaction interleaving, rejection of post-fence admission, snapshot blocking until terminal commit/rollback, deterministic projection replay, multi-version eligibility coverage fencing, migration equivalence, and single-writer rollback. No source/schema extraction or cross-service SQL was introduced.
All four current CodeRabbit inline findings were verified/repaired/replied to and the review-thread API now reports them resolved/outdated. Current-head required workflows are fresh/non-terminal except dynamic Code Quality SUCCESS, so there is no merge/release claim yet.
seonghobae commentedon Sep 7, 2026
#275 current exact head is now
4f7f2babb0b7462cd08050824e799cfcc9ba5707; #274 remains open and ADR 0274 remains Proposed.One additional migration-integrity finding was found during self-review after the earlier cutover fixes: a drained authoritative legacy snapshot can itself be invalid or unrepresentable under the target Position-capacity invariants. The ADR/traceability now fail closed on cases such as overlapping allocation above
1.0000, incomplete staffable Position coverage, or ambiguous tenant/identity mapping. Cutover emits deterministic immutable discrepancy evidence, performs no authority switch, and explicitly forbids dropping, clamping, splitting, rounding, synthesizing, or silently rewriting source facts to manufacture target validity. Governed correction must occur under the still-authoritative legacy owner, followed by a complete projection rerun.Acceptance now includes deterministic replay of this failure path: identical invalid source snapshots must yield identical discrepancy identity/digest and leave Organization authority disabled. This is still documentation/acceptance design only; no Position/Assignment source/schema extraction or cross-service SQL was introduced. Current-head required workflows are fresh and non-terminal, so there is no merge/release claim.
seonghobae commentedon Sep 7, 2026
Fresh #275 self-review repaired one additional distributed-capacity gap on exact successor
51fad427407263ae9dfa7373b335a3067fe20331.A
confirmedOrganization debit cannot be shrunk/released from a mutable People read after Assignment correction/end. Every capacity-affecting create/revise/move/end now has its own terminal PeopleAssignmentAttemptOutcomebound to the exact prior Assignment version, exact prior reservation version/digest set, resulting Assignment facts and any new delta fences. Capacity-increasing revisions fence the positive Position/time-slice delta before People commits; a Position move fences target occupancy before People commit and confirms target before source release. Decrease/shortening/end may commit in People first, but old confirmed capacity stays reserved until the immutable terminal revision receipt is applied. Crash/reordering therefore biases toward temporary over-reservation rather than under-reservation/overbooking.ADR and traceability were updated in ordinary forward commits
aea8542d...and51fad427...; production source/schema remain untouched and ADR 0274 remains Proposed. Acceptance now explicitly requires stale/duplicate/out-of-order revision receipts, increase/extension/move crash windows, and exact prior-version/digest CAS-style evidence before any confirmed capacity is reduced.seonghobae commentedon Sep 7, 2026
Second #275 self-review found and repaired a separate People-side lineage race after the confirmed-capacity work.
Distinct revision attempts can both be prepared against Assignment V1 and obtain individually valid Organization delta fences. Serializing only their different
assignment_attempt_idvalues would not prevent both from committing and forking the Assignment lineage. ADR 0274 now requires every capacity-affecting revision/end to serialize the stable Assignment root and compare-and-set the exact expected prior Assignment version inside the People transaction. At most one attempt can advance V1; a stale-predecessor loser is terminally non-committing, and its unused Organization delta fences remain consumed until the exact abort/conflict receipt is applied. Retrying the user intent requires a new attempt bound to the new authoritative predecessor.Ordinary forward repair commits: ADR
d4412fb8..., traceability488d67b2..., doctoringdb016f47.... No runtime/schema extraction was started; ADR remains Proposed.seonghobae commentedon Sep 7, 2026
#275의 current Proposed ADR review에서 구현 전 고정해야 할 두 가지 추가 contract gap을 검증하고 owner lane에 반영했습니다. #274는 계속 open이며 source/schema extraction은 아직 금지합니다.
reserve의 tenant-qualified idempotency record와 semantic digest는 exactassignment_attempt_id를 포함해야 합니다. 같은 key/digest 의미를 다른 attempt ID로 alias하면 안 됩니다.arm_commit_fence도 exact expected reservation version + attempt ID + digest를 검증/CAS하고 mismatch는 fail closed여야 합니다.receipt_id, issuer/audience, tenant, contract version, owner authority/rollback epoch, exact attempt, semantic digest, target reservation/Assignment identity/version/digest와 terminal fence disposition(consumed/unused_releasable)을 결합해야 합니다. Equivalent replay만 idempotent하며 forged/wrong-audience/cross-tenant/stale-epoch/same-ID-different-payload/wrong-target-or-version-or-disposition은 domain mutation 전에 fail closed합니다. Confirm/release/apply-revision은 exact target reservation version CAS로만 적용합니다.Executable acceptance에 same-key/different-attempt rejection, forged signature/issuer/audience/tenant/epoch/target rejection, equivalent replay vs contradictory replay, concurrent revision winner/loser receipt의 duplicate/out-of-order delivery와 per-fence disposition/version CAS, cutover/rollback 이후 superseded epoch receipt rejection을 포함했습니다.
Current #275 exact head is
93aa3d9c14ce24a4a7d822a3967b27348e0f7f38; 이 head는 문서/traceability 계약만 강화하며 runtime owner/API/schema/security implementation이나 distributed correctness를 주장하지 않습니다. Causal order#64 -> #96 -> #119 -> #274 executable owner stack -> drained/fenced migration은 유지합니다.seonghobae commentedon Sep 8, 2026
Fresh rollback/epoch-transition RED against current #275 ADR
c66372247dafe3a12c6c109c506e6ab336ad9008: the documented transaction-drain barrier is necessary but not sufficient once the new cross-context protocol is live.Counterexample: Organization has a valid epoch-E
commit_fencedreservation R for attempt A; People has received or may later receive the authentic E receipt, but A is not terminal yet. Rollback fences new admissions and drains database transactions. Because no People transaction need be open at that instant, the current drain can close. The rollback then projects authoritative Assignment history, increments the authority/rollback epoch, and rejects E receipts. R is not a confirmed Assignment fact, so it has no lossless legacy projection. A delayed People create can either (a) commit under E before its consumer authority is atomically advanced, after the rollback snapshot, or (b) be rejected after the epoch advance while leaving R/attempt recovery evidence stranded. Merely rejecting stale receipts therefore does not itself create a safe authority handoff.Required repair before ADR 0274 can return to review-ready: add a protocol-quiescence barrier in addition to transaction drain. Before any cutover/rollback epoch becomes superseded, freeze creation of new
held/commit_fencedattempts under that epoch; reclaim/release terminally safeheldstate; drive everycommit_fencedattempt to an authenticated terminal People outcome and apply it so no unresolved post-fence debit remains; apply every already-committed revision/confirm/release receipt needed to make Organization ledger and People Assignment history equivalent; then take the projection/high-water snapshot and switch epochs.confirmedoccupancy may be projected because it is bound to terminal Assignment evidence. If the design chooses to carry unresolved protocol state across an epoch instead, it needs an explicit successor-epoch re-attestation/handoff manifest with exactly-once authority semantics; silent stale-epoch rejection is insufficient.Executable acceptance: hold an epoch-E
commit_fencedattempt with no terminal People outcome, start rollback, and prove the epoch switch cannot complete. Then race a delayedcreate_assignment/terminalization during quiescence and prove exactly one terminal outcome is applied before the snapshot. After E is superseded, no E receipt can create new Assignment/capacity state, and no unresolved E debit/attempt remains orphaned. This is a correctness/recovery finding, not a request to weaken stale-epoch rejection or add a distributed lock.seonghobae commentedon Sep 8, 2026
Repair landed ordinary-forward in the dedicated #275 docs owner: ADR commit
df20b6ef9121f1a9129a5d37855931eb5db65d62, followed by TRACEABILITY commitb05210f60479dc71f4e26b56416c82d037143f99(current exact #275 head). #275 remains Draft and ADR 0274 remains Proposed.The repaired contract distinguishes initial legacy cutover from retirement of an already-live cross-context authority epoch. Initial cutover still uses the durable legacy transaction-admission drain before snapshot. A later rollback/epoch migration now adds two-sided protocol quiescence: Organization stops issuing retiring-epoch reservations/fences; People stops new receipt-authorized admissions and drains every already-admitted transaction; then every
commit_fencedattempt reaches one terminal People outcome and its exact disposition is applied, all pending capacity-changing terminal receipts are settled, and a deterministic quiescence manifest proves zero unresolved fences/unapplied outcomes plus Organization/People equivalence before snapshot and epoch increment. Historical old-epoch receipts remain verifiable for audit but cannot authorize new writes after rollover.TRACEABILITY now includes the forced RED/GREEN interleaving: hold an epoch-E fenced attempt without terminal People outcome, start rollback, race delayed/not-yet-admitted and already-admitted People mutations, and prove the authority switch cannot snapshot/supersede E until admissions drain and exact terminal settlement is complete. This repairs the recovery design only; it does not authorize source/schema extraction, ADR Accepted, or predecessor bypass.
seonghobae commentedon Sep 8, 2026
#275 owner-path update: exact
b05210f60479dc71f4e26b56416c82d037143f99on protecteddevelop@eb9757f8649aaad026a9865508d9aad50c1a7a4fis now technically GREEN on Foundation34178933576, Security34178933587, SAST34178933583, CodeQL34178933569, Required OpenCode34178932322, Required Noema34178932307, Strix34178932262, and merge scheduler34178932275. All returned inline threads are resolved/outdated. I advanced #275 from Draft to Ready for Review to admit fresh independent review; this does not Accept ADR 0274 or authorize source/schema extraction. A qualifying independent human approval is still absent, so no normal protected merge was attempted. The implementation causal order remains #64 protected integration -> #96 protected integration -> #119 protected adoption/integration -> executable two-service/PostgreSQL RED/GREEN for this protocol -> writer-fenced cutover/rollback. No self/model approval, admin bypass, no-op retrigger, force update, mutable-owner source copy, or cross-service SQL was used.seonghobae commentedon Sep 8, 2026
#275 fresh current-head review에서 두 recovery gap을 추가로 검증해 ordinary-forward로 수리했습니다.
c42ad108은 live epoch retirement를 durableEpochAuthorityTransition으로 단일화합니다. 정확한 source(authority_epoch, rollback_epoch)pair CAS가 transition winner를 정하고, transition 이전에 Organization에 admission된 E reservation/fence transaction은 transaction-lifetime receipt를 가져 모두 commit/rollback으로 terminal이 되기 전 fence settlement·quiescence manifest·snapshot이 금지됩니다.1307ecdtraceability는 (1) pre-drain Organization transaction을 열린 채 rollover해 snapshot이 차단되고 그 commit state가 settlement에 포함되는 race, (2) 동일 source epoch pair에서 두 rollover controller를 경쟁시켜 active transition/activation receipt/target writer가 각각 하나만 남는 CAS race를 acceptance로 추가했습니다. 동일 target/digest replay는 winner를 idempotently 관측하고 contradictory target/digest는 fresh authority에서 재시도하거나 fail closed합니다. #275는 이 finding 때문에 Draft로 낮췄고 새 exact head1307ecd95718290bde40571bcff240c893c14227의 fresh required evidence를 다시 받는 중입니다. 이 invariant는 executable owner stack과 migration/rollback acceptance에 승계하되 ADR 0274는 계속 Proposed이며 source extraction은 승인하지 않습니다.seonghobae commentedon Sep 8, 2026
#275에서 live epoch rollover recovery invariant를 한 단계 더 좁혔습니다.
abortedEpochAuthorityTransition은 source(authority_epoch, rollback_epoch)의 uniqueness를 해제하지 않고 같은 transition identity가 계속 그 source pair의 유일 lineage로 남아야 합니다. 동등 retry만 expected transition version + still-current source pair + no activation receipt + byte-equivalent target/digest를 검증해 같은 row를aborted -> draining으로 CAS하고retry_generation을 증가시킵니다. 이전 generation의 Organization/People admission receipts, discrepancy/manifest candidate는 immutable audit evidence로만 남기며 새 generation의 drain/quiescence proof로 재사용하지 않습니다. 두 equivalent retry가 경쟁하면 한 CAS만 다음 generation을 열고 다른 쪽은 동일 winner를 관측해야 하며, 다른 target/digest·stale source·existing activation은 fail closed입니다. Canonical implementation acceptance에는 abort→retry, concurrent retry, generation-evidence isolation을 포함해 one source pair→one transition lineage→at most one target writer를 검증해 주세요. Proposed ADR repair:3f814b71, traceability:f7ef4f4f.seonghobae commentedon Sep 8, 2026
#275 current repair handoff (head
d6006e6634e1cdbd7824269e4d9ba6708d59a166, ADR remains Proposed; no source extraction): fresh review exposed four additional protocol P1s plus one recovery finding, all verified against Accepted ADR 0004/current ADR and repaired ordinary-forward.Canonical implementation acceptance to inherit:
key_id/key version for the claimed issuer + authority/rollback epoch +domain_transitionpurpose, not merely verify against a historical key set. Retired keys may remain audit-verifiable but cannot authorize current writes.abortedis permitted only after both captured admission drains are terminal and all capacity-changing settlement obligations are applied. Abort/retry cannot carry an unresolved old-generation mutation past snapshot/activation.d6006e66TRACEABILITY now contains concrete real-concurrency/security/recovery RED/GREEN interleavings for each rule. #274 should implement these only after the existing #64 -> #96 -> #119 protected prerequisite order; do not copy #275 documentation as runtime proof.seonghobae commentedon Sep 8, 2026
Current owner-path checkpoint: #275 exact
d6006e6634e1cdbd7824269e4d9ba6708d59a166over protecteddevelop@eb9757f8649aaad026a9865508d9aad50c1a7a4fnow has terminal SUCCESS for Foundation plus all seven protected required workflow identities and has been moved from Draft to Ready for Review. All visible inline findings are resolved/outdated. This remains Proposed ADR evidence only: there is still no qualifying independentAPPROVEDreview, and the executable two-service/PostgreSQL protocol, real-concurrency p95 evidence, prerequisite owner integrations (#64/#96/#119), writer-fenced migration, and normal protected integration remain mandatory before ADR Accepted or Position source/schema extraction. No predecessor review, model review, admin bypass, mutable owner source, or docs-only Strix parent SUCCESS is promoted beyond its actual evidence scope.seonghobae commentedon Sep 8, 2026
#275 fresh self-review found a retirement-liveness invariant that belongs in the canonical implementation acceptance.
When rollover closes ordinary epoch-E Organization/People business admission, it must not make already-captured E obligations impossible to settle. An unresolved
commit_fencedattempt can still require Peopleterminalize_attemptfollowed by exact Organization confirm/release/apply-revision/settlement transactions. If those writes must use the closed ordinary E admission path, protocol quiescence deadlocks.Durable owner requirement for implementation:
terminalize_attempt, exact receipt application/confirm/release/settlement);quiesced, close reconciliation admission, capture its high-water, drain every receipt through that mark, re-read the authoritative work ledger empty, and re-prove Organization/People equivalence;The executable RED/GREEN must specifically start with E ordinary business admission closed plus one unresolved fenced attempt, prove ordinary terminalization is rejected, prove the same exact obligation can settle through transition-scoped reconciliation, prove new E business work remains rejected, and hold one recovery transaction open to show snapshot/activation cannot pass until the recovery high-water drain closes.
ADR/TRACEABILITY owner #275 repair:
7434f83f11aae69c1360babffeb87c602cc05d41->e97067631c262515c0a9580a8087b95c6a42c1af. ADR remains Proposed; this is implementation acceptance, not extraction authorization.seonghobae commentedon Sep 8, 2026
#275에서 새 recovery RED를 확인해 canonical acceptance를 보강했습니다. 이전 ADR은 source-pair transition 생성과 Organization/People E business-admission close/high-water capture를 한 번의 atomic operation처럼 기술했는데, 두 bounded-context owner가 분리된 뒤에는 이를 단일 local transaction으로 구현할 수 없어 distributed-ACID 가정을 암묵적으로 도입합니다.
현재 #275 exact
9d8d19b4e9c5d4964f9cef2a1b6958729ea31198에서는 transition lineage CAS는 source pair/target intent만 소유하고, Organization/People이 각각 owner-local gate CAS로 E admission을 닫고 local high-water를 capture한 뒤 authenticated closure receipt를 T/G/(E,R)에 결합하도록 수정했습니다. 두 closure receipt가 모두 존재하기 전에는 recovery freeze/quiesce/abort/snapshot/activation 금지입니다. 먼저 닫힌 owner는 즉시 새 E admission을 거절하고, 늦게 닫히는 owner의 gap 중 admission은 그 owner의 later high-water에 포함되어 drain됩니다. 구현 RED/GREEN에는 staggered close gap, missing closure receipt, gap-admitted transaction drain, owner-local CAS idempotency/contradiction을 포함해 주세요. Cross-service SQL이나 두 owner DB를 묶는 transaction으로 구현하면 안 됩니다.seonghobae commentedon Sep 8, 2026
#275 exact
3c0ff9668b2c6ce10f9ed221722cf522f211a26e에서 retry evidence 경계를 한 번 더 분리했습니다. 구현 acceptance에서는 Organization/People의 source-epoch admission gate가 transition lineage 안에서 한 번만 닫히고, 그때 발급된 owner-local closure receipt/high-water를 abort/retry 이후에도 같은 lineage-stable source-boundary evidence로 유지해야 합니다.closed_at_retry_generation은 provenance이고 새 retry마다 closure receipt를 다시 발급하라는 뜻이 아닙니다. Source business admission은 retry에서 재개방하지 않습니다.반대로 generation마다 새로 증명해야 하는 것은 ordinary-drain, bounded reconciliation, capacity settlement, discrepancy/work-ledger 재평가, quiescence입니다. G의 drain/reconciliation/settlement/quiescence artifact는 immutable audit evidence로 남기되 G+1 acceptance를 만족시키지 못하며, G+1은 authoritative owner ledgers/state에서 fresh proof를 다시 계산해야 합니다. 따라서 구현 RED에는
abort -> retry에서 (a) owner closure receipt/high-water identity가 그대로 유지되고, (b) source admission이 닫힌 채이며, (c) generation-scoped proof만 새 identity/digest로 재계산되고, (d) old generation proof를 주입하면 fail closed되는 경우를 함께 넣어 주세요. ADR은 계속 Proposed이고 이 handoff는 Position source/schema extraction 승인이 아닙니다.seonghobae commentedon Sep 8, 2026
Fresh recovery/operability RED from ADR 0274 exact
3c0ff9668b2c6ce10f9ed221722cf522f211a26e: the current retry-safeabortedpath intentionally leaves both source(authority_epoch=E, rollback_epoch=R)business-admission gates closed and allows only byte-equivalent retry of the abandoned target intent. That is safe against source-pair ABA during retry, but it leaves no governed path for an operator to abandon the rollout permanently and restore the still-current source writer. Reopening the old(E,R)gates directly is not safe because new(E,R)admissions would occur after the immutable closure high-water and could make old closure evidence appear reusable for a later retirement.Canonical implementation acceptance needs a distinct source-resume cancellation path before Position extraction:
(E,R) -> (E,R+1)for the same source writer, rather than reopening(E,R);(E,R)business/closure/quiescence evidence becomes audit-only and cannot authorize a new write or later retirement proof;source_resumedor canonical equivalent) with an immutable resume receipt; it cannot later retry/activate the abandoned target;#275 was returned to Draft rather than treating its exact-head workflow GREEN as design correctness. ADR remains Proposed; #64/#96/#119 and real two-service/PostgreSQL evidence remain prerequisites.
seonghobae commentedon Sep 8, 2026
#275 source-resume RED를 canonical implementation acceptance로 승계합니다. The decision is cleanly numbered as Proposed ADR 0275 at current #275 exact head
082a4e99785758a394e72ab3b784833dc4a05b4f; the temporary second file claiming ADR number 0274 was removed. Source:docs/adr/0275-governed-source-resume-after-permanent-rollover-cancellation.md,docs/traceability/position-capacity-source-resume-cancellation.md, and the APA 7 doctoring indocs/doctoring/position-capacity-reservation-references.md.Durable invariant: retryable
abortedkeeps the old(E,R)business gates closed and same-lineage retry semantics. A governed permanent target abandonment must not reopen(E,R). It may restore the same source writer only after both owner ordinary drains, reconciliation drain, all capacity-changing settlement, and fresh equivalence/quiescence checks are complete and no activation receipt exists. Organization and People independently CAS-adopt a fresh source pair(E,R+1)while old gates remain closed, emit authenticated adoption receipts, and reopen business admission only after both receipts are mutually consistent and one transition-version CAS records terminalsource_resumed. The abandoned lineage can never retry/activate after that; future migration starts a new lineage from(E,R+1), and all(E,R)closure/quiescence evidence is audit-only for future authority.Implementation RED/GREEN must include retry-vs-permanent-cancel concurrency, one-owner-adopted/other-not fail-closed behavior, old
(E,R)admission rejection, stale closure-proof rejection for(E,R+1), crashes after one/both adoptions and after terminal resume, key-purpose/receipt negative cases, zero target activation/dual writer, preservation of settled HR truth, and restored source mutation availability. Do not implement this as cross-service SQL/2PC, direct gate reopen, a second transition for(E,R), or target activation followed by artificial rollback. ADR 0274 and ADR 0275 remain Proposed; #275 remains Draft and does not authorize Position source/schema extraction.seonghobae commentedon Sep 8, 2026
#275 fresh recovery review exposed one additional correctness boundary for the canonical implementation owner. Permanent cancellation must linearize on the durable
EpochAuthorityTransitionbefore either owner installs fresh source authority: retry and cancellation race the same expected transition version asaborted -> drainingversusaborted -> source_resuming. Only a winningsource_resumingCAS may authorize Organization/People owner-local(E,R+1)adoption. If retry wins, adoption count must be zero; if cancellation wins, retry and target activation fail closed.Once either owner has adopted
(E,R+1), recovery is roll-forward only through that exact immutable source-resume intent. Do not permit return toaborted/draining, owner reversion to(E,R), revival of the abandoned target, a second(E,R)lineage, or a successor transition sourced from partially finalized(E,R+1). After both authenticated adoption receipts are durable, CASsource_resuming -> source_resumed, then let each owner open only its already-installed fresh-pair business gate after verifying the terminal resume receipt. A later transition from(E,R+1)is admissible only after terminalsource_resumedand both fresh-pair source gates are open, and it must capture new closure receipts/high-water marks.Implementation acceptance should also reject cancellation before
source_resumingwhen actor/purpose/policy version/transition pair/decision digest is stale or mismatched; crash aftersource_resumingbut before first adoption must replay the same intent rather than create a second intent or fall back to retry. #275 documents this in ordinary-forwarde0e50fa5...and executable acceptance planning920463613...; ADRs remain Proposed and this handoff does not authorize Position source/schema extraction.seonghobae commentedon Sep 8, 2026
#275 current-head recovery review exposed one more implementation acceptance invariant for the canonical Position/Assignment protocol owner.
SourceAuthorityResumeReceiptis not merely audit metadata: it is the terminal evidence that permits Organization and People to reopen HR business admission after permanent cancellation. Treat it as durable write-authority evidence. The canonical implementation must therefore use a signed/versioned envelope bound to the exact transition identity and pre/post version, terminalsource_resumedstate, source writer, old(E,R)and fresh(E,R+1)pairs, source-resume intent, cancellation decision/policy evidence, both exact owner adoption receipt IDs/digests, tenant/audiences where applicable, signing-key identity/version,domain_transitionpurpose, and canonical payload digest. A retired oraudit_onlykey cannot authorize the fresh pair.The owner-local gate-open CAS must also be pinned to the exact authority/gate version installed by that owner's adoption receipt plus the terminal resume receipt identity/digest. Replay must not refresh the expected gate version from current owner state. Required RED/GREEN: complete a valid resume and open
(E,R+1); start a later successor transition that closes/advances that gate; then replay the old otherwise-validSourceAuthorityResumeReceipt. The replay must fail without reopening either gate. Also reject forged/unsigned, wrong-audience, stale-transition, wrong-pair, wrong-purpose, mismatched-digest, and historical-audit_only-key resume receipts before owner gate mutation.#275 records this in ADR/traceability via ordinary-forward
172e6664...andb36f3930.... This is executable implementation acceptance only; it does not authorize Position source/schema extraction, bypass #64/#96/#119 protected prerequisites, or permit cross-service SQL.