Skip to content

Keep a project's agent from restoring notes into another project - #254

Merged
BunsDev merged 2 commits into
mainfrom
notes-restore-scope
Oct 16, 2026
Merged

BunsDev merged 2 commits into
mainfrom
notes-restore-scope

Conversation

@BunsDev

@BunsDev BunsDev commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Part of #241 (finding 2: scoped restore can write into another project).

What was wrong

Probe: project A has a page Plans with a child Old plan. The child is trashed, then the Plans folder is dragged into project B. A's agent calls the Pages restore tool on Old plan.

On main the restore succeeded. The trashed note's recorded path still pointed into A, so inScope passed. But NotesStore.trash(id, false) restores every ancestor of the note and restoreFile puts the note under its live parent. The note came back at Personal/theirs/Plans/Old plan.md (B), and Plans in B got a third version, "Restored page", attributed to A's agent.

What changed

apps/desktop/src/main/engine/pages-agents.ts: before a restore, the tool walks the note's parent and every ancestor above it (the same chain trash(id, false) restores) and refuses with "This note is outside this project's notes." if any of them is out of scope. This applies the existing revert check (which checks the historical parent) to every page the restore would touch. A note without a parent lands at its own trashed path, which the existing inScope(page.id) check already covers. Nothing in notes-store.ts changes, so this stays clear of the other #241 findings there (finding 3, stale trashed paths after moves, is a separate PR).

Regression test

apps/desktop/test/pages-agents.test.ts, "scoped Pages restore refuses a note whose parent folder moved to another project":

  • reproduces the probe and expects a refusal, with B's folder listing unchanged, B's parent page and its history unchanged, the note still trashed, and no receipt;
  • in the same project, restores a note whose trashed parent is in scope, and checks that both come back inside A.

On unmodified main the test failed at the refusal assertion (isError was undefined: the restore went through). A standalone probe against the built code showed the note landing under Personal/theirs/Plans/ and the parent page going from 2 to 3 versions. With the fix the probe gets the refusal and the parent page stays at 2 versions.

Verification

  • node --test dist/test/pages-agents.test.js: 17/17 pass
  • pnpm build && pnpm test from the worktree root: exit 0, no failures. Worktree scripts 107/107, core 16/16, desktop 1,437 tests (1,421 pass, 16 skipped, 0 fail) plus 21/21 native and update checks, site 10/10, store-desktop 16/16. The new test passed in the full run.
  • After rebasing onto Make Analytics look like the rest of Modex and fit the window #248 (which only moved the docs/status.md list), build:main and the pages-agents, notes-* and workspace-analytics test files: 95/95 pass.

Coven-Harness: Claude Code / claude-opus-5-5

Before: a note trashed in project A whose parent folder was later dragged
into project B could still be restored by A's agent. Its trashed path still
pointed into A, so the scope check passed, but the restore put the note
under its parent in B and wrote a "Restored page" version to that page.

After: the Pages restore tool checks the note's parent and every ancestor
above it, as revert already checks the old parent, and refuses when any of
them is outside the project's notes. Nothing is written and no version is
added. Restores inside the project, including ones that bring back trashed
ancestors, work as before.

Coven-Harness: Claude Code / claude-opus-5-5
@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
modex Ready Ready Preview Oct 16, 2026 4:24am UTC

Request Review

@github-code-quality

Copy link
Copy Markdown

Code coverage overview

Languages: TypeScript

TypeScript / code-coverage-agent

main 5efafc4 notes-restore-scope 2be8816 +/-
61% 61% 0%
View coverage of the 8 most impacted files.
File main 5efafc4 notes-restore-scope 2be8816 +/-
apps/desktop/sr...desktop-host.ts 98% 98% 0%
apps/desktop/sr...sktop-client.ts 99% 99% 0%
apps/desktop/sr...ne/companion.ts 97% 97% 0%
apps/desktop/sr.../notes-store.ts 98% 98% 0%
apps/desktop/sr...pages-agents.ts 100% 100% 0%
apps/desktop/sr...ngine/runner.ts 99% 99% 0%
packages/core/src/agent.ts 92% 92% 0%
apps/desktop/sr...vent-journal.ts 98% 99% +1%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.

@BunsDev
BunsDev merged commit 23c2b20 into main Oct 16, 2026
13 checks passed
@BunsDev
BunsDev deleted the notes-restore-scope branch October 16, 2026 05:32
BunsDev added a commit that referenced this pull request Oct 16, 2026
)

The paired iPhone flow failed three times on 2026-10-15/16 (CI runs
38707861067, 38747016392 and 38832248437, attempt 1 each) timing out on
the fixture's /resume. The simulator's unified log in each xcresult
(simctl_diagnostics/<device>/system.logarchive) shows two causes.

The UI-test runner makes no other request, so URLSession's first use in
that process came at this step, minutes after the simulator booted. Its
setup (cfprefsd, nsurlsessiond's storage container, trustd) decided most
of the outcome:

  run                     call to send     reply
  38747016392 a1 (red)    never in 44 s    -
  38707861067 a1 (red)    20 s             none in 8.4 s
  38832248437 a1 (red)    1.3 s            none in 10 s
  38832248437 a2 (green)  0.16 s           204 in 72 ms
  38836144274 a1 (green)  0.08 s           204 in 62 ms

resumeMac now POSTs over a plain BSD socket to 127.0.0.1, which needs
none of those daemons, within the suite's scaled budget. The old 10 s
request timeout was the one wait UITestBudget did not scale.

In 38707861067 and 38832248437 the POST did reach the fixture, which
neither answered nor brought the companion back (the phone kept getting
connection refused), yet exited at once on SIGTERM at the end of the
job. 38848571338 (PR #254) failed the same way at the first reply: the
fixture left a snapshot and then a TLS ClientHello unanswered for 5 s and
8 s, the app marked the Mac disconnected, and the create-thread tap
landed on the disabled button 0.2 s later. Locally the fixture answers
/resume in 3 ms after 8-80 s outages, and dispose() returns at once with
a phone connection in any state. The fixture writes no log, so that cause
stays open. It now logs the phone's calls, the pause and the resume, and
each event-loop stall of 1 s or more with the CPU time, major faults and
context switches during it.

Overall XCTest load did not separate red from green: green 38832248437
attempt 2 had the slowest app launches (54 s) of the five runs.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
… and the routines spec (#310)

v0.2.0 was published on 2026-10-18 (23:14Z) from the signed tag on 01b1b8a. This
follows Val's status layout (#290):

- Shipped gains a v0.2.0 row linking its release and its review.
- The 13 "On main, unreleased" items move unchanged to a new "Status summary"
  at the end of the v0.2.0 review, along with the old In flight entry. Each one
  was introduced by a commit in the tag (#248, #253, #254, #257 to #261, #263,
  #264, #266, #267, #269). The CF slices and #283's Markdown parser came after
  the tag, so they stay.
- In flight keeps a short follow-ups entry for the gates no receipt records yet:
  the independent download checks, the banner upgrade on the release Mac, and
  the Companion 0.2.0 (7) TestFlight upload.
- The routines spec and the README now say Phase 1, and most of Phase 0, ship
  from v0.2.0.

Unchanged: the website's static fallback (apps/site/release.json) and the
release evidence, which stay with the release owner. Claimed on #290.

Coven-Harness: Claude Code / claude-opus-5-5

This branch was successfully deployed

1 active deployment
Preview — 2be88161 Deployed Oct 16, 2026 by vercel[bot]
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