SECURITY: prove every admin endpoint denies standard users (fail-closed guard) + hide Admin UI for non-admins #483

Closed
opened 2026-09-30 05:35:27 +00:00 by kayg · 18 comments
Owner

Request (owner, 2026-09-30, Better Settings grill Q7)

"Admin half shown to admins only and you need to verify that standard users running admin endpoints get denied."

Scope

  1. Server (the real guard): prove that EVERY admin operation denies a standard User (403), an anonymous caller (401), a stale admin session where a fresh one is required (step-up 403), and an App Password of any scope.
    • tests/adversarial/authz_matrix.py has an ADMIN_OPERATIONS list. Make it fail closed: derive the admin set from the OpenAPI contract (every admin_* operation id and every /api/v1/admin/ path, plus any route whose handler calls the admin guard). A new admin route that is not in the matrix must fail the gate, in the same way as the #472 classification guard.
    • Read each admin handler and confirm that the check runs BEFORE any lookup or side effect (no timing or existence leaks).
    • Run it live on a real local server and quote the result.
  2. UI: the Admin half of Settings, admin deep links (/settings/admin/...) and palette entries are hidden from non-admins. A direct URL shows a plain "not available" or redirects, and makes no admin API calls. Add an e2e as a standard User.
  3. Also: admin-only MCP, WebMCP and CLI commands deny the same way.
    Fix any hole found, with a regression test. Merge blocker class.
## Request (owner, 2026-09-30, Better Settings grill Q7) "Admin half shown to admins only and you need to verify that standard users running admin endpoints get denied." ## Scope 1. **Server (the real guard):** prove that EVERY admin operation denies a standard User (403), an anonymous caller (401), a stale admin session where a fresh one is required (step-up 403), and an App Password of any scope. - `tests/adversarial/authz_matrix.py` has an `ADMIN_OPERATIONS` list. Make it fail closed: derive the admin set from the OpenAPI contract (every `admin_*` operation id and every `/api/v1/admin/` path, plus any route whose handler calls the admin guard). A new admin route that is not in the matrix must fail the gate, in the same way as the #472 classification guard. - Read each admin handler and confirm that the check runs BEFORE any lookup or side effect (no timing or existence leaks). - Run it live on a real local server and quote the result. 2. **UI:** the Admin half of Settings, admin deep links (`/settings/admin/...`) and palette entries are hidden from non-admins. A direct URL shows a plain "not available" or redirects, and makes no admin API calls. Add an e2e as a standard User. 3. **Also:** admin-only MCP, WebMCP and CLI commands deny the same way. Fix any hole found, with a regression test. Merge blocker class.
Author
Owner

Started #483 on branch job/admin-deny-483. Base SHA: c9738e9b4e.

Audit scope: admin guard order, fail-closed contract coverage, standard User Settings and palette access, and client command surfaces.

Started #483 on branch job/admin-deny-483. Base SHA: c9738e9b4eea1af80a8153bde45ecb899ca2b440. Audit scope: admin guard order, fail-closed contract coverage, standard User Settings and palette access, and client command surfaces.
Author
Owner

Finding #483: the matrix accepted any /api/v1/admin/ path without requiring an ADMIN_OPERATIONS entry. Current Apps surfaces, search integrity and dedup operations were absent from that inventory. The step-up list omitted admin_app_surfaces_put and admin_check_search_integrity. Commit a0611fdce adds contract and Rust handler/route-middleware inventory checks and eight offline regression tests; CI and the local runner invoke the guard before fixtures.

Handler audit: wire.rs admin operations call admin before protected state reads or writes; route middleware rejects before handler extraction. Auth handlers place RequireScope<Admin/FreshAdmin> before Path/Json extraction. set_instance_plugin has route middleware before extraction and repeats the role/scope/freshness check before registry mutation. Pause/resume route middleware requires freshness although their handler check only requires role. MCP, WebMCP and CLI expose no admin operations at this revision.

Decisions: keep the current redirect to /settings/account for standard Users; add production browser coverage instead of a new UI state. Keep the existing server policies, and derive matrix review requirements from their declarations.

Finding #483: the matrix accepted any /api/v1/admin/ path without requiring an ADMIN_OPERATIONS entry. Current Apps surfaces, search integrity and dedup operations were absent from that inventory. The step-up list omitted admin_app_surfaces_put and admin_check_search_integrity. Commit a0611fdce adds contract and Rust handler/route-middleware inventory checks and eight offline regression tests; CI and the local runner invoke the guard before fixtures. Handler audit: wire.rs admin operations call admin before protected state reads or writes; route middleware rejects before handler extraction. Auth handlers place RequireScope<Admin/FreshAdmin> before Path/Json extraction. set_instance_plugin has route middleware before extraction and repeats the role/scope/freshness check before registry mutation. Pause/resume route middleware requires freshness although their handler check only requires role. MCP, WebMCP and CLI expose no admin operations at this revision. Decisions: keep the current redirect to /settings/account for standard Users; add production browser coverage instead of a new UI state. Keep the existing server policies, and derive matrix review requirements from their declarations.
Author
Owner

Progress #483: commits a0611fdce, 249671e94 and b77858a05 are on job/admin-deny-483. The offline admin guard covers 39 operations and 23 step-up policies. Eleven classification/scope tests pass. MCP, WebMCP and CLI have no admin commands; a regression gate now requires explicit review if one is added. WebMCP rejects Admin destinations before navigation.

The full web suite completed with two unchanged tests exceeding its 5-second timeout:

Test Files  2 failed | 135 passed (137)
     Tests  2 failed | 886 passed (888)

Failures: date-formatting.test.ts (source reuse scan), and ThemePicker.svelte.test.ts (keyboard submenu). No assertions or expectations were changed. ThemePicker passes in isolation with a 30-second execution allowance:

Test Files  1 passed (1)
     Tests  2 passed (2)

The date-formatting test and touched client tests are running with the same allowance. Server compilation and the requested full Rust gates are in progress. Fetch plus merge of origin/dev completed once: Already up to date. Production browser checks will run as soon as the local server build completes.

Progress #483: commits a0611fdce, 249671e94 and b77858a05 are on job/admin-deny-483. The offline admin guard covers 39 operations and 23 step-up policies. Eleven classification/scope tests pass. MCP, WebMCP and CLI have no admin commands; a regression gate now requires explicit review if one is added. WebMCP rejects Admin destinations before navigation. The full web suite completed with two unchanged tests exceeding its 5-second timeout: ``` Test Files 2 failed | 135 passed (137) Tests 2 failed | 886 passed (888) ``` Failures: date-formatting.test.ts (source reuse scan), and ThemePicker.svelte.test.ts (keyboard submenu). No assertions or expectations were changed. ThemePicker passes in isolation with a 30-second execution allowance: ``` Test Files 1 passed (1) Tests 2 passed (2) ``` The date-formatting test and touched client tests are running with the same allowance. Server compilation and the requested full Rust gates are in progress. Fetch plus merge of origin/dev completed once: `Already up to date.` Production browser checks will run as soon as the local server build completes.
Author
Owner

Finding #483: while /api/v1/auth/me was pending, /settings/admin/users exposed the Users document title and heading Copy link destination even though admin components and navigation were gated. Commit e6660e47f makes the title, desktop heading and phone sheet metadata generic Settings until the User Role is admin. The production test delays the real Role response and asserts those destinations stay hidden. It retains the existing standard User redirect to /settings/account.

Final web checks now pass without changed expectations: svelte-check found 0 errors and 0 warnings; 137 test files and 888 tests passed with two workers and a 30-second execution allowance. The first live run caught a fixture typo (invite/start instead of the existing invites/start); commit f3d90c396 fixes the fixture and the live matrix is running on the real local server.

Finding #483: while /api/v1/auth/me was pending, /settings/admin/users exposed the Users document title and heading Copy link destination even though admin components and navigation were gated. Commit e6660e47f makes the title, desktop heading and phone sheet metadata generic Settings until the User Role is admin. The production test delays the real Role response and asserts those destinations stay hidden. It retains the existing standard User redirect to /settings/account. Final web checks now pass without changed expectations: svelte-check found 0 errors and 0 warnings; 137 test files and 888 tests passed with two workers and a 30-second execution allowance. The first live run caught a fixture typo (invite/start instead of the existing invites/start); commit f3d90c396 fixes the fixture and the live matrix is running on the real local server.
Author
Owner

Live round #483: 39 operations, 834 requests, 15 App Password scope classes. All received HTTP responses matched the required denial. One SLOW-only interruption: DELETE /api/v1/auth/invites/{id}, app_password_all_full/valid, socket timed out at 20 seconds. Host load average immediately after the round: 74.50 78.65 76.17. No 5xx or accepted privileged request was reported.

Commit 8bae7d777 adds a focused operation selector and an execution timeout setting. A single follow-up checks revoke_invite only with a 60-second allowance, then runs the production browser coverage. It does not repeat the full sweep, bypass the contract inventory gate, or change any status expectation.

Live round #483: 39 operations, 834 requests, 15 App Password scope classes. All received HTTP responses matched the required denial. One SLOW-only interruption: DELETE /api/v1/auth/invites/{id}, app_password_all_full/valid, socket timed out at 20 seconds. Host load average immediately after the round: `74.50 78.65 76.17`. No 5xx or accepted privileged request was reported. Commit 8bae7d777 adds a focused operation selector and an execution timeout setting. A single follow-up checks revoke_invite only with a 60-second allowance, then runs the production browser coverage. It does not repeat the full sweep, bypass the contract inventory gate, or change any status expectation.
Author
Owner

Production browser proof #483 passed on head 724972066a:

$ bun e2e/admin-denial.mjs
Authorization matrix: 1 OpenAPI operations; 20 requests across 3 base identities plus invalid/stale session probes and 15 App Password scope classes; valid/malformed session bodies on 0 privileged body routes; App Passwords use valid bodies; policy classes {'public': 0, 'public_link': 0, 'user': 0, 'admin': 1}
PASS standard User: 96 admin links denied; Settings and palette hidden at 390/820/1440px in light/dark; zero admin API calls

The single timed-out operation from the full 834-request round passed all 20 identities in this focused check. The full round received 833 matching denial responses and one SLOW timeout; no 5xx or successful privileged response. Handler audit confirms the admin guard precedes protected lookup and side effects.

24 screenshots cover Settings, direct-link redirect, palette and pending Role at phone 390 px, tablet 820 px and desktop 1440 px in light and dark. Attachments are uploading for the orchestrator review. Full Rust gates remain in progress.

Production browser proof #483 passed on head 724972066a4daa7f4ad525ed88e438b98362c5ce: ``` $ bun e2e/admin-denial.mjs Authorization matrix: 1 OpenAPI operations; 20 requests across 3 base identities plus invalid/stale session probes and 15 App Password scope classes; valid/malformed session bodies on 0 privileged body routes; App Passwords use valid bodies; policy classes {'public': 0, 'public_link': 0, 'user': 0, 'admin': 1} PASS standard User: 96 admin links denied; Settings and palette hidden at 390/820/1440px in light/dark; zero admin API calls ``` The single timed-out operation from the full 834-request round passed all 20 identities in this focused check. The full round received 833 matching denial responses and one SLOW timeout; no 5xx or successful privileged response. Handler audit confirms the admin guard precedes protected lookup and side effects. 24 screenshots cover Settings, direct-link redirect, palette and pending Role at phone 390 px, tablet 820 px and desktop 1440 px in light and dark. Attachments are uploading for the orchestrator review. Full Rust gates remain in progress.
Author
Owner
Production screenshots for the #483 visual review. Real local server; standard User with member Role; no placeholder data. Inspected original-resolution phone, tablet and desktop captures for icon/text alignment. Visual acceptance remains with the orchestrator. [direct-link-paper-1440.png](https://git.kayg.org/attachments/af4fa13d-6e32-43db-a27d-67dfaea69dee) [direct-link-paper-390.png](https://git.kayg.org/attachments/791d8bb8-e81e-4f07-8158-3ed45dfb2262) [direct-link-paper-820.png](https://git.kayg.org/attachments/3c6c9af8-a476-48d3-8cec-707e4bebebc6) [direct-link-tokyo-night-1440.png](https://git.kayg.org/attachments/9998fd57-5925-41d1-95cf-ce9d70de76d1) [direct-link-tokyo-night-390.png](https://git.kayg.org/attachments/f751bbe3-2a08-4ddf-ad91-6247832f6fd5) [direct-link-tokyo-night-820.png](https://git.kayg.org/attachments/f69b2716-c203-495f-9c61-36f1bf61f2f8) [palette-paper-1440.png](https://git.kayg.org/attachments/a3613c42-3f71-403f-aa4f-c76a0fc61122) [palette-paper-390.png](https://git.kayg.org/attachments/c59d616a-7bfe-4854-b091-51f3183afc1f) [palette-paper-820.png](https://git.kayg.org/attachments/4e9d58e4-9af3-47da-894d-3256bc26be96) [palette-tokyo-night-1440.png](https://git.kayg.org/attachments/73b92dc2-cf8b-4a28-98ab-a9477876d1b4) [palette-tokyo-night-390.png](https://git.kayg.org/attachments/b4047ef1-2df2-4008-8197-b7fa40cad9b0) [palette-tokyo-night-820.png](https://git.kayg.org/attachments/2127a998-e69b-4a0d-a93c-5b35bfe81fcc) [pending-role-paper-1440.png](https://git.kayg.org/attachments/8cfed805-c76b-4c76-9dce-96d5f0013019) [pending-role-paper-390.png](https://git.kayg.org/attachments/3bfc33ea-ffa7-44d0-8976-405d1707b6c3) [pending-role-paper-820.png](https://git.kayg.org/attachments/09091cf6-68ae-4112-9182-77cedb39423d) [pending-role-tokyo-night-1440.png](https://git.kayg.org/attachments/bfc49587-84ea-4c26-b747-b887570a5fd4) [pending-role-tokyo-night-390.png](https://git.kayg.org/attachments/6e45febb-1023-4c38-afa5-65d7d52aa459) [pending-role-tokyo-night-820.png](https://git.kayg.org/attachments/4ae666d2-7fd6-4ea8-9e91-7541376d48c4) [settings-paper-1440.png](https://git.kayg.org/attachments/afa64794-dd72-4783-bc55-519fd5ff313a) [settings-paper-390.png](https://git.kayg.org/attachments/be20fed4-b31e-439b-b76d-1838fcecece0) [settings-paper-820.png](https://git.kayg.org/attachments/d549a526-695c-45c8-bc22-ee332f038a9c) [settings-tokyo-night-1440.png](https://git.kayg.org/attachments/88466d72-777e-4290-981b-582342379f62) [settings-tokyo-night-390.png](https://git.kayg.org/attachments/65150a44-6752-45fa-8902-0b73700d29a9) [settings-tokyo-night-820.png](https://git.kayg.org/attachments/1acfb2b5-ba83-407a-8374-e11f7081fa67)
Author
Owner

Coverage finding #483: a synthetic undocumented handler inside a nested Rust module, with a multiline admin(...).await call, did not trigger the first scanner. The regression failed with AssertionError: RuntimeError not raised. Commit 724972066 matches function indentation and multiline await calls; all 13 regression tests now pass. Current contract/guard agreement remains 39 operations. No Rust runtime policy changed.

Full verification logs (including the first SLOW timeout and focused follow-up):

admin-denial-live.log

admin-denial-targeted-and-ui.log

admin-classification-current.log

web-check-current.log

web-test-final.log

Coverage finding #483: a synthetic undocumented handler inside a nested Rust module, with a multiline `admin(...).await` call, did not trigger the first scanner. The regression failed with `AssertionError: RuntimeError not raised`. Commit 724972066 matches function indentation and multiline await calls; all 13 regression tests now pass. Current contract/guard agreement remains 39 operations. No Rust runtime policy changed. Full verification logs (including the first SLOW timeout and focused follow-up): [admin-denial-live.log](https://git.kayg.org/attachments/ff5193d6-984f-496e-b01d-2e1628fe31e8) [admin-denial-targeted-and-ui.log](https://git.kayg.org/attachments/28b4fb87-8eab-4f18-8307-99e3a3d9ea01) [admin-classification-current.log](https://git.kayg.org/attachments/b7fb1993-4d84-454b-a762-7fe8ca8d0df7) [web-check-current.log](https://git.kayg.org/attachments/d4712651-c9de-477a-be7f-001a3ac0d851) [web-test-final.log](https://git.kayg.org/attachments/33b51ac7-de88-4643-84de-cb77a4f9faf8)
Author
Owner

Final test review #483: the browser observer covered /api/v1/admin/ and auth Users/invites but omitted the auth-owned signup-policy and re-enrolment-issuance writes. Commit 763ab5108 adds both exact routes. The six device/theme cases are running again with this complete observer. No frontend runtime or Rust behavior changed. Commit d2f022f23 also documents the request-timeout invariant: waiting can be configured, but denial status requirements stay exact.

Final test review #483: the browser observer covered /api/v1/admin/ and auth Users/invites but omitted the auth-owned signup-policy and re-enrolment-issuance writes. Commit 763ab5108 adds both exact routes. The six device/theme cases are running again with this complete observer. No frontend runtime or Rust behavior changed. Commit d2f022f23 also documents the request-timeout invariant: waiting can be configured, but denial status requirements stay exact.
Author
Owner

Final-head checks #483, 763ab51084:

cargo clippy --all-targets -- -D warnings exited 0.

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 152m 37s

bun run test --maxWorkers=2 --testTimeout=30000 on the final frontend code exited 0.

 Test Files  137 passed (137)
      Tests  888 passed (888)

The complete admin API traffic observer passed all 96 links in all six device/theme cases. Workspace cargo test is compiling.

clippy.log

admin-classification-final.log

web-test-head.log

admin-denial-final-ui.log

Final-head checks #483, 763ab510845b98cc75ef6aab51caef52f4f5faf6: `cargo clippy --all-targets -- -D warnings` exited 0. ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 152m 37s ``` `bun run test --maxWorkers=2 --testTimeout=30000` on the final frontend code exited 0. ``` Test Files 137 passed (137) Tests 888 passed (888) ``` The complete admin API traffic observer passed all 96 links in all six device/theme cases. Workspace cargo test is compiling. [clippy.log](https://git.kayg.org/attachments/646de913-7d21-4ae5-9070-a570ee6fb8d0) [admin-classification-final.log](https://git.kayg.org/attachments/b850dab5-2faa-41cd-a0bc-13e2ba307c1a) [web-test-head.log](https://git.kayg.org/attachments/b1b66af6-3d50-436e-b062-632185ea48b7) [admin-denial-final-ui.log](https://git.kayg.org/attachments/cd11a7f3-16ca-448f-b6f4-f00ea49cee5d)
Author
Owner

Coverage finding #483: an admin operation with a conflicting public policy could be reviewed but omitted from the focused live matrix. A synthetic public classification for get_config reproduced the gap (AssertionError: RuntimeError not raised). Commit a20f358d42 requires every derived admin operation to have the exact admin policy before selecting requests. All 14 classification tests pass; current coverage remains 39 operations and 23 step-up policies. No Rust or frontend runtime source changed. Workspace cargo test is compiling calternal-server.

Coverage finding #483: an admin operation with a conflicting public policy could be reviewed but omitted from the focused live matrix. A synthetic public classification for get_config reproduced the gap (`AssertionError: RuntimeError not raised`). Commit a20f358d42eed358806971c676298a4864cf03cd requires every derived admin operation to have the exact admin policy before selecting requests. All 14 classification tests pass; current coverage remains 39 operations and 23 step-up policies. No Rust or frontend runtime source changed. Workspace cargo test is compiling calternal-server.
Author
Owner

#483 implementation report

Branch: job/admin-deny-483. Head: a20f358d42.

Built:

  • A fail-closed admin classification gate in CI and the local runner. OpenAPI admin operation IDs, /api/v1/admin/ paths, Rust scope/handler guards and route middleware must agree with the reviewed matrix. New, missing and duplicate route identities fail before requests. Coverage: 39 operations, 23 step-up policies; 14 regression tests.
  • Real Owner App Password fixtures for every valid protocol/access class, including upload-only and combined full scope. Every admin operation requires anonymous 401, standard User 403, App Password 403, and stale Admin 403 where step-up is required. Session bodies also test extraction order.
  • A production browser test with a real member User. All 96 admin links, Settings navigation and palette entries are denied at 390/820/1440 px in light and dark, with zero admin API calls. The observer includes auth-owned admin routes.
  • A fix for the pending-Role title and Copy link leak. Unknown or non-admin Role gets generic Settings metadata before the existing Account redirect.
  • MCP, WebMCP and CLI inventory regressions. These surfaces currently have no admin commands. WebMCP rejects Admin destinations before calling its browser adapter.

Files:

.forgejo/workflows/ci.yml
apps/web/e2e/admin-denial.mjs
apps/web/package.json
apps/web/src/lib/search/SearchResultRow.svelte
apps/web/src/lib/webmcp/tools.test.ts
apps/web/src/routes/settings/[...path]/+page.svelte
tests/adversarial/authz_matrix.py
tests/adversarial/run.sh
tests/adversarial/test_admin_classification.py

Handler audit: all admin checks precede protected resource lookup and side effects. Auth handlers put RequireScope before Path/Json; server routes have middleware before extraction and repeat admin checks in handlers. Instance plugin changes repeat scope, Role and freshness before registry mutation. Job pause/resume middleware requires freshness even though the handler check only requires Role.

Gate output (verbatim):

cargo fmt --check: exit 0, no output.

cargo clippy --all-targets -- -D warnings: exit 0.

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 152m 37s

bun run check: exit 0.

svelte-check found 0 errors and 0 warnings

bun run test --maxWorkers=2 --testTimeout=30000: exit 0; no expectation changes.

 Test Files  137 passed (137)
      Tests  888 passed (888)
   Duration  874.55s (transform 41%, environment 22%, import 20%, tests 13%, setup 4%)

Classification:

..............
----------------------------------------------------------------------
Ran 14 tests in 2.083s

OK
Admin coverage: 39 reviewed operations; contract and Rust guards agree

Live round (verbatim):

Authorization matrix: 39 OpenAPI operations; 834 requests across 3 base identities plus invalid/stale session probes and 15 App Password scope classes; valid/malformed session bodies on 14 privileged body routes; App Passwords use valid bodies; policy classes {'public': 0, 'public_link': 0, 'user': 0, 'admin': 39}
!! DELETE /api/v1/auth/invites/{id} as app_password_all_full/valid: server response -1: b'timed out'
authorization matrix found 1 failures

All 833 received responses matched the required denial. One 20-second socket timeout was SLOW-only; host load was 74.50 78.65 76.17. The focused follow-up checked only revoke_invite with a 60-second execution allowance and passed all 20 identities. The full sweep was not repeated.

Final browser/targeted check (verbatim):

$ bun e2e/admin-denial.mjs
Authorization matrix: 1 OpenAPI operations; 20 requests across 3 base identities plus invalid/stale session probes and 15 App Password scope classes; valid/malformed session bodies on 0 privileged body routes; App Passwords use valid bodies; policy classes {'public': 0, 'public_link': 0, 'user': 0, 'admin': 1}
PASS standard User: 96 admin links denied; Settings and palette hidden at 390/820/1440px in light/dark; zero admin API calls

cargo test: exit 101. Completed crate summaries and failure (verbatim):

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 63 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 138.83s
test result: ok. 26 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.84s
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.35s
10,000-block open, first sync and snapshot took 2.962869727s
test result: FAILED. 23 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.90s
error: test failed, to rerun pass `-p calternal-collab --lib`

The unchanged calternal-collab timing test requires two seconds and took 2.962869727s on this shared host. This is a SLOW finding. No assertion or threshold was changed, and no retry was run. Cargo stopped at this crate; later workspace crates were not reached. The Rust gate is not passing.

Known gaps: the complete Rust test gate did not pass because of the unchanged Collab timing test; later crates remain unverified by this run. no admin commands exist on MCP, WebMCP or CLI to exercise. Visual acceptance remains with the orchestrator; 24 production screenshots are attached. The first default-5-second web run had two unchanged tests time out; the complete final suite passed with the 30-second execution allowance. The live SLOW case passed in the focused follow-up.

Decisions:

  • Keep the current standard User redirect to /settings/account. Show generic Settings metadata until an admin Role is known.
  • Use valid bodies for App Password cases to keep each credential below its denial-audit budget; session cases retain valid/malformed bodies.
  • Permit one-operation checks for SLOW findings without bypassing the full inventory gate. Execution timeout is configurable; status expectations are unchanged.

No dependencies or Rust runtime policies changed. origin/dev was fetched and merged once before final gates: Already up to date. No push or deployment. The issue stays open.

Verification attachments:

admin-denial-live.log

admin-denial-targeted-and-ui.log

admin-classification-current.log

web-check-current.log

web-test-final.log

clippy.log

admin-classification-final.log

web-test-head.log

admin-denial-final-ui.log

Screenshots:

direct-link-paper-1440.png

direct-link-paper-390.png

direct-link-paper-820.png

direct-link-tokyo-night-1440.png

direct-link-tokyo-night-390.png

direct-link-tokyo-night-820.png

palette-paper-1440.png

palette-paper-390.png

palette-paper-820.png

palette-tokyo-night-1440.png

palette-tokyo-night-390.png

palette-tokyo-night-820.png

pending-role-paper-1440.png

pending-role-paper-390.png

pending-role-paper-820.png

pending-role-tokyo-night-1440.png

pending-role-tokyo-night-390.png

pending-role-tokyo-night-820.png

settings-paper-1440.png

settings-paper-390.png

settings-paper-820.png

settings-tokyo-night-1440.png

settings-tokyo-night-390.png

settings-tokyo-night-820.png

Latest classification gate:

admin-classification-final.log

Full Rust test log:

cargo-test.log

Cleanup: web build output was deleted. cargo clean output:

     Removed 16381 files, 14.0GiB total
#483 implementation report Branch: job/admin-deny-483. Head: a20f358d42eed358806971c676298a4864cf03cd. Built: - A fail-closed admin classification gate in CI and the local runner. OpenAPI admin operation IDs, /api/v1/admin/ paths, Rust scope/handler guards and route middleware must agree with the reviewed matrix. New, missing and duplicate route identities fail before requests. Coverage: 39 operations, 23 step-up policies; 14 regression tests. - Real Owner App Password fixtures for every valid protocol/access class, including upload-only and combined full scope. Every admin operation requires anonymous 401, standard User 403, App Password 403, and stale Admin 403 where step-up is required. Session bodies also test extraction order. - A production browser test with a real member User. All 96 admin links, Settings navigation and palette entries are denied at 390/820/1440 px in light and dark, with zero admin API calls. The observer includes auth-owned admin routes. - A fix for the pending-Role title and Copy link leak. Unknown or non-admin Role gets generic Settings metadata before the existing Account redirect. - MCP, WebMCP and CLI inventory regressions. These surfaces currently have no admin commands. WebMCP rejects Admin destinations before calling its browser adapter. Files: ``` .forgejo/workflows/ci.yml apps/web/e2e/admin-denial.mjs apps/web/package.json apps/web/src/lib/search/SearchResultRow.svelte apps/web/src/lib/webmcp/tools.test.ts apps/web/src/routes/settings/[...path]/+page.svelte tests/adversarial/authz_matrix.py tests/adversarial/run.sh tests/adversarial/test_admin_classification.py ``` Handler audit: all admin checks precede protected resource lookup and side effects. Auth handlers put RequireScope before Path/Json; server routes have middleware before extraction and repeat admin checks in handlers. Instance plugin changes repeat scope, Role and freshness before registry mutation. Job pause/resume middleware requires freshness even though the handler check only requires Role. Gate output (verbatim): cargo fmt --check: exit 0, no output. cargo clippy --all-targets -- -D warnings: exit 0. ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 152m 37s ``` bun run check: exit 0. ``` svelte-check found 0 errors and 0 warnings ``` bun run test --maxWorkers=2 --testTimeout=30000: exit 0; no expectation changes. ``` Test Files 137 passed (137) Tests 888 passed (888) Duration 874.55s (transform 41%, environment 22%, import 20%, tests 13%, setup 4%) ``` Classification: ``` .............. ---------------------------------------------------------------------- Ran 14 tests in 2.083s OK Admin coverage: 39 reviewed operations; contract and Rust guards agree ``` Live round (verbatim): ``` Authorization matrix: 39 OpenAPI operations; 834 requests across 3 base identities plus invalid/stale session probes and 15 App Password scope classes; valid/malformed session bodies on 14 privileged body routes; App Passwords use valid bodies; policy classes {'public': 0, 'public_link': 0, 'user': 0, 'admin': 39} !! DELETE /api/v1/auth/invites/{id} as app_password_all_full/valid: server response -1: b'timed out' authorization matrix found 1 failures ``` All 833 received responses matched the required denial. One 20-second socket timeout was SLOW-only; host load was `74.50 78.65 76.17`. The focused follow-up checked only revoke_invite with a 60-second execution allowance and passed all 20 identities. The full sweep was not repeated. Final browser/targeted check (verbatim): ``` $ bun e2e/admin-denial.mjs Authorization matrix: 1 OpenAPI operations; 20 requests across 3 base identities plus invalid/stale session probes and 15 App Password scope classes; valid/malformed session bodies on 0 privileged body routes; App Passwords use valid bodies; policy classes {'public': 0, 'public_link': 0, 'user': 0, 'admin': 1} PASS standard User: 96 admin links denied; Settings and palette hidden at 390/820/1440px in light/dark; zero admin API calls ``` cargo test: exit 101. Completed crate summaries and failure (verbatim): ``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 63 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 138.83s test result: ok. 26 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.84s test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.35s 10,000-block open, first sync and snapshot took 2.962869727s test result: FAILED. 23 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.90s error: test failed, to rerun pass `-p calternal-collab --lib` ``` The unchanged calternal-collab timing test requires two seconds and took 2.962869727s on this shared host. This is a SLOW finding. No assertion or threshold was changed, and no retry was run. Cargo stopped at this crate; later workspace crates were not reached. The Rust gate is not passing. Known gaps: the complete Rust test gate did not pass because of the unchanged Collab timing test; later crates remain unverified by this run. no admin commands exist on MCP, WebMCP or CLI to exercise. Visual acceptance remains with the orchestrator; 24 production screenshots are attached. The first default-5-second web run had two unchanged tests time out; the complete final suite passed with the 30-second execution allowance. The live SLOW case passed in the focused follow-up. Decisions: - Keep the current standard User redirect to /settings/account. Show generic Settings metadata until an admin Role is known. - Use valid bodies for App Password cases to keep each credential below its denial-audit budget; session cases retain valid/malformed bodies. - Permit one-operation checks for SLOW findings without bypassing the full inventory gate. Execution timeout is configurable; status expectations are unchanged. No dependencies or Rust runtime policies changed. origin/dev was fetched and merged once before final gates: `Already up to date.` No push or deployment. The issue stays open. Verification attachments: [admin-denial-live.log](https://git.kayg.org/attachments/ff5193d6-984f-496e-b01d-2e1628fe31e8) [admin-denial-targeted-and-ui.log](https://git.kayg.org/attachments/28b4fb87-8eab-4f18-8307-99e3a3d9ea01) [admin-classification-current.log](https://git.kayg.org/attachments/b7fb1993-4d84-454b-a762-7fe8ca8d0df7) [web-check-current.log](https://git.kayg.org/attachments/d4712651-c9de-477a-be7f-001a3ac0d851) [web-test-final.log](https://git.kayg.org/attachments/33b51ac7-de88-4643-84de-cb77a4f9faf8) [clippy.log](https://git.kayg.org/attachments/646de913-7d21-4ae5-9070-a570ee6fb8d0) [admin-classification-final.log](https://git.kayg.org/attachments/b850dab5-2faa-41cd-a0bc-13e2ba307c1a) [web-test-head.log](https://git.kayg.org/attachments/b1b66af6-3d50-436e-b062-632185ea48b7) [admin-denial-final-ui.log](https://git.kayg.org/attachments/cd11a7f3-16ca-448f-b6f4-f00ea49cee5d) Screenshots: [direct-link-paper-1440.png](https://git.kayg.org/attachments/af4fa13d-6e32-43db-a27d-67dfaea69dee) [direct-link-paper-390.png](https://git.kayg.org/attachments/791d8bb8-e81e-4f07-8158-3ed45dfb2262) [direct-link-paper-820.png](https://git.kayg.org/attachments/3c6c9af8-a476-48d3-8cec-707e4bebebc6) [direct-link-tokyo-night-1440.png](https://git.kayg.org/attachments/9998fd57-5925-41d1-95cf-ce9d70de76d1) [direct-link-tokyo-night-390.png](https://git.kayg.org/attachments/f751bbe3-2a08-4ddf-ad91-6247832f6fd5) [direct-link-tokyo-night-820.png](https://git.kayg.org/attachments/f69b2716-c203-495f-9c61-36f1bf61f2f8) [palette-paper-1440.png](https://git.kayg.org/attachments/a3613c42-3f71-403f-aa4f-c76a0fc61122) [palette-paper-390.png](https://git.kayg.org/attachments/c59d616a-7bfe-4854-b091-51f3183afc1f) [palette-paper-820.png](https://git.kayg.org/attachments/4e9d58e4-9af3-47da-894d-3256bc26be96) [palette-tokyo-night-1440.png](https://git.kayg.org/attachments/73b92dc2-cf8b-4a28-98ab-a9477876d1b4) [palette-tokyo-night-390.png](https://git.kayg.org/attachments/b4047ef1-2df2-4008-8197-b7fa40cad9b0) [palette-tokyo-night-820.png](https://git.kayg.org/attachments/2127a998-e69b-4a0d-a93c-5b35bfe81fcc) [pending-role-paper-1440.png](https://git.kayg.org/attachments/8cfed805-c76b-4c76-9dce-96d5f0013019) [pending-role-paper-390.png](https://git.kayg.org/attachments/3bfc33ea-ffa7-44d0-8976-405d1707b6c3) [pending-role-paper-820.png](https://git.kayg.org/attachments/09091cf6-68ae-4112-9182-77cedb39423d) [pending-role-tokyo-night-1440.png](https://git.kayg.org/attachments/bfc49587-84ea-4c26-b747-b887570a5fd4) [pending-role-tokyo-night-390.png](https://git.kayg.org/attachments/6e45febb-1023-4c38-afa5-65d7d52aa459) [pending-role-tokyo-night-820.png](https://git.kayg.org/attachments/4ae666d2-7fd6-4ea8-9e91-7541376d48c4) [settings-paper-1440.png](https://git.kayg.org/attachments/afa64794-dd72-4783-bc55-519fd5ff313a) [settings-paper-390.png](https://git.kayg.org/attachments/be20fed4-b31e-439b-b76d-1838fcecece0) [settings-paper-820.png](https://git.kayg.org/attachments/d549a526-695c-45c8-bc22-ee332f038a9c) [settings-tokyo-night-1440.png](https://git.kayg.org/attachments/88466d72-777e-4290-981b-582342379f62) [settings-tokyo-night-390.png](https://git.kayg.org/attachments/65150a44-6752-45fa-8902-0b73700d29a9) [settings-tokyo-night-820.png](https://git.kayg.org/attachments/1acfb2b5-ba83-407a-8374-e11f7081fa67) Latest classification gate: [admin-classification-final.log](https://git.kayg.org/attachments/23fff6d5-dcf4-40f0-9458-d9246b7625a5) Full Rust test log: [cargo-test.log](https://git.kayg.org/attachments/13d74769-bb63-435c-875b-b5e31b15b0d2) Cleanup: web build output was deleted. cargo clean output: ``` Removed 16381 files, 14.0GiB total ```
Author
Owner

The production browser denial pass failed an existing pending-Role title assertion:

AssertionError: pending Role hides the admin page title
+ actual - expected
+ 'Calendar · calternal'
- 'Settings · calternal'

The preceding assertions passed: no admin header link and no “Copy link to Users” action while Role was pending. The title did not expose an admin section. Both job/admin-deny-483's merged tree and the fetched dev cutoff use displayedHeader.title; the shared overlay header retains the background Calendar title for a cold Settings link. This is a cosmetic expectation mismatch, not an authorization failure. I kept the expectation unchanged. The pending-title behavior needs the orchestrator's decision. The offline admin coverage guard passed with 39 reviewed operations, and the separate valid-body live authorization matrix is running.

The production browser denial pass failed an existing pending-Role title assertion: ``` AssertionError: pending Role hides the admin page title + actual - expected + 'Calendar · calternal' - 'Settings · calternal' ``` The preceding assertions passed: no admin header link and no “Copy link to Users” action while Role was pending. The title did not expose an admin section. Both `job/admin-deny-483`'s merged tree and the fetched dev cutoff use `displayedHeader.title`; the shared overlay header retains the background Calendar title for a cold Settings link. This is a cosmetic expectation mismatch, not an authorization failure. I kept the expectation unchanged. The pending-title behavior needs the orchestrator's decision. The offline admin coverage guard passed with 39 reviewed operations, and the separate valid-body live authorization matrix is running.
Author
Owner

The bounded valid-body authorization matrix completed all 1891 requests, with 59 failures. No unauthorized success or 5xx was listed. Most failures were a 403 body/code mismatch:

!! GET /api/v1/admin/config as standard/valid: expected 403 (forbidden), received 403 (None)
!! PUT /api/v1/admin/config as stale_admin/valid: expected step-up 403 (forbidden), received 403 (None)
!! POST /api/v1/auth/sessions/revoke-all as standard/valid: authorized session revocation returned 403
!! POST /api/v1/auth/sessions/revoke-all as admin/valid: authorized session revocation returned 403
authorization matrix found 59 failures

The existing sample() chooses False for booleans. Therefore the schema-valid app_surfaces_put and admin_app_surfaces_put fixtures disable a surface. The authorized writes can switch off API access before later admin-denial cases. session_context then correctly returns the disabled-surface 403 text before the admin route guard, rather than the guard's JSON forbidden code. The long run also leaves the Web sessions used at the final fresh-assertion revocation checks older than at fixture setup; this is a likely contributor to those two 403s and is not yet independently verified.

I kept the existing fixtures and expectations unchanged under the owner rule. The existing focused ADMIN_DENIAL_ONLY mode is being checked with fresh fixtures and valid bodies; it sends no authorized admin configuration writes. This keeps the whole-matrix failure distinct from a focused proof of fail-closed admin guards. The matrix's final text still says “valid/malformed” because that label is hard-coded, but this bounded run used valid bodies only. Malformed and exploit/DoS phases were not run.

Decision needed from the orchestrator: repair the matrix fixture order/bodies and freshness setup before treating the full matrix as green. The integration branch cannot be reported as all gates green while this result remains.

The bounded valid-body authorization matrix completed all 1891 requests, with 59 failures. No unauthorized success or 5xx was listed. Most failures were a 403 body/code mismatch: ``` !! GET /api/v1/admin/config as standard/valid: expected 403 (forbidden), received 403 (None) !! PUT /api/v1/admin/config as stale_admin/valid: expected step-up 403 (forbidden), received 403 (None) !! POST /api/v1/auth/sessions/revoke-all as standard/valid: authorized session revocation returned 403 !! POST /api/v1/auth/sessions/revoke-all as admin/valid: authorized session revocation returned 403 authorization matrix found 59 failures ``` The existing `sample()` chooses `False` for booleans. Therefore the schema-valid `app_surfaces_put` and `admin_app_surfaces_put` fixtures disable a surface. The authorized writes can switch off API access before later admin-denial cases. `session_context` then correctly returns the disabled-surface 403 text before the admin route guard, rather than the guard's JSON `forbidden` code. The long run also leaves the Web sessions used at the final fresh-assertion revocation checks older than at fixture setup; this is a likely contributor to those two 403s and is not yet independently verified. I kept the existing fixtures and expectations unchanged under the owner rule. The existing focused `ADMIN_DENIAL_ONLY` mode is being checked with fresh fixtures and valid bodies; it sends no authorized admin configuration writes. This keeps the whole-matrix failure distinct from a focused proof of fail-closed admin guards. The matrix's final text still says “valid/malformed” because that label is hard-coded, but this bounded run used valid bodies only. Malformed and exploit/DoS phases were not run. Decision needed from the orchestrator: repair the matrix fixture order/bodies and freshness setup before treating the full matrix as green. The integration branch cannot be reported as all gates green while this result remains.
Author
Owner

Finding during real-server gate review: AUTHZ_MATRIX_ONLY=1 reached its exit check only after search_chaos.py, Mail/Money probes, and the broad API round, so the focused authz gate still ran unrelated probes. I moved its dispatch to immediately after real User/session fixture setup. bash -n tests/adversarial/run.sh passes; the live matrix will run in the final round.

Finding during real-server gate review: `AUTHZ_MATRIX_ONLY=1` reached its exit check only after `search_chaos.py`, Mail/Money probes, and the broad API round, so the focused authz gate still ran unrelated probes. I moved its dispatch to immediately after real User/session fixture setup. `bash -n tests/adversarial/run.sh` passes; the live matrix will run in the final round.
Author
Owner

Merge round 3 integration report

State: incomplete; do not fast-forward this snapshot to dev yet. The four-hour job limit ended while CLI Clippy was compiling. No completed final-tree gate failed. No source branch was dropped because the completed gates showed no failing branch.

  • Branch: job/merge-round-3
  • Head: 3e5056d485e9021d2d1f708613e783b0b901b447
  • Included in order: job/multiget-500, job/dav-delete-471, job/iso-435, job/admin-deny-483, job/attach-427, job/hidden-420, job/files-sel-keys, job/small-bugs-3, job/sweep-478.
  • Additional commits: 92f5803f3, 3ea69e317, 26b986bc4, 0948cffa1, 99c088193, df6b07a5a, 3e5056d48.

Completed gate output (verbatim excerpts)

cargo fmt --check exited 0 with no output.

  • DAV clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 56.92s`
  • DAV tests:
    test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.37s
    test result: ok. 36 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s
    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.31s
  • Notes Core clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 14.97s`
  • Notes Core tests:
    test result: ok. 512 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.21s
    test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.21s
    test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
    test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.35s
    test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
  • Notes plugin clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 2m 55s`
  • Notes plugin tests: test result: ok. 127 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 151.26s
  • Files clippy (after comment fix): Finished \dev` profile [unoptimized + debuginfo] target(s) in 48.77s`
  • Files tests: test result: ok. 144 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 178.62s
    The dev-version migration test passed: test tests::dev_files_schema_upgrades_through_share_log_and_sidecar_migrations ... ok
  • Calendar clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 1m 53s`
  • Calendar tests:
    test result: ok. 52 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.01s
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s
    test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s
  • Photos clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 1m 13s`
  • Photos tests: test result: ok. 45 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 11.10s
  • Search clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 55.79s`
  • Search tests:
    test result: ok. 36 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 46.71s
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.38s
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s
    test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.44s
    test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s
    test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
    test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 5.66s
    test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
  • Embed clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 27.32s`
  • Embed tests: test result: ok. 31 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 1.93s
  • Filesystem clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 10.78s`
  • Filesystem tests:
    test result: ok. 50 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 9.04s
    test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.56s
  • Server clippy: Finished \dev` profile [unoptimized + debuginfo] target(s) in 1m 48s`
  • Server tests: test result: ok. 85 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 19.67s

Other completed checks:

  • Parity matrix: 190 web API actions, 122 shortcuts, 2 static commands, 136 menu actions, 31 settings groups, 172 actions with adapter gaps
  • Cross-User classification gate: 311 operations classified; its test suite printed Ran 5 tests in 0.246s and OK.
  • Admin coverage: 39 reviewed operations; contract and Rust guards agree; its test suite printed Ran 14 tests in 2.404s and OK.
  • Migration audit: ai: 4 migrations, no duplicate numbers; analytics: 2 migrations, no duplicate numbers; calendar: 3 migrations, no duplicate numbers; files: 18 migrations, no duplicate numbers; mail: 8 migrations, no duplicate numbers; notes: 19 migrations, no duplicate numbers; notifications: 4 migrations, no duplicate numbers; photos: 6 migrations, no duplicate numbers; video: 1 migrations, no duplicate numbers.

Remaining work

  • CLI Clippy was interrupted at the four-hour limit while checking dependencies; CLI tests and both Auth gates did not run.
  • The generated contract check, web bun run check, bun run test, and bun run build are pending.
  • The live two-User matrix, authz matrix, DAV round (including Apple’s 100-href and DELETE re-parent replays), sidecar probe, and attachment e2e are pending.
  • Production-browser screenshots for each affected screen at 390/820/1440 px in light/dark mode are pending. No visual review artifacts were produced.
  • The new benchmark profile was added, but its local run and comparison with docs/perf/baseline.json are pending.
  • cargo clean is running but has not returned yet; apps/web/build was removed.

Decisions

  • Files migration IDs follow merge order after dev’s 0015: 0016 share_search_invalidations, 0017 log_attachment_trash, 0018 sidecar_pairs. The populated dev-schema upgrade test passed.
  • Hidden-file Settings copy leads with the User-visible result and uses “Photo edit files (.xmp, .aae)”.
  • The parity exception snapshot was regenerated and reviewed for the newly merged API and Files UI actions.

The branch contains the merged code and commits, but the listed pending gates mean this is not a green merge candidate yet.

## Merge round 3 integration report **State: incomplete; do not fast-forward this snapshot to `dev` yet.** The four-hour job limit ended while CLI Clippy was compiling. No completed final-tree gate failed. No source branch was dropped because the completed gates showed no failing branch. - Branch: `job/merge-round-3` - Head: `3e5056d485e9021d2d1f708613e783b0b901b447` - Included in order: `job/multiget-500`, `job/dav-delete-471`, `job/iso-435`, `job/admin-deny-483`, `job/attach-427`, `job/hidden-420`, `job/files-sel-keys`, `job/small-bugs-3`, `job/sweep-478`. - Additional commits: `92f5803f3`, `3ea69e317`, `26b986bc4`, `0948cffa1`, `99c088193`, `df6b07a5a`, `3e5056d48`. ### Completed gate output (verbatim excerpts) `cargo fmt --check` exited 0 with no output. - DAV clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 56.92s` - DAV tests: `test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.37s` `test result: ok. 36 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s` `test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.31s` - Notes Core clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 14.97s` - Notes Core tests: `test result: ok. 512 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.21s` `test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.21s` `test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s` `test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.35s` `test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s` - Notes plugin clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 2m 55s` - Notes plugin tests: `test result: ok. 127 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 151.26s` - Files clippy (after comment fix): `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 48.77s` - Files tests: `test result: ok. 144 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 178.62s` The dev-version migration test passed: `test tests::dev_files_schema_upgrades_through_share_log_and_sidecar_migrations ... ok` - Calendar clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 1m 53s` - Calendar tests: `test result: ok. 52 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.01s` `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s` `test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s` - Photos clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 1m 13s` - Photos tests: `test result: ok. 45 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 11.10s` - Search clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 55.79s` - Search tests: `test result: ok. 36 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 46.71s` `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.38s` `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s` `test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.44s` `test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s` `test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s` `test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 5.66s` `test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s` - Embed clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 27.32s` - Embed tests: `test result: ok. 31 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 1.93s` - Filesystem clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 10.78s` - Filesystem tests: `test result: ok. 50 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 9.04s` `test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.56s` - Server clippy: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 1m 48s` - Server tests: `test result: ok. 85 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 19.67s` Other completed checks: - `Parity matrix: 190 web API actions, 122 shortcuts, 2 static commands, 136 menu actions, 31 settings groups, 172 actions with adapter gaps` - `Cross-User classification gate: 311 operations classified`; its test suite printed `Ran 5 tests in 0.246s` and `OK`. - `Admin coverage: 39 reviewed operations; contract and Rust guards agree`; its test suite printed `Ran 14 tests in 2.404s` and `OK`. - Migration audit: `ai: 4 migrations, no duplicate numbers`; `analytics: 2 migrations, no duplicate numbers`; `calendar: 3 migrations, no duplicate numbers`; `files: 18 migrations, no duplicate numbers`; `mail: 8 migrations, no duplicate numbers`; `notes: 19 migrations, no duplicate numbers`; `notifications: 4 migrations, no duplicate numbers`; `photos: 6 migrations, no duplicate numbers`; `video: 1 migrations, no duplicate numbers`. ### Remaining work - CLI Clippy was interrupted at the four-hour limit while checking dependencies; CLI tests and both Auth gates did not run. - The generated contract check, web `bun run check`, `bun run test`, and `bun run build` are pending. - The live two-User matrix, authz matrix, DAV round (including Apple’s 100-href and DELETE re-parent replays), sidecar probe, and attachment e2e are pending. - Production-browser screenshots for each affected screen at 390/820/1440 px in light/dark mode are pending. No visual review artifacts were produced. - The new benchmark profile was added, but its local run and comparison with `docs/perf/baseline.json` are pending. - `cargo clean` is running but has not returned yet; `apps/web/build` was removed. ### Decisions - Files migration IDs follow merge order after dev’s 0015: 0016 `share_search_invalidations`, 0017 `log_attachment_trash`, 0018 `sidecar_pairs`. The populated dev-schema upgrade test passed. - Hidden-file Settings copy leads with the User-visible result and uses “Photo edit files (.xmp, .aae)”. - The parity exception snapshot was regenerated and reviewed for the newly merged API and Files UI actions. The branch contains the merged code and commits, but the listed pending gates mean this is not a green merge candidate yet.
Author
Owner

Merge round 3 report addendum

Branch job/merge-round-3, HEAD 3e5056d485e9021d2d1f708613e783b0b901b447.

  • Cleanup completed after the main report: Removed 25367 files, 17.5GiB total.
  • apps/web/build removal check: web build output removed: True.
  • git diff --check exited 0 with no output; the worktree has no modified or untracked files.
  • The 4-hour cutoff also prevented a full re-read of documentation comments across all 234 changed files. This remains an audit gap alongside the pending gates listed in the main report.

The branch is still not a green merge candidate.

## Merge round 3 report addendum Branch `job/merge-round-3`, HEAD `3e5056d485e9021d2d1f708613e783b0b901b447`. - Cleanup completed after the main report: `Removed 25367 files, 17.5GiB total`. - `apps/web/build` removal check: `web build output removed: True`. - `git diff --check` exited 0 with no output; the worktree has no modified or untracked files. - The 4-hour cutoff also prevented a full re-read of documentation comments across all 234 changed files. This remains an audit gap alongside the pending gates listed in the main report. The branch is still not a green merge candidate.
Author
Owner

Merged in merge round 3 and deployed to calternal.cloud in cc25c441b (healthy).

Merged in merge round 3 and deployed to calternal.cloud in cc25c441b (healthy).
kayg closed this issue 2026-09-30 23:22:39 +00:00
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#483
No description provided.