Skip to content

fix(project): preserve canonical raw-carrier data in filesystem saves (#553) - #802

Merged
qnbs merged 10 commits into
mainfrom
feat/553-fs-canonical-save-20260921
Sep 21, 2026
Merged

qnbs merged 10 commits into
mainfrom
feat/553-fs-canonical-save-20260921

Conversation

@qnbs

@qnbs qnbs commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Purpose / root cause

Existing CURRENT Tauri filesystem saves previously serialized the typed StoryProject projection directly. That could discard opaque persisted fields and round unsafe numeric literals even though the canonical project boundary retained the original raw JSON carrier.

Exact implementation

PR #802 keeps new-project creation unchanged and routes existing project.json files through FsProjectStore.persistExistingCanonicalProject. The bounded path reads and decompresses the stored text, admits it as CURRENT canonical data, computes the source generation, builds only the owned autosave edit, commits through commitOwnedProjectEdit, requires a COMMITTED result, compresses the returned raw carrier, and performs the existing atomic filesystem replacement. The final structural correction extracts the raw-carrier array/object merge branches without changing their recursive semantics.

Invariants preserved

  • Preserve-first raw-carrier writeback keeps opaque top-level and nested fields and exact raw numeric tokens, including 9007199254740993.
  • Only CURRENT admitted sources may be written; generation fencing, owned-edit-only mutation, verification, and fail-closed refusal remain in force.
  • ProjectCanonicalWritebackError retains project ID and technical detail while exposing the stable UI-safe refusal message.
  • Omitted owned optional fields are removed explicitly; schemaVersion removal is rejected; refused saves do not create auto-snapshots.
  • Existing marker ordering and atomic replacement semantics are unchanged; marker failure remains best-effort only after the primary project save succeeds.

Explicitly out of scope

No StorageManager/backend-routing change, IndexedDB fallback removal, dual-write, legacy migration redesign, recovery/retry redesign, Rust Core or Qt authority switch, or R-15 implementation is included. The existing Tauri filesystem-first path and its non-canonical IndexedDB fallback boundary remain outside this writer slice. The external-writer check/rename race still requires a native cross-process CAS or lock and is an explicit follow-up; this PR does not claim to solve it. #553 is not being closed by this PR.

Regression proof

The focused filesystem, autosave-bridge, and writeback tests cover opaque top-level and nested descendants, exact unsafe and unchanged numeric tokens, owned-field removals, CURRENT admission, schemaVersion refusal, source-read/replacement refusal, generation fencing, and refused-save snapshot isolation. The current source and tests contain no typed JSON.parse-to-projection-to-JSON.stringify reconstruction of the canonical raw carrier.

Validation

  • Focused Biome: passed.
  • Focused Vitest bridge/writeback suites: passed.
  • pnpm run typecheck: passed.
  • git diff --check: passed.
  • Prospective PR budget: OK within the absolute governance ceiling.
  • Local CodeScene staged delta: production mergeRawCarrierValue Bumpy Road Ahead fixed; no suppression added.
  • pnpm run ci:prepush: passed.
  • Exact-head native CI/CD run 35579547583: passed, including Node 22/24, Playwright E2E, Build, Storybook, Deep E2E, Browser Quality, security, signatures, and governance.
  • Exact-head CodeQL, CodeAnt, Semgrep, GitGuardian, Socket, Vercel, Codecov, CHANGELOG, attribution, and reviewer-governance checks: passed.

Exact-head review dispositions

  • CodeScene result 7627569 has only the test-duplication maintainability finding; the introduced production-code critical finding is gone. Test duplication remains FOLLOW_UP/ADVISORY_ONLY because the behavior-specific refusal tests should not be mechanically obscured.
  • CodeFactor currently exposes six Complexity entries tied to older revisions b907d8a, 93e986d, and 1c7ce93; no finding is attached to exact head 4bd3b33. These historical/pre-existing analyzer entries are FOLLOW_UP/ADVISORY_ONLY, not a source blocker.
  • The current P1 review claims about owned-field removal, opaque descendants, outline handling, numeric-token preservation, schemaVersion removal, and refused-save snapshot isolation were revalidated against exact head 4bd3b33 and resolved as fixed/outdated. The native external-writer CAS limitation remains explicitly deferred.
  • Cubic, DeepSource, and Sourcery are provider-skipped/quota states and were not manually retriggered.

This is a bounded #553 filesystem raw-carrier writeback slice. It does not claim universal persistence parity, complete #553 acceptance, Rust Core observation-ledger completion, or native authority migration.

The Tauri filesystem save path previously serialized the typed StoryProject projection directly. That could discard opaque persisted fields and round unsafe numeric literals even though the canonical project boundary retained the original raw JSON carrier.

Existing CURRENT files now pass through canonical admission and the shared owned-edit/writeback verifier before the atomic filesystem replacement. The autosave bridge accepts both Redux ProjectData and flat StoryProject inputs, and raw JSON compression preserves the admitted text instead of parsing and reserializing it. New-project creation remains unchanged; non-CURRENT sources still fail closed.

The slice deliberately does not change storageService backend selection, add an IndexedDB fallback or dual-write, migrate legacy data, or switch Rust Core authority. The existing native filesystem-first path is hardened while its existing non-canonical fallback remains outside this writer. Technical refusal detail is retained on the error object but the public message is safe for UI callers.

Validation:
- git diff --check
- pnpm exec biome check services/projectAutosaveEditBridge.ts services/fs/fsCore.ts services/fs/projectFsStore.ts tests/unit/services/fs/fsStores.test.ts
- vitest run tests/unit/services/fs/fsStores.test.ts tests/unit/services/projectAutosaveEditBridge.test.ts --reporter=dot (131 tests passed)
The repository docs guard derives the Vitest test count from the current source set. Adding the filesystem raw-carrier regression increased that count from 8102 to 8103 while the 616-file count and all other metrics stayed unchanged.

The generated README badge, technology table, directory-map note, and metrics section are synchronized through the existing metrics script; no product or test behavior was changed.

Validation:
- pnpm run sync:readme
- git diff --check
- cumulative PR budget: 5 files, 105 meaningful lines, 2 commits
@vercel

vercel Bot commented Sep 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
worldscript-studio Ready Ready Preview Sep 21, 2026 8:46am UTC

@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 3b539403-0a48-4cc5-9933-3e9c58baeb48

📥 Commits

Reviewing files that changed from the base of the PR and between c08d905 and 3a3bc48.

📒 Files selected for processing (7)
  • README.md
  • services/fs/fsCore.ts
  • services/projectAutosaveEditBridge.ts
  • services/projectDocumentWriteback.ts
  • tests/unit/services/fs/fsCore.test.ts
  • tests/unit/services/projectAutosaveEditBridge.test.ts
  • tests/unit/services/projectDocumentWriteback.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • README.md
  • tests/unit/services/projectAutosaveEditBridge.test.ts
  • services/projectAutosaveEditBridge.ts
  • services/projectDocumentWriteback.ts

Included review availability: 3 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.


📝 Walkthrough

Walkthrough

Existing CURRENT Tauri project files now use raw-text-preserving canonical writeback. The save path applies generation-checked owned edits and fails closed for unsupported sources. New files use direct creation. Tests cover these behaviors.

Changes

Canonical project writeback

Layer / File(s) Summary
Owned edit and raw JSON contracts
services/projectAutosaveEditBridge.ts, services/projectDocumentWriteback.ts
Autosave edits restrict ownership to declared fields, support removals and preserved collection fields, and accept ProjectData or StoryProject. Raw JSON handling preserves numeric tokens that would change during round-tripping.
Canonical commit and verification
services/projectDocumentWriteback.ts
Writeback applies replacements and removals, preserves opaque entity fields when requested, and verifies the resulting document.
Canonical save and refusal handling
services/fs/projectFsStore.ts, services/fs/fsCore.ts
Existing files use CURRENT admission, generation-checked writeback, raw JSON compression, and asynchronous pre-replacement checks. Failures raise ProjectCanonicalWritebackError; missing files use direct creation.
Regression coverage and project records
tests/unit/services/fs/fsStores.test.ts, tests/unit/services/fs/fsCore.test.ts, tests/unit/services/projectAutosaveEditBridge.test.ts, tests/unit/services/projectDocumentWriteback.test.ts, CHANGELOG.md, README.md
Tests cover preservation, removals, numeric tokens, generation conflicts, refusal cases, callback ordering, and temporary-file cleanup. The changelog and test-count references are updated.

Priority: ➖ Normal


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

@sourcery-ai

sourcery-ai Bot commented Sep 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Existing project saves now use admitted, generation-checked raw-carrier writeback instead of full reserialization, preserving opaque fields and exact numeric tokens while exposing a stable refusal error on unsafe writes; focused tests and repository test-count documentation are updated.

Sequence diagram for admitted filesystem project save

sequenceDiagram
    participant Store as FsProjectStore
    participant FS as Filesystem
    participant Admission as admitCanonicalProjectDocument
    participant Edit as buildAutosaveOwnedProjectEdit
    participant Writeback as commitOwnedProjectEdit

    Store->>FS: exists(projectFile)
    alt new project file
        Store->>FS: writeTextFileAtomic(compressData(projectToPersist))
    else existing project file
        Store->>FS: readTextFile(projectFile)
        Store->>Admission: admitCanonicalProjectDocument(currentRaw, storedProjectSchema)
        Admission-->>Store: canonical raw carrier
        Store->>Edit: buildAutosaveOwnedProjectEdit(projectToPersist, currentRaw)
        Store->>Writeback: commitOwnedProjectEdit(expectedGeneration, currentRaw, edit)
        alt COMMITTED
            Writeback-->>Store: updated raw carrier
            Store->>FS: writeTextFileAtomic(compressJsonText(writeback.raw))
        else refused
            Writeback-->>Store: conflict or non-admitted result
            Store-->>Store: ProjectCanonicalWritebackError
        end
    end
Loading

File-Level Changes

Change Details Files
Adds raw-carrier-preserving writeback for existing filesystem project saves.
  • Reads and decompresses the existing project JSON before saving.
  • Admits the source and commits only owned autosave edits against its generation.
  • Preserves opaque fields and unsafe integer tokens while updating project-owned data.
  • Adds a stable save-refusal error boundary for read, admission, and writeback failures.
services/fs/projectFsStore.ts
services/projectAutosaveEditBridge.ts
services/fs/fsCore.ts
Extends compression and autosave helpers to support already-serialized JSON and filesystem-shaped project data.
  • Introduces compression of raw JSON text without parse/reserialize cycles.
  • Allows autosave edits to consume either editor state or StoryProject entity arrays.
services/fs/fsCore.ts
services/projectAutosaveEditBridge.ts
Adds focused coverage for filesystem raw-carrier preservation and updates documented test counts.
  • Verifies edited fields change while opaque data and exact large numeric tokens survive.
  • Updates README test-count badges, tables, and metrics from 8102+ to 8103+.
tests/unit/services/fs/fsStores.test.ts
README.md

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codeant-ai

codeant-ai Bot commented Sep 21, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 4bd3b333
Scan Time: 2026-09-21 09:25:36 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 3.8% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

The PR reference guard requires the real GitHub PR number in the Unreleased release notes, which only exists after the draft PR is created.

This metadata-only bootstrap correction records the bounded #553 filesystem raw-carrier save change as PR #802. It intentionally changes no source, tests, runtime behavior, or release version.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@codecov

codecov Bot commented Sep 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 36 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
services/projectDocumentWriteback.ts 78.66% 3 Missing and 13 partials ⚠️
services/projectAutosaveEditBridge.ts 81.66% 0 Missing and 11 partials ⚠️
services/fs/projectFsStore.ts 78.57% 5 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

…on (#553)

Root cause:
The existing-project filesystem save path combined orchestration, canonical admission, raw-carrier writeback, and refusal-detail branching in saveProjectUnlocked. That structure obscured the preserve-first boundary and left the newly added fail-closed paths under-covered.

Correction:
Extract the existing CURRENT-project sequence into persistExistingCanonicalProject and resolve writeback refusal details through an explicit status helper. Add regression coverage for non-CURRENT admission and source-read failure, asserting the stable UI-safe error while proving the stored source remains unchanged. Synchronize README test metrics through the repository-owned script.

Preserved invariants:
The helper still reads and admits the stored raw carrier, requires CURRENT admission, computes the generation fence, applies only the owned autosave edit, requires COMMITTED, and atomically replaces the compressed raw carrier. Opaque fields, unknown nested data, exact unsafe numeric tokens, fail-closed refusal, marker ordering, and the public error boundary remain unchanged.

Scope boundaries:
New-project creation, StorageManager routing, IndexedDB fallback behavior, dual-write, legacy migration, Rust Core authority, Qt/R-15 work, and retry or recovery semantics are unchanged. CodeFactor/CodeScene structural findings are addressed only for saveProjectUnlocked; unrelated pre-existing complex methods remain follow-up/advisory.

Validation:
- Biome focused check: passed
- Vitest fs/autosave focus: 133 tests passed
- pnpm run typecheck: passed
- git diff --check: passed
- pnpm run pr:budget -- --base origin/main --prospective: OK (6 files, 164 meaningful lines, 4 commits)
- cs delta --staged: saveProjectUnlocked Bumpy Road Ahead fixed; Code Health 3.90 -> 4.02
- PR_BUDGET_BASE=<verified PR base> pnpm run ci:prepush: passed
codescene-access[bot]

This comment was marked as outdated.

@qnbs qnbs changed the title chore(meta): reserve PR number fix(project): preserve canonical raw-carrier data in filesystem saves (#553) Sep 21, 2026
@qnbs
qnbs marked this pull request as ready for review September 21, 2026 01:38
@codeant-ai

codeant-ai Bot commented Sep 21, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed 4bd3b33 Sep 21, 2026 · 08:45 08:45
✅ Incremental review completed 3a3bc48 Sep 21, 2026 · 06:52 06:53
✅ Incremental review completed 93e986d Sep 21, 2026 · 05:05 05:05
✅ Reviewed your PR 1c7ce93 Sep 21, 2026 · 01:39 01:42

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @qnbs, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 1 day and 22 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@codeant-ai

codeant-ai Bot commented Sep 21, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Sep 21, 2026
Comment thread services/fs/projectFsStore.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
services/fs/projectFsStore.ts (1)

747-747: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Use a conditional replacement when external edits are supported.

withLegacyRoutingOperation already serializes in-process project.json writers through the production FileSystemService. writeTextFileAtomic only serializes local atomic writes and performs an unconditional rename. If another process changes project.json after the read at line 722, line 747 can replace that newer file and discard its opaque fields. Add a filesystem-level compare-and-replace based on the admitted source generation, or document that external modification is unsupported. An application lock alone cannot coordinate a separate process.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: f3d03312-f07b-44d6-b56d-0c6dcd03a650

📥 Commits

Reviewing files that changed from the base of the PR and between 87dd85a and 1c7ce93.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • README.md
  • services/fs/fsCore.ts
  • services/fs/projectFsStore.ts
  • services/projectAutosaveEditBridge.ts
  • tests/unit/services/fs/fsStores.test.ts

Included review availability: 4 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Comment thread services/fs/fsCore.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c7ce93b3d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread services/fs/projectFsStore.ts Outdated
Comment thread services/fs/projectFsStore.ts Outdated
Comment thread services/fs/projectFsStore.ts
Root cause:
The canonical filesystem writer normalized read, admission, and commit refusals, but a failure during the final atomic replacement still escaped as a raw filesystem error. That broke the stable save-refusal boundary for callers even though the original source remained preserved.

Correction:
Wrap only the existing writeTextFileAtomic call in the canonical filesystem helper and retain the underlying technical failure as ProjectCanonicalWritebackError.detail. Add a regression test for atomic replacement failure and update the existing replacement-failure assertion to verify the stable public message plus technical detail. Synchronize the repository-owned README test metrics for the added test.

Preserved invariants:
The existing raw-carrier admission, CURRENT-only write authority, generation fence, owned-edit-only mutation, atomic replacement implementation, temporary-file cleanup, marker ordering, and preserve-first behavior are unchanged. The source file remains untouched when replacement fails.

Scope boundaries:
This is the single in-scope CodeAnt API-boundary correction for #553. It does not change StorageManager routing, IndexedDB fallback behavior, dual-write, migration, retry policy, Rust Core or Qt authority, recovery design, or any unrelated analyzer finding.

Validation:
- Focused Biome check and git diff --check passed.
- Focused Vitest fs/autosave run passed: 134 tests.
- PR_BUDGET_BASE=87dd85a3b9001770bc9838b65e9fc7f02d4d77c6 pnpm run ci:prepush passed.
- Local CodeScene staged delta was attempted but unavailable because the CLI could not create a network socket in this environment; remote CodeScene remains the authoritative review gate.
codescene-access[bot]

This comment was marked as outdated.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5ff60b0b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread services/fs/projectFsStore.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Convert source-inspection failures to the stable refusal error. · projectFsStore.ts:836

services/fs/projectFsStore.ts:836
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Convert source-inspection failures to the stable refusal error.

withLegacyRoutingOperation does not transform errors from its callback. If apis.exists(projectFile) rejects, the raw filesystem error reaches saveProject callers before either write path is selected. Catch this inspection failure and throw ProjectCanonicalWritebackError with the filesystem detail.

🧹 Nitpick comments (1)
services/fs/projectFsStore.ts (1)

836-836: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the required QNBS-v3 rationale comment.

Document why absent files use direct creation while existing files use raw-carrier writeback. For example: // QNBS-v3: create new sources directly; preserve admitted raw carriers only when replacing an existing source.

As per coding guidelines, “Bei jeder inhaltlich relevanten Änderung in TypeScript oder JavaScript einen einzeiligen Kommentar im Format // QNBS-v3: [...] ergänzen.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 6bd02df7-425f-4a0d-bfa5-7d4a7fa2f88b

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7ce93 and b5ff60b.

📒 Files selected for processing (3)
  • README.md
  • services/fs/projectFsStore.ts
  • tests/unit/services/fs/fsStores.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

Included review availability: 3 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Comment thread services/fs/projectFsStore.ts Outdated
#553)

Root cause:
- Full-snapshot autosave treated bootstrap-spread unknown top-level and entity properties as owned edits, so opaque values could be rounded or overwritten.
- An omitted owned optional field produced no removal edit, allowing stale persisted state to reappear after reset/import.
- Filesystem admission fenced only the initial read; an external replacement between readback and rename could be overwritten.

Correction:
- Whitelist declared ProjectData, Character, and World fields; merge only those fields over existing entities while preserving opaque raw members and all non-round-tripping numeric tokens.
- Add preserve-first removal of omitted owned optional top-level fields, while keeping existing filesystem-only legacy-routing metadata explicit in the filesystem store.
- Re-read and compare the admitted source generation immediately before each atomic filesystem rename, and fail closed through the stable ProjectCanonicalWritebackError boundary on a mismatch.

Invariants and scope:
- CURRENT-only admission, generation fencing, owned-edit verification, opaque-field survival, exact raw numeric tokens, atomic replacement, and stable UI-safe refusal text remain enforced.
- No StorageManager routing change, IndexedDB dual-write, fallback removal, retry redesign, Rust Core authority switch, Qt/R-15 work, or unrelated tooling change.

Validation:
- Focused Vitest suites: 179 tests passed.
- Changed-file Biome check, typecheck, and git diff --check passed.
- README synchronized to 8111+ tests / 616 files.
- PR budget OK: 8 files, 591 meaningful lines, 6 commits.
- ci:prepush passed.
- Local CodeScene review/delta unavailable because CLI 1.0.41 could not create a socket for license/telemetry in the constrained environment.
@codeant-ai codeant-ai Bot added size:XL This PR changes 500-999 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Sep 21, 2026
codescene-access[bot]

This comment was marked as outdated.

@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown

[check-pr-size] PR size is over the target tier (normal profile): 10 files, 1096 meaningful lines, 10 commits — limit ≤8 files / ≤400 lines / ≤6 commits. Consider splitting into smaller, independently reviewable PRs.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 93e986df6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread services/projectAutosaveEditBridge.ts
Comment thread services/fs/projectFsStore.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 161e41de-f43c-4bfc-b697-346efdd9c185

📥 Commits

Reviewing files that changed from the base of the PR and between b5ff60b and 93e986d.

📒 Files selected for processing (7)
  • README.md
  • services/fs/fsCore.ts
  • services/fs/projectFsStore.ts
  • services/projectAutosaveEditBridge.ts
  • services/projectDocumentWriteback.ts
  • tests/unit/services/fs/fsStores.test.ts
  • tests/unit/services/projectAutosaveEditBridge.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • services/fs/projectFsStore.ts

Included review availability: 4 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Comment thread services/fs/fsCore.ts Outdated
Comment thread services/projectAutosaveEditBridge.ts
Comment thread services/projectDocumentWriteback.ts
Root cause: the existing-project save path normalized read, admission, canonical writeback, and atomic replacement failures, but the project.json existence probe still ran outside the stable refusal boundary. A rejected probe could therefore expose a raw filesystem error before the save path selected direct creation or raw-carrier writeback.

Correction: catch only the project-source existence inspection and rethrow ProjectCanonicalWritebackError with the technical filesystem detail. Add a regression proving the stable UI-safe message, project ID, diagnostic detail, and unchanged stored source. Record the non-obvious absent-file versus existing-file persistence boundary with one English QNBS-v3 rationale. Synchronize README metrics through the repository-owned script for the added test.

Preserved invariants and scope: new-project creation remains direct; existing CURRENT sources still use admitted raw-carrier writeback, owned edits, generation fencing, verification, and atomic replacement. No StorageManager routing, fallback, dual-write, migration, retry, Rust Core, Qt, or R-15 behavior changed.

Validation: focused Biome passed; focused fs tests passed with 121 tests; autosave/writeback tests passed with 59 tests; README synchronization and docs/release truth passed; PR budget passed at absolute limits with 8 files, 624 meaningful lines, and 7 commits before this commit; ci:prepush passed sequentially, including CSP, native readiness, Tauri plugin parity, QNBS-v3, and TypeScript single-checker admission.
codescene-access[bot]

This comment was marked as outdated.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c08d905f9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread services/projectAutosaveEditBridge.ts
Comment thread services/projectAutosaveEditBridge.ts
Comment thread services/fs/fsCore.ts Outdated
Comment thread services/projectDocumentWriteback.ts
…553)

The exact-head review wave exposed remaining preserve-first gaps: omitted optional entity fields could survive a full-snapshot restore, parsed nested containers could round opaque numeric descendants, schemaVersion could be removed through the owned-edit API, and synchronous image admission yielded before rename.

Canonical autosave edits now carry per-entity removal metadata, overlay known nested world records onto the admitted raw carrier, and reject schemaVersion removal. Filesystem admission keeps synchronous checks adjacent to atomic replacement while asynchronous source checks remain awaited. These changes preserve opaque fields, exact numeric tokens, generation fencing, fail-closed admission, and atomic replacement without adding fallback or dual-write behavior.

The external-writer race between final filesystem revalidation and rename remains a native CAS/lock prerequisite and is intentionally not represented as solved by this bounded TypeScript slice. New-project creation, backend routing, Rust/Core authority, and R-15 remain unchanged.

Validation:

- pnpm exec biome check on all changed source/test files

- focused Vitest: 62 project writeback/autosave tests, 26 fsCore tests, and 121 fsStores tests passed

- pnpm run typecheck

- pnpm run ci:prepush
codescene-access[bot]

This comment was marked as outdated.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a3bc48baf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread services/projectAutosaveEditBridge.ts Outdated
Comment thread services/fs/projectFsStore.ts
Comment thread services/projectAutosaveEditBridge.ts Outdated
… success (#553)

A full-snapshot autosave could replace owned top-level values with a typed projection, dropping opaque descendants or rewriting unchanged unsafe numeric tokens. The filesystem store also scheduled recovery snapshots before canonical admission and writeback had succeeded.

Merge typed values over the admitted raw carrier recursively, match identity-bearing array entries by id, preserve unchanged RawNumberLiteral tokens, and use the preserve-first serializer for top-level overlays. Schedule the non-fatal auto-snapshot only after authoritative project replacement succeeds. Add regression coverage for opaque top-level descendants, unchanged unsafe numeric tokens, and refused-save snapshot isolation.

CURRENT-only admission, generation fencing, owned-edit verification, stable refusal errors, and atomic replacement remain unchanged. The external cross-process CAS/lock prerequisite remains explicitly out of scope; new-project creation, backend routing, fallback behavior, Rust Core, Qt, and R-15 are unchanged. CodeScene test-duplication remains advisory follow-up rather than an analyzer-only source wave.

Validation:

- Biome and git diff --check

- Vitest: projectAutosaveEditBridge 23/23, fsStores 122/122, projectDocumentWriteback 41/41, fsCore 26/26

- pnpm run typecheck

- PR budget: 10 files, 1080 meaningful lines, 9 commits, status OK

- PR_BUDGET_BASE=origin/main pnpm run ci:prepush
codescene-access[bot]

This comment was marked as outdated.

Root cause:
The recursive raw-carrier overlay kept number, array, and object dispatch in one function. That introduced nested conditional complexity in a newly added persistence helper and made the preservation boundary harder to audit, even though the behavior was correct.

Correction:
Extract raw-record detection, id indexing, array-entry selection, array merging, and object merging into narrowly named helpers. The public dispatcher now only selects the existing number, array, object, or scalar behavior.

Preserved invariants and scope:
RawNumberLiteral tokens remain byte-exact when the typed numeric value is unchanged; id-bearing arrays still match by stable id with index fallback; object overlays still preserve opaque descendants and delete explicit undefined values. Canonical admission, CURRENT-only authority, generation fencing, owned-edit verification, stable refusal errors, and filesystem atomic replacement are unchanged. No test-duplication cleanup, external CAS/lock work, fallback/routing change, or broader #553 expansion is included.

Validation:
- pnpm exec biome check services/projectDocumentWriteback.ts
- focused Vitest: projectDocumentWriteback and projectAutosaveEditBridge, 64 tests passed
- pnpm run typecheck
- local cs delta --staged: mergeRawCarrierValue Complex Method, Complex Conditional, and Bumpy Road Ahead fixed; Code Health 8.95 -> 10.00
- PR budget prospective: 10 files, 1096 meaningful lines, 10 commits, status OK
- PR_BUDGET_BASE=origin/main pnpm run ci:prepush: passed
@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Sep 21, 2026

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gates Failed
Prevent hotspot decline (1 hotspot with Code Duplication)

Our agent can fix these. Install it.

Gates Passed
2 Quality Gates Passed

Reason for failure
Prevent hotspot decline Violations Code Health Impact
fsStores.test.ts 1 rule in this hotspot 8.28 → 7.79 Suppress

See analysis details in CodeScene

Quality Gate Profile: The Bare Minimum
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@qnbs
qnbs merged commit a6397a9 into main Sep 21, 2026
46 of 48 checks passed
@qnbs
qnbs deleted the feat/553-fs-canonical-save-20260921 branch September 21, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant