Skip to content

fix(cli): bind queued exports to collecting accounts - #94

Merged
callumflack merged 2 commits into
mainfrom
callum/account-bound-exports
Oct 11, 2026
Merged

callumflack merged 2 commits into
mainfrom
callum/account-bound-exports

Conversation

@callumflack

Copy link
Copy Markdown
Member

An interrupted export collected under Account A could upload under B after switching both login and Personal Server. Recollecting under B could also overwrite A's accepted bytes.

Base: main. PRs #92 and #93 are merged; this PR has no open PR dependencies.

Bind each accepted export to its collecting Account deployment/address, exact file and SHA-256. Both runtimes write unique result paths. Save the receipt before uploading, and condition publication/settlement on the accepted export generation.

Deferred sync requires matching collection identity, reported server ownership and unchanged bytes. Conflicting or unattributed pending data stays local; manual sync exits 5 and automatic/scheduled collection reports sync_pending. Returning to A can recover with an expired Account login and a valid Personal Server session without recollection.

Tradeoff: one accepted export per source. B cannot recollect that source while A's export is unresolved. Legacy/unattributed exports remain inspectable and cannot be silently adopted. Fresh accountless upload and explicit destination consent remain scoped to that collection.

Verification:

  • Actual built-CLI regression: 4 failures before the fix; all 15 ownership/recovery/partial-sync/race cases pass after it.
  • pnpm validate: 844 tests pass, plus TypeScript, ESLint and formatting.
  • pnpm preflight:cli: focused tests, build, formatting and isolated transcript generation. Local demo HTTP and native browser launches were disabled; this is not native or hosted proof.
  • Independent test/comment review and structured code review.
  • Shared fake demo seed uses an expired synthetic Account and matching export receipt; it preserves the local-inspection example without bypassing the new guard.

All export/upload fixtures are fake and local. No provider or real-account effects were used.

@callumflack

Copy link
Copy Markdown
Member Author

PASS+NOTES — independent CLI shipping verification

I did not author this patch. I verified the actual CLI on the current base and PR head using Poteto Shipping with the Codex adapter and control-cli. Cursor cloud was unavailable, so this was an independent local verification lane. GitHub CLI was used; Origin was unavailable.

  • Base (main): 6a8aa6fe2277245319c5a996444a27d4557beaa1
  • Head: 2d44c6af9fdf4795bc489711cc8a167056b653be
  • Merge-base: 01986d6b130b8d7d249438249e5f1a3619566678
  • Stable patch-id of git diff <base>...<head>: 22401ab19e94e03e3ebb7e8c24e761a466d290b7

Both checkouts built successfully with node node_modules/typescript/bin/tsc --build followed by node scripts/copy-vendor.mjs, using existing dependencies without installation. The standalone account-switch harness launched each built CLI against isolated fake accounts, a fake connector, and a loopback HTTP server. Both runs first collected under A and reached persisted ingest_failed state after the interrupted upload.

Actual CLI observations for collect A → interrupted upload → switch account/server to B → server sync:

BASE: FAIL collect A, interrupt upload, switch to B, sync: exit=0, uploadsToB=1, exportPreserved=true
HEAD: PASS collect A, interrupt upload, switch to B, sync: exit=5, uploadsToB=0, exportPreserved=true

The base's invariant failure is expected and reproduces the original bug. The head blocks the foreign upload and preserves A's saved bytes.

Head regression suite: node node_modules/vitest/vitest.mjs run test/cli/export-owner.test.ts — 15/15 tests passed, 1/1 file passed. This includes return-to-A recovery with expired Account login and a valid Personal Server token, legacy/malformed receipts, changed bytes, missing server owner, deployment mismatch, fresh accountless upload, automatic/scheduled reporting, competing publication, and stale settlement.

No actionable behavioral/spec findings from independent source and caller review. Two non-blocking documentation notes remain: the public guarded updateSourceState overload at src/core/state-store.ts:126 lacks TSDoc describing publish/settle, false meaning no write, and the preserved two-argument Promise<void> contract; src/core/source-export.ts:44 could say “SHA-256 of the exact accepted file bytes” to avoid implying hashing precedes parsing. Neither changes the observed behavior.

Proof limits: these are actual local CLI executions with fake credentials/connectors and a fake HTTP server, not real provider, native-app, hosted-account, or production proof. No real credentials or provider accounts were used. No source/test files were changed. Both verification checkouts were clean after execution. This verdict applies only to the head and patch-id recorded above; the root shipping agent owns the final live checks and merge.

@callumflack
callumflack merged commit fa20249 into main Oct 11, 2026
7 checks passed
github-actions Bot pushed a commit that referenced this pull request Oct 11, 2026
## [0.41.2](v0.41.1...v0.41.2) (2026-10-11)

### Bug Fixes

* **cli:** bind queued exports to collecting accounts ([#94](#94)) ([fa20249](fa20249))
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.41.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant