BLOCKER: require account authority before permanently emptying Trash #739

Open
opened 2026-10-02 13:07:53 +00:00 by kayg · 20 comments
Owner

F1 — BLOCKER: data authority can permanently empty Trash

crates/plugins/files/src/lib.rs:3492 calls Root::empty_trash through
home and user. Both use data authority. The route does not use
account_user, a recent assertion, or the Agent resource boundary to deny
permanent removal. The Files manifest requires data scope. The registry marks
empty_trash as a data mutation and exposes it on all three tool surfaces.

DESIGN §27 permits autonomous Agent container edits and deletes to Trash.
This route removes the recovery boundary: the Agent token can discard the
User's existing Trash. --confirm and tool hints cannot replace a route guard.
The same handler remains in merge-round-7a, at line 3615.

Fix: require account authority before any filesystem or Index change. Keep
ordinary Trash operations available to data authority. Update registry policy.
Regression checks: deny data-only Agent authority with unchanged Trash and
Index; permit the owning account session; retain read-only, Home-prefix and
other-User denial checks. Verify the same denial through CLI and MCP.

Audit context: sec-mcp-scopes job, base origin/dev c4a61e8cf0; merge-round-7a 2f4482ded also reviewed. Source review only. No product change or exploit reproduction. DESIGN §§21, 27, 41 and 55. Duplicate search: scope, MCP, injection, Trash and feed issue titles; existing #328/#329/#431 cover features, not these specific missing guards.

F1 — BLOCKER: data authority can permanently empty Trash `crates/plugins/files/src/lib.rs:3492` calls `Root::empty_trash` through `home` and `user`. Both use data authority. The route does not use `account_user`, a recent assertion, or the Agent resource boundary to deny permanent removal. The Files manifest requires data scope. The registry marks `empty_trash` as a data mutation and exposes it on all three tool surfaces. DESIGN §27 permits autonomous Agent container edits and deletes to Trash. This route removes the recovery boundary: the Agent token can discard the User's existing Trash. `--confirm` and tool hints cannot replace a route guard. The same handler remains in merge-round-7a, at line 3615. Fix: require account authority before any filesystem or Index change. Keep ordinary Trash operations available to data authority. Update registry policy. Regression checks: deny data-only Agent authority with unchanged Trash and Index; permit the owning account session; retain read-only, Home-prefix and other-User denial checks. Verify the same denial through CLI and MCP. Audit context: sec-mcp-scopes job, base origin/dev c4a61e8cf090170f35b1bed3350d9de20c83ecd5; merge-round-7a 2f4482ded also reviewed. Source review only. No product change or exploit reproduction. DESIGN §§21, 27, 41 and 55. Duplicate search: scope, MCP, injection, Trash and feed issue titles; existing #328/#329/#431 cover features, not these specific missing guards.
Author
Owner

Started scopefix on job/scopefix, base 2f4482ded066d9c5d9c59130377907f7fd2916c9 (merge-round-7a). Own #739, #743, #746 and #774. I will add account guards before Trash removal and Calendar grant changes, correct registry scope declarations, and mark MCP payloads as untrusted data. Existing route authorization and User boundaries remain in force. No push or deploy.

Started scopefix on `job/scopefix`, base `2f4482ded066d9c5d9c59130377907f7fd2916c9` (merge-round-7a). Own #739, #743, #746 and #774. I will add account guards before Trash removal and Calendar grant changes, correct registry scope declarations, and mark MCP payloads as untrusted data. Existing route authorization and User boundaries remain in force. No push or deploy.
Author
Owner

Confirmed the registry defect with a generator regression: 33 account checks fail before the fix (the 26 audited entries plus Trash emptying and six Calendar feed management entries). The plugin manifest still requires data on Files, Calendar and Notifications, so their declarations must include both data and account. User Background Work and Tab order require account only.

Decision: retain one reviewed contracts/action-authority.json supplement until OpenAPI security declarations cover these routes. The registry supplies the server account guard and all adapters. Permanent Trash removal and public Calendar grant mutations also declare fresh auth; management reads require account authority without step-up. This follows DESIGN §§21, 27 and 41.

Confirmed the registry defect with a generator regression: 33 account checks fail before the fix (the 26 audited entries plus Trash emptying and six Calendar feed management entries). The plugin manifest still requires data on Files, Calendar and Notifications, so their declarations must include both `data` and `account`. User Background Work and Tab order require `account` only. Decision: retain one reviewed `contracts/action-authority.json` supplement until OpenAPI security declarations cover these routes. The registry supplies the server account guard and all adapters. Permanent Trash removal and public Calendar grant mutations also declare fresh auth; management reads require account authority without step-up. This follows DESIGN §§21, 27 and 41.
Author
Owner

Web retry slice committed. bun run check: svelte-check found 0 errors and 0 warnings. Production web build passed. The first web test run had Test Files 1 failed | 153 passed (154) and Tests 1 failed | 1072 passed (1073): KeyboardShortcutsCard > puts the current view group first and closes with Escape timed out at 5000 ms on the shared host. No assertions changed. Rerun with bun run test --maxWorkers=2 --testTimeout=30000 --hookTimeout=30000 passed all 1073 tests. Rust dependency compilation is still active; no Rust completion claim yet.

Web retry slice committed. `bun run check`: `svelte-check found 0 errors and 0 warnings`. Production web build passed. The first web test run had `Test Files 1 failed | 153 passed (154)` and `Tests 1 failed | 1072 passed (1073)`: `KeyboardShortcutsCard > puts the current view group first and closes with Escape` timed out at 5000 ms on the shared host. No assertions changed. Rerun with `bun run test --maxWorkers=2 --testTimeout=30000 --hookTimeout=30000` passed all 1073 tests. Rust dependency compilation is still active; no Rust completion claim yet.
Author
Owner

Security source is now saved in concern-sized commits to survive host termination: Files recovery guard 8628dd6e2; Calendar grant guard 122c22030; registry route enforcement and scope-aware MCP discovery b61c2b25e; untrusted MCP results and legacy Log compatibility 3fbcecaea.

Rust gates remain pending. The first per-crate build has spent over an hour compiling dependencies on the shared host. These commits are not claimed to pass Rust gates and are not ready for merge without those results. Web checks/tests/build and the 14 registry tests have passed. The focused real-server round, benchmark and twelve production screenshot captures await this worktree's server binary.

Security source is now saved in concern-sized commits to survive host termination: Files recovery guard 8628dd6e2; Calendar grant guard 122c22030; registry route enforcement and scope-aware MCP discovery b61c2b25e; untrusted MCP results and legacy Log compatibility 3fbcecaea. Rust gates remain pending. The first per-crate build has spent over an hour compiling dependencies on the shared host. These commits are not claimed to pass Rust gates and are not ready for merge without those results. Web checks/tests/build and the 14 registry tests have passed. The focused real-server round, benchmark and twelve production screenshot captures await this worktree's server binary.
Author
Owner

Build finding: after more than an hour of dependency work, the four active compiler-cache clients for this job were sleeping in wait_woken, aged 81, 99, 133 and 145 seconds. No Rust gate result had completed. I stopped only this job's pending cargo processes and restarted a single sequential per-crate gate pipeline with RUSTC_WRAPPER empty. The preset target directory is unchanged; CARGO_BUILD_JOBS=4, CARGO_INCREMENTAL=0, CARGO_PROFILE_DEV_DEBUG=line-tables-only and worktree TMPDIR remain set. The API check is now compiling directly. This is a build-workflow change, not a product change.

Current HEAD: 628e7b85c. The production web build, web check, all 1073 web tests and all 14 registry tests passed. Rust gates and the real-server evidence are still pending.

Build finding: after more than an hour of dependency work, the four active compiler-cache clients for this job were sleeping in wait_woken, aged 81, 99, 133 and 145 seconds. No Rust gate result had completed. I stopped only this job's pending cargo processes and restarted a single sequential per-crate gate pipeline with RUSTC_WRAPPER empty. The preset target directory is unchanged; CARGO_BUILD_JOBS=4, CARGO_INCREMENTAL=0, CARGO_PROFILE_DEV_DEBUG=line-tables-only and worktree TMPDIR remain set. The API check is now compiling directly. This is a build-workflow change, not a product change. Current HEAD: 628e7b85c. The production web build, web check, all 1073 web tests and all 14 registry tests passed. Rust gates and the real-server evidence are still pending.
Author
Owner

Implemented scopefix; validation incomplete at the 3-hour limit. Not ready for merge.

Branch: job/scopefix. Base: 2f4482ded0. Head: 1f1ba91cc3.
The required origin/dev and origin/job/merge-round-7a merges both returned Already up to date. No push or deploy was performed.

Built:

  • Files checks owning account and data authority before the mutation lock, permanent Trash removal, Index changes or publication. A regression test checks unchanged Trash and Index after denial and owning account success.
  • Calendar public feed management checks account/data authority and the existing data_user resource boundary before writes. Subscriptions retain data authority.
  • Registry scope policy corrects the 26 audited mismatches plus set_user_plugin and the seven Trash/Calendar actions. The server applies the reviewed requirements before body extraction. MCP discovery filters declared scopes. Tests audit detected data/account/admin guards across declared handlers and exercise every eligible action through the registry middleware.
  • Generated and legacy MCP API results share an untrusted-data/provenance envelope; errors, server instructions, llms.txt and the public Skill carry the trust boundary. Legacy Log selection preserves the envelope after a successful write. Inert Note/Mail/search fixtures also check reserved payload fields.
  • Web Trash and Calendar grant writes use the existing passkey step-up helper.
  • A focused cross-User/admin/MCP/API/CLI probe, serial/burst bench profile and twelve production screenshot captures with macOS rendering are prepared.

UX gaps closed: stale account sessions retry through the shared passkey flow; cancelling the Trash check says Nothing changed. UX gaps left: real-server pointer/touch/keyboard walkthrough and all screenshots remain unverified.

Decisions: only reviewed declarations opt into central enforcement, to preserve existing public capability flows (including download_app_password_profile) and legacy route guards. Permanent removal and Calendar grant mutations require recent assertions; grant reads require account/data without an assertion. MCP payloads move under data with fixed trust/source fields; these identify API provenance, not content authorship, and do not guarantee prompt-injection prevention.

Verified gate output (verbatim excerpts):

registry-test.log

..............
----------------------------------------------------------------------
Ran 14 tests in 2.631s

OK

calternal-api-clippy.log

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 51s

calternal-api-test.log

    Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 12s
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-plugin-files-clippy.log

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 40m 00s

web-check-final.log

svelte-check found 0 errors and 0 warnings

web-test-head.log

 Test Files  154 passed (154)
      Tests  1073 passed (1073)
   Duration  664.68s (transform 31%, environment 28%, import 21%, tests 14%, setup 6%)

web-build-final.log

✓ built in 3m 13s
✓ built in 245ms
✓ built in 9m 2s
  Wrote site to "build"
  ✔ done

Exit statuses confirmed: cargo fmt --check 0 (no output); calternal-api clippy/test 0; calternal-plugin-files clippy 0; web check/test/build 0. Registry has 340 operations and 322 generated tools; all 14 registry tests pass. Python syntax, shell syntax, screenshot-script syntax and git diff --check pass.

Known gaps: Files tests were stopped during compilation; Calendar/server clippy and tests did not run to completion. The local server/CLI binary was not built, so no real-server round, MCP probe, bench measurements or screenshots ran. No measured performance regression claim is made; the profile includes notes.list baseline reference p50 1.3ms/p95 3.1ms, with no equivalent account-denial/envelope baseline. No screenshots exist to attach.

Build evidence: the first dependency build spent over an hour through the shared sccache. Active clients waited 81–145 seconds. Restarting this job without RUSTC_WRAPPER produced API gates and Files clippy; the latter finished in 40m 00s. The first default web test run had one 5000ms KeyboardShortcutsCard timeout; full runs with two workers and 30000ms timeouts passed. No existing assertion or fixture was changed to hide a failure. Remaining Rust checks must run before merge.

Source comments were re-read. No migrations or dependency versions were changed. Build output is being cleaned with cargo clean; web build output and Python caches were removed. Review artifacts remain gitignored.

Files:

  • apps/web/src/lib/files/api.ts
  • apps/web/src/lib/webmcp/tools.ts
  • apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte
  • bench/scopefix-739.py
  • contracts/action-authority.json
  • contracts/actions.json
  • crates/calternal-api/src/actions.rs
  • crates/calternal-server/src/agent_docs.rs
  • crates/calternal-server/src/agent_docs/skill_intro.md
  • crates/calternal-server/src/authz.rs
  • crates/calternal-server/src/mcp.rs
  • crates/calternal-server/src/wire.rs
  • crates/plugins/calendar/src/feeds/publication.rs
  • crates/plugins/files/src/lib.rs
  • docs/action-registry.md
  • docs/mcp.md
  • scripts/action_registry.py
  • scripts/test_action_registry.py
  • tests/adversarial/mcp_probe.py
  • tests/adversarial/run.sh
  • tests/adversarial/scopefix_review.mjs
Implemented scopefix; validation incomplete at the 3-hour limit. Not ready for merge. Branch: job/scopefix. Base: 2f4482ded066d9c5d9c59130377907f7fd2916c9. Head: 1f1ba91cc39ac1b8abcdec8c266919c5e3691302. The required origin/dev and origin/job/merge-round-7a merges both returned Already up to date. No push or deploy was performed. Built: - Files checks owning account and data authority before the mutation lock, permanent Trash removal, Index changes or publication. A regression test checks unchanged Trash and Index after denial and owning account success. - Calendar public feed management checks account/data authority and the existing data_user resource boundary before writes. Subscriptions retain data authority. - Registry scope policy corrects the 26 audited mismatches plus set_user_plugin and the seven Trash/Calendar actions. The server applies the reviewed requirements before body extraction. MCP discovery filters declared scopes. Tests audit detected data/account/admin guards across declared handlers and exercise every eligible action through the registry middleware. - Generated and legacy MCP API results share an untrusted-data/provenance envelope; errors, server instructions, llms.txt and the public Skill carry the trust boundary. Legacy Log selection preserves the envelope after a successful write. Inert Note/Mail/search fixtures also check reserved payload fields. - Web Trash and Calendar grant writes use the existing passkey step-up helper. - A focused cross-User/admin/MCP/API/CLI probe, serial/burst bench profile and twelve production screenshot captures with macOS rendering are prepared. UX gaps closed: stale account sessions retry through the shared passkey flow; cancelling the Trash check says Nothing changed. UX gaps left: real-server pointer/touch/keyboard walkthrough and all screenshots remain unverified. Decisions: only reviewed declarations opt into central enforcement, to preserve existing public capability flows (including download_app_password_profile) and legacy route guards. Permanent removal and Calendar grant mutations require recent assertions; grant reads require account/data without an assertion. MCP payloads move under data with fixed trust/source fields; these identify API provenance, not content authorship, and do not guarantee prompt-injection prevention. Verified gate output (verbatim excerpts): registry-test.log ```text .............. ---------------------------------------------------------------------- Ran 14 tests in 2.631s OK ``` calternal-api-clippy.log ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 4m 51s ``` calternal-api-test.log ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 12s test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` calternal-plugin-files-clippy.log ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 40m 00s ``` web-check-final.log ```text svelte-check found 0 errors and 0 warnings ``` web-test-head.log ```text Test Files 154 passed (154) Tests 1073 passed (1073) Duration 664.68s (transform 31%, environment 28%, import 21%, tests 14%, setup 6%) ``` web-build-final.log ```text ✓ built in 3m 13s ✓ built in 245ms ✓ built in 9m 2s Wrote site to "build" ✔ done ``` Exit statuses confirmed: cargo fmt --check 0 (no output); calternal-api clippy/test 0; calternal-plugin-files clippy 0; web check/test/build 0. Registry has 340 operations and 322 generated tools; all 14 registry tests pass. Python syntax, shell syntax, screenshot-script syntax and git diff --check pass. Known gaps: Files tests were stopped during compilation; Calendar/server clippy and tests did not run to completion. The local server/CLI binary was not built, so no real-server round, MCP probe, bench measurements or screenshots ran. No measured performance regression claim is made; the profile includes notes.list baseline reference p50 1.3ms/p95 3.1ms, with no equivalent account-denial/envelope baseline. No screenshots exist to attach. Build evidence: the first dependency build spent over an hour through the shared sccache. Active clients waited 81–145 seconds. Restarting this job without RUSTC_WRAPPER produced API gates and Files clippy; the latter finished in 40m 00s. The first default web test run had one 5000ms KeyboardShortcutsCard timeout; full runs with two workers and 30000ms timeouts passed. No existing assertion or fixture was changed to hide a failure. Remaining Rust checks must run before merge. Source comments were re-read. No migrations or dependency versions were changed. Build output is being cleaned with cargo clean; web build output and Python caches were removed. Review artifacts remain gitignored. Files: - `apps/web/src/lib/files/api.ts` - `apps/web/src/lib/webmcp/tools.ts` - `apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte` - `bench/scopefix-739.py` - `contracts/action-authority.json` - `contracts/actions.json` - `crates/calternal-api/src/actions.rs` - `crates/calternal-server/src/agent_docs.rs` - `crates/calternal-server/src/agent_docs/skill_intro.md` - `crates/calternal-server/src/authz.rs` - `crates/calternal-server/src/mcp.rs` - `crates/calternal-server/src/wire.rs` - `crates/plugins/calendar/src/feeds/publication.rs` - `crates/plugins/files/src/lib.rs` - `docs/action-registry.md` - `docs/mcp.md` - `scripts/action_registry.py` - `scripts/test_action_registry.py` - `tests/adversarial/mcp_probe.py` - `tests/adversarial/run.sh` - `tests/adversarial/scopefix_review.mjs`
Author
Owner

Independent read-only review started on branch job/rev2-scopefix, base 440e19dce2. Target 1f1ba91cc3. I will review scope enforcement, permanent Trash removal, Calendar grants, MCP result boundaries and passkey retries. LIGHT rules prohibit builds and tests. Findings will be committed in review-scopefix.md and audit-findings.md.

Independent read-only review started on branch job/rev2-scopefix, base 440e19dce23040ac8ebaae88f0469b6535b1afcb. Target 1f1ba91cc39ac1b8abcdec8c266919c5e3691302. I will review scope enforcement, permanent Trash removal, Calendar grants, MCP result boundaries and passkey retries. LIGHT rules prohibit builds and tests. Findings will be committed in review-scopefix.md and audit-findings.md.
Author
Owner

Independent read-only review complete. Head: 83edef3318. Files: audit-findings.md and review-scopefix.md. Target: 1f1ba91cc3. Final git fetch origin and git merge origin/dev output: Already up to date. git diff --check produced no output.

Independent review of scopefix (#739)

Review target: 1f1ba91cc39ac1b8abcdec8c266919c5e3691302.
Review base: 440e19dce23040ac8ebaae88f0469b6535b1afcb.
Review branch: job/rev2-scopefix.

Result: two P2 findings. No new blocking authorization or data-loss defect
was confirmed by source review. Fix F1 and add the regressions in F2.

Scope and method

Read CLAUDE.md, CONTEXT.md, DESIGN §§21, 27, 41, 55 and 58, and issue
bodies #739, #743, #746 and #774. Read the requested
git diff origin/dev...1f1ba91cc. This range includes merged work. Isolate
2f4482ded..1f1ba91cc to attribute the scopefix changes. Read route wiring,
authentication extensions, principals, grant handlers, result adapters,
registry generation, the benchmark and changed tests. Search for existing
scope, retry and principal helpers with rg. Read the test history with
git log -p. The author's report was not used as evidence.

LIGHT rules prohibit builds, tests, servers and browsers. All statements below
come from source. No exploit was run. File references refer to the target SHA.

F1 — P2: Calendar grant retry does not keep the submitted form

apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte:93 constructs
the request body inside the new withStepUp closure. The closure reads live
name, source, detail, Include notes, Include dated Tasks and colour.
selected, the source ID, was captured before the first request at line 86.
Only the submit button is disabled at line 210. Inputs, source choices,
Cancel and sheet dismissal remain available.

If the first request returns 403 because the assertion is stale, the passkey
check introduces a second wait. Form changes during either wait can make the
retry grant differ from the submitted grant. It can also combine a new source
with the original source ID. Closing the sheet at line 54 does not prevent
the retry from creating a grant or opening the returned link sheet.

Concrete fix: capture and validate one complete body before the first request.
Use the same body for both attempts. Disable edits while the action is pending.
Use a cancellation or generation check to prevent a second write after Cancel
or sheet dismissal. Reuse withStepUp; do not add another passkey flow.

Regression: hold the initial denial and assertion with deferred promises.
Change detail/source or dismiss the sheet. Assert an unchanged request body
for an active retry and no second request after dismissal. This test targets
the new retry behaviour. Privacy by default and UX completeness require the
grant to match the User's action; DESIGN §§21 and 41 require the shared auth
path.

Duplicate searches: passkey feed, Calendar feed, fresh auth. Track this
branch defect on #739. No separate issue is needed.

F2 — P2: Live freshness and passkey retries have no regression coverage

crates/calternal-server/src/authz.rs:224 tests freshness on a new test router,
with parse_json instead of the real handler. It inserts freshness itself and
covers only Trash. The broad matrix at line 142 always sets freshness true.
tests/adversarial/mcp_probe.py:302 checks App Passwords with no account scope.
Those requests cannot prove the denial of a stale owning account session.
tests/adversarial/scopefix_review.mjs:26 only navigates and captures screens.
No new web test checks a passkey retry or cancellation.

The tests would still pass if the new central guard were removed from
build_live_app: the test router installs its own guard, while the production
handlers still reject data-only principals. This leaves the live freshness
requirement without a regression check. This is a coverage defect, not proof
of a live authorization bypass.

Concrete fix: use the real server router with a stale owning account session.
Check Trash and all five Calendar grant mutations. Check unchanged Trash,
feed definitions and token hashes on denial. Refresh the same session and
check success. An assertion on another session must not help. Add web tests
for one retry after successful assertion and no retry after cancellation.
These tests must fail when route wiring or a sensitive fresh_auth declaration
is removed. Preserve existing status and state expectations.

Duplicate search: stale session. Track the branch coverage gap on #739.

Checks that support the change

  • empty_trash now calls the existing account_user before it takes the
    mutation lock. That helper also requires data authority and rejects a
    resource scope for another User. Ordinary Trash routes retain their guards.
  • Calendar grant management uses one principal helper for all six routes.
    It requires account authority, then reuses data_user for the owning User.
    Subscription routes are not changed by the scopefix commits.
  • The reviewed registry declarations require both data and account for Trash
    and grants. The five grant mutations and Trash also declare fresh auth.
    The central route layer is installed before the outer session layer wraps
    direct routes. The MCP inner router gets its own session resolution before
    dispatch. Freshness therefore comes from the validated credential.
  • The cached method/path table avoids a full registry scan on each normal
    route call. Existing data, resource and admin guards remain in place.
  • MCP envelopes use one helper for generated and legacy responses. User fields
    remain below data; they cannot replace fixed trust or source fields. The
    legacy Log helper now selects its entry below data, so it does not report
    a parsing failure after a successful batch write. Guidance states that the
    label does not stop prompt injection or grant authority.
  • The Files test checks unchanged Trash and an Index row on data-only denial.
    The principal test checks data-only, account-only and other-User denial.
    These tests would fail on the prior missing account guards.
  • Test history changes the MCP discovery expectation to omit account tools
    and unwraps the new result contract. It retains payload checks and adds trust
    checks. No weakened status or state expectation was found in the scopefix
    commits. F2 concerns missing coverage.
  • Comments in the changed product files explain the account boundary, fixed
    result metadata and legacy Log handling. The freshness test comment claims
    grant coverage that its body does not provide; correct it with F2.

Performance and reuse

The normal route check uses a cached table and a small scope comparison.
Calendar uses the existing data_user; Files uses account_user; the web
uses the existing passkey retry. No duplicate passkey implementation was added.

crates/calternal-server/src/mcp.rs:1251 scans the full registry for each listed
tool. Discovery remains proportional to tool count times action count. The
prior implementation also scanned for non-admin discovery. The new version
also scans for admin discovery. A cached action-name table could reduce this
work. This is an observation, not a measured regression or merge blocker.
bench/scopefix-739.py covers discovery, denied account calls, large Note reads,
CPU, RSS and a bounded burst. No benchmark was run in this LIGHT review.
DESIGN §58 defines discovery and setup; it does not set a latency threshold.

Validation and gates

Gate output: none. No gate command was run. The LIGHT job forbids Rust and web
builds and tests. No server, browser, adversarial round or benchmark was run.
git diff --check is the only final content check. No dependency was changed,
so no crate or package version lookup was needed.

Files and known gaps

Only audit-findings.md and review-scopefix.md are changed by this review.
No product code, lockfile or test was changed. Runtime behaviour, performance
numbers, screenshot quality and successful gate results remain unverified.
Both findings remain for the build job. No outside-branch defect was filed.

UX gaps closed

None. This is a read-only review.

UX gaps left

The Calendar retry can read changed form state and continue after dismissal
(F1). Successful and cancelled passkey flows need regression tests (F2).

Decisions

Use the last pre-scopefix commit to separate scopefix changes from merged work.
Keep branch findings on #739. Treat synthetic freshness coverage separately
from real route coverage. Do not run gates, clean build output or create UI
artifacts in a LIGHT job.

Independent read-only review complete. Head: 83edef331890882a9a57111c3daee35c2b73febd. Files: audit-findings.md and review-scopefix.md. Target: 1f1ba91cc39ac1b8abcdec8c266919c5e3691302. Final git fetch origin and git merge origin/dev output: Already up to date. git diff --check produced no output. # Independent review of scopefix (#739) Review target: `1f1ba91cc39ac1b8abcdec8c266919c5e3691302`. Review base: `440e19dce23040ac8ebaae88f0469b6535b1afcb`. Review branch: `job/rev2-scopefix`. Result: two P2 findings. No new blocking authorization or data-loss defect was confirmed by source review. Fix F1 and add the regressions in F2. ## Scope and method Read `CLAUDE.md`, `CONTEXT.md`, DESIGN §§21, 27, 41, 55 and 58, and issue bodies #739, #743, #746 and #774. Read the requested `git diff origin/dev...1f1ba91cc`. This range includes merged work. Isolate `2f4482ded..1f1ba91cc` to attribute the scopefix changes. Read route wiring, authentication extensions, principals, grant handlers, result adapters, registry generation, the benchmark and changed tests. Search for existing scope, retry and principal helpers with `rg`. Read the test history with `git log -p`. The author's report was not used as evidence. LIGHT rules prohibit builds, tests, servers and browsers. All statements below come from source. No exploit was run. File references refer to the target SHA. ## F1 — P2: Calendar grant retry does not keep the submitted form `apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte:93` constructs the request body inside the new `withStepUp` closure. The closure reads live `name`, `source`, `detail`, Include notes, Include dated Tasks and colour. `selected`, the source ID, was captured before the first request at line 86. Only the submit button is disabled at line 210. Inputs, source choices, Cancel and sheet dismissal remain available. If the first request returns 403 because the assertion is stale, the passkey check introduces a second wait. Form changes during either wait can make the retry grant differ from the submitted grant. It can also combine a new source with the original source ID. Closing the sheet at line 54 does not prevent the retry from creating a grant or opening the returned link sheet. Concrete fix: capture and validate one complete body before the first request. Use the same body for both attempts. Disable edits while the action is pending. Use a cancellation or generation check to prevent a second write after Cancel or sheet dismissal. Reuse `withStepUp`; do not add another passkey flow. Regression: hold the initial denial and assertion with deferred promises. Change detail/source or dismiss the sheet. Assert an unchanged request body for an active retry and no second request after dismissal. This test targets the new retry behaviour. Privacy by default and UX completeness require the grant to match the User's action; DESIGN §§21 and 41 require the shared auth path. Duplicate searches: `passkey feed`, `Calendar feed`, `fresh auth`. Track this branch defect on #739. No separate issue is needed. ## F2 — P2: Live freshness and passkey retries have no regression coverage `crates/calternal-server/src/authz.rs:224` tests freshness on a new test router, with `parse_json` instead of the real handler. It inserts freshness itself and covers only Trash. The broad matrix at line 142 always sets freshness true. `tests/adversarial/mcp_probe.py:302` checks App Passwords with no account scope. Those requests cannot prove the denial of a stale owning account session. `tests/adversarial/scopefix_review.mjs:26` only navigates and captures screens. No new web test checks a passkey retry or cancellation. The tests would still pass if the new central guard were removed from `build_live_app`: the test router installs its own guard, while the production handlers still reject data-only principals. This leaves the live freshness requirement without a regression check. This is a coverage defect, not proof of a live authorization bypass. Concrete fix: use the real server router with a stale owning account session. Check Trash and all five Calendar grant mutations. Check unchanged Trash, feed definitions and token hashes on denial. Refresh the same session and check success. An assertion on another session must not help. Add web tests for one retry after successful assertion and no retry after cancellation. These tests must fail when route wiring or a sensitive `fresh_auth` declaration is removed. Preserve existing status and state expectations. Duplicate search: `stale session`. Track the branch coverage gap on #739. ## Checks that support the change - `empty_trash` now calls the existing `account_user` before it takes the mutation lock. That helper also requires data authority and rejects a resource scope for another User. Ordinary Trash routes retain their guards. - Calendar grant management uses one principal helper for all six routes. It requires account authority, then reuses `data_user` for the owning User. Subscription routes are not changed by the scopefix commits. - The reviewed registry declarations require both data and account for Trash and grants. The five grant mutations and Trash also declare fresh auth. The central route layer is installed before the outer session layer wraps direct routes. The MCP inner router gets its own session resolution before dispatch. Freshness therefore comes from the validated credential. - The cached method/path table avoids a full registry scan on each normal route call. Existing data, resource and admin guards remain in place. - MCP envelopes use one helper for generated and legacy responses. User fields remain below `data`; they cannot replace fixed trust or source fields. The legacy Log helper now selects its entry below `data`, so it does not report a parsing failure after a successful batch write. Guidance states that the label does not stop prompt injection or grant authority. - The Files test checks unchanged Trash and an Index row on data-only denial. The principal test checks data-only, account-only and other-User denial. These tests would fail on the prior missing account guards. - Test history changes the MCP discovery expectation to omit account tools and unwraps the new result contract. It retains payload checks and adds trust checks. No weakened status or state expectation was found in the scopefix commits. F2 concerns missing coverage. - Comments in the changed product files explain the account boundary, fixed result metadata and legacy Log handling. The freshness test comment claims grant coverage that its body does not provide; correct it with F2. ## Performance and reuse The normal route check uses a cached table and a small scope comparison. Calendar uses the existing `data_user`; Files uses `account_user`; the web uses the existing passkey retry. No duplicate passkey implementation was added. `crates/calternal-server/src/mcp.rs:1251` scans the full registry for each listed tool. Discovery remains proportional to tool count times action count. The prior implementation also scanned for non-admin discovery. The new version also scans for admin discovery. A cached action-name table could reduce this work. This is an observation, not a measured regression or merge blocker. `bench/scopefix-739.py` covers discovery, denied account calls, large Note reads, CPU, RSS and a bounded burst. No benchmark was run in this LIGHT review. DESIGN §58 defines discovery and setup; it does not set a latency threshold. ## Validation and gates Gate output: none. No gate command was run. The LIGHT job forbids Rust and web builds and tests. No server, browser, adversarial round or benchmark was run. `git diff --check` is the only final content check. No dependency was changed, so no crate or package version lookup was needed. ## Files and known gaps Only `audit-findings.md` and `review-scopefix.md` are changed by this review. No product code, lockfile or test was changed. Runtime behaviour, performance numbers, screenshot quality and successful gate results remain unverified. Both findings remain for the build job. No outside-branch defect was filed. ## UX gaps closed None. This is a read-only review. ## UX gaps left The Calendar retry can read changed form state and continue after dismissal (F1). Successful and cancelled passkey flows need regression tests (F2). ## Decisions Use the last pre-scopefix commit to separate scopefix changes from merged work. Keep branch findings on #739. Treat synthetic freshness coverage separately from real route coverage. Do not run gates, clean build output or create UI artifacts in a LIGHT job.
Author
Owner

Review fixes started on job/scopefix at 1f1ba91cc3. F1: capture the submitted Calendar grant body, freeze pending edits, and cancel retry on sheet dismissal through the shared step-up helper. F2: add production-router freshness and web cancellation regressions. The latest verification policy assigns full live matrices to the merge round; focused regression tests run here.

Review fixes started on job/scopefix at 1f1ba91cc39ac1b8abcdec8c266919c5e3691302. F1: capture the submitted Calendar grant body, freeze pending edits, and cancel retry on sheet dismissal through the shared step-up helper. F2: add production-router freshness and web cancellation regressions. The latest verification policy assigns full live matrices to the merge round; focused regression tests run here.
Author
Owner

F1 fix: the complete submitted feed body is captured before the first request, controls are disabled while pending, and the existing OverlaySurface closeGuard cancels the retry at dismissal intent (before its outro). Deferred tests cover Cancel during the initial denial, Cancel during assertion, Escape during assertion, and unchanged body on success. Focused summary: Test Files 2 passed (2); Tests 7 passed (7). Web gate: svelte-check found 0 errors and 0 warnings. No existing expectations were changed. F2 production-router coverage is added and awaiting the server build.

F1 fix: the complete submitted feed body is captured before the first request, controls are disabled while pending, and the existing OverlaySurface closeGuard cancels the retry at dismissal intent (before its outro). Deferred tests cover Cancel during the initial denial, Cancel during assertion, Escape during assertion, and unchanged body on success. Focused summary: Test Files 2 passed (2); Tests 7 passed (7). Web gate: svelte-check found 0 errors and 0 warnings. No existing expectations were changed. F2 production-router coverage is added and awaiting the server build.
Author
Owner

Regression evidence: all four new Calendar component cases fail against the reviewed 1f1ba91cc implementation (Test Files 1 failed (1); Tests 4 failed (4)). The fixed implementation passes. Rust compilation through shared sccache advanced only through basic dependencies while clients waited 24–51 seconds. Stopped only this worktree clippy process tree and restarted with RUSTC_WRAPPER empty, retaining the preset target directory and four build jobs. Full live matrices remain assigned to the merge round by the latest verification policy.

Regression evidence: all four new Calendar component cases fail against the reviewed 1f1ba91cc implementation (Test Files 1 failed (1); Tests 4 failed (4)). The fixed implementation passes. Rust compilation through shared sccache advanced only through basic dependencies while clients waited 24–51 seconds. Stopped only this worktree clippy process tree and restarted with RUSTC_WRAPPER empty, retaining the preset target directory and four build jobs. Full live matrices remain assigned to the merge round by the latest verification policy.
Author
Owner

UX follow-up: added an optional AbortSignal to the existing assertFreshWithPasskey helper. Calendar dismissal now cancels the browser ceremony as well as its retry, so a closed form does not stay pending until the browser prompt times out. This is a small public helper addition in apps/web/src/lib/auth/passkeys.ts; existing callers retain their behavior. Focused tests check cancellation during challenge loading and propagation to navigator.credentials.get, with no assertion finish after cancellation.

UX follow-up: added an optional AbortSignal to the existing assertFreshWithPasskey helper. Calendar dismissal now cancels the browser ceremony as well as its retry, so a closed form does not stay pending until the browser prompt times out. This is a small public helper addition in apps/web/src/lib/auth/passkeys.ts; existing callers retain their behavior. Focused tests check cancellation during challenge loading and propagation to navigator.credentials.get, with no assertion finish after cancellation.
Author
Owner

Production-router regression saved in an atomic commit to keep uncommitted work below the owner time limit. It creates actual owning sessions, asserts a different session first, checks 403 with retained Trash/Index/feed definitions/token hashes, then refreshes the denied session and checks all six writes. Server clippy passed: Finished dev profile [unoptimized + debuginfo] target(s) in 16m 18s. cargo test is still compiling; this commit is not claimed to pass runtime validation yet. Web focused tests: Test Files 3 passed (3); Tests 11 passed (11). Updated production web build passed.

Production-router regression saved in an atomic commit to keep uncommitted work below the owner time limit. It creates actual owning sessions, asserts a different session first, checks 403 with retained Trash/Index/feed definitions/token hashes, then refreshes the denied session and checks all six writes. Server clippy passed: Finished `dev` profile [unoptimized + debuginfo] target(s) in 16m 18s. cargo test is still compiling; this commit is not claimed to pass runtime validation yet. Web focused tests: Test Files 3 passed (3); Tests 11 passed (11). Updated production web build passed.
Author
Owner

Progress at head 90309d8360: all review changes are saved and the worktree is clean. Server clippy passed in 16m 18s, web check and all 11 focused tests passed, and the production web build passed. The server test compiler has run about 18 minutes on the final test binary without a completion or failure result. Files/Calendar gates and the server binary are queued behind it. Runtime freshness and the screenshot walkthrough are still unverified. The screenshot runner is committed and masks bearer URLs and QR codes before attachment. No full live matrix or performance run was started in this branch under the latest verification policy.

Progress at head 90309d8360c4ca43d6c38bdb5500d47f8022edc4: all review changes are saved and the worktree is clean. Server clippy passed in 16m 18s, web check and all 11 focused tests passed, and the production web build passed. The server test compiler has run about 18 minutes on the final test binary without a completion or failure result. Files/Calendar gates and the server binary are queued behind it. Runtime freshness and the screenshot walkthrough are still unverified. The screenshot runner is committed and masks bearer URLs and QR codes before attachment. No full live matrix or performance run was started in this branch under the latest verification policy.
Author
Owner

Additional source-level concern for the merge round: publication.rs public_feed resolves only the token row id/owner and then renders the current Home FeedDefinition. parse_definition explicitly says Home definitions are user-editable plaintext and validates syntax/output fields. Files shares::resolve_write delegates ordinary Home paths to the data resolver; no Calendar definition-specific authorization guard was found in that writer. No indirect write, capability widening or live reproduction was run, so this is not claimed as a confirmed runtime bypass. The cross-plugin authorization check must establish whether definition edits are intended content edits of the published view or can change the protected grant selectors outside account/fresh-auth management. DESIGN section 2 and issue 743 require that distinction to be clear. Do not treat direct-route freshness coverage as proof of that indirect boundary.

Additional source-level concern for the merge round: publication.rs public_feed resolves only the token row id/owner and then renders the current Home FeedDefinition. parse_definition explicitly says Home definitions are user-editable plaintext and validates syntax/output fields. Files shares::resolve_write delegates ordinary Home paths to the data resolver; no Calendar definition-specific authorization guard was found in that writer. No indirect write, capability widening or live reproduction was run, so this is not claimed as a confirmed runtime bypass. The cross-plugin authorization check must establish whether definition edits are intended content edits of the published view or can change the protected grant selectors outside account/fresh-auth management. DESIGN section 2 and issue 743 require that distinction to be clear. Do not treat direct-route freshness coverage as proof of that indirect boundary.
Author
Owner

Runtime verification finding (#739):

The new production-router test passed when run directly from the built server test executable:

test wire::tests::owning_session_freshness_protects_trash_and_calendar_grants ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 172 filtered out; finished in 25.87s

It uses real session resolution and all six production writes, with unchanged recovery/grant state on denial and success only after the same session is refreshed.

The per-crate server suite failed in the existing full_app_setup_session_config_and_backup child, at wire.rs:8279: Elapsed(()), waiting five seconds for a User archive job. No existing assertion was changed.

test result: FAILED. 166 passed; 1 failed; 6 ignored; 0 measured; 0 filtered out; finished in 434.88s

Files/Calendar gates and production screenshots remain in progress. Full XUser/authz/MCP matrices are assigned to the combined merge round by the latest verification policy.

Runtime verification finding (#739): The new production-router test passed when run directly from the built server test executable: ``` test wire::tests::owning_session_freshness_protects_trash_and_calendar_grants ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 172 filtered out; finished in 25.87s ``` It uses real session resolution and all six production writes, with unchanged recovery/grant state on denial and success only after the same session is refreshed. The per-crate server suite failed in the existing full_app_setup_session_config_and_backup child, at wire.rs:8279: Elapsed(()), waiting five seconds for a User archive job. No existing assertion was changed. ``` test result: FAILED. 166 passed; 1 failed; 6 ignored; 0 measured; 0 filtered out; finished in 434.88s ``` Files/Calendar gates and production screenshots remain in progress. Full XUser/authz/MCP matrices are assigned to the combined merge round by the latest verification policy.
Author
Owner

Build verification finding (#739):

The standalone server link failed after I enabled Cargo offline mode to avoid the shared package-cache wait. ort-sys treats that setting as a request to skip configuring its prebuilt runtime. The error was:

error: linking with `cc` failed: exit status: 1
          rust-lld: error: undefined symbol: OrtGetApiBase

The runtime archive is already cached on this host. The retry now sets ORT_LIB_PATH to that archive directory. The build script emits native onnxruntime and stdc++ linker flags; dependent crates are recompiling. No dependency or product-code change is needed. The standalone server, screenshots and Files/Calendar gates remain pending. The production-router regression previously passed from the separately built test executable.

Build verification finding (#739): The standalone server link failed after I enabled Cargo offline mode to avoid the shared package-cache wait. ort-sys treats that setting as a request to skip configuring its prebuilt runtime. The error was: ``` error: linking with `cc` failed: exit status: 1 rust-lld: error: undefined symbol: OrtGetApiBase ``` The runtime archive is already cached on this host. The retry now sets ORT_LIB_PATH to that archive directory. The build script emits native onnxruntime and stdc++ linker flags; dependent crates are recompiling. No dependency or product-code change is needed. The standalone server, screenshots and Files/Calendar gates remain pending. The production-router regression previously passed from the separately built test executable.
Author
Owner

Scopefix review round — review fixes committed; verification incomplete at the four-hour limit.

Branch: job/scopefix. Start: 1f1ba91cc3. Current head: 5d840ff6ad.
Merged origin/dev once before final gates. No push, deploy or merge into dev/main.

Built:

  • F1: capture and validate the complete Calendar grant before its first request. Reuse it on retry. Disable edits while pending. Cancel at sheet dismissal intent through the shared closeGuard, before the outro. Ignore late results after dismissal.
  • Reuse the shared passkey helper with an optional AbortSignal. A dismissed action stops an unsent assertion and cannot start a second write. Requests already sent can still complete.
  • F2: add a real production-router regression with owning sessions, a fresh assertion on another session, unchanged Trash/Index/feed definitions/token hashes on denial, and success after refreshing the denied session. Add it to the existing process-isolated live-app runner.
  • Keep Calendar text inputs at the 44 px touch target floor.
  • Add a standalone production screenshot walkthrough for 390/820/1440 px, light/dark, macOS emulation, touch/pointer and Escape. It checks 403 then 201 for retry, and only 403 for cancellation. Review images mask bearer URLs and QR codes.

Verified output (verbatim excerpts):

svelte-check found 0 errors and 0 warnings
 Test Files  3 passed (3)
      Tests  11 passed (11)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 16m 18s

The last line is calternal-server clippy. After the touch change, the focused Calendar file passed:

 Test Files  1 passed (1)
      Tests  4 passed (4)

The server suite's failed child summary was:

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 172 filtered out; finished in 80.68s

cargo fmt --check and git diff --check produced no output, exit 0. Updated production web build passed. node --check tests/adversarial/scopefix_review.mjs also produced no output, exit 0.

Old-code regression output:

 Test Files  1 failed (1)
      Tests  4 failed (4)

All four new Calendar component cases fail against the reviewed CalendarFeedsGroup.svelte at 1f1ba91cc. The fixed code passes. No existing expectation was changed.

Runtime freshness result (verbatim):

test wire::tests::owning_session_freshness_protects_trash_and_calendar_grants ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 172 filtered out; finished in 25.87s

The built server test executable ran this exact ignored test in its own process. Cargo's redundant focused invocation was stopped while it waited for the shared package cache.

Server suite failure (verbatim):

test result: FAILED. 166 passed; 1 failed; 6 ignored; 0 measured; 0 filtered out; finished in 434.88s

The process-isolated runner stopped in the pre-existing full_app_setup_session_config_and_backup child at wire.rs:8279, Elapsed(()), while waiting five seconds for a User archive job. A direct run of the same child also failed:

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 172 filtered out; finished in 105.15s

No existing assertion was changed. The cause is unconfirmed. Do not treat the server suite as passed.

Known gaps: Files and Calendar crate gates did not complete. The standalone server first failed to link because my Cargo offline setting suppressed ONNX Runtime configuration. The retry set ORT_LIB_PATH to the existing cached runtime and reached linking. I stopped it at the job time limit. No usable server binary or production screenshot evidence was produced. The screenshot runner was syntax-checked but not executed. A source-only concern about mutable Calendar definition selectors and grant scope remains unverified, as already noted on #739; no bypass was reproduced. The combined cross-plugin matrix must settle that boundary. This round does not establish merge readiness.

UX gaps closed: submitted values stay stable through both waits; pending fields are disabled; Cancel and Escape prevent retries; dismissed results cannot reopen the link sheet; cancellation can stop the browser prompt; feed text inputs meet the touch floor.
UX gaps left: real browser walkthrough and screenshots are pending.

Decisions: use existing closeGuard to cancel before motion ends, preserve the shared motion durations, and add one optional signal to the existing passkey helper. Do not roll back a request already sent. Run live-app coverage in the existing child-process runner because plugin state is process-wide. Full live matrices follow the latest verification policy and run once on the combined merge branch. No dependency or migration changed.

For the merge round:

  • Finish the Files and Calendar per-crate gates (one crate at a time): cargo clippy -p calternal-plugin-files --all-targets -- -D warnings; cargo test -p calternal-plugin-files; cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings; cargo test -p calternal-plugin-calendar.
  • Rebuild the production web and compatible server, then run CALTERNAL_SERVER_BIN= bun tests/adversarial/scopefix_review.mjs. Capture all 42 states and attach the masked images. Verify retry/cancel copy and the 44 px text-input touch floor.
  • Run full web tests in apps/web: bun run test --maxWorkers=2.
  • With the normal local disposable test fixture, run ADVERSARIAL_MATRIX_DIRECT=1 python3 tests/adversarial/xuser_matrix.py and ADVERSARIAL_MATRIX_DIRECT=1 python3 tests/adversarial/authz_matrix.py. Prove cross-User and authorization isolation on the combined branch.
  • Run python3 tests/adversarial/mcp_probe.py in that fixture. Prove credential-scoped discovery and fixed untrusted-result envelopes.
  • Run the production freshness regression as part of cargo test -p calternal-server; its child process must prove denial and unchanged recovery/grant state for all six writes, then owning-session success.
  • The broad matrices above were not run in this job, as the 2026-10-02 verification policy assigns them to the combined merge round. No performance run is requested for this authorization issue.

Files changed in this round:

  • apps/web/src/lib/auth/passkeys.ts
  • apps/web/src/lib/auth/passkeys.test.ts
  • apps/web/src/routes/settings/api.svelte.ts
  • apps/web/src/routes/settings/api.svelte.test.ts
  • apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte
  • apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte.test.ts
  • crates/calternal-server/src/authz.rs
  • crates/calternal-server/src/wire.rs
  • tests/adversarial/scopefix_review.mjs

Build correction evidence:

error: linking with `cc` failed: exit status: 1
          rust-lld: error: undefined symbol: OrtGetApiBase

That first failure was caused by my offline build setting, not a source change. The corrected build emitted native onnxruntime/stdc++ link flags but was cancelled before completion. No dependency or license change was made.

Cleanup: the own build pipeline was stopped. cargo clean and removal of only apps/web/build and apps/web/.svelte-kit/output are in progress. Automatic approval review rejected the original cleanup command because it used rm -rf; the replacement uses checked, explicit output directories.

Scopefix review round — review fixes committed; verification incomplete at the four-hour limit. Branch: job/scopefix. Start: 1f1ba91cc39ac1b8abcdec8c266919c5e3691302. Current head: 5d840ff6ad728b8d4bcfc87de20d15a9dd326613. Merged origin/dev once before final gates. No push, deploy or merge into dev/main. Built: - F1: capture and validate the complete Calendar grant before its first request. Reuse it on retry. Disable edits while pending. Cancel at sheet dismissal intent through the shared closeGuard, before the outro. Ignore late results after dismissal. - Reuse the shared passkey helper with an optional AbortSignal. A dismissed action stops an unsent assertion and cannot start a second write. Requests already sent can still complete. - F2: add a real production-router regression with owning sessions, a fresh assertion on another session, unchanged Trash/Index/feed definitions/token hashes on denial, and success after refreshing the denied session. Add it to the existing process-isolated live-app runner. - Keep Calendar text inputs at the 44 px touch target floor. - Add a standalone production screenshot walkthrough for 390/820/1440 px, light/dark, macOS emulation, touch/pointer and Escape. It checks 403 then 201 for retry, and only 403 for cancellation. Review images mask bearer URLs and QR codes. Verified output (verbatim excerpts): ``` svelte-check found 0 errors and 0 warnings Test Files 3 passed (3) Tests 11 passed (11) Finished `dev` profile [unoptimized + debuginfo] target(s) in 16m 18s ``` The last line is calternal-server clippy. After the touch change, the focused Calendar file passed: ``` Test Files 1 passed (1) Tests 4 passed (4) ``` The server suite's failed child summary was: ``` test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 172 filtered out; finished in 80.68s ``` cargo fmt --check and git diff --check produced no output, exit 0. Updated production web build passed. node --check tests/adversarial/scopefix_review.mjs also produced no output, exit 0. Old-code regression output: ``` Test Files 1 failed (1) Tests 4 failed (4) ``` All four new Calendar component cases fail against the reviewed CalendarFeedsGroup.svelte at 1f1ba91cc. The fixed code passes. No existing expectation was changed. Runtime freshness result (verbatim): ``` test wire::tests::owning_session_freshness_protects_trash_and_calendar_grants ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 172 filtered out; finished in 25.87s ``` The built server test executable ran this exact ignored test in its own process. Cargo's redundant focused invocation was stopped while it waited for the shared package cache. Server suite failure (verbatim): ``` test result: FAILED. 166 passed; 1 failed; 6 ignored; 0 measured; 0 filtered out; finished in 434.88s ``` The process-isolated runner stopped in the pre-existing full_app_setup_session_config_and_backup child at wire.rs:8279, Elapsed(()), while waiting five seconds for a User archive job. A direct run of the same child also failed: ``` test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 172 filtered out; finished in 105.15s ``` No existing assertion was changed. The cause is unconfirmed. Do not treat the server suite as passed. Known gaps: Files and Calendar crate gates did not complete. The standalone server first failed to link because my Cargo offline setting suppressed ONNX Runtime configuration. The retry set ORT_LIB_PATH to the existing cached runtime and reached linking. I stopped it at the job time limit. No usable server binary or production screenshot evidence was produced. The screenshot runner was syntax-checked but not executed. A source-only concern about mutable Calendar definition selectors and grant scope remains unverified, as already noted on #739; no bypass was reproduced. The combined cross-plugin matrix must settle that boundary. This round does not establish merge readiness. UX gaps closed: submitted values stay stable through both waits; pending fields are disabled; Cancel and Escape prevent retries; dismissed results cannot reopen the link sheet; cancellation can stop the browser prompt; feed text inputs meet the touch floor. UX gaps left: real browser walkthrough and screenshots are pending. Decisions: use existing closeGuard to cancel before motion ends, preserve the shared motion durations, and add one optional signal to the existing passkey helper. Do not roll back a request already sent. Run live-app coverage in the existing child-process runner because plugin state is process-wide. Full live matrices follow the latest verification policy and run once on the combined merge branch. No dependency or migration changed. For the merge round: - Finish the Files and Calendar per-crate gates (one crate at a time): cargo clippy -p calternal-plugin-files --all-targets -- -D warnings; cargo test -p calternal-plugin-files; cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings; cargo test -p calternal-plugin-calendar. - Rebuild the production web and compatible server, then run CALTERNAL_SERVER_BIN=<combined-server> bun tests/adversarial/scopefix_review.mjs. Capture all 42 states and attach the masked images. Verify retry/cancel copy and the 44 px text-input touch floor. - Run full web tests in apps/web: bun run test --maxWorkers=2. - With the normal local disposable test fixture, run ADVERSARIAL_MATRIX_DIRECT=1 python3 tests/adversarial/xuser_matrix.py and ADVERSARIAL_MATRIX_DIRECT=1 python3 tests/adversarial/authz_matrix.py. Prove cross-User and authorization isolation on the combined branch. - Run python3 tests/adversarial/mcp_probe.py in that fixture. Prove credential-scoped discovery and fixed untrusted-result envelopes. - Run the production freshness regression as part of cargo test -p calternal-server; its child process must prove denial and unchanged recovery/grant state for all six writes, then owning-session success. - The broad matrices above were not run in this job, as the 2026-10-02 verification policy assigns them to the combined merge round. No performance run is requested for this authorization issue. Files changed in this round: - apps/web/src/lib/auth/passkeys.ts - apps/web/src/lib/auth/passkeys.test.ts - apps/web/src/routes/settings/api.svelte.ts - apps/web/src/routes/settings/api.svelte.test.ts - apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte - apps/web/src/routes/settings/apps/CalendarFeedsGroup.svelte.test.ts - crates/calternal-server/src/authz.rs - crates/calternal-server/src/wire.rs - tests/adversarial/scopefix_review.mjs Build correction evidence: ``` error: linking with `cc` failed: exit status: 1 rust-lld: error: undefined symbol: OrtGetApiBase ``` That first failure was caused by my offline build setting, not a source change. The corrected build emitted native onnxruntime/stdc++ link flags but was cancelled before completion. No dependency or license change was made. Cleanup: the own build pipeline was stopped. cargo clean and removal of only apps/web/build and apps/web/.svelte-kit/output are in progress. Automatic approval review rejected the original cleanup command because it used rm -rf; the replacement uses checked, explicit output directories.
Author
Owner

Cleanup complete for head 5d840ff6ad. The full verification report is in the preceding comment.

     Removed 13701 files, 6.5GiB total

Both apps/web/build and apps/web/.svelte-kit/output are removed. The working tree is clean. No push, deploy or merge into dev/main occurred. The review fixes are committed; the failed archive test, incomplete Files/Calendar gates and browser evidence remain for the merge round.

Cleanup complete for head 5d840ff6ad728b8d4bcfc87de20d15a9dd326613. The full verification report is in the preceding comment. ``` Removed 13701 files, 6.5GiB total ``` Both apps/web/build and apps/web/.svelte-kit/output are removed. The working tree is clean. No push, deploy or merge into dev/main occurred. The review fixes are committed; the failed archive test, incomplete Files/Calendar gates and browser evidence remain for the merge round.
Author
Owner

Crate gates on job/scopefix (server crate), run by Claude:

  • cargo fmt --check: exit 0
  • cargo clippy -p calternal-server --all-targets -- -D warnings: Finished \dev` profile [unoptimized + debuginfo] target(s) in 23.30s(afterapps/web/buildwas produced withbun run build; without it RustEmbedfails:folder '.../apps/web/build/' does not exist`; environment only, not a code defect)
  • cargo test -p calternal-server --no-fail-fast -- --test-threads=4:
    test result: ok. 167 passed; 0 failed; 6 ignored; 0 measured; 0 filtered out; finished in 11.97s
    

No fixes needed. Ready for the merge round (server crate gates).

Crate gates on `job/scopefix` (server crate), run by Claude: - `cargo fmt --check`: exit 0 - `cargo clippy -p calternal-server --all-targets -- -D warnings`: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 23.30s` (after `apps/web/build` was produced with `bun run build`; without it `RustEmbed` fails: `folder '.../apps/web/build/' does not exist`; environment only, not a code defect) - `cargo test -p calternal-server --no-fail-fast -- --test-threads=4`: ``` test result: ok. 167 passed; 0 failed; 6 ignored; 0 measured; 0 filtered out; finished in 11.97s ``` No fixes needed. Ready for the merge round (server crate gates).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#739
No description provided.