Skip to content

feat(authority): add verified portable journal recovery - #4887

Merged
huangruiteng merged 4 commits into
mainfrom
codex/authority-portable-recovery-0922
Sep 22, 2026
Merged

huangruiteng merged 4 commits into
mainfrom
codex/authority-portable-recovery-0922

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

Canonical writes need a recoverable history before whole-Goal cutover. Existing shadow rollback quarantines a pre-promotion lineage; copying only the current projection would lose older operation identities and receipts.

loopx authority-archive export|verify|restore now captures one exact retained prefix, verifies its complete state/history, and restores it into an isolated File or SQLite store with resumable exact-prefix readback. The same TS contract supports a service-owned PostgreSQL store. Related to #4574, shared-authority D3/L8 and TS migration T3/T4; base: main.

Scope And Continuation

Complete within the recovery-artifact scope. The CLI previews restore, requires an exact archive digest and explicit execution, rejects occupied unrelated destinations, and never changes a registry, provider selector, writer fence or execution grant. Operation ids, events, receipts and historical projections survive; destination physical revision tokens belong to the destination.

The existing L8 package still owns source draining, executor fencing, target adoption, later-write accounting and cohort approval. Integrate the accepted reviewed-promotion work in #4879 and command recovery in #4886 separately. SQLite D2/profile qualification and default onboarding remain open; the RFC's conditional 5–8 delivery-package estimate is unchanged. This archive covers coordination authority, not the entire Goal runtime.

The maintenance pass reuses AuthorityStore and the existing state-delta codec instead of adding provider-specific export formats or Python state reconstruction. No existing Python business writer is retired in this slice.

Validation

  • Tested revision: aa41ae4959eedce93ddfc2e9dc7d574adebc1ac1 (runtime code unchanged after 495b8f90e; final two-line digest documentation clarification requalified)
  • Run state: finished
  • Input classes: synthetic, public_fixture, authorized_private_read_only
Check kind Result Evidence / limitation
integration passed 26 archive cases on real File/SQLite/PostgreSQL: exact history, re-signed invalid records, truncation, concurrent source appends, interrupted restore, lost acknowledgement, conflicting targets and checkpoint-window history.
real_entrypoint passed 2 Python CLI source-provider cases, each restoring both local targets: preview, repeat recovery, independent-process readback and active-runtime destination rejection.
real_backend passed PostgreSQL 16.15 on an isolated server/database; four portable recovery directions and the existing real service admission suite. Node 22.22.3, source Python 3.13.13.
regression_parity passed 232 selected state-log/archive/PostgreSQL conformance checks. Initial four subprocess failures used the system Python; rerunning with the worktree environment passed all 232.
real_entrypoint passed Canonical copies initialized from a consistent authorized read-only capture, followed by two new mutations. Four local CLI directions and two PostgreSQL recoveries preserve all captured records and all three canonical transactions. Private evidence stays local; no active Goal was promoted or modified.
real_entrypoint passed Additional owner-selected already-canonical Goal: read-only File export, both isolated local restore targets and repeat recovery preserve all five retained transactions including the original promotion receipt. Source authority file bytes stayed unchanged.
static passed TypeScript compiler program, 22 configured mypy files plus the new CLI module, changed Python Ruff checks, public-boundary scan and diff checks. An initial import-following mypy invocation included unrelated unconfigured modules; configured and focused checks pass.
manual passed Exact-scope change-quality record verified. Risk-selected premerge canary passed.

Coverage: complete archive/recovery user journey and negative boundaries; whole-Goal promotion, source fencing/adoption, production default qualification and elapsed soak are not claimed. Archive checksums detect corruption, not author authenticity. A modified input during recovery leaves a failed isolated copy rather than an activated authority.

Frontend / Visual Evidence

UI impact: none. This is an administrative CLI transport with compact responses; it does not change frontend/Lark settings, provider selection, the projection owner or packaged presentation.

Type Of Change / Area

New feature, documentation and tests; control plane. Shared Goal Authority, R5/G2/G5; shared-authority D3/L8 recovery artifact and TS-owned persistence semantics.

Shared-authority RFC Fixture Impact

  • Reuses loopx_coordination_production_scale_fixture_v0, both native and imported schemas.
  • Checks full historical projections, archived membership, leases, ordering, arbitrary retained metadata, original receipts and destination revision lineage; adds a retained-history case across SQLite checkpoint windows.
  • Real File/SQLite/PostgreSQL arms ran. The captured legacy source is read-only; canonical consumer copies are explicitly isolated. This is not a promotion rehearsal or a claim of NoKV qualification.

Boundary Checklist

  • No private state, credentials, raw traces, internal links or local paths in public artifacts.
  • No duplicated maintainer benchmark work.
  • Scoped to the linked authority migration outcome and existing successor.
  • UI impact marked none with the concrete owner boundary.
  • Every commit includes a DCO sign-off.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…play

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

动机

这个 PR 补齐的是 canonical authority 的“可恢复历史”,不是又一条 provider promotion 路径。

已有 projection copy 或 shadow rollback artifact 可以保存某个状态切片,却不能证明完整保留了每个 operation id、event、receipt 和历史 projection。对 AuthorityStore 来说,这些历史不是可丢的调试信息:lost-ACK recovery、idempotency fencing 和后续重放都依赖它们。只复制最终 head,即使 hash 一样,也可能隐藏被遗漏、重排或补偿掉的中间操作。

本 PR 提供了一个完整而独立的恢复 artifact:从选中的 canonical store 固定一个 retained prefix,流式重建并 seal;随后可以完整 verify,并在不触碰活跃 Goal 的前提下恢复到独立 File/SQLite store,或由 service-owned AuthorityStore 恢复到 PostgreSQL。真正的 provider adoption、writer fencing 和 later-write accounting 仍留在单独的 authority transition 边界。

改动思路

实现严格复用现有 owner,而不是引入 provider-specific backup format:

  1. Source truth 仍是 AuthorityStore.loadAuthority/storeIdentity/scanCommitted;每条 transaction 保存 operation/events/receipts,加上用现有 authority_state_log codec 计算的 state delta。
  2. Archive 是 versioned NDJSON:header 固定 goal/provider/store identity/cursor/revision/final projection digest;transaction 必须 cursor 连续、operation 唯一、previous digest 连续,且逐条重建后的 projection hash 精确匹配;terminal seal 必须覆盖 header 指定的 captured head,且后面只能 EOF。
  3. Export 允许 source 在 captured cursor 之后继续 append,但不允许 captured prefix、store identity 或 captured head 被改写。完成文件先独立 verify,再用同目录 hard link 发布,已有 output 永不替换。
  4. Restore 先完整 verify 和 digest bind,再只对缺失 suffix 执行 commit;每次写后读取 exact journal row 和 receipt。lost response 不触发第二次 write,而是由 readback 证明;existing prefix 必须逐条 semantic match,extra/divergent target 直接拒绝。
  5. Python CLI 只做参数/路径传输;格式、state reconstruction 和 write authority 全在 TypeScript。Local restore 只能进入新建或 exact-bound 的私有目录,永远不注册为 active runtime。

具体改动

  • authority_archive.ts 新增严格 decoder、streaming exporter 和 resumable exact-prefix restorer。
  • local_authority_archive.ts 新增本地 admin envelope:verify 纯读、export 读取 registry-selected source、restore preview 不创建目录、--execute 才创建 0700 destination + binding + isolated store。
  • loopx authority-archive export|verify|restore 注册到 CLI;response 明确 authority_changed=falseexecution_authority_granted=false
  • authority-archive.md 给出可运行命令、digest readback、重复恢复、隐私边界、hash 非签名、PostgreSQL service boundary 和 separate-cutover 要求。
  • tests 覆盖真实 File/SQLite/PostgreSQL 双向恢复、CLI 独立进程 readback、corruption/re-signed tampering、source concurrent append、checkpoint window、interrupted/lost-ACK restore 和 occupied target。

关键代码讲解

  1. verifyAuthorityArchive / records:只有读到 seal 后又确认 EOF,才 yield verified。校验不是只看每行 checksum,而是边读边重建 state、检查 cursor/operation/revision/head,因此重新计算被篡改行的 hash 也无法绕过语义规则。
  2. exportAuthorityArchive:初始 head 是 captured boundary。它只扫描到该 cursor,然后检查最后一条 revision/重建 projection/store identity 与 captured header 一致;后续 append 被允许,不需要不必要地停止 source writes。
  3. restoreAuthorityArchive:source provider revision 只作为 provenance,destination 生成自己的 physical revision;semantic comparison 精确保留 cursor、operation、events、receipts、projection。对 ambiguous write,代码只 read back,不重发。
  4. manageLocalAuthorityArchive:restore-binding 把 destination 与 goal/archive digest/provider 绑定,existing unbound/mismatched directory 不会被“顺手采用”。即使成功,也只返回 requires_separate_authority_cutover=true
  5. handle_authority_archive_commandretry_safe=false,restore 需要显式 digest,并通过第二次 --execute 将 preview 与 mutation 分开;Python 不读取或构造 canonical journal。

对主干的风险

未发现 blocking finding。最重要的风险不是 JSON 能否 parse,而是一个最终 state 正确但历史不完整的 artifact 被误认为可恢复。本实现通过 cursor continuity、unique operation、逐 transaction projection hash、receipt readback 和 terminal head/seal 把这一反例锁死。

我执行的验证:

  • latest-main integration merge:干净,无冲突;git diff --check 通过。
  • npm run typecheck:control-plane:通过。
  • authority_archive.test.ts:22/22 passed,包含 File/SQLite roundtrip、tampering、concurrent append、interrupted/lost-ACK recovery 和 divergent target。
  • test_authority_archive.py:2 passed,走真实 source-checkout CLI 的 export/verify/preview/execute/repeat readback。
  • 我启动了一个隔离的本地 PostgreSQL 16 实例并运行 authority_archive_postgresql.integration.test.ts:4/4 passed(File↔PostgreSQL、SQLite↔PostgreSQL);实例在测试后停止,没有接触任何活跃 Goal 或共享数据库。

最初 detached worktree 没有 node_modules,PostgreSQL test 在 import pg 前失败;绑定仓库依赖树后真实测试通过。这是 setup gap,不是 product failure。

语义与 CI 对齐

新的 loopx_authority_archive_v0 是对已有 AuthorityStore retention/state-delta vocabulary 的合理扩展。它准确叫 archive/isolated restore,没有把 artifact 描述成 provider activation、rollback 或 execution authority。archive_sha256 明确是 record-chain terminal digest,不是 raw file SHA,也不是 author signature;文档把 authentication/tenant/cutover 留给现有 owner。CLI availability 不会 activation,normal runtime、scheduler、quota、frontend、Lark 和 provider selection 均保持原行为。packet 明确 wait_for_ci=false,本结论不依赖远端 CI。

我的整体评价

这个 exact head 的范围虽然超过一千行,但生产机制集中:271 行核心 format/replay、65 行本地隔离 adapter、63 行 Python transport,其余主要是公共文档和真实后端/negative coverage。它没有为了“以后可能迁移”新造 provider abstraction,而是直接复用了 AuthorityStore 与 state-delta owner;对恢复这种低频高后果能力,这个严格度和体量是合理的。

剩余风险也被正确限定:hash chain 只在现有 filesystem trust boundary 内检测 corruption/bind reviewed content,不认证作者;production tenant auth、大体量长 soak、credentialed service integration 和 active provider cutover 仍是独立 gate。由于这是 control-plane/runtime 与 persisted recovery contract,仍必须由 maintainer 决定合并,我没有执行 merge。

English verdict: APPROVE — the exact head provides a complete, verified, provider-neutral journal recovery artifact while keeping restored state isolated from active authority; no blocking finding remains.

@huangruiteng
huangruiteng merged commit c41e094 into main Sep 22, 2026
26 of 28 checks passed
@huangruiteng
huangruiteng deleted the codex/authority-portable-recovery-0922 branch September 22, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant