Skip to content

fix(score): publish attachments through private bounded storage - #1241

Draft
seonghobae wants to merge 65 commits into
developfrom
fix/score-attachment-publication-1239
Draft

seonghobae wants to merge 65 commits into
developfrom
fix/score-attachment-publication-1239

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Owner / scope

Canonical Score Storage / Score Attachment lane for #1239. This PR owns write-time score confidentiality, bounded publication, no-clobber/object identity, abandoned-stage recovery, identity-safe cleanup, retention/removal authority, and the narrow Score UI orchestration that sequences project metadata acceptance against score-byte publication/deletion. It does not absorb #865 read-time allocation/content validation, #970 Project Persistence workspace/durable-project authority, or Resource Admission/MIR.

Protected base remains develop@314ddeae7b775a4957594b599358c8255617eb2e. #865 remains the foundation owner at 1f4877413e2eed30b224eaf1b095af3b0b905cb0; fresh compare is ahead 52 / behind 0. This branch stays Draft until #865 integrates.

Exact current head: bbfa0b5693590f12ef29d4a9545426f34ed558c1.

Publication / recovery contract retained

  • Descriptor-bounded 25 MiB copy; growth, truncation, wrong magic and oversize fail closed.
  • Unix stage creation requests 0600; Windows consumes and tests the app-owned parent-DACL inheritance contract.
  • No-clobber hard-link publication attests the destination to the synchronized stage object: Unix device/inode; Windows volume serial + 128-bit FILE_ID_INFO.
  • Windows stage cleanup and explicit deletion are handle-bound through FileDispositionInfo; Unix deletion pins the parent directory and uses openat(..., O_NOFOLLOW) + identity recheck + unlinkat, with the final basename race still explicit.
  • Removal returns false only for an observed missing child under an existing scores workspace; unsafe/indeterminate state remains an error.
  • A successfully returned attachment is proven readable through the production resolver/bounded reader from a fresh native process.
  • A current-contract writer that dies leaving only a synchronized reserved .score-<uuid>.stage is recovered under an OS-released cross-process workspace lease before the next publication. Stage+destination ambiguity remains preserved/fail-closed rather than guessed.

The process-abandoned staging repair remains the earlier source lineage: 2c55e34... realistic child-process stage fixture, ad2c463... lease/recovery owner, a1f21f1... crate-root routing, with current exact native evidence below. It does not claim packaged-Tauri process kill, every internal publisher instruction point, explicit cancellation, disk-full, permission failure, power loss, old unleased-build compatibility, or post-link lifecycle reconciliation.

Metadata/byte lifecycle ordering — source RED → causal repair

A second buyer-visible defect existed in ScoreView: attachment publication called onSongUpdate without awaiting a durability outcome and immediately opened the PDF; detach deleted the PDF first and only then updated attachment metadata. Once #970 supplies an async durable project mutation owner, those orders can respectively present an uncommitted attachment as accepted or leave durable metadata pointing at bytes the same interaction already destroyed.

Source-level RED 5450dbbb7dfdb449c393e1b4d94bc60559db4989 adds three component regressions:

  1. rejected async attachment metadata must not trigger PDF read/open acceptance;
  2. rejected detach metadata must not call destructive score removal;
  3. accepted detach must invoke metadata acceptance before Score Storage deletion.

The general ci run for that test-only head was superseded/cancelled by descendants before it produced a terminal verdict, so no hosted RED is claimed. The pre-repair source is deterministic RED for all three assertions because it neither awaited onSongUpdate on attach nor sequenced detach metadata before deletion.

Causal repair 48d77841da464e8b21d2a998b58f13ee5d23311d makes ScoreView consume void | boolean | Promise<void | boolean> from the project owner. false means the async owner did not durably accept the metadata. Attach then leaves the already-published PDF untouched as a recovery candidate and does not open/present it as accepted. Detach commits metadata first; only an accepted update may call remove_score_pdf. A later deletion failure can leave an unreferenced cleanup candidate, but cannot create a durable project reference to bytes this interaction already deleted. Legacy synchronous void remains compatible for the pre-#970 stack and is explicitly not durability evidence.

docs/traceability/score-attachment-publication.md is code-current at this head, including alternatives, Security Notes, test points and claim boundaries.

Security Notes

  • Untrusted input: selected PDF bytes/path and pre-existing score destination, stage, lock and retention names.
  • Trust boundary: OS-selected source → score admission → Score Storage lease/recovery → private staging → identity-attested publication; detach additionally crosses project-metadata acceptance before exact Score Storage deletion. fix(project): stage saves before atomic publication #970 still owns broader app-owned workspace ancestry/link/ACL and durable project state.
  • Safe failure: oversize/growth/truncation/wrong magic, destination collision, suspicious reserved state, symlink/reparse/non-regular object, identity mismatch, lease contention, missing/indeterminate workspace, unsafe resolution and stage+destination ambiguity fail closed. Rejected metadata acceptance never authorizes destructive delete.
  • Privacy: Unix stage/lock creation requests 0600; Windows consumes the parent DACL. Ordinary errors do not emit PDF bytes or absolute buyer paths.
  • Alternatives rejected: blind stage glob deletion, time/PID heuristics, deleting ambiguous stage+destination state, and byte-first detach before project metadata acceptance.

Exact-current-head evidence / remaining acceptance

Exact bbfa0b5693590f12ef29d4a9545426f34ed558c1 score-storage-native run 35473815994 is terminal SUCCESS on Windows Server 2025 job 105979458946 and macOS job 105979459071; both checked out the exact head and passed the owned Score Storage/recovery unit and native publication/retention/interruption/restart/wiring regressions.

That native workflow does not execute the new React lifecycle-ordering regression. Exact-head repository ci run 35473815972 is still queued, so frontend GREEN is not claimed. build-baseline run 35473815978 is in progress; Security Scan 35473815985, SBOM 35473815974, SAST Semgrep 35473815989, and CodeQL PR 35473815973 remain queued/pending. There is no qualifying independent non-author approval. Owner-native success is not repository-wide GREEN.

Still required: #865 protected integration; #970 workspace/durable-project reconciliation and fresh Windows ACL acceptance; restart reconciliation for PDF published -> metadata not durable; post-link interruption/cancellation/disk-full/permission/power-loss recovery; complete project deletion/recovery rollback; old unleased-build compatibility disposition; remaining Unix name-race disposition; exact frontend/coverage/security/SBOM/review settlement; packaged fault evidence; signing/notarization and immutable release/updater rollback. Do not force-push, weaken gates, or manufacture settlement.

UI Delivery Gate: FAIL — interaction ordering changed and has focused component regression source, but exact-head browser/E2E, keyboard/touch, Narrator/VoiceOver, 400% zoom, responsive and locale evidence were not added in this slice.

Commercial Release Gate: FAIL — stage-only process-abandonment and native storage evidence remain GREEN, and metadata-before-delete ordering is repaired in source, but exact frontend CI plus the lifecycle/release acceptance above remain open.

Integrate protected develop@749511c3ad4000090048718f685c6bee6b3d2c25 into the canonical #864 owner branch without rewriting history. Preserve the shipped npm/PDF security baseline and first-playable-range changelog truth while retaining the bounded native score-PDF read implementation and regressions.
Re-emit the unchanged #864 tree so cancelled/never-materialized current-head workflow evidence is replaced by fresh exact-head runs. No production, test, dependency, workflow, or documentation content changes.
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Collaborator Author

Fresh current-head sweep: source remains abed538d02e4813286ea62ac5c3aab914b89a774, Draft/Open/mergeable. score-storage-native run 35466662334 is terminal SUCCESS; repository-wide lanes are still nonterminal. In the current Actions inventory build-baseline run 35466662454 is queued (not in progress), and ci, Security Scan, SBOM, Semgrep, CodeQL PR plus required review/scheduler lanes are also queued. Formal reviews are empty and there are no review threads, so there is no qualifying independent approval.

The next source repair is intentionally not being guessed into this branch. Fresh product trace shows successful attachScorePdf publication is followed only by an in-memory onSongUpdate/setJobResult; there is no durable Project Persistence metadata commit coupled to the PDF publication. I recorded that lifecycle prerequisite on #970 and #1239. Until #970 exposes a durable reconciliation contract, the current stage + destination ambiguity must remain fail-closed/preserved rather than auto-deleted or auto-adopted.

No Ready transition, merge, blind rerun, no-op wake commit, or evidence transfer from predecessor heads.

Copy link
Copy Markdown
Collaborator Author

Owner-path update only; no Score Storage source change in this turn. #970 now has a Project Persistence descendant (5110271e0a350707a85d5efd45b1e1d03e7fdf53) that persists local-project mutations to a fixed native project.bscope target before accepted React state changes. This is the missing foundation for eventual score attachment metadata reconciliation, but it is not yet protected integration and its exact-head checks are still settling.

Keep #1241 Draft and do not infer that post-link interruption is solved. The remaining ambiguous window is still score PDF durable -> project metadata not yet durable; Score Storage must preserve/fail closed rather than delete or adopt based on filesystem presence alone. After #970/#962 has protected, restart-discoverable lifecycle semantics, reconcile #1241 by ordinary/non-force ancestry and reacquire exact Windows ACL/recovery evidence.

Copy link
Copy Markdown
Collaborator Author

Consumer contract finding for the later Project Persistence reconciliation: current ScoreView treats onSongUpdate as a synchronous void callback and does not await it. #970 now makes App.handleSongUpdate asynchronous so local-project React state changes only after fixed app-owned project.bscope persistence succeeds, but ScoreView currently clears isAttaching and opens the published PDF immediately after invoking the callback. Preserve #1241 source for now; once #970/#962 is protected/restart-discoverable, reconcile by ordinary/non-force ancestry and make attachment acknowledgement explicit/awaitable without deleting ambiguous published bytes on metadata-save failure. Detach is separately unresolved because storage deletion currently precedes durable metadata removal.

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