|
| 1 | +# Reviewed coordination promotion and recovery |
| 2 | + |
| 3 | +A promotion moves a Goal's Todo/lease coordination authority from the legacy |
| 4 | +source to its selected canonical provider. Preview, writer fencing, provider |
| 5 | +commit, and acknowledgement are distinct steps. A successful preview is neither |
| 6 | +a grant nor evidence that cutover has happened. |
| 7 | + |
| 8 | +The operator can now save the exact preview, execute that plan, and recover its |
| 9 | +original transaction without reconstructing intent from a later Markdown view. |
| 10 | +The TypeScript coordination boundary owns plan validation, qualification, |
| 11 | +fencing and receipt proof; Python only loads the file and transports the request. |
| 12 | + |
| 13 | +## Preview and execute |
| 14 | + |
| 15 | +Use an explicitly enabled, bootstrapped and qualified runtime shadow. Its |
| 16 | +qualification must cover real mutations and required event classes; an empty |
| 17 | +shadow or a saved JSON file cannot substitute for that evidence. Existing v0 |
| 18 | +promotion still requires `hard_lease`. Provider selection and migration approval |
| 19 | +remain separate from these commands. |
| 20 | + |
| 21 | +```bash |
| 22 | +loopx --format json coordination-shadow promote \ |
| 23 | + --goal-id example-goal \ |
| 24 | + --minimum-operations 3 \ |
| 25 | + --require-event-kind todo_update > reviewed-promotion.json |
| 26 | + |
| 27 | +loopx --format json coordination-shadow promote \ |
| 28 | + --goal-id example-goal --reviewed-plan reviewed-promotion.json |
| 29 | + |
| 30 | +loopx --format json coordination-shadow promote \ |
| 31 | + --goal-id example-goal --reviewed-plan reviewed-promotion.json --execute |
| 32 | +``` |
| 33 | + |
| 34 | +Inspect `ok`, `promotion.status`, the plan's target provider, source revision, |
| 35 | +projection digest and qualification policy before execution. The saved file may |
| 36 | +be the entire successful CLI preview or its `promotion.plan.reviewed_plan` |
| 37 | +envelope. Keep it in operator-owned local storage: it carries a runtime path and |
| 38 | +Goal identity, so it is not a public collaboration artifact. |
| 39 | + |
| 40 | +`--reviewed-plan` owns the operation id and qualification policy. Combining it |
| 41 | +with `--minimum-operations` or `--require-event-kind` is an error. A normal |
| 42 | +`promote` command without a saved plan retains its existing defaults. |
| 43 | + |
| 44 | +Execution captures and qualifies the source again under the existing locks. If |
| 45 | +the computed plan digest differs, it returns |
| 46 | +`local_authority_reviewed_plan_changed` before engaging a writer fence. Review a |
| 47 | +new preview after legitimate source changes; do not edit the old digest to force |
| 48 | +acceptance. The digest detects changed intent; the durable fence and provider |
| 49 | +state establish whether that intent may proceed. |
| 50 | + |
| 51 | +## Recover the original cutover |
| 52 | + |
| 53 | +```bash |
| 54 | +loopx --format json coordination-shadow recover-promotion \ |
| 55 | + --goal-id example-goal --reviewed-plan reviewed-promotion.json |
| 56 | + |
| 57 | +loopx --format json coordination-shadow recover-promotion \ |
| 58 | + --goal-id example-goal --reviewed-plan reviewed-promotion.json --execute |
| 59 | +``` |
| 60 | + |
| 61 | +Recovery resolves the registered Goal and runtime but does not read legacy |
| 62 | +Markdown or require the transient shadow opt-in. It requires the exact existing |
| 63 | +writer fence. It never creates a missing fence, selects another provider, or |
| 64 | +falls back to a legacy source. |
| 65 | + |
| 66 | +| Durable state | Preview | With `--execute` | |
| 67 | +| --- | --- | --- | |
| 68 | +| No matching writer fence | Reject | Reject | |
| 69 | +| Matching fence, no canonical commit, exact qualified shadow retained | `recovery_ready` | Commit and read back | |
| 70 | +| Original promotion committed, including a later canonical head | `replayed` | `replayed`; no business write | |
| 71 | +| Different canonical initialization or inconsistent receipt lineage | Reject | Reject | |
| 72 | +| Provider unavailable | Report provider failure | Report provider failure | |
| 73 | + |
| 74 | +For an uncommitted recovery, the original shadow revision, projection, capture |
| 75 | +binding, complete transaction lineage, outbox settlement, operation count and |
| 76 | +event coverage must still qualify. Recovery validates these durable facts under |
| 77 | +the same maintenance guard used by canonical writers. It does not pretend to |
| 78 | +observe fresh source parity after the source has ceased to be authority. |
| 79 | + |
| 80 | +A thrown commit acknowledgement can mean that the provider already committed. |
| 81 | +Both promotion paths therefore share one commit/readback implementation. It |
| 82 | +attempts the business commit once, then checks the persisted receipt and first |
| 83 | +transaction. The receipt body, operation id, cursor, provider revision and |
| 84 | +initial projection must agree. A matching proof reports success/recovery even |
| 85 | +if later work has advanced the head. A missing or conflicting proof remains a |
| 86 | +failure; an unavailable proof read is not silently treated as absence. |
| 87 | + |
| 88 | +The returned promotion revision and cursor identify the original cutover, not |
| 89 | +the current head. `executed=false` on a replay means this invocation performed no |
| 90 | +business write. Inspect `legacy_writer_fenced` and reconciliation evidence when |
| 91 | +an execution fails; do not infer that a failure left legacy writers usable. |
| 92 | + |
| 93 | +## Bounded capture proof transport |
| 94 | + |
| 95 | +A long Goal can exceed the existing 2 MiB RPC response budget before promotion: |
| 96 | +sequence recovery used to return a full head and full projections for retained |
| 97 | +transactions. The `outbox_read` proof read model now keeps full lineage validation |
| 98 | +inside TypeScript, while returning progress, receipts, projection digests and |
| 99 | +partition markers. Sequence allocation requests no transaction rows; drain uses |
| 100 | +the compact rows. Existing full diagnostic reads retain their default contract. |
| 101 | +No transport limit, stored population or transaction validation is weakened. |
| 102 | +A pending outbox still blocks promotion; use the existing bounded |
| 103 | +`authority-shadow drain --goal-id example-goal --budget-seconds 60` operation |
| 104 | +and inspect its result before retrying preview. |
| 105 | + |
| 106 | +## Product and rollout boundary |
| 107 | + |
| 108 | +This is an operator CLI administration journey. It adds no dashboard, Lark or |
| 109 | +managed-Turn automatic migration trigger, settings editor, capability grant or |
| 110 | +new provider selector. Those surfaces continue to consume canonical data through |
| 111 | +the existing routing/projection contracts after a separately authorized cutover. |
| 112 | + |
| 113 | +File and SQLite use their existing local stores. PostgreSQL follows the same |
| 114 | +transaction/readback contract through its service-owned factory; a local CLI |
| 115 | +selector alone does not provide a PostgreSQL connection or tenant authority. |
| 116 | + |
| 117 | +The claim-preserving migration work in PR #4870 is a complementary prerequisite |
| 118 | +for Goals that need explicit `preserve` or a claim-preserving `hard_lease` |
| 119 | +transition. The two changes overlap the promotion orchestration and must be |
| 120 | +integrated and tested together; this saved-plan feature alone does not enable |
| 121 | +that policy conversion on a v0-only checkout. |
| 122 | + |
| 123 | +Default-on promotion, SQLite long-duration qualification, post-promotion export |
| 124 | +or rollback, and retirement of remaining Python callers retain their RFC gates. |
| 125 | +Recovery is a forward completion/readback operation, not rollback. Do not remove |
| 126 | +a live fence, reset canonical storage, or replace the source to make recovery |
| 127 | +pass. Before execution, abandoning a saved preview needs no runtime mutation. |
| 128 | + |
| 129 | +## Validation contract |
| 130 | + |
| 131 | +Durable tests cover real File/SQLite CLI preview, saved-plan execution, source |
| 132 | +drift, policy override rejection, later canonical writes and recovery after |
| 133 | +legacy deletion. The provider conformance suite uses the shared production-scale |
| 134 | +fixture in legacy and native record shapes, preserving the complete Todo/lease |
| 135 | +population through File, SQLite and a real isolated PostgreSQL server. |
| 136 | + |
| 137 | +Negative receipt tests independently corrupt the receipt index and first |
| 138 | +transaction. Interrupted-commit tests distinguish failure before commit from a |
| 139 | +lost acknowledgement after commit. These are synthetic fault injections, not a |
| 140 | +claim of arbitrary process-death or elapsed-soak coverage. Real local rehearsals |
| 141 | +must use read-only captured sources and disposable copies; never promote an |
| 142 | +active Goal merely to validate this refactor. |
0 commit comments