E1-11 v2: vehicle outbox payload entityType fix with review findings - #48
Merged
Conversation
…dary fix Re-apply all E1-11 work after the revert of PR #45 with six owner-review findings fixed: (1) OutboxCoalescenceParityTest uses real SqlDelightFuelEntryRepository; (2) both coalescence orders tested; (3) backlog scope includes integration-boundary adaptation; (4) handoff Files Changed complete; (5) ADR-0111 stale no-gate corrected; (6) checkpoint refreshed. The Firestore boundary excludes entityType from FirestoreWrite and validates it against EntitySnapshot.entityType. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidru85
had a problem deploying
to
cloud-runtime-verification
September 2, 2026 09:56 — with
GitHub Actions
Error
…kpoint Continuous Progress Documentation (D-105) requires the checkpoint to reflect the latest material state, which now includes the commit, the push and the open pull request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidru85
deployed
to
cloud-runtime-verification
September 2, 2026 09:57 — with
GitHub Actions
Active
…lescence OutboxCoalescenceParityTest.cascadeDeleteThenSubsequentFuelEntryCoalescence- RetainsCanonicalKeySet re-applied the payload produced by the cascade itself, so it exercised cascade-then-cascade while its name, the E1-11 acceptance criterion and docs/handoff-E1-11.md all claimed cascade-then-direct. The test now captures the direct payload from the real SqlDelightFuelEntryRepository create path before the cascade delete and re-applies that direct payload, making the direct writer the last writer of the coalesced row. It asserts the direct and cascade payloads are exactly the canonical key set, that they differ from each other so the re-application cannot degenerate into a repeat of the cascade write, and that the final row equals the direct payload and keeps the canonical keys and "entityType":"FUEL_ENTRY". Renamed to cascadeDeleteThenDirectFuelEntryCoalescenceRetainsCanonicalKeySet, with the fixture helper renamed coalesceFuelEntryOutboxPayload and its comment corrected. The direct-then-cascade test is unchanged. Also adds the missing trailing newline to docs/handoff-E1-11.md, refreshes its checkpoint, acceptance evidence and verification, and appends a correction entry to docs/PROJECT_LOG.md. No contract, schema, migration, architecture or decision change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidru85
deployed
to
cloud-runtime-verification
September 2, 2026 10:19 — with
GitHub Actions
Active
… owner AGENTS.md requires the handoff checkpoint to state verification results including failures and their established owner. The first ios-simulator-build run on d1cf977 failed because ViewModelLifecycleTests .testFuelEntryFormViewModelModeDerivations crashed the XCTest runner in tearDown with "AndroidxDriverConnectionPool.close() called while 1 reader connection(s) still checked out", thrown from SwiftAppGraph.close() through DatabaseHandle.close(). Re-running the job passed with no code change. The range 971f31e..d1cf977 touches only docs/ and shared/src/commonTest/, neither of which reaches the Shared framework the iOS host tests link against, so E1-11 is not the cause. The defect belongs to the E1-12 graph-close work and is recorded as a residual risk, not fixed here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidru85
deployed
to
cloud-runtime-verification
September 2, 2026 10:47 — with
GitHub Actions
Active
provider-decoupling failed on 83292e8, a documentation-only commit, with FuelEntryStateHolderTest.unsupportedLocaleCurrencyFallsBackToEur [iosSimulatorArm64] FAILED and "Test running process exited unexpectedly". That is the exact test class the AGENTS.md Repository State already names as the open E1-12 defect, "FuelEntryStateHolderTest Kotlin/Native SIGSEGV on graph close". Together with the ios-simulator-build crash on d1cf977, this confirms the branch is exposed to one known Kotlin/Native graph-close defect that surfaces nondeterministically across jobs. Recorded as a residual risk with E1-12 as the established owner; not fixed here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davidru85
deployed
to
cloud-runtime-verification
September 2, 2026 10:52 — with
GitHub Actions
Active
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
entityTypeoutbox fix after PR E1-11: vehicle outbox payload entityType fix #45 was merged in error and reverted by PR Revert "E1-11: vehicle outbox payload entityType fix" #47, with all six owner-review findings fixed.VehicleOutboxMappernow emits"entityType":"VEHICLE"and"entityType":"FUEL_ENTRY"as the first key of the Vehicle payload and the cascade Fuel Entry tombstone payload, satisfyingdocs/CONTRACTS.md §8.FirebaseRemoteSyncSource.toFirestoreWritevalidates the payloadentityTypeagainstEntitySnapshot.entityTypeand excludes it fromFirestoreWrite.fields, keeping the closed remote schema ofdocs/CONTRACTS.md §16intact. A missing, unknown or mismatched value returnsRemoteError.InvalidArgument.The six review findings
OutboxCoalescenceParityTestused a handcrafted payload builder — fixed. It now buildsSqlDelightVehicleRepositoryandSqlDelightFuelEntryRepositoryover the same in-memory database and obtains the direct Fuel Entry outbox payload through the real create path.d1cf977after the PR review. The first attempt re-applied the payload produced by the cascade itself, so it proved cascade-then-cascade while claiming cascade-then-direct.directFuelEntryWriteThenCascadeDeleteProducesTheSameCanonicalKeySetproves direct-then-cascade;cascadeDeleteThenDirectFuelEntryCoalescenceRetainsCanonicalKeySetnow captures the direct payload from the realSqlDelightFuelEntryRepositorycreate path before the cascade and re-applies that payload, so the direct writer is genuinely the last writer of the coalesced row.docs/BACKLOG.mdnow state that the required integration-boundary adaptation in:integration:firebase-firestoreis included and that no schema or migration changed.git diff --name-status mainexactly.docs/handoff-E1-11.mdcarries the current phase, verification evidence and next step.Story
E1-11 - :feature:vehicle Outbox Payload entityType FixReady Check
ProposedorPendingIn-Progress Checkpoint
story/E1-11-outbox-entitytype-fix-v2frommain(76883c2)d1cf977OutboxCoalescenceParityTest(it proved cascade-then-cascade, not cascade-then-direct), added the missing trailing newline todocs/handoff-E1-11.md, refreshed the handoff checkpoint / acceptance evidence / verification, and appended a correction entry todocs/PROJECT_LOG.md--rerun-tasksBUILD SUCCESSFUL (87 tasks executed); full non-instrumented command BUILD SUCCESSFUL (627 actionable tasks);contractCheckoutput inspected, every assertion[PASS];git diff --checkclean; no known failuresScope Completed
VehicleOutboxMapper.toVehicleOutboxPayloadOrNullemits"entityType":"VEHICLE"as the first key, covering create, update and vehicle tombstone call sites.VehicleOutboxMapper.toFuelEntryTombstonePayloademits"entityType":"FUEL_ENTRY"as the first key of the cascade-delete tombstone payload.FirebaseRemoteSyncSource.toFirestoreWriterequires the payloadentityTypeto matchEntitySnapshot.entityType, excludes it fromFirestoreWrite.fields, and returnsRemoteError.InvalidArgumentfor missing, unknown or mismatched values. The payload key is the file-localENTITY_TYPE_FIELDconstant, matching the existing*_FIELDconvention of that file.VehicleOutboxMapperTestadded, including theentityTypeEnumNamesMatchTheOutboxWireValuesanchor.VehicleRepositoryCreateTest,VehicleRepositoryUpdateTestandVehicleRepositoryDeleteTestassertentityTypeon every write path with exact canonical key sets.VehicleRepositoryDeleteTest.cascadeDeleteIsIdempotentWhenItIsTheLastWriterOfAFuelEntryOutboxRowproves the cascade is idempotent.OutboxCoalescenceParityTestin:sharedproves both coalescence orders produce the same canonical key set using the real repositories.FirebaseRemoteSyncSourceEntityTypeBoundaryTest(5 tests) proves the Firestore boundary excludesentityTypeand rejects mismatches for both Vehicle and Fuel Entry.VehicleFormStateHolderTest.vehicleOutboxPayloadWithEntityTypeReachesRemoteSyncSourceAsAValidSnapshotend-to-end regression test.Acceptance Evidence
VehicleOutboxMapperTest.permanentOwnerVehiclePayloadIncludesEntityTypeVehicleandfuelEntryTombstonePayloadIncludesEntityTypeFuelEntryprove both mapper emissions.entityTypeasserted for each write path: Vehicle create, Vehicle update, Vehicle tombstone, cascade Fuel Entry tombstone.(FUEL_ENTRY, entityId)outbox row keeps the same canonical key set regardless of write order, both orders driven by the realSqlDelightFuelEntryRepository:OutboxCoalescenceParityTest.directFuelEntryWriteThenCascadeDeleteProducesTheSameCanonicalKeySet— direct then cascade.OutboxCoalescenceParityTest.cascadeDeleteThenDirectFuelEntryCoalescenceRetainsCanonicalKeySet— cascade then direct. It captures the direct payload before the cascade, asserts both payloads carry exactly the canonical key set, asserts the two payloads differ so the re-application cannot degenerate into a repeat of the cascade write, then coalesces the direct payload onto the cascade-written row and asserts the final row equals the direct payload and keeps the canonical keys and"entityType":"FUEL_ENTRY".iosSimulatorArm64.entityTypeand validates it:FirebaseRemoteSyncSourceEntityTypeBoundaryTest(5 tests).VehicleFormStateHolderTest.vehicleOutboxPayloadWithEntityTypeReachesRemoteSyncSourceAsAValidSnapshot.LOCAL_OWNER + PENDING + no outboxinvariant preserved:VehicleOutboxMapperTest.localOwnerNeverProducesAnOutboxPayload.Out of Scope / Not Done
DatabaseMutations(core/database/.../DatabaseMutations.kt), the:feature:fueland:sharedliterals, and the.sqCHECK/SQL literals keep their string literals because:core:databasecannot depend on:core:sync(docs/TECHNICAL_PLAN.md §4) and unification requires a gated relocation ofEntityType. Any centralization is deferred to E3-13 (issue E3-13: Single source of truth for the outbox entityType wire value #46).Files Changed
AGENTS.mdREADME.mddocs/BACKLOG.mddocs/DECISION_BOARD.mddocs/PROJECT_LOG.mddocs/SPECIFICATION.mddocs/TECHNICAL_PLAN.mddocs/adr/0111-outbox-entity-type-token-ownership.md(new)docs/adr/README.mddocs/handoff-E1-11.md(new)feature/vehicle/src/commonMain/kotlin/com/ruizurraca/carapp/feature/vehicle/data/VehicleOutboxMapper.ktfeature/vehicle/src/commonTest/kotlin/com/ruizurraca/carapp/feature/vehicle/data/VehicleOutboxMapperTest.kt(new)feature/vehicle/src/commonTest/kotlin/com/ruizurraca/carapp/feature/vehicle/data/VehicleRepositoryCreateTest.ktfeature/vehicle/src/commonTest/kotlin/com/ruizurraca/carapp/feature/vehicle/data/VehicleRepositoryDeleteTest.ktfeature/vehicle/src/commonTest/kotlin/com/ruizurraca/carapp/feature/vehicle/data/VehicleRepositoryTestScope.ktfeature/vehicle/src/commonTest/kotlin/com/ruizurraca/carapp/feature/vehicle/data/VehicleRepositoryUpdateTest.ktintegration/firebase-firestore/src/commonMain/kotlin/com/ruizurraca/carapp/integration/firebase/firestore/FirebaseRemoteSyncSource.ktintegration/firebase-firestore/src/commonTest/kotlin/com/ruizurraca/carapp/integration/firebase/firestore/FirebaseRemoteSyncSourceEntityTypeBoundaryTest.kt(new)integration/firebase-firestore/src/commonTest/kotlin/com/ruizurraca/carapp/integration/firebase/firestore/FirebaseRemoteSyncSourceTest.ktshared/src/commonTest/kotlin/com/ruizurraca/carapp/OutboxCoalescenceParityTest.kt(new)shared/src/commonTest/kotlin/com/ruizurraca/carapp/VehicleFormStateHolderTest.ktDecisions Made
:feature:vehiclecommonTest files derive their outbox lookup keys fromEntityType.*.name; payload value assertions stay as exact string literals. TheentityTypeEnumNamesMatchTheOutboxWireValuesanchor pins the enum to the contract. No refactorization is assigned to E3-03; E3-13 (issue E3-13: Single source of truth for the outbox entityType wire value #46) owns the future centralization.entityTypeEnumNamesMatchTheOutboxWireValuestest is a characterization pin of an existing invariant and therefore has no RED phase. This is the only TDD deviation and it is declared here and in the ADR.Verification Run
Sharedframework from:composition:ios)Commands or checks run:
Contract Impact
docs/CONTRACTS.md§:docs/CONTRACTS.md §8already mandatesentityTypeand§16already closes the remote schema. This story makes the code conform to both.Decision Board Impact
docs/DECISION_BOARD.md(D-110) and the related ADR:docs/adr/0111-outbox-entity-type-token-ownership.mdShared-Write Modules Touched
core/database— confirm no other story is modifying itProject Log Entry
docs/PROJECT_LOG.mdHuman Review Gate
Gates are defined canonically in
AGENTS.md.docs/SPECIFICATION.md,docs/CONTRACTS.md,docs/DECISION_BOARD.md,AGENTS.md,docs/adr/**,docs/identifiers.md,docs/versions-matrix.md,firestore/**,core/sync/**,core/auth/**,core/database/**, money types)Applies: gated paths
AGENTS.md,docs/SPECIFICATION.md,docs/DECISION_BOARD.mdanddocs/adr/**(the D-110 mirrors and ADR-0111). The change also touchesintegration/firebase-firestoreproduction code. The gate is the owner's review and merge decision. The agent MUST NOT merge this pull request.Risks or Follow-ups
E1-06follow-up. Closes [Bug]: Vehicle cascade Fuel Entry tombstone omits entityType #36.ios-simulator-buildrun ond1cf977failed:ViewModelLifecycleTests.testFuelEntryFormViewModelModeDerivationscrashed the XCTest runner duringtearDownwithkotlin.IllegalStateException: AndroidxDriverConnectionPool.close() called while 1 reader connection(s) still checked out, thrown fromSwiftAppGraph.close()->DefaultAppGraph.close()->DatabaseHandle.close()->AndroidxSqliteDriver.close(). The runner restarted and every other test in the suite passed. Re-running the job passed in 12m11s with no code change. The range971f31e..d1cf977touches onlydocs/andshared/src/commonTest/, neither of which reaches theSharedframework the iOS host tests link against, so E1-11 is not the cause. Same graph-close defect family thatE1-12already owns; recorded as a residual risk, not fixed here.docs/PROJECT_LOG.mdcarries a correction entry recording that the first version of the second parity test did not prove the order it named. The earlier entry is left intact per the log's append-only rule.entityTypetoken.DatabaseMutations,:feature:fuel,:sharedand.sqliterals remain independent until E3-13.🤖 Generated with Claude Code