Skip to content

fix(ai-workspace): a write whose status save fails is reported as done, with a warning - #1416

Merged
Juliusolsson05 merged 7 commits into
mainfrom
fix/ai-workspace-write-status-warning
Sep 27, 2026
Merged

Juliusolsson05 merged 7 commits into
mainfrom
fix/ai-workspace-write-status-warning

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Fixes #1285.

Plan: docs/plans/2026-09-27-ai-workspace-write-status-warning.md, the first commit on the branch.

Problem

Change

  • AiWorkspaceRegistry.writeFile. A failed status refresh after a successful write no longer fails the write. It returns ok: true with an optional warning: new on AiWorkspaceWriteFileResult's success branch, documented as "the file IS written; do not retry". It logs the error, and still emits file-written to every workspace holding the file. The owed-copy invariant is unchanged: state is still never saved while a copy is owed.

  • preserveOwedCopy. A copy failure now says what is wrong and how to unblock it, keeping only the errno code: "AI Workspace storage needs attention: N unreadable row(s) must be copied aside before saving, and the copy could not be written (EISDIR). "

  • Review a (589d42a9):

    • The editor keeps a retained storageWarning from Save and Overwrite, shown through the file list's existing alert and cleared by the next clean write.
    • The warning is main's fixed sentence (AI_WORKSPACE_STATUS_NOT_SAVED), never a raw filesystem message.
    • changed emits are guarded, so a throwing listener cannot fail a landed write or starve other workspaces.
    • The refusal advice now depends on the errno: occupied path, missing folder, permissions or read-only, or full disk.
  • Review b (d8f1eae6):

    • The storage notice is now durable in main. get returns a runtime-only storageWarning while an owed copy cannot be written, and the editor reads it on every load, so a workspace switch or remount shows it again.
    • ENOTDIR gets the generic advice, and EROFS gets read-only-volume advice.
  • Verification b (5ea05e1d): get also reports AI_WORKSPACE_STATUS_NOT_SAVED while the last state save failed, even when the owed copy itself succeeded, until a later save succeeds.

Tests (AiWorkspaceRegistry.test.ts)

The input is the real recorded registry state, testing/fixtures/ai-workspace/real-workspaces-2026-09-25.json (two workspaces from the owner's file). Its paths are redacted, so one real entry is pointed at a temp file. One real workspace is made malformed so a copy is owed, as the #1260 tests do. The copy path is blocked with a directory.

  • The write. writeFile puts v2 on disk, returns ok: true with a warning that the file was saved but the status was not, and leaves the state file unchanged.
  • Red without the fix. The test is committed on its own, before the fix: { ok: false, error: 'is a directory' }.
  • The refused save. A following create is refused with the actionable message.
  • Mutations. Dropping the warning, and misreporting the write as failed, each fail the test.
  • Review a cases. The file reads back after the warning; an ordinary write has no warning; a throwing listener still gives ok: true; a missing folder gets ENOENT advice. The always-warn, unguarded-emit and wrong-advice mutations each fail.
  • Review b cases. get reports the blocked storage, clears it once the copy is written, and never persists it. The refusal carries no raw error text. A file shared by two workspaces notifies both, even when a listener throws.
  • Verification b case. The copy succeeds, then the state write fails: get keeps reporting the unsaved status, and a successful save clears it.
  • Not covered at component level: the editor's display of the notice. There is no AiWorkspaceEditor renderer harness, and the change there is three lines.

Verification

  • npx tsc -b clean.
  • src/main/aiWorkspace: 23 tests, run with --testTimeout 60000 under a machine load of about 60.

🤖 Generated with Claude Code

@Juliusolsson05 Juliusolsson05 added type:bug Something works wrong class:C3-silent-failure The app knows it failed and does not say sev:P3 Minor labels Sep 27, 2026
Juliusolsson05 and others added 3 commits September 27, 2026 05:20
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rite (#1285), red

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e, with a warning (#1285)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05
Juliusolsson05 force-pushed the fix/ai-workspace-write-status-warning branch from c4fb5e7 to 462a2e2 Compare September 27, 2026 12:22
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Process gate q107 (W1). History rebuilt, force-pushed over my own c4fb5e74: plan first (da06c8ce), then the fail-first test (ba94fef3, red), then the fix (462a2e22). The test now uses the real recorded registry state, not a hand-built one. It pins that the file is written and that ok:true carries the warning: dropping the warning or misreporting failure each fail it. The body is updated. Reviews start at this head. The one reviewer started earlier was closed before it reported.

…e-specific advice (review a)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for review a (head 589d42a9)

  • P2: the editor discards the warning. Fixed. A retained storageWarning, set from the result on both Save and Overwrite and cleared by the next write without a warning, is shown via the file list's existing alert. It is separate from error, which loadWorkspace clears after every save. The text is main's fixed AI_WORKSPACE_STATUS_NOT_SAVED sentence, which says "could not save its status" as you suggested. No raw filesystem message reaches the UI, and the cause is logged. There is no component-level test, because no AiWorkspaceEditor harness exists; this is stated in the body.
  • P3: a throwing changed listener turns a landed write into ok:false. Fixed. Each emit is guarded and logged. Test: a throwing listener gives ok:true, the file is v2, and the listener was called.
  • P3: wrong advice for non-collision errors. Fixed. The advice now depends on the code: EISDIR, EEXIST or ENOTDIR (occupied path); ENOENT (missing folder); EACCES, EPERM or EROFS (not writable); ENOSPC (full disk); anything else (check the folder is writable). Test: ENOENT gets the missing-folder advice.
  • Mutation survivors.
    • "Warning on every write" is now killed (an ordinary write has no warning).
    • "Suppress emits" is killed by the listener test.
    • "Drop the errno" is killed, since the EISDIR and ENOENT codes are asserted.
    • Reads after the warning are asserted.
    • "Synthetic refresh throw" and "clear knownFilePaths" are covered in practice by the read-back assertion, which needs knownFilePaths intact.

…DIR/EROFS advice (review b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for review b (head d8f1eae6)

  • P2: the warning disappears on a workspace switch, and is never shown if the switch happens mid-write. Fixed at the source.
    • Main now owns the fact. get returns a runtime-only storageWarning (AI_WORKSPACE_STORAGE_BLOCKED) while a copy is owed and its last attempt failed. The flag is set on the copy failure and cleared on success, and it is never persisted: get returns a copy.
    • The editor's loadWorkspace sets the notice from it on every load. A remount therefore shows it, and so does an editor that missed the write result because the switch happened mid-write.
    • Tests: get reports it; once the path is unblocked and a save succeeds, it clears; the state file never contains it. Dropping get's notice fails the test.
  • P3: ENOTDIR and EROFS advice. Fixed. ENOTDIR (a component of the folder path is a file) now gets the generic advice. EROFS gets "on a read-only volume".
  • Survivors. Fixed. Appending the raw error message to the refusal now fails the test, since raw text is asserted absent. Stopping after the first workspace's emit fails the new two-workspace test. The editor wiring is still untested at component level; that is stated in the body.

…e (review c nits)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for review c (MERGE-READY at 589d42a9; head is now 2bb9d19c)

  • 1–2: the emit on the warning path and the fan-out were unpinned. Fixed in d8f1eae6 (review b's round): a file shared by two workspaces notifies both even when the listener throws. The "stop after the first" mutation now fails.
  • 3: four advice branches are untested (EACCES/EPERM, EROFS, ENOSPC, generic). No change. Producing those errnos needs filesystem mocking in this real-filesystem suite. EISDIR and ENOENT are tested end to end.
  • 4: the editor display is untested. Acknowledged, as stated in the body. d8f1eae6 moved the durable signal into main's get, which is tested.
  • Nits. Fixed (2bb9d19c): the quoted refusal sentence in the plan and body now matches the code, and the test title reads "gives no warning on an ordinary write…".

…ate write (verification b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for verification b (head 5ea05e1d)

  • The status-save notice cleared when the copy succeeded but the state write failed. Fixed.
    • The registry now also tracks lastSaveFailed, set by any failed save step and cleared by a successful save.
    • get reports AI_WORKSPACE_STORAGE_BLOCKED while the copy is blocked, otherwise AI_WORKSPACE_STATUS_NOT_SAVED while the last save failed. The editor's next load, or a workspace switch, keeps the notice until a save really succeeds.
  • Test: your exact sequence on the real recorded state: blocked copy, unblock, state path made a directory, write gives ok:true with the warning and the copy exists, get still reports it, then a successful save clears it. Red on 2bb9d19c.

@Juliusolsson05

Copy link
Copy Markdown
Owner Author

READY (W1). Head 5ea05e1d. Exact-head CI is green. Every reviewer is MERGE-READY: a (verify-report-a), b (verify2-report-b), c (report-c). History is plan, then red test, then fix (manager gate q107). Dispositions for every round are posted above. merge-gate.sh --member: GATE PASS. Mergeable and clean.

@Juliusolsson05
Juliusolsson05 merged commit 3c30c11 into main Sep 27, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the fix/ai-workspace-write-status-warning branch September 27, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C3-silent-failure The app knows it failed and does not say sev:P3 Minor type:bug Something works wrong

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI Workspace writeFile reports failure after the file landed, when the owed-copy save fails

1 participant