Reviewed semantic head: 14b06080e0881f41b5363b8a4a3ffd0c3b3aed31
Base and prior review-record head: 355554078675c9aaf924b649d2bdcd3636e40d4a
Initial implementation and control head: 2fa7fada018613063c2f0d4a915f244ec68412d1
Evaluate Review fix head: 8f0481ad1e5d096a372da7df0002c8d4c2319c9d
Scope: the cleanup-start boundary in core, dependent live-query and Effect invalidation, and the PowerSync late on-demand hook and trigger-disposal repair. The primary executable owner remains packages/db/tests/collection-cleanup-restart-oracle.test.ts. PowerSync's packages/powersync-db-collection/tests/load-hooks.test.ts is a real-provider refinement. This documentation closeout does not change the reviewed semantic tree.
Issue #1891 exposed an existing cleanup barrier rather than inventing a new one. The API and documentation tell callers to await cleanup before starting a new sync run. The minimal reproduction returned a held Promise from a cleanup callback and observed public cleanup settle before that Promise continued. At the time of the report, the callback type and Collection implementation treated the cleanup as void and ignored the runtime Promise.
That defect matters even when no row assertion fails immediately. If an old and replacement Collection use the same SQLite resource, old cleanup can affect the replacement or persist deletes after replacement hydration. Without a cleanup settlement barrier, an application needs a separate drain or cancellation barrier before constructing the replacement.
Two contracts were possible:
The project selected the first contract. The initiation-only alternative would have required explicit documentation and another public barrier; silently using those semantics does not satisfy the existing cleanup and restart contract. Issue #1889 is adjacent but distinct. It concerns late startup markReady and initial-query readiness. Issue #1891 concerns settlement and invalidation of an ending sync run. Cleanup start is not startup readiness, a Collection status, or proof that adapter resources settled.
The earlier repair added awaitable cleanup and preserved these laws:
The previous exact-head review left two PowerSync histories open: an onLoadSubset hook that returns its disposer after cleanup starts, and trigger disposal created by that pending acquisition. Merely delaying terminal cleaned-up until those resources settled was rejected. In the staged-preload counterexample, the dependent live query remained active, accepted the late baseline, and fulfilled preload before source-cleanup publication. The durable design therefore needed an earlier invalidation boundary distinct from terminal status.
At the reviewed head, cleanup start synchronously closes restart admission, puts each dependent live query in terminal error once, marks dependent Effects disposed, and detaches subscription demand. Throwing and reentrant observers do not strand teardown or observer ownership. The source Collection keeps its prior public status while adapter cleanup is pending. PowerSync owns pending on-demand loads, late hook cleanup, already released cleanup Promises, and trigger disposal until they settle. A pending hook-acquisition failure participates in cleanup failure selection, while an expected transaction-abort consequence does not replace the primary cleanup failure. Only after all owned work settles does core publish cleaned-up and settle public cleanup.
The model's dependent: terminal field is an explicit abstraction. It combines two different public observations: live-query status error and Effect.disposed === true. It does not claim those dependents have the same cleanup machinery. Cleanup start maps to the internal synchronous callback boundary. Cleanup settlement maps to the later public cleanup Promise cut.
| Requirement | Outcome |
|---|---|
| ORC-001: contract authority and limits | Pass. Collection.cleanup(), the await-before-restart documentation, the glossary's cleanup and cleanup-start terms, and the live-query architecture authorize the result. The exact held-Promise reproduction and same-SQLite replacement hazard establish why settlement matters. The owner remains partial: it does not establish full demand/replay histories, provider transport shutdown, wrapper-wide persistence, general row-publication laws, Effect-handler ordering beyond the pending-disposal case, or native hosts other than the tested Node SQLite path. |
| ORC-002: independent judgment | Pass. The expected timeline comes from the approved cleanup contract and rejected staged-preload counterexample. expectedCleanupBoundary imports no lifecycle transition, callback registry, adapter collector, or Promise classifier. Held gates and sentinel errors compute observations independently of production's cleanup queues and task sets. |
| ORC-003: distinguishable responsibilities | Pass. The five responsibilities are listed above. The executable owner keeps its contract, limits, pure model, bounded scenarios, public production drivers, recorders, and named cleanup-start and cleanup-settlement checks together. Its repeated-alias, pending-handler, throwing-observer, and late-registration refinements are explicit fixed cases outside the two-checkpoint model. PowerSync keeps provider-specific ownership and failure histories beside its real SQLite driver. |
| ORC-004: generated-history grammar controls | Not applicable. This follow-up makes no generated-history or input-grammar coverage claim. Its scenario products are bounded enumerations, and the late-hook and trigger tests are fixed controlled schedules. Reconstruction, ablation, random range, and exclusion controls are therefore not triggered. |
| ORC-005: production path and observation | Pass. Core reaches public cleanup(), preload(), startSyncImmediate(), live-query status, and Effect disposal. It also observes repeated aliases, pending handler settlement, cleanup-start observer counts, exact observer failures, teardown completion, and later restart. PowerSync reaches its public Collection adapter through a real PowerSyncDatabase using Node SQLite, a source Collection, and a dependent live query. Positive witnesses include entered hook gates, disposer call counts, source subscriber and observer counts, and trigger creation/disposal. The recorders retain early settlement, wrong status, duplicate terminal reports, wrong error cause or precedence, unexpected publication or adapter logging, and unhandled rejection at the held checkpoints. |
| ORC-006: checker calibration | Pass. At the initial implementation head, C-CORE-LIVE-START left a held live query loading instead of error (1 assertion failure, 18 passes); M-CORE-EFFECT-START observed active instead of terminal (1 assertion failure, 19 skipped); M-PS-TRIGGER-AWAIT settled both disposer lanes early (2 assertion failures, 18 skipped); C-PS-LATE-HOOK settled both late-hook lanes early (2 assertion failures, 18 skipped); and C-PS-RELEASED-HOOK settled both released-hook lanes early (2 assertion failures, 20 skipped). Final review controls reproduced duplicate alias reporting (expected 1, received 2), one leaked late Effect observer (expected 0, received 1), and a throwing late observer poisoning the next cleanup. The final in-flight-disposer mutant invoked without awaiting its Promise; both lanes reached the disposer and then failed the pending-settlement assertion (2 failures, 25 skipped). Every temporary mutation and control was restored. None was a timeout, setup failure, or unreached path. |
| ORC-007: fixed and random campaigns with direct replay | Not applicable. No important generated property, generator, seed, run budget, shrink path, or replay interface changed. The bounded and fixed controls do not claim random coverage. |
| ORC-008: stateful-model minimality | Not applicable. expectedCleanupBoundary is stateless recomputation over a prior status and checkpoint. It does not introduce, remove, combine, or split mutable state in a stateful reference model. The distinct cleanup-start and cleanup-settlement inputs preserve the next-action distinction that restart admission exposes. |
| ORC-009: vocabulary mapping | Pass. cleanup start, cleanup, restart, sync run, Collection status, settlement, demand, physical acquisition, and acquisition release retain their glossary meanings. The model-only dependent: terminal abstraction maps explicitly to live-query terminal error or Effect disposed. Cleanup start is an internal invalidation cut, not the terminal cleaned-up status; cleanup settlement is distinct from the terminal event's earlier restart-admission cut. PowerSync's cleanup collector and shared active trigger-disposal Promise express implementation ownership, not new public lifecycle states. |
| ORC-010: failure fidelity and cleanup | Pass. The tests do not shrink or normalize failures. Exact sentinel identity, SyncCleanupError.cause, settlement order, raw publication arrays, adapter logs, and unhandled-rejection arrays preserve the violated law and checkpoint. Core releases held gates, completes teardown after an observer throws, and cleans dependents in finally. PowerSync attempts all owned work, deterministically prefers trigger-disposal failure over later collector inspection, adopts a hook-acquisition failure without double logging it, and does not promote expected SyncTransactionAbortedError collateral to the primary cleanup failure. withTestCleanup retains the primary harness failure as AggregateError.cause and keeps secondary cleanup errors distinguishable. The mutants were restored before green verification. |
| ORC-011: independent second formulation | Pass. The named shared-fault hypothesis is that core or a wrapper starts cleanup but discards a returned Promise or late-owned task. Core's in-memory timeline, the prior persisted same-resource/retired-owner refinement, and PowerSync's real SQLite hook/trigger path use meaningfully different machinery. Repeated-alias live-query reporting, pending Effect-handler disposal, and throwing or reentrant observer registration independently test cleanup-start invalidation and ownership. These formulations share only the settlement, invalidation, once-only release, and primary-failure laws. They make no equivalence claim about row ordering, projection, duplicates, or nonempty results. The same-SQLite multi-process and mobile-host risks remain outside this review. |
ORC-012 is satisfied by this versioned record for semantic head 14b06080e0881f41b5363b8a4a3ffd0c3b3aed31. It records every ORC-001 through ORC-011 outcome and is linked from the coverage map. The initial 2fa7fada018613063c2f0d4a915f244ec68412d1 head remains only as provenance for the controls and pre-fix review findings described below.
At the initial 2fa7fada implementation head, the PowerSync trigger mutant changed disposeTrackingAfterAbort() from an owned Promise in Promise.allSettled([trackingDisposal, ...pendingLoads]) to fire-and-forget. Both fulfillment and rejection lanes reached disposer invocation. While the disposer gate remained held, the public cleanup Promise settled. The restored implementation stayed pending, then fulfilled or rejected with the exact sentinel cause after release.
The initial late-hook control held onLoadSubset before it returned a cleanup callback. Cleanup start had to make the dependent live query terminal immediately while the source status stayed ready. Releasing the hook installed its disposer without creating a trigger. Public cleanup remained pending until that disposer settled. The rejection lane produced one SyncCleanupError with the sentinel cause and no unhandled rejection. Rows and publication arrays stayed empty.
The already released hook control retired the live query first. Its held hook cleanup was no longer in the active demand map when Collection cleanup began. The restored adapter adopted that Promise into the cleanup collector. Both lanes remained pending, invoked the hook once, and produced no unhandled rejection. This distinguishes ownership from mere presence in the current demand map.
The initial core live-query control kept only the terminal cleaned-up listener. With source cleanup held, the exact cleanup-start checkpoint observed live-query status loading instead of error. This is the same missing distinction that made the staged-preload counterexample possible.
Final core review reproduced three ownership and once-only failures before the fixes in 8f0481ad:
The repaired lifecycle removes a late observer when its immediate registration callback throws. Effect registration releases the callback when cleanup becomes active before the unsubscribe handle can be retained. A fatal live query now ignores later alias notifications. Focused coverage also holds an Effect handler open while cleanup start disposes the Effect and releases its source subscription, and proves that a throwing observer still allows adapter teardown, terminal publication, guard release, and later restart. Omitting cleanup-start delivery from the final core tree produced three intended assertion failures.
Final PowerSync review exposed two more failure-loss paths. First, reconciliation could claim an in-flight trigger disposer after cleanup took its snapshot. The cleanup-owned call then saw no published disposer and settled without joining the invocation already in progress. The shared activeTrackingDisposal Promise makes both owners join the same settlement. The fulfillment and rejection lanes remain pending through disposer invocation; a mutant that invoked but did not await the disposer failed both pending checks.
Second, rejection directly from a pending onLoadSubset hook was awaited but discarded. The active cleanup collector now retains that failure. The first broader repair treated every captured pending-load rejection as a cleanup failure, but the staged-baseline collateral test showed that this incorrectly promoted an expected SyncTransactionAbortedError. The final repair records the hook-acquisition failure at its boundary and leaves expected transaction-abort collateral secondary. A combined probe rejects hook cleanup first and trigger disposal later: cleanup waits for both, deterministically reports the trigger error, emits no duplicate adapter log, and creates no unhandled rejection. Ordinary unload remains synchronous and reports a later unadopted hook failure exactly once.
Two earlier controls remain independent evidence rather than final-head mutants. The prior R4 ordering mutant produced adapter cleanup -> public Promise continuation -> cleaned-up event; the core oracle rejected it because the terminal event must precede the continuation. The terminal-only delay candidate awaited late PowerSync resources but left the dependent live query active. Its staged preload fulfilled before cleanup publication; the expected outcome was rejection at cleanup start. The assertion failure removed that candidate in favor of cleanup start.
At the reviewed head:
At the Evaluate Review fix head 8f0481ad, before the final behavior-preserving deduplication simplification:
The initial 2fa7fada implementation receipts were 32 of 32 focused core and Effect tests, 22 of 22 focused PowerSync load-hook tests, and 165 of 165 full PowerSync tests across 11 files, plus the stated DB, DB IVM, PowerSync, lint, format, and diff checks. One earlier PowerSync package run at that stage raced its build and could not resolve dist/esm/index.d.ts; that was a setup-order failure, and the post-build 165-test rerun was green.
Reviewed semantic head: 889b930170c7c23b75f020297d96fa92f3c31142
Review baseline: 9cc05782a035ca737e2c3dc360ae4ed417be2045
A later review found that a live query could start while one source's adapter cleanup was pending. Cleanup-start observer registration correctly delivered the active boundary synchronously and put the live query in terminal error, but the builder continued its setup loop. At the baseline, the controlled first-source history observed one subscription on both the cleaning source and the later healthy source. An intermediate guard stopped later acquisition but left one earlier healthy-source subscription when the cleaning source occupied the second position. The final repair throws the exact terminal setup error after unregistering the current observers. The existing sync-entry rollback then releases all earlier source ownership and prevents later acquisition.
The coverage gap was an inverted lifecycle history. Existing tests created a dependent before cleanup began or registered an Effect during active cleanup. They did not start a live query after cleanup start, nor vary the cleaning source's position in a multi-source query. The permanent two-cell refinement holds adapter cleanup, places the cleaning source first and second, calls the public live-query preload path, and checks the exact terminal error, one report, and zero retained subscriptions on both sources.
| Requirement | Outcome |
|---|---|
| ORC-001: contract authority and limits | Pass. The glossary and live-query architecture require cleanup start to put dependent live queries in terminal error. This focused refinement adds the setup-order consequence: setup that observes that terminal boundary retains no partial source ownership. It does not expand the oracle's transport, row-publication, or full demand-history claims. |
| ORC-002: independent judgment | Pass. A held adapter-cleanup gate creates the boundary. Exact public status, error text, report count, and source subscriber counts supply the expected observations without importing builder state or transition helpers. |
| ORC-003: distinguishable responsibilities | Pass. The existing oracle opening retains its contract, two-checkpoint model, fixed history grammar, public driver, and refinement checks. The new fixed source-position matrix is named as an adjacent ownership refinement outside the small timeline model. |
| ORC-004: generated-history grammar controls | Not applicable. The first/second source product is a bounded two-cell enumeration, not a generated-history coverage claim. |
| ORC-005: production path and observation | Pass. The driver calls real source preload() and cleanup(), creates a real joined live-query Collection, and calls its public preload(). Both cells reach synchronous cleanup-start delivery while adapter cleanup remains held. The checkpoint observes terminal status and exact subscriber ownership before cleanup settlement. |
| ORC-006: checker calibration | Pass. At the baseline, the first-source cell failed with { cleaning: 1, healthy: 1 } instead of zeros. The intermediate stop-only guard made that cell pass but the second-source cell failed with { cleaning: 0, healthy: 1 }. Neither result was a timeout, setup failure, or unreached path. The final rollback repair passes both cells. |
| ORC-007: fixed and random campaigns with direct replay | Not applicable. No important generated property, seed, budget, shrink path, or replay interface changed. |
| ORC-008: stateful-model minimality | Not applicable. No stateful reference-model state was added, removed, combined, or split. Source position is a fixed driver dimension. |
| ORC-009: vocabulary mapping | Pass. Cleanup start, cleanup settlement, live-query Collection, source Collection, subscription, and sync run retain their glossary meanings. Partial source ownership means subscriptions acquired before terminal setup rollback; it is an observation, not a new lifecycle state. |
| ORC-010: failure fidelity and cleanup | Pass. The test preserves exact status, error, log, and subscriber-count observations. finally releases the adapter gate, cleans the live query and both sources, and cannot replace the primary assertion failure. |
| ORC-011: independent second formulation | Pass. The existing late-Effect-registration refinement independently proves immediate disposal and observer release. The new live-query path instead exercises joined-source setup rollback and subscription ownership. Both refine cleanup-start invalidation without equating their teardown machinery. |
ORC-012 is satisfied by this versioned section for semantic head 889b930170c7c23b75f020297d96fa92f3c31142. Focused verification passed the 30-test cleanup/restart oracle, the 12-test Effect-disposal oracle, and the 199-test Collection subscription-lifecycle oracle. The DB build, DB TypeScript check, targeted ESLint and Prettier checks, and git diff --check also passed.
The ancestor record issue-1891-async-cleanup.md reviewed semantic head 21ed924352e91e47f0643de6d2542051ddd12d20 and was versioned by 355554078675c9aaf924b649d2bdcd3636e40d4a. Its receipts were 349 core tests, 292 persistence tests with one existing todo, and 154 PowerSync tests, plus the stated type, lint, format, and build checks. That record classified pending late hooks and trigger disposal as an open design gap and named cleanup start only as the destination. This follow-up implements and calibrates that destination; it does not rewrite the earlier verdict.
Reviewed semantic head: 20407a501a2441cd3780198809e75c00eb410925
The frozen preservation contract required the model to retain cleanup start, all-owned retirement, observer and adapter failure aggregation, late-registration rollback, one notification per owner per boundary, terminal dependent ownership across source restart, and the PowerSync pending-resource refinement. The smallest reconstruction is a graph of independent laws rather than one model of every lifecycle manager:
The core timeline now models all three observable cuts. At cleanup start, restart admission is closed, public status is unchanged, and cleanup is pending. At terminal publication, admission is open and status is cleaned-up while the original cleanup Promise remains pending. Promise settlement follows. The model still does not reproduce callback registries, manager order, provider tasks, replay, or publication machinery.
Every known valid witness reconstructs from the graph above. The timeline owns the held-adapter case. Separate driver refinements cover live queries, Effects, repeated aliases, late observers, source position, abort and release reentry, runtime cleanup return shape, concurrent callers, terminal-event restart, and failure combinations. PowerSync separately covers each provider task origin and their consequential overlap.
One-at-a-time ablation produced these results:
| Removed coordinate or relation | Lost distinction |
|---|---|
| Cleanup start | Dependents remain active while adapter cleanup is pending. |
| Terminal-publication cut | Event-time restart and event-before-Promise order disappear. |
| Cleanup-Promise cut | Adapter settlement becomes indistinguishable from public completion. |
| Sync-run identity | A source restart may revive an already-terminal dependent. |
| Registration by cleanup boundary | Once-per-boundary and once-per-lifetime become indistinguishable. |
| Cleaning-source position | Setup can retain an owner acquired before the cleaning source. |
| Adapter/local failure provenance | Rejection timing may choose the wrong primary failure. |
| Runtime return shape | A contextual-void Promise can be mistaken for synchronous cleanup. |
| PowerSync task origin | A late hook, released hook, or in-flight trigger can fall outside adapter ownership. |
| PowerSync task-set growth | Cleanup can snapshot tasks once and settle before a late disposer. |
The two-attempt reentry coordinate does not justify a full Cartesian expansion by itself, but it retains one repeated-denial observation without meaningful runtime cost. The existing eight reentry cells therefore remain. Stable case IDs now calibrate the complete abort|release × plain|nested × 1|2 product and the complete event|await × source|live-query product. The executable check asserts the expected order, completeness, and uniqueness of all twelve IDs. The source-position matrix and PowerSync result-shape matrices are explicit one-dimensional enumerations with no derived product or skipped cells.
Nearby invalid states are rejected or kept outside this owner:
No independent marginal case was supplied after the grammar froze. Range is therefore untested beyond the current core driver and the Node SQLite PowerSync refinement. Existing persistence and PowerSync cases shaped the grammar and are not held-out range evidence. Browser, mobile, multi-process, and provider transport behavior remain outside the claim.
The core owner adds three focused witnesses:
The PowerSync refinement adds the higher-order overlap omitted by the separate late-hook and in-flight-trigger cases. One cleanup starts while trigger creation and another subset hook are both pending. Both later cleanup functions are owned and invoked once. Hook cleanup rejects first while trigger disposal stays pending. Cleanup remains pending, then rejects with trigger disposal as the deterministic primary failure, without duplicate adapter logging or an unhandled rejection.
The direct observer-plus-adapter checker was calibrated with a temporary priority-inversion mutant. The mutant made the observer failure the aggregate cause and failed the intended test at its exact cause assertion: one failure and 33 skipped tests. Restoring production made all 34 core oracle tests pass. No production change was required.
| Prompt | Disposition |
|---|---|
| Real-provider conformance | Applicable to the provider refinement. PowerSync runs through @powersync/node and a real temporary Node SQLite database. The in-memory core fixture makes no provider-value claim. |
| Minimal ambiguity | Applicable and covered. Incidental non-Promise return, runtime Promise, synchronous throw, asynchronous rejection, and fulfillment have separate witnesses. |
| Name invariance | Not applicable. No user-controlled name or SQL alias selects cleanup classification. |
| Representation symmetry | Applicable only to cleanup return representation. Function and object cleanup forms plus contextual-void runtime Promises preserve the same settlement law. |
| Await-boundary transitions | Applicable and covered at cleanup start, terminal publication, adapter settlement, late hook return, trigger creation, and disposer settlement. |
| Local/transport refinement | Boundary only. Core and Node SQLite share lifecycle settlement; provider transport termination remains explicitly unclaimed. |
| Partial-construction cleanup | Applicable and covered for cleaning-source position, pending hook acquisition, trigger creation, and their combined overlap. This does not claim every provider construction step. |
| Value-and-work refinement | Not applicable as a bounded-work law. Exact once counts protect ownership, not performance or transfer cardinality. |
The semantic head ran with Node 24.19.0, pnpm 11.1.0, Vitest 3.2.4, TypeScript 5.9.3, @powersync/node 0.20.0, and Darwin 25.6.0 on arm64.
The first full DB-package verification executed 6,406 assertions across 216 files but the type checker rejected the event recorder's broad CollectionStatus annotation. The follow-up semantic head above narrows that observation from the already-discriminated event value. The targeted oracle and its type check then passed. The final full DB-package rerun passed all 6,406 tests across 216 files with no type errors.
Reviewed semantic head: eb93f42094a365f5b2b599504ed4ffea4da66bbb
Review baseline: 7de64caeeb46c0a6d2b9f5c7e7cf424d7e6193da
A final current-head review found one failure-fidelity gap. Cleanup start attempted every observer, but runAllCallbacks rethrew only the first observer failure and beginCleanup retained only one local failure. With two throwing observers and a rejecting adapter cleanup, the public AggregateError retained the wrapped adapter failure and the first observer failure but silently dropped the second observer failure.
The fixed oracle is the smallest distinguishing history: two cleanup-start observers throw distinct sentinel errors in registration order, adapter cleanup rejects after a held gate, and cleanup then admits a new sync run. The RED run reached both observers and failed because AggregateError.errors had two entries instead of three. Production now records every local teardown failure in execution order. Adapter cleanup remains the primary cause; a lone local failure keeps its identity; multiple local-only failures use the first local failure as their aggregate cause. Teardown, terminal publication, and later restart still complete.
| Requirement | Reconciliation outcome |
|---|---|
| ORC-001 | Pass. The existing cleanup contract and ORC-010 failure-fidelity law require each cleanup diagnostic to remain distinguishable. The test does not claim provider transport shutdown or full demand/replay coverage. |
| ORC-002 | Pass. Expected error identity and order come from callback execution order and adapter-primary precedence, not the production aggregator. |
| ORC-003 | Pass. The existing contract, timeline model, fixed failure history, public Collection driver, and exact aggregate comparison remain distinguishable. |
| ORC-004 | Not applicable. This is one fixed controlled history, not a generated-grammar claim. |
| ORC-005 | Pass. The test calls public preload, cleanup, and restart entry points; observer call order, exact error identities, status, and restart are observed. |
| ORC-006 | Pass. Baseline production was the hostile wrong design. It reached both callbacks and failed only the missing-third-error assertion. |
| ORC-007 | Not applicable. No important generated property changed. |
| ORC-008 | Not applicable. No stateful reference model changed. |
| ORC-009 | Pass. Cleanup start, adapter cleanup, local teardown, settlement, and restart retain their glossary meanings. |
| ORC-010 | Pass. The aggregate keeps the adapter failure as cause and retains both observer failures in execution order while cleanup releases resources and permits restart. |
| ORC-011 | Not applicable. The named defect is information loss in one concrete aggregator; no meaningfully independent semantic formulation is needed beyond exact sentinel identities and order. |
ORC-012 is satisfied for this repair by the requirement-by-requirement record above. The focused RED run failed one of one selected tests with no type errors. After the repair, the complete cleanup/restart oracle passed 37 of 37 tests, and the five-suite lifecycle collateral passed 204 of 204 tests with no type errors. Targeted ESLint, Prettier, and git diff --check passed.