CALENDAR: document stacks show no previews and sit right-aligned (should preview and sit left) #410

Closed
opened 2026-09-29 07:32:15 +00:00 by kayg · 46 comments
Owner

Bug (owner, 2026-09-29, Calendar Day view)

A stack of 62 saved documents shows three blank white cards and a generic document glyph with a count: "why no previews for the documents in the calendar and they should be left indented btw, not right!"

Fix

  • Previews: document stacks and single documents show real thumbnails: PDF first page (render with the existing media pipeline — vips with PDF support or pdfium, in the media sandbox), images as images, Markdown/text as a rendered text snippet card, Office files as their first page when a converter is available or a typed card with the file name. Thumbnails are generated through the Jobs registry and cached like photo thumbnails; placeholders show the file-type glyph until ready, never blank cards.
  • Placement: stacks sit left-aligned in the lane (starting at the lane's left inset like event cards), not pushed to the right edge.
  • The fanned stack shows the three newest documents' previews; the count badge stays.
  • Evidence: production screenshots of a day with a PDF stack, a mixed stack, and a single document at 390/820/1440 light and dark (theme asserted).
## Bug (owner, 2026-09-29, Calendar Day view) A stack of 62 saved documents shows three blank white cards and a generic document glyph with a count: "why no previews for the documents in the calendar and they should be left indented btw, not right!" ## Fix - **Previews:** document stacks and single documents show real thumbnails: PDF first page (render with the existing media pipeline — vips with PDF support or pdfium, in the media sandbox), images as images, Markdown/text as a rendered text snippet card, Office files as their first page when a converter is available or a typed card with the file name. Thumbnails are generated through the Jobs registry and cached like photo thumbnails; placeholders show the file-type glyph until ready, never blank cards. - **Placement:** stacks sit left-aligned in the lane (starting at the lane's left inset like event cards), not pushed to the right edge. - The fanned stack shows the three newest documents' previews; the count badge stays. - Evidence: production screenshots of a day with a PDF stack, a mixed stack, and a single document at 390/820/1440 light and dark (theme asserted).
Author
Owner

Starting issue #410 on job/doc-stack.

  • Base SHA: c46515046871936ad681c4d00e8b76de69dde3b7 (dev merge-base)
  • Branch head: c46515046871936ad681c4d00e8b76de69dde3b7

I am tracing the existing Files thumbnail job and Calendar stack rendering before implementing PDF and document previews plus left alignment.

Starting issue #410 on `job/doc-stack`. - Base SHA: `c46515046871936ad681c4d00e8b76de69dde3b7` (`dev` merge-base) - Branch head: `c46515046871936ad681c4d00e8b76de69dde3b7` I am tracing the existing Files thumbnail job and Calendar stack rendering before implementing PDF and document previews plus left alignment.
Author
Owner

Finding: the Calendar item API returned thumbnail hashes only for image and video files, even though the Files worker owns the thumbnail cache and job queue. I added a small public calternal_plugin_files::thumbnail_supported query so Calendar can identify formats the existing worker can render without duplicating extension/MIME rules; PDF and approved text files use the Files files.thumbnail job.

Finding: DESIGN §39 described the standalone pile rail on the right, while issue #410 requires stacks left-aligned in the lane. I updated §39 and the Calendar grid together so the decision and implementation agree.

Finding: the Calendar item API returned thumbnail hashes only for image and video files, even though the Files worker owns the thumbnail cache and job queue. I added a small public `calternal_plugin_files::thumbnail_supported` query so Calendar can identify formats the existing worker can render without duplicating extension/MIME rules; PDF and approved text files use the Files `files.thumbnail` job. Finding: DESIGN §39 described the standalone pile rail on the right, while issue #410 requires stacks left-aligned in the lane. I updated §39 and the Calendar grid together so the decision and implementation agree.
Author
Owner

Finding during the local media-sandbox smoke test: libvips rejected page on thumbnail_source, and the normal VIPS_BLOCK_UNTRUSTED setting blocks its PDF/SVG loaders. I changed PDF rendering to use a fixed /tmp/calternal-input.pdf symlink to inherited stdin so pdfload selects its loader by suffix, and left the block variable unset only in the isolated, resource-limited document-thumbnail mode. Evidence: a valid one-page PDF and a server-generated text-card SVG both produced WebP through the sandbox; the PDF header probe reported only the first page at 180×80.

Finding during the local media-sandbox smoke test: libvips rejected `page` on `thumbnail_source`, and the normal `VIPS_BLOCK_UNTRUSTED` setting blocks its PDF/SVG loaders. I changed PDF rendering to use a fixed `/tmp/calternal-input.pdf` symlink to inherited stdin so pdfload selects its loader by suffix, and left the block variable unset only in the isolated, resource-limited document-thumbnail mode. Evidence: a valid one-page PDF and a server-generated text-card SVG both produced WebP through the sandbox; the PDF header probe reported only the first page at 180×80.
Author
Owner

Finding: pile cards need newest-first order, but Calendar's RFC 3339 at values include UTC offsets. String ordering can put an earlier item ahead of a later item in the repeated hour at the autumn DST change. I switched the UI to compare parsed instants and added a regression case for the Europe/Berlin fold (02:15+02:00 vs 02:05+01:00).

Finding: pile cards need newest-first order, but Calendar's RFC 3339 `at` values include UTC offsets. String ordering can put an earlier item ahead of a later item in the repeated hour at the autumn DST change. I switched the UI to compare parsed instants and added a regression case for the Europe/Berlin fold (02:15+02:00 vs 02:05+01:00).
Author
Owner

Finding: the Calendar range returned supported thumbnail hashes for PDF and text attachments, but queued Jobs only for files saved as standalone Calendar items. An older PDF attached to a Log could therefore stay on a glyph forever when Calendar was the only screen opened. I am queueing visible attachment previews through the existing Files registry with a 64-job cap per range, and the Agenda deck now uses the shared FileThumb retry behavior while the Job runs. The Calendar test now checks that an old PDF attachment creates a files.thumbnail Job.

Finding: the Calendar range returned supported thumbnail hashes for PDF and text attachments, but queued Jobs only for files saved as standalone Calendar items. An older PDF attached to a Log could therefore stay on a glyph forever when Calendar was the only screen opened. I am queueing visible attachment previews through the existing Files registry with a 64-job cap per range, and the Agenda deck now uses the shared FileThumb retry behavior while the Job runs. The Calendar test now checks that an old PDF attachment creates a `files.thumbnail` Job.
Author
Owner

Review finding: the first typed Office fallback showed only “DOCX”, but the issue requires the file name on the typed card. The shared Files thumbnail component now renders the file name with the type label, and the production browser script asserts Quarterly.docx is visible. The UI check and screenshot run are still pending.

Review finding: the first typed Office fallback showed only “DOCX”, but the issue requires the file name on the typed card. The shared Files thumbnail component now renders the file name with the type label, and the production browser script asserts `Quarterly.docx` is visible. The UI check and screenshot run are still pending.
Author
Owner

The first focused Files test run exposed an incorrect new assertion: .txt is not in the existing media-suffix allowlist, so filename_matches_format intentionally treats it as unknown. I changed the test to use the known .png suffix for a PDF mismatch. No existing test expectation or runtime behavior changed; the focused suite is running again.

The first focused Files test run exposed an incorrect new assertion: `.txt` is not in the existing media-suffix allowlist, so `filename_matches_format` intentionally treats it as unknown. I changed the test to use the known `.png` suffix for a PDF mismatch. No existing test expectation or runtime behavior changed; the focused suite is running again.
Author
Owner

The first Calendar compile found that RawRow stores its indexed path in its sort key and has no path field. The thumbnail capability check now uses the projected CalendarItem.path, which is the same Home-relative path used in the API response. I am rerunning the focused Calendar suite against this fix.

The first Calendar compile found that `RawRow` stores its indexed path in its sort key and has no `path` field. The thumbnail capability check now uses the projected `CalendarItem.path`, which is the same Home-relative path used in the API response. I am rerunning the focused Calendar suite against this fix.
Author
Owner

The Calendar range test now reaches the thumbnail Job assertion, and exposed that its SQLite fixture applied only plugin migrations, not the built-in calternal-db Jobs migrations used by the server. I added the built-in migration set to the fixture so the regression check exercises the real queue schema; the focused suite is running again.

The Calendar range test now reaches the thumbnail Job assertion, and exposed that its SQLite fixture applied only plugin migrations, not the built-in `calternal-db` Jobs migrations used by the server. I added the built-in migration set to the fixture so the regression check exercises the real queue schema; the focused suite is running again.
Author
Owner

Calendar E2E evidence: the Calendar items API returns saved files, while the grid omits a file linked from a Log entry because loadItems removes Log-linked files from standalone stacks (DESIGN §39). The screenshot fixture now keeps the attached Receipt PDF in the earlier 08:00 slot and uses a separate PDF as the third standalone file in the 09:00 stack. This preserves the attached-file deck check and keeps the three-item preview assertion valid at 390, 820, and 1440 px.

Calendar E2E evidence: the Calendar items API returns saved files, while the grid omits a file linked from a Log entry because `loadItems` removes Log-linked files from standalone stacks (DESIGN §39). The screenshot fixture now keeps the attached Receipt PDF in the earlier 08:00 slot and uses a separate PDF as the third standalone file in the 09:00 stack. This preserves the attached-file deck check and keeps the three-item preview assertion valid at 390, 820, and 1440 px.
Author
Owner

Implemented and pushed #410 on job/doc-stack.

What changed

  • Calendar stacks stay left-aligned. Calendar stack cards and Log attachment decks now show document previews using the existing Files thumbnail Jobs pipeline. PDF rendering runs inside the media sandbox. The Calendar API provides thumbnail hash and media type for the relevant items.
  • Files renders bounded text cards for supported text documents and uses the existing file icon path when there is no image preview. Calendar uses role tokens and the shared FileThumb / FileIcon components.
  • Added PDF hostile-input fixtures and probe coverage for malformed files, oversized page counts, compressed expansion, and JavaScript actions. Added a focused media-only adversarial runner mode.
  • Added production Calendar E2E screenshot coverage and updated the design and generated API contract.

Files

Changed 28 files across crates/plugins/{files,calendar}, deploy, packages/ui, apps/web, contracts/openapi.json, packages/api-client, tests/adversarial, and docs/DESIGN.md.

Screenshots

Production screenshots were captured at 390, 820, and 1440 px in light and dark for Calendar Day and Agenda. The E2E selected theme through the app and asserted its palette/settings before each capture. All 12 PNGs are attached to this issue:

Checks

cargo fmt --all -- --check
(no output; exit 0)

cargo test -p calternal-plugin-files
passed: 132 passed, 1 ignored; doc-tests: 0

cargo test -p calternal-plugin-calendar
passed: 49 + 1 + 3 tests across test binaries; doc-tests: 0

production web build
✓ built in 49.93s
Wrote site to "build"
✔ done

production Calendar screenshot E2E
CALENDAR DOC STACK SCREENSHOTS PASSED
exit_code=0

cargo clippy --all-targets -- -D warnings
    Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/doc-stack/crates/calternal-db)
[interrupted at the four-hour job timebox before completion]

The four-hour job timebox was reached during Clippy's cold compile. cargo test --workspace, web bun run check, web bun run test, and the real-server hostile-PDF adversarial round were not run. The focused adversarial cases are added but remain unverified in that round. SLOW-only findings were not evaluated. cargo clean removed 12,194 files (8.0 GiB); apps/web/build and apps/web/.svelte-kit were deleted.

Decisions where the design is silent

  • When a document does not yield a raster preview, show a bounded text card: at most 2 MiB input and an 8 KiB excerpt capped at eight lines. Otherwise use its file-type icon and name.
  • Use the first PDF page for a preview, with a 64 MiB PDF input cap, and render it through the existing media sandbox and Jobs registry.

Head: f99df200d8f2cf347ed5ff54dfd5c10471ae374f. Branch pushed to origin/job/doc-stack. git merge dev was done once before final checks; no merge into dev was made.

Implemented and pushed #410 on `job/doc-stack`. **What changed** - Calendar stacks stay left-aligned. Calendar stack cards and Log attachment decks now show document previews using the existing Files thumbnail Jobs pipeline. PDF rendering runs inside the media sandbox. The Calendar API provides thumbnail hash and media type for the relevant items. - Files renders bounded text cards for supported text documents and uses the existing file icon path when there is no image preview. Calendar uses role tokens and the shared `FileThumb` / `FileIcon` components. - Added PDF hostile-input fixtures and probe coverage for malformed files, oversized page counts, compressed expansion, and JavaScript actions. Added a focused media-only adversarial runner mode. - Added production Calendar E2E screenshot coverage and updated the design and generated API contract. **Files** Changed 28 files across `crates/plugins/{files,calendar}`, `deploy`, `packages/ui`, `apps/web`, `contracts/openapi.json`, `packages/api-client`, `tests/adversarial`, and `docs/DESIGN.md`. **Screenshots** Production screenshots were captured at 390, 820, and 1440 px in light and dark for Calendar Day and Agenda. The E2E selected theme through the app and asserted its palette/settings before each capture. All 12 PNGs are attached to this issue: - Day 390: [light](https://git.kayg.org/attachments/7694ae2e-49d8-4f02-9cec-445f65c70ae1), [dark](https://git.kayg.org/attachments/6ac1453b-77bf-4be4-815f-68a94fc2391a) - Day 820: [light](https://git.kayg.org/attachments/891d9271-be2d-49d3-831e-efe1dcfcc7c5), [dark](https://git.kayg.org/attachments/98dcfd6f-a7a4-4b53-ac47-f5969b4d3f72) - Day 1440: [light](https://git.kayg.org/attachments/e91a5b17-34d0-4ce1-8088-4c2f2c0f06cf), [dark](https://git.kayg.org/attachments/dcbf1844-8f0e-4cfd-a47b-9305b55e7a01) - Agenda 390: [light](https://git.kayg.org/attachments/5c124ccd-6f96-48a0-a5cc-f670229d59c0), [dark](https://git.kayg.org/attachments/6747990c-462e-4203-bbaf-4bbb6bdb1580) - Agenda 820: [light](https://git.kayg.org/attachments/1da7f272-843b-4e9c-bb13-ee73fbccc1fe), [dark](https://git.kayg.org/attachments/2b6ddc10-950a-4402-987a-f452068f14cf) - Agenda 1440: [light](https://git.kayg.org/attachments/021cc457-7ff3-44f5-983b-2f5b18635129), [dark](https://git.kayg.org/attachments/c1b85e71-d700-4855-8005-39112f72b8f0) **Checks** ```text cargo fmt --all -- --check (no output; exit 0) cargo test -p calternal-plugin-files passed: 132 passed, 1 ignored; doc-tests: 0 cargo test -p calternal-plugin-calendar passed: 49 + 1 + 3 tests across test binaries; doc-tests: 0 production web build ✓ built in 49.93s Wrote site to "build" ✔ done production Calendar screenshot E2E CALENDAR DOC STACK SCREENSHOTS PASSED exit_code=0 cargo clippy --all-targets -- -D warnings Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/doc-stack/crates/calternal-db) [interrupted at the four-hour job timebox before completion] ``` The four-hour job timebox was reached during Clippy's cold compile. `cargo test --workspace`, web `bun run check`, web `bun run test`, and the real-server hostile-PDF adversarial round were not run. The focused adversarial cases are added but remain unverified in that round. SLOW-only findings were not evaluated. `cargo clean` removed 12,194 files (8.0 GiB); `apps/web/build` and `apps/web/.svelte-kit` were deleted. **Decisions where the design is silent** - When a document does not yield a raster preview, show a bounded text card: at most 2 MiB input and an 8 KiB excerpt capped at eight lines. Otherwise use its file-type icon and name. - Use the first PDF page for a preview, with a 64 MiB PDF input cap, and render it through the existing media sandbox and Jobs registry. Head: `f99df200d8f2cf347ed5ff54dfd5c10471ae374f`. Branch pushed to `origin/job/doc-stack`. `git merge dev` was done once before final checks; no merge into dev was made.
Author
Owner

Correction to the screenshot list in my final report: the Calendar Day 390 dark screenshot link is https://git.kayg.org/attachments/6ac1453b-77bf-4be4-815f-f68a94fc2391 . The PNG is attached to this issue.

Correction to the screenshot list in my final report: the Calendar Day 390 dark screenshot link is https://git.kayg.org/attachments/6ac1453b-77bf-4be4-815f-f68a94fc2391 . The PNG is attached to this issue.
Author
Owner

Resuming #410 from job/doc-stack at f99df200d8 (current dev merge-base: 191b179baa). Previous implementation and focused adversarial mode are present. I am fixing the reported 1440 px stack sizing, phone back-card slivers, lane inset, and short Log row height, then I will run the missed web and per-crate gates plus the hostile-PDF round and attach fresh screenshots.

Resuming #410 from job/doc-stack at f99df200d8f2cf347ed5ff54dfd5c10471ae374f (current dev merge-base: 191b179baac3ef4f5bebfe07ce91c4b7a887ace2). Previous implementation and focused adversarial mode are present. I am fixing the reported 1440 px stack sizing, phone back-card slivers, lane inset, and short Log row height, then I will run the missed web and per-crate gates plus the hostile-PDF round and attach fresh screenshots.
Author
Owner

Finding: at the default 48 px hour height, a desktop 30-minute slot made a square preview only 22 px tall (). I now let each fixed slot's visual window use the free interval before the next slot, capped at one hour; the card stays inside that interval and scales with the lane. This keeps the §39 grouping and avoids visual overlap. I moved the count into the card corner, matched the Event inset, fanned cards into the lane, and kept typed labels off back cards. #384 is still absent from dev (local dev is 09e238d); its comment asks for one readable title line on short Log entries, so I am adding that CSS floor here and will note it for reconciliation.

Finding: at the default 48 px hour height, a desktop 30-minute slot made a square preview only 22 px tall (). I now let each fixed slot's visual window use the free interval before the next slot, capped at one hour; the card stays inside that interval and scales with the lane. This keeps the §39 grouping and avoids visual overlap. I moved the count into the card corner, matched the Event inset, fanned cards into the lane, and kept typed labels off back cards. #384 is still absent from dev (local dev is 09e238d); its comment asks for one readable title line on short Log entries, so I am adding that CSS floor here and will note it for reconciliation.
Author
Owner

Finding: at the default 48 px hour height, a desktop 30-minute slot made a square preview only 22 px tall (minimum of slot height minus 2 px and pile size minus 6 px). I now let each fixed slot's visual window use the free interval before the next slot, capped at one hour; the card stays inside that interval and scales with the lane. This keeps the §39 grouping and avoids visual overlap. I moved the count into the card corner, matched the Event inset, fanned cards into the lane, and kept typed labels off back cards. #384 is still absent from dev (local dev is 09e238d); its comment asks for one readable title line on short Log entries, so I am adding that CSS floor here and will note it for reconciliation.

Finding: at the default 48 px hour height, a desktop 30-minute slot made a square preview only 22 px tall (minimum of slot height minus 2 px and pile size minus 6 px). I now let each fixed slot's visual window use the free interval before the next slot, capped at one hour; the card stays inside that interval and scales with the lane. This keeps the §39 grouping and avoids visual overlap. I moved the count into the card corner, matched the Event inset, fanned cards into the lane, and kept typed labels off back cards. #384 is still absent from dev (local dev is 09e238d); its comment asks for one readable title line on short Log entries, so I am adding that CSS floor here and will note it for reconciliation.
Author
Owner

The required one-time merge of dev is complete at 22644994. I resolved the E2E helper import conflict by keeping the newer lazy screenshot dependency plus both pdfFixture and setThemeContext; git diff --check passed before completing the merge. The focused stack changes and test coverage are intact. I am now running post-merge gates and production screenshots.

The required one-time merge of `dev` is complete at `22644994`. I resolved the E2E helper import conflict by keeping the newer lazy screenshot dependency plus both `pdfFixture` and `setThemeContext`; `git diff --check` passed before completing the merge. The focused stack changes and test coverage are intact. I am now running post-merge gates and production screenshots.
Author
Owner

Post-merge clippy found one warning promoted to an error in the incoming calternal-fs code: crates/calternal-fs/src/root.rs:379 compares a byte array as [b'1'], triggering clippy::byte-char-slices under -D warnings. I am changing it to the equivalent byte-string spelling (*b"1"); this keeps behavior unchanged and allows the required crate gates to proceed. I will include calternal-fs in the per-crate gates because this fix touches it.

Post-merge clippy found one warning promoted to an error in the incoming `calternal-fs` code: `crates/calternal-fs/src/root.rs:379` compares a byte array as `[b'1']`, triggering `clippy::byte-char-slices` under `-D warnings`. I am changing it to the equivalent byte-string spelling (`*b"1"`); this keeps behavior unchanged and allows the required crate gates to proceed. I will include `calternal-fs` in the per-crate gates because this fix touches it.
Author
Owner

The Calendar crate clippy gate found three branch-side warnings under -D warnings: items.rs:421 uses a comparator that can be sort_by_key, and view.rs helpers load_log_details (line 1095) and resolve_attachments (line 1196) each take eight parameters. I am simplifying the sort and grouping their per-request state so the internal signatures stay readable without suppressing clippy or changing the Calendar response contract.

The Calendar crate clippy gate found three branch-side warnings under `-D warnings`: `items.rs:421` uses a comparator that can be `sort_by_key`, and `view.rs` helpers `load_log_details` (line 1095) and `resolve_attachments` (line 1196) each take eight parameters. I am simplifying the sort and grouping their per-request state so the internal signatures stay readable without suppressing clippy or changing the Calendar response contract.
Author
Owner

Forgejo #410 — finished

Branch: job/doc-stack
Base: 191b179baac3ef4f5bebfe07ce91c4b7a887ace2
Merged dev once at 226449940 before final gates.
Head: 49bdd6f640cca134ab8aec32b349efb7fa178e9e

Built

  • Calendar document piles now use the open time before the next pile, up to one hour, while keeping fixed slot grouping. The preview scales with the lane, keeps the Calendar item inset, and stays within the available time. The count sits inside the front card corner. Back cards show an image or neutral glyph without cropped Office labels. The hover tooltip lists file names.
  • Short Log entries keep one title line, as requested by #384.
  • Added real-server browser assertions for stack sizes, badge containment, back-card content, tooltip names, the singleton Office card, pile spacing, and the short Log title.
  • Fixed merge-gate clippy findings in Calendar state helpers and a byte comparison in calternal-fs. These edits do not change behavior.

Files

  • Calendar UI: packages/ui/src/components/calendar/{GridColumn.svelte,ActivityStack.svelte,AttachmentDeck.svelte,attachments.ts,layout.ts,model.ts}, packages/ui/src/components/files/{FileIcon.svelte,FileThumb.svelte}.
  • Calendar data and API: apps/web/src/lib/calendar/{data.ts,grid.test.ts}, crates/plugins/calendar/src/{items.rs,view.rs}, crates/plugins/files/src/{lib.rs,listing.rs,media.rs,thumbnails.rs}, crates/calternal-fs/src/root.rs, contracts/openapi.json, packages/api-client/src/generated.ts.
  • E2E, adversarial and runtime: apps/web/e2e/{calendar-doc-stack.mjs,files.mjs,harness.mjs}, apps/web/package.json, deploy/{Containerfile.runtime,media-sandbox}, tests/adversarial/{attack.py,media_uploads.py,run.sh}.
  • Design: docs/DESIGN.md §39.

Gates

bun run check:

Text sizes use shared role tokens.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/doc-stack/apps/web
Getting Svelte diagnostics...
svelte-check found 0 errors and 0 warnings

bun run test:

 Test Files  126 passed (126)
      Tests  810 passed (810)
   Start at  16:15:04
   Duration  131.29s (transform 45%, environment 25%, import 16%, tests 10%, setup 4%)

bun run build: ✓ built in 58.21s; static adapter wrote build and completed. The build emitted the existing vendor use client directive warnings.

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

cargo clippy -p calternal-fs --all-targets -- -D warnings:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 26s

cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 36.29s

cargo clippy -p calternal-plugin-files --all-targets -- -D warnings:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 28s

cargo clippy -p calternal-server --all-targets -- -D warnings:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 41m 44s

Per-crate test summaries:

calternal-fs: test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 13.10s
calternal-fs storage: test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.97s
calternal-plugin-calendar: test result: ok. 50 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.87s
calternal-plugin-calendar cache: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s
calternal-plugin-calendar protocol: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s
calternal-plugin-files: test result: ok. 132 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 132.64s
calternal-server: test result: ok. 82 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 11.75s

Production E2E output: CALENDAR DOC STACK SCREENSHOTS PASSED.

Hostile media/PDF round:

finish 200
login web 200 installation 200
cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ]
invite 200
second user finish 200
fixture passkey login requests: 30/30
HEIF/AVIF/PDF upload probe passed

Cleanup: Removed 16975 files, 9.7GiB total; removed apps/web/build and apps/web/.svelte-kit/output. The worktree is clean. Screenshot files remain under artifacts/calendar-doc-stack-410/ and are attached below. No screenshots were committed.

Decisions

  • Keep 30-minute desktop and 60-minute touch grouping. Use the empty interval to the next pile, capped at one hour, for preview size; cap the card by that interval so piles do not overlap.
  • Keep Office type/name on the front card only. Use the existing warm tooltip for the file names.
  • #384's one-line Log minimum was not in the merged dev, so the same minimum remains local in GridColumn.svelte for later reconciliation.

Known gaps

  • No known functional or gate failures. The 2026-09-29 owner rule assigns full-workspace Cargo clippy/tests to the orchestrator; this branch ran the required per-crate gates, including calternal-server for the contract change.
  • Visual review remains with the orchestrator; the requested screenshots are attached here.

Screenshots

Grid: 390 light, 390 dark, 820 light, 820 dark, 1440 light, 1440 dark.

Agenda: 390 light, 390 dark, 820 light, 820 dark, 1440 light, 1440 dark.

3× desktop stack crops: light, dark.

# Forgejo #410 — finished Branch: `job/doc-stack` Base: `191b179baac3ef4f5bebfe07ce91c4b7a887ace2` Merged `dev` once at `226449940` before final gates. Head: `49bdd6f640cca134ab8aec32b349efb7fa178e9e` ## Built - Calendar document piles now use the open time before the next pile, up to one hour, while keeping fixed slot grouping. The preview scales with the lane, keeps the Calendar item inset, and stays within the available time. The count sits inside the front card corner. Back cards show an image or neutral glyph without cropped Office labels. The hover tooltip lists file names. - Short Log entries keep one title line, as requested by #384. - Added real-server browser assertions for stack sizes, badge containment, back-card content, tooltip names, the singleton Office card, pile spacing, and the short Log title. - Fixed merge-gate clippy findings in Calendar state helpers and a byte comparison in `calternal-fs`. These edits do not change behavior. ## Files - Calendar UI: `packages/ui/src/components/calendar/{GridColumn.svelte,ActivityStack.svelte,AttachmentDeck.svelte,attachments.ts,layout.ts,model.ts}`, `packages/ui/src/components/files/{FileIcon.svelte,FileThumb.svelte}`. - Calendar data and API: `apps/web/src/lib/calendar/{data.ts,grid.test.ts}`, `crates/plugins/calendar/src/{items.rs,view.rs}`, `crates/plugins/files/src/{lib.rs,listing.rs,media.rs,thumbnails.rs}`, `crates/calternal-fs/src/root.rs`, `contracts/openapi.json`, `packages/api-client/src/generated.ts`. - E2E, adversarial and runtime: `apps/web/e2e/{calendar-doc-stack.mjs,files.mjs,harness.mjs}`, `apps/web/package.json`, `deploy/{Containerfile.runtime,media-sandbox}`, `tests/adversarial/{attack.py,media_uploads.py,run.sh}`. - Design: `docs/DESIGN.md` §39. ## Gates `bun run check`: ```text Text sizes use shared role tokens. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/doc-stack/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test`: ```text Test Files 126 passed (126) Tests 810 passed (810) Start at 16:15:04 Duration 131.29s (transform 45%, environment 25%, import 16%, tests 10%, setup 4%) ``` `bun run build`: `✓ built in 58.21s`; static adapter wrote `build` and completed. The build emitted the existing vendor `use client` directive warnings. `cargo fmt --check`: exit 0, no output. `cargo clippy -p calternal-fs --all-targets -- -D warnings`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 26s ``` `cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 36.29s ``` `cargo clippy -p calternal-plugin-files --all-targets -- -D warnings`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 28s ``` `cargo clippy -p calternal-server --all-targets -- -D warnings`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 41m 44s ``` Per-crate test summaries: ```text calternal-fs: test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 13.10s calternal-fs storage: test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.97s calternal-plugin-calendar: test result: ok. 50 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.87s calternal-plugin-calendar cache: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s calternal-plugin-calendar protocol: test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s calternal-plugin-files: test result: ok. 132 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 132.64s calternal-server: test result: ok. 82 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 11.75s ``` Production E2E output: `CALENDAR DOC STACK SCREENSHOTS PASSED`. Hostile media/PDF round: ```text finish 200 login web 200 installation 200 cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ] invite 200 second user finish 200 fixture passkey login requests: 30/30 HEIF/AVIF/PDF upload probe passed ``` Cleanup: `Removed 16975 files, 9.7GiB total`; removed `apps/web/build` and `apps/web/.svelte-kit/output`. The worktree is clean. Screenshot files remain under `artifacts/calendar-doc-stack-410/` and are attached below. No screenshots were committed. ## Decisions - Keep 30-minute desktop and 60-minute touch grouping. Use the empty interval to the next pile, capped at one hour, for preview size; cap the card by that interval so piles do not overlap. - Keep Office type/name on the front card only. Use the existing warm tooltip for the file names. - #384's one-line Log minimum was not in the merged `dev`, so the same minimum remains local in `GridColumn.svelte` for later reconciliation. ## Known gaps - No known functional or gate failures. The 2026-09-29 owner rule assigns full-workspace Cargo clippy/tests to the orchestrator; this branch ran the required per-crate gates, including `calternal-server` for the contract change. - Visual review remains with the orchestrator; the requested screenshots are attached here. ## Screenshots Grid: [390 light](https://git.kayg.org/attachments/71425ffc-209a-456f-a9ca-26edb7c94374), [390 dark](https://git.kayg.org/attachments/13cacb14-dbb9-49bc-a322-0825a0067f6f), [820 light](https://git.kayg.org/attachments/75a0bf43-fb08-44ab-93c6-8cd1c279bda8), [820 dark](https://git.kayg.org/attachments/b81a457f-961f-4f0d-921c-9ae06c6fe3de), [1440 light](https://git.kayg.org/attachments/013e85ce-7569-42ab-932c-4e98aa6902a7), [1440 dark](https://git.kayg.org/attachments/3a038252-0b89-4183-9696-004d5f31f808). Agenda: [390 light](https://git.kayg.org/attachments/c04b009c-487c-4318-9a05-3d0af41a39e4), [390 dark](https://git.kayg.org/attachments/f0487d64-77f4-47ae-94ea-d46698ba2bb7), [820 light](https://git.kayg.org/attachments/f0866a8a-ed88-4db0-8098-f24117a02f82), [820 dark](https://git.kayg.org/attachments/f3d11fcc-996a-49b3-a982-269587397b6c), [1440 light](https://git.kayg.org/attachments/a3c5b3e5-04c4-46e6-bd14-9c875ce7e0ee), [1440 dark](https://git.kayg.org/attachments/8d12001c-5e0b-4497-81c2-9b5615697fe7). 3× desktop stack crops: [light](https://git.kayg.org/attachments/f02b9779-e71b-4811-a5c6-34721be75f0e), [dark](https://git.kayg.org/attachments/a4bcdddd-a862-4dc7-bed0-692b6ac4a37f).
Author
Owner

Screenshot note: issue #410 already had an earlier 12-image set with these filenames. I left those existing attachments intact. The final report links the fresh 12 full captures and two new 3× crops; the issue now has 26 screenshot attachments in total.

Screenshot note: issue #410 already had an earlier 12-image set with these filenames. I left those existing attachments intact. The final report links the fresh 12 full captures and two new 3× crops; the issue now has 26 screenshot attachments in total.
Author
Owner

Starting round 3 on job/doc-stack. Rebuilding from origin/dev at 918b4764ad and will retain only the doc-stack commits; visual fixes are scoped to the Calendar document stack. Previous gate and hostile-PDF probe results are accepted per the issue.

Starting round 3 on job/doc-stack. Rebuilding from origin/dev at 918b4764aded5aa385ef784acb8887cf4361bf36 and will retain only the doc-stack commits; visual fixes are scoped to the Calendar document stack. Previous gate and hostile-PDF probe results are accepted per the issue.
Author
Owner

Round 3 finding on the clean origin/dev base (918b4764): GET /api/v1/calendar/items returns thumbnail URLs only for images and videos. The Files thumbnail worker also skips text files and does not render PDFs. Therefore the Day grid can show available real image/video previews, shared FileName Office cards, and themed glyph placeholders, but this base cannot show a PDF first page or rendered text excerpt. I kept this pass to the requested visual corrections and did not restore the #400 merge or alter the Files worker.

Round 3 finding on the clean `origin/dev` base (918b4764): `GET /api/v1/calendar/items` returns thumbnail URLs only for images and videos. The Files thumbnail worker also skips text files and does not render PDFs. Therefore the Day grid can show available real image/video previews, shared `FileName` Office cards, and themed glyph placeholders, but this base cannot show a PDF first page or rendered text excerpt. I kept this pass to the requested visual corrections and did not restore the #400 merge or alter the Files worker.
Author
Owner

Resuming round 3 on branch job/doc-stack. Fetched origin/dev at 369ab6a2f9. I found uncommitted width and ordering checks for the desktop stack; I am verifying and committing that slice before rebuilding from origin/dev with only #410 commits.

Resuming round 3 on branch job/doc-stack. Fetched origin/dev at 369ab6a2f9fc673e3564b94857fbecfeb04df404. I found uncommitted width and ordering checks for the desktop stack; I am verifying and committing that slice before rebuilding from origin/dev with only #410 commits.
Author
Owner

Finding: the current round-3 branch’s Calendar projection only returns thumbnail URLs for image/video MIME types (), and its screenshot test accepts a glyph placeholder. That drops the previously accepted PDF/text thumbnail path from the backup branch, so I am restoring the #410 Files/Calendar preview commits while rebuilding from origin/dev; the #400 merge will be excluded.

Finding: the current round-3 branch’s Calendar projection only returns thumbnail URLs for image/video MIME types (), and its screenshot test accepts a glyph placeholder. That drops the previously accepted PDF/text thumbnail path from the backup branch, so I am restoring the #410 Files/Calendar preview commits while rebuilding from origin/dev; the #400 merge will be excluded.
Author
Owner

Finding: the current round-3 branch's Calendar projection only returns thumbnail URLs for image and video MIME types in crates/plugins/calendar/src/items.rs::project, and its screenshot test accepts a glyph placeholder. That drops the previously accepted PDF/text thumbnail path from the backup branch. I am restoring the #410 Files/Calendar preview commits while rebuilding from origin/dev; the #400 merge will be excluded.

Finding: the current round-3 branch's Calendar projection only returns thumbnail URLs for image and video MIME types in `crates/plugins/calendar/src/items.rs::project`, and its screenshot test accepts a glyph placeholder. That drops the previously accepted PDF/text thumbnail path from the backup branch. I am restoring the #410 Files/Calendar preview commits while rebuilding from origin/dev; the #400 merge will be excluded.
Author
Owner

Round 3 resumed on job/doc-stack. The original branch point was 369ab6a2f9; I fetched origin/dev at 55a2f90feb and will rebuild this branch from that base using only the doc-stack commits.

Round 3 resumed on job/doc-stack. The original branch point was 369ab6a2f9fc673e3564b94857fbecfeb04df404; I fetched origin/dev at 55a2f90feb8a52d91f8bbc1347889b509af02260 and will rebuild this branch from that base using only the doc-stack commits.
Author
Owner

Starting round 3 on job/doc-stack. The clean worktree currently has the #410 implementation commits on top of origin/dev at 55a2f90feb8a52d91f8bbc1347889b509af02260; I will rebuild from the fetched origin/dev and retain only the #410 commits.

Starting round 3 on `job/doc-stack`. The clean worktree currently has the #410 implementation commits on top of `origin/dev` at `55a2f90feb8a52d91f8bbc1347889b509af02260`; I will rebuild from the fetched `origin/dev` and retain only the #410 commits.
Author
Owner

Round-3 review evidence is reproducible in the saved 1440 px capture: the front card is about 55 px wide in a roughly 990 px Calendar lane, begins at the hour gutter, and the Office fallback name wraps as Solo.do / cx. The dark 3x crop shows the PDF page content on white; I am checking the refreshed production capture to distinguish page colours from the themed card backing. The local branch has been rebuilt from fetched origin/dev and replays only the ten #410 commits.

Round-3 review evidence is reproducible in the saved 1440 px capture: the front card is about 55 px wide in a roughly 990 px Calendar lane, begins at the hour gutter, and the Office fallback name wraps as `Solo.do` / `cx`. The dark 3x crop shows the PDF page content on white; I am checking the refreshed production capture to distinguish page colours from the themed card backing. The local branch has been rebuilt from fetched `origin/dev` and replays only the ten #410 commits.
Author
Owner

Correction to my start note: 55a2f90f was the previous common ancestor, not the fetched origin/dev tip. The fetched tip was dfb5964a2; I reset to that SHA and replayed only the ten #410 commits. 02be0946 is absent from both the rebuilt branch and origin/dev.

Correction to my start note: `55a2f90f` was the previous common ancestor, not the fetched `origin/dev` tip. The fetched tip was `dfb5964a2`; I reset to that SHA and replayed only the ten #410 commits. `02be0946` is absent from both the rebuilt branch and `origin/dev`.
Author
Owner

The fresh production E2E reached the 390 px size checks successfully, then stopped in the inset assertion. That assertion parsed --calendar-event-edge-inset as a float, but the token is calc(8px * var(--ui-scale)); it cannot be compared as a numeric pixel value. I will resolve the CSS length in the test and keep its existing ±1 px placement requirement.

The fresh production E2E reached the 390 px size checks successfully, then stopped in the inset assertion. That assertion parsed `--calendar-event-edge-inset` as a float, but the token is `calc(8px * var(--ui-scale))`; it cannot be compared as a numeric pixel value. I will resolve the CSS length in the test and keep its existing ±1 px placement requirement.
Author
Owner

The fresh production E2E now passes the grid inset check and saves the 390 px capture. It then finds the Agenda count pill outside its front card: ActivityStack.svelte positions it at right: -4px; bottom: -4px, and the existing E2E containment assertion fails. I will anchor it inside the card and keep that assertion unchanged.

The fresh production E2E now passes the grid inset check and saves the 390 px capture. It then finds the Agenda count pill outside its front card: `ActivityStack.svelte` positions it at `right: -4px; bottom: -4px`, and the existing E2E containment assertion fails. I will anchor it inside the card and keep that assertion unchanged.
Author
Owner

The first badge adjustment still failed: the Agenda button is calc(var(--size) + 6px) wide to leave room for the fan, while each preview card is only var(--size) wide. A 2 px inset from the button edge therefore leaves the pill 4 px outside the card. The badge now measures its inset from the card width; the original containment check stays in place.

The first badge adjustment still failed: the Agenda button is `calc(var(--size) + 6px)` wide to leave room for the fan, while each preview card is only `var(--size)` wide. A 2 px inset from the button edge therefore leaves the pill 4 px outside the card. The badge now measures its inset from the card width; the original containment check stays in place.
Author
Owner

Files crate gate finding: cargo test -p calternal-plugin-files ran 133 tests; 131 passed, 1 was ignored, and tests::internal_temp_paths_never_enter_index_during_atomic_write_reconcile_storm failed. It hit its existing five-minute timeout while running the 1,000-write/four-scan test; the cancelled writer also logged atomic write 272 failed: entry not found. The suite took 439.47 seconds. Host load at inspection was 71.80 74.14 69.89. This is consistent with a SLOW-only shared-host timeout; no test expectation was changed. I am retaining the exact failure in the final gate report.

Files crate gate finding: `cargo test -p calternal-plugin-files` ran 133 tests; 131 passed, 1 was ignored, and `tests::internal_temp_paths_never_enter_index_during_atomic_write_reconcile_storm` failed. It hit its existing five-minute timeout while running the 1,000-write/four-scan test; the cancelled writer also logged `atomic write 272 failed: entry not found`. The suite took 439.47 seconds. Host load at inspection was `71.80 74.14 69.89`. This is consistent with a SLOW-only shared-host timeout; no test expectation was changed. I am retaining the exact failure in the final gate report.
Author
Owner

Round 3 report

Built the Calendar document preview flow and corrected the rejected desktop presentation for #410. The stack now scales with its lane (55%, with 90 px phone and 96 px desktop minimums, capped at 112 px), uses the lane's event inset, and keeps its cards within the available hour. It reuses the shared FileName component for middle truncation and extension attachment. Typed/blank fallback cards use the theme --surface and --hair; a rendered PDF page keeps its own document colours. The agenda count badge stays inside the front card. Thumbnail queueing, Calendar attachment projection, PDF/text media handling, and hostile-PDF coverage are included.

Production E2E saved 14 screenshots under artifacts/calendar-doc-stack-410/: grid and Agenda at 390/820/1440 in light and dark, plus 1440 grid 3x crops in both themes. They remain local; scripts/fj issue comment has no attachment option, so I could not upload them through that CLI.

Files

apps/web/e2e/calendar-doc-stack.mjs, apps/web/e2e/files.mjs, apps/web/e2e/harness.mjs, apps/web/package.json, apps/web/src/lib/calendar/data.ts, apps/web/src/lib/calendar/grid.test.ts, contracts/openapi.json, crates/plugins/calendar/src/items.rs, crates/plugins/calendar/src/view.rs, crates/plugins/files/src/lib.rs, crates/plugins/files/src/listing.rs, crates/plugins/files/src/media.rs, crates/plugins/files/src/thumbnails.rs, deploy/Containerfile.runtime, deploy/media-sandbox, docs/DESIGN.md, packages/api-client/src/generated.ts, packages/ui/src/components/calendar/ActivityStack.svelte, packages/ui/src/components/calendar/AttachmentDeck.svelte, packages/ui/src/components/calendar/GridColumn.svelte, packages/ui/src/components/calendar/attachments.ts, packages/ui/src/components/calendar/layout.ts, packages/ui/src/components/calendar/model.ts, packages/ui/src/components/files/FileIcon.svelte, packages/ui/src/components/files/FileThumb.svelte, tests/adversarial/attack.py, tests/adversarial/media_uploads.py, tests/adversarial/run.sh.

Gates

  • cargo fmt --check: no output; exit 0.
  • cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings: Finished \dev` profile [unoptimized + debuginfo] target(s) in 51m 10s`
  • cargo test -p calternal-plugin-calendar: 51 passed, 0 failed; integration tests 1 + 3 passed; doc-tests 0.
  • cargo clippy -p calternal-plugin-files --all-targets -- -D warnings: Finished \dev` profile [unoptimized + debuginfo] target(s) in 8m 49s`
  • cargo test -p calternal-plugin-files: 131 passed, 1 failed, 1 ignored; finished in 439.47s. Failure: tests::internal_temp_paths_never_enter_index_during_atomic_write_reconcile_storm hit its five-minute timeout. The cancelled writer logged atomic write 272 failed: entry not found. Host load at inspection was 71.80 74.14 69.89; no test expectation changed. This is recorded as a SLOW-only shared-host timeout.
  • cargo clippy -p calternal-server --all-targets -- -D warnings: Finished \dev` profile [unoptimized + debuginfo] target(s) in 38m 12s`
  • cargo test -p calternal-server: interrupted with exit 130 at the four-hour job timebox while the test-profile build was compiling; no test result was produced.
  • bun run check: svelte-check found 0 errors and 0 warnings.
  • bun run test: Test Files 136 passed (136); Tests 878 passed (878).
  • Production E2E: CALENDAR DOC STACK SCREENSHOTS PASSED.
  • The web gates and E2E ran before the final origin/dev merge; the merge did not change Calendar code. They were not rerun after that merge due to the timebox. cargo clean and web build-output cleanup are also still pending for the same reason.

Decisions

  • A typed or blank fallback card uses the theme page surface and hairline. A loaded PDF preview preserves the PDF page's own colours.
  • Width uses 55% of the lane, with 90 px phone and 96 px desktop floors, a 22 px narrow Week floor, and a 112 px cap. Height fits the next pile or up to one free hour.
  • Reuse #384's shared one-line floor and #142's FileName component. No local copy of the one-line minimum remains.

The branch was rebuilt from origin/dev and contains only the #410 commits plus the required upstream merge and the round-three visual fix; merge 02be0946 (#400) is absent. Head: ded0693ede9a37e2a43393afa0786e55218330d0.

## Round 3 report Built the Calendar document preview flow and corrected the rejected desktop presentation for #410. The stack now scales with its lane (55%, with 90 px phone and 96 px desktop minimums, capped at 112 px), uses the lane's event inset, and keeps its cards within the available hour. It reuses the shared `FileName` component for middle truncation and extension attachment. Typed/blank fallback cards use the theme `--surface` and `--hair`; a rendered PDF page keeps its own document colours. The agenda count badge stays inside the front card. Thumbnail queueing, Calendar attachment projection, PDF/text media handling, and hostile-PDF coverage are included. Production E2E saved 14 screenshots under `artifacts/calendar-doc-stack-410/`: grid and Agenda at 390/820/1440 in light and dark, plus 1440 grid 3x crops in both themes. They remain local; `scripts/fj issue comment` has no attachment option, so I could not upload them through that CLI. ## Files `apps/web/e2e/calendar-doc-stack.mjs`, `apps/web/e2e/files.mjs`, `apps/web/e2e/harness.mjs`, `apps/web/package.json`, `apps/web/src/lib/calendar/data.ts`, `apps/web/src/lib/calendar/grid.test.ts`, `contracts/openapi.json`, `crates/plugins/calendar/src/items.rs`, `crates/plugins/calendar/src/view.rs`, `crates/plugins/files/src/lib.rs`, `crates/plugins/files/src/listing.rs`, `crates/plugins/files/src/media.rs`, `crates/plugins/files/src/thumbnails.rs`, `deploy/Containerfile.runtime`, `deploy/media-sandbox`, `docs/DESIGN.md`, `packages/api-client/src/generated.ts`, `packages/ui/src/components/calendar/ActivityStack.svelte`, `packages/ui/src/components/calendar/AttachmentDeck.svelte`, `packages/ui/src/components/calendar/GridColumn.svelte`, `packages/ui/src/components/calendar/attachments.ts`, `packages/ui/src/components/calendar/layout.ts`, `packages/ui/src/components/calendar/model.ts`, `packages/ui/src/components/files/FileIcon.svelte`, `packages/ui/src/components/files/FileThumb.svelte`, `tests/adversarial/attack.py`, `tests/adversarial/media_uploads.py`, `tests/adversarial/run.sh`. ## Gates - `cargo fmt --check`: no output; exit 0. - `cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings`: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 51m 10s` - `cargo test -p calternal-plugin-calendar`: 51 passed, 0 failed; integration tests 1 + 3 passed; doc-tests 0. - `cargo clippy -p calternal-plugin-files --all-targets -- -D warnings`: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 8m 49s` - `cargo test -p calternal-plugin-files`: 131 passed, 1 failed, 1 ignored; finished in 439.47s. Failure: `tests::internal_temp_paths_never_enter_index_during_atomic_write_reconcile_storm` hit its five-minute timeout. The cancelled writer logged `atomic write 272 failed: entry not found`. Host load at inspection was `71.80 74.14 69.89`; no test expectation changed. This is recorded as a SLOW-only shared-host timeout. - `cargo clippy -p calternal-server --all-targets -- -D warnings`: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 38m 12s` - `cargo test -p calternal-server`: interrupted with exit 130 at the four-hour job timebox while the test-profile build was compiling; no test result was produced. - `bun run check`: `svelte-check found 0 errors and 0 warnings`. - `bun run test`: `Test Files 136 passed (136)`; `Tests 878 passed (878)`. - Production E2E: `CALENDAR DOC STACK SCREENSHOTS PASSED`. - The web gates and E2E ran before the final origin/dev merge; the merge did not change Calendar code. They were not rerun after that merge due to the timebox. `cargo clean` and web build-output cleanup are also still pending for the same reason. ## Decisions - A typed or blank fallback card uses the theme page surface and hairline. A loaded PDF preview preserves the PDF page's own colours. - Width uses 55% of the lane, with 90 px phone and 96 px desktop floors, a 22 px narrow Week floor, and a 112 px cap. Height fits the next pile or up to one free hour. - Reuse #384's shared one-line floor and #142's `FileName` component. No local copy of the one-line minimum remains. The branch was rebuilt from `origin/dev` and contains only the #410 commits plus the required upstream merge and the round-three visual fix; merge `02be0946` (#400) is absent. Head: `ded0693ede9a37e2a43393afa0786e55218330d0`.
Author
Owner

Started merge-round-0930 on job/merge-round-0930, from origin/dev at 5474493b170f5d12b3d0fc9ccf5c3880e3d24bae. Unicode branch fast-forwarded to c42843a48935f5e4352d09720fe4076ae93315bc. Next: combine doc-stack, review sandbox isolation and limits, regenerate contracts, run per-crate gates and one local media probe. No push or deployment.

Started merge-round-0930 on `job/merge-round-0930`, from `origin/dev` at `5474493b170f5d12b3d0fc9ccf5c3880e3d24bae`. Unicode branch fast-forwarded to `c42843a48935f5e4352d09720fe4076ae93315bc`. Next: combine doc-stack, review sandbox isolation and limits, regenerate contracts, run per-crate gates and one local media probe. No push or deployment.
Author
Owner

Merge audit findings (#410 / #473):

  • Combined both job branches, then fetched and merged origin/dev once. Calendar conflicts keep shared radius tokens and doc-stack badge sizing. Focused tests: Test Files 2 passed (2) / Tests 27 passed (27).
  • The original sandbox policy test failed: document sandbox: host path is visible. The wrapper mounted all host /etc, had no seccomp policy and used content-selected native loaders. Hardening in progress restricts runtime mounts, freezes the root, denies sockets and namespace/kernel operations, and uses explicit PDF/SVG loader actions. Real boundary and decoder checks now pass; final Rust/server validation is pending.
  • Web check found packages/ui/src/components/files/FileThumb.svelte:122: line-height uses a numeric literal (1). Replaced it with the shared Caption leading token; full check is running.
  • Parity inventory is stale after the Composer changed to the batch Log endpoint: /journal/log/batch is present in CLI but has no MCP/WebMCP batch adapter. Regenerated inventory records that existing gap; the single-entry adapters still exist. No endpoint behavior was changed to hide a gap.

Decisions: raw SVG uploads stay outside document-thumbnail mode; only server-generated escaped SVG text cards enter it. Fixed loader actions use private intermediate pixels. Runtime mounts contain public code/font assets only. A complete document render is capped at 60 seconds.

Merge audit findings (#410 / #473): - Combined both job branches, then fetched and merged `origin/dev` once. Calendar conflicts keep shared radius tokens and doc-stack badge sizing. Focused tests: `Test Files 2 passed (2)` / `Tests 27 passed (27)`. - The original sandbox policy test failed: `document sandbox: host path is visible`. The wrapper mounted all host `/etc`, had no seccomp policy and used content-selected native loaders. Hardening in progress restricts runtime mounts, freezes the root, denies sockets and namespace/kernel operations, and uses explicit PDF/SVG loader actions. Real boundary and decoder checks now pass; final Rust/server validation is pending. - Web check found `packages/ui/src/components/files/FileThumb.svelte:122: line-height uses a numeric literal (1)`. Replaced it with the shared Caption leading token; full check is running. - Parity inventory is stale after the Composer changed to the batch Log endpoint: `/journal/log/batch` is present in CLI but has no MCP/WebMCP batch adapter. Regenerated inventory records that existing gap; the single-entry adapters still exist. No endpoint behavior was changed to hide a gap. Decisions: raw SVG uploads stay outside document-thumbnail mode; only server-generated escaped SVG text cards enter it. Fixed loader actions use private intermediate pixels. Runtime mounts contain public code/font assets only. A complete document render is capped at 60 seconds.
Author
Owner

Security audit progress:

The read-only review found that an original Home inode must not be inherited by an untrusted PDF loader. The worker now copies at most 64 MiB into a memory file through the new minimal calternal_fs::sealed_media_input helper. All four kernel seals protect its bytes. The launcher rejects an unsealed descriptor before namespace setup. Real boundary tests prove rejection and sealed input; real PDF/SVG actions still produce WebP. The reviewer confirmed the blocker is resolved.

Hardening also restricts runtime mounts, remounts the root read-only, clears the environment, applies inherited seccomp, allows native library threads but no separate native child processes, forces explicit loaders, and bounds the complete render and Job. C builds pass for x86_64 and arm64. Notes-core clippy/tests pass; the other required Rust gates are running one crate at a time.

The pre-existing image/video handle boundary is tracked in #501. No decoder compromise was reproduced. Snapshot copying is byte-bounded; an already-blocked OS read can outlive cancellation.

Web gates: svelte-check found 0 errors and 0 warnings; suite Tests 1 failed | 891 passed (892) (font-menu 5000ms timeout), isolated rerun Tests 1 passed (1). Production build and parity check pass.

Decisions added to DESIGN §39: raw SVG uploads remain outside this mode; fixed PDF/SVG actions, bounded sealed PDF input, 60-second document render budget, and 180-second full Job budget (keeps room for existing image renders).

Security audit progress: The read-only review found that an original Home inode must not be inherited by an untrusted PDF loader. The worker now copies at most 64 MiB into a memory file through the new minimal `calternal_fs::sealed_media_input` helper. All four kernel seals protect its bytes. The launcher rejects an unsealed descriptor before namespace setup. Real boundary tests prove rejection and sealed input; real PDF/SVG actions still produce WebP. The reviewer confirmed the blocker is resolved. Hardening also restricts runtime mounts, remounts the root read-only, clears the environment, applies inherited seccomp, allows native library threads but no separate native child processes, forces explicit loaders, and bounds the complete render and Job. C builds pass for x86_64 and arm64. Notes-core clippy/tests pass; the other required Rust gates are running one crate at a time. The pre-existing image/video handle boundary is tracked in #501. No decoder compromise was reproduced. Snapshot copying is byte-bounded; an already-blocked OS read can outlive cancellation. Web gates: `svelte-check found 0 errors and 0 warnings`; suite `Tests 1 failed | 891 passed (892)` (font-menu 5000ms timeout), isolated rerun `Tests 1 passed (1)`. Production build and parity check pass. Decisions added to DESIGN §39: raw SVG uploads remain outside this mode; fixed PDF/SVG actions, bounded sealed PDF input, 60-second document render budget, and 180-second full Job budget (keeps room for existing image renders).
Author
Owner

Merge round progress on job/merge-round-0930: both job branches and the one required origin/dev refresh are merged. Current committed head is 2ed9a9076; the worker and sandbox integration remain under test.

The real bubblewrap boundary test passes. It checks absent host paths, a read-only root, UID/GID 65534, stripped host environment, denied network sockets, inherited limits, denied native fork and permitted native threads. PDF stdin must have all four kernel seals and fit the 64 MiB cap. Explicit PDF and generated-SVG actions produce WebP; the PDF action rejects JPEG input. The source-inode concern in the older image/video modes is tracked separately in #501.

Contract regeneration has no diff. Rust per-crate gates continue on the shared host. Web checks pass. The full web suite had one 5-second font-menu timeout; that test passed alone. The real-server media round is running now. Production screenshots and the final gate report will follow.

Merge round progress on `job/merge-round-0930`: both job branches and the one required `origin/dev` refresh are merged. Current committed head is `2ed9a9076`; the worker and sandbox integration remain under test. The real bubblewrap boundary test passes. It checks absent host paths, a read-only root, UID/GID 65534, stripped host environment, denied network sockets, inherited limits, denied native fork and permitted native threads. PDF stdin must have all four kernel seals and fit the 64 MiB cap. Explicit PDF and generated-SVG actions produce WebP; the PDF action rejects JPEG input. The source-inode concern in the older image/video modes is tracked separately in #501. Contract regeneration has no diff. Rust per-crate gates continue on the shared host. Web checks pass. The full web suite had one 5-second font-menu timeout; that test passed alone. The real-server media round is running now. Production screenshots and the final gate report will follow.
Author
Owner

One time-boxed real-server media round is complete. The probe used the merged server, authenticated Users and the real private/public Files upload routes. Its only finding was:

!! server health after crafted media uploads: status=200, elapsed=6.71s, body=b''
media upload findings: 1

This is SLOW-only: the health response was the expected HTTP 200; elapsed time exceeded the existing 5-second threshold. No other finding was reported, including the valid-PDF thumbnail and malformed-PDF rejection checks. The harness exited 1 because it combines timing and functional findings. The shared-host rule says SLOW-only findings do not block this merge. Existing test expectations are unchanged, and the round will not be repeated.

Production screenshot capture is in progress. Rust gates are still running, one crate at a time, with four build jobs and small debug output.

One time-boxed real-server media round is complete. The probe used the merged server, authenticated Users and the real private/public Files upload routes. Its only finding was: ``` !! server health after crafted media uploads: status=200, elapsed=6.71s, body=b'' media upload findings: 1 ``` This is SLOW-only: the health response was the expected HTTP 200; elapsed time exceeded the existing 5-second threshold. No other finding was reported, including the valid-PDF thumbnail and malformed-PDF rejection checks. The harness exited 1 because it combines timing and functional findings. The shared-host rule says SLOW-only findings do not block this merge. Existing test expectations are unchanged, and the round will not be repeated. Production screenshot capture is in progress. Rust gates are still running, one crate at a time, with four build jobs and small debug output.
Author
Owner

Production review evidence at f8fedc535 (API and UI behavior unchanged by the capture helper). All 26 captures came from the real production build and real local Files/Calendar/Search APIs. The capture passed its existing geometry, thumbnail, filename and strict theme assertions. Claude remains the visual reviewer.

CALENDAR DOC STACK SCREENSHOTS PASSED
Width Scheme Day grid Agenda Files Search
390 light View View View View
390 dark View View View View
820 light View View View View
820 dark View View View View
1440 light View View View View
1440 dark View View View View

Desktop stack crops: light, dark.

Known preference inconsistency: #507. It did not change the rendered-theme assertions. The helper now saves from a page without an active settings store before loading the evidence route.

Production review evidence at `f8fedc535` (API and UI behavior unchanged by the capture helper). All 26 captures came from the real production build and real local Files/Calendar/Search APIs. The capture passed its existing geometry, thumbnail, filename and strict theme assertions. Claude remains the visual reviewer. ``` CALENDAR DOC STACK SCREENSHOTS PASSED ``` | Width | Scheme | Day grid | Agenda | Files | Search | |---|---|---|---|---|---| | 390 | light | [View](https://git.kayg.org/attachments/13f69230-230d-48b6-bb0b-33e9389ab7b5) | [View](https://git.kayg.org/attachments/bdf6c5c9-a51a-4efc-8218-f9f2e39a92da) | [View](https://git.kayg.org/attachments/c4fd2b0c-0cff-4200-9aa3-f6675ea142b6) | [View](https://git.kayg.org/attachments/ee0b288c-2756-41f3-a491-e39c9d33524b) | | 390 | dark | [View](https://git.kayg.org/attachments/7710aa26-1206-4d0b-8461-b97e3a31042e) | [View](https://git.kayg.org/attachments/f6e632e5-8ca0-4e05-b6c8-4ef2eed6d79e) | [View](https://git.kayg.org/attachments/175e7fe7-b8da-4adc-9fc1-5234b0c0466b) | [View](https://git.kayg.org/attachments/76879086-8aed-48f1-b44d-9def464d2675) | | 820 | light | [View](https://git.kayg.org/attachments/aa553fd2-2aaf-4604-8df1-b681df10ad94) | [View](https://git.kayg.org/attachments/f29d051f-96da-43c9-9513-74158537b967) | [View](https://git.kayg.org/attachments/82e1ba3d-5f8c-4aba-85c2-50994af60dfb) | [View](https://git.kayg.org/attachments/399a782a-9f14-4668-9943-6f686d846262) | | 820 | dark | [View](https://git.kayg.org/attachments/958fb8a3-8008-49d3-88b7-4d8cccdec998) | [View](https://git.kayg.org/attachments/ff191477-aed6-4a0d-83e2-75ccc75ef6c2) | [View](https://git.kayg.org/attachments/cb4ab47d-e9f0-4cfb-9a03-eede9326c4ba) | [View](https://git.kayg.org/attachments/3f5dc628-5146-4885-b94f-dbc1ca52bd6d) | | 1440 | light | [View](https://git.kayg.org/attachments/a1ced35b-4b33-4311-aeef-1f7da51a622a) | [View](https://git.kayg.org/attachments/d4fa99ff-b6ae-41c9-a2aa-38cb3db9bd44) | [View](https://git.kayg.org/attachments/1b7ee63d-8be6-4f56-8c36-69bf524982f5) | [View](https://git.kayg.org/attachments/97d0de76-43b1-4092-89f1-ae218da10115) | | 1440 | dark | [View](https://git.kayg.org/attachments/9028d123-b41e-434f-8ece-bb582c94f430) | [View](https://git.kayg.org/attachments/3bf7d3c4-6bab-411e-a31a-5fee2b2d1bd7) | [View](https://git.kayg.org/attachments/a49b12fb-2e75-4aaa-9606-36df5ae245e1) | [View](https://git.kayg.org/attachments/f3e74ffa-09e9-4754-be56-8f37e721f0ec) | Desktop stack crops: [light](https://git.kayg.org/attachments/7dfb2840-64d8-4dbf-970c-e26ec2bdca6e), [dark](https://git.kayg.org/attachments/785fc512-51de-4a66-aed8-f0584c74d16a). Known preference inconsistency: #507. It did not change the rendered-theme assertions. The helper now saves from a page without an active settings store before loading the evidence route.
Author
Owner

Further audit evidence:

  • The real namespace test now checks the full open-descriptor table. Only stdio reaches the native decoder. This closes the proof gap where an extra parent directory handle could bypass hidden mounts.
  • Oversized sealed inputs and partly sealed inputs are rejected before namespace setup. A sealed JPEG cannot use the fixed PDF action.
  • Bounded PDF link/script metadata and SVG external references/entity text passed inside the real sandbox. The existing PDF fixtures remain byte-identical.
  • #510 records a separate derived-preview cache identity gap: rendering depends on MIME/suffix, while cache keys use the content hash. No original-file damage or authorization bypass was reproduced. The shared cache contract was not changed in this merge audit.

Calendar, notes-core, tags, money and calternal-fs have passed clippy and tests. Files has passed clippy; its tests are compiling. The remaining Notes, Search and Server gates will follow. Production evidence has passed and is attached here. The full gate report will be posted on #410 and #473.

Further audit evidence: - The real namespace test now checks the full open-descriptor table. Only stdio reaches the native decoder. This closes the proof gap where an extra parent directory handle could bypass hidden mounts. - Oversized sealed inputs and partly sealed inputs are rejected before namespace setup. A sealed JPEG cannot use the fixed PDF action. - Bounded PDF link/script metadata and SVG external references/entity text passed inside the real sandbox. The existing PDF fixtures remain byte-identical. - #510 records a separate derived-preview cache identity gap: rendering depends on MIME/suffix, while cache keys use the content hash. No original-file damage or authorization bypass was reproduced. The shared cache contract was not changed in this merge audit. Calendar, notes-core, tags, money and calternal-fs have passed clippy and tests. Files has passed clippy; its tests are compiling. The remaining Notes, Search and Server gates will follow. Production evidence has passed and is attached here. The full gate report will be posted on #410 and #473.
Author
Owner

Final namespace regression found that bubblewrap PID 1 retained the outer dummy environment marker through /proc/1/environ, although the decoder environment was cleared. The wrapper now clears the environment before namespace creation too. The real namespace test checks PID 1 marker absence and denies monitor host-root/working-directory access. The test failed before the change and passes after it. No host environment contents were printed. DESIGN §39 records the boundary. Existing media modes retain their prior launch behavior.

Final namespace regression found that bubblewrap PID 1 retained the outer dummy environment marker through `/proc/1/environ`, although the decoder environment was cleared. The wrapper now clears the environment before namespace creation too. The real namespace test checks PID 1 marker absence and denies monitor host-root/working-directory access. The test failed before the change and passes after it. No host environment contents were printed. DESIGN §39 records the boundary. Existing media modes retain their prior launch behavior.
Author
Owner

Head 40ccef12818eb27592994a2666021ee34b2b3cf5: the document sandbox passes the defensive audit after hardening. Real namespace tests prove sealed bounded PDF input, fixed private symlink, hidden host paths, read-only root, no inherited directory descriptors, blocked network, inherited resource/seccomp policy, native thread-only children, and cleared PID 1 monitor environment/root/cwd boundary. Actual PDF/SVG tools produce WebP and reject an incorrect PDF input kind. The final read-only review found no remaining material gap. Rust fmt and Notes gates pass; Search and Server per-crate gates remain in progress. Full final report and raw gate output will follow on both #410 and #473.

Head `40ccef12818eb27592994a2666021ee34b2b3cf5`: the document sandbox passes the defensive audit after hardening. Real namespace tests prove sealed bounded PDF input, fixed private symlink, hidden host paths, read-only root, no inherited directory descriptors, blocked network, inherited resource/seccomp policy, native thread-only children, and cleared PID 1 monitor environment/root/cwd boundary. Actual PDF/SVG tools produce WebP and reject an incorrect PDF input kind. The final read-only review found no remaining material gap. Rust fmt and Notes gates pass; Search and Server per-crate gates remain in progress. Full final report and raw gate output will follow on both #410 and #473.
Author
Owner

The first cargo test -p calternal-search run exited101: a_file_trashed_during_its_add_never_appears_in_search did not observe the marker in wait_for_hit (indexer.rs97). The indexer binary reported19pass/1fail in64.00s. The same compiled test run alone, with unchanged assertions, passed in11.37s. A follow-up Search suite will run after Server gates to cover the tests skipped when Cargo stopped. These observations show an intermittent polling failure; they do not establish its cause. Both logs are retained.

The first `cargo test -p calternal-search` run exited101: `a_file_trashed_during_its_add_never_appears_in_search` did not observe the marker in `wait_for_hit` (indexer.rs97). The indexer binary reported19pass/1fail in64.00s. The same compiled test run alone, with unchanged assertions, passed in11.37s. A follow-up Search suite will run after Server gates to cover the tests skipped when Cargo stopped. These observations show an intermittent polling failure; they do not establish its cause. Both logs are retained.
Author
Owner

Head: 40ccef12818eb27592994a2666021ee34b2b3cf5 on job/merge-round-0930.

Built: merged job/tag-unicode-473, then job/doc-stack, and merged the one required origin/dev refresh. Resolved Calendar token conflicts. Regenerated contracts without a diff. Refreshed the parity inventory for the merged batch Log caller.

Security verdict: the document renderer boundary passes the defensive audit after hardening. The native decoder has private namespaces, no network, a read-only root, UID/GID 65534, an inherited seccomp policy and bounded resources. Host Homes, data, secret-bearing host directories and extra parent descriptors are absent. The outer environment is cleared before bubblewrap starts, including its PID 1 monitor; monitor host-root and working-directory access are denied by the tested namespace. PDF input is a bounded, fully sealed memory file, not a Home inode. The fixed private symlink names only fd 0. Explicit PDF and generated-SVG actions select loaders. Native tools can create threads only. Complete Jobs and document renders have explicit wall budgets.

Files:

  • deploy/media-sandbox and deploy/media-sandbox-dropcaps.c: isolation, fixed actions, seals and native thread restrictions.
  • crates/calternal-fs/src/{lib.rs,thumbnails.rs}: bounded sealed input helper and regression tests.
  • crates/plugins/files/src/thumbnails.rs: early size/read bounds, shared media slot, fixed loaders, Job/render budgets and failure tests.
  • tests/adversarial/{media-sandbox-policy.c,test-document-sandbox.sh,prepare-media-runtime.sh,media_uploads.py,run.sh}: namespace, descriptor, input and bounded parser checks.
  • packages/ui/src/components/{calendar/GridColumn.svelte,files/FileThumb.svelte}: merged shared radius/Caption tokens.
  • apps/web/e2e/calendar-doc-stack.mjs: production Calendar, Files and Search evidence at 390/820/1440 in light/dark.
  • docs/{DESIGN.md,parity-exceptions.json,parity-matrix.md}: reasoning and the merged parity inventory.

Gates, verbatim:

cargo fmt --check: exit=0
cargo clippy -p calternal-notes-core --all-targets -- -D warnings: exit=0
cargo test -p calternal-notes-core: exit=0
cargo clippy -p calternal-tags --all-targets -- -D warnings: exit=0
cargo test -p calternal-tags: exit=0
cargo clippy -p calternal-fs --all-targets -- -D warnings: exit=0
cargo clippy -p calternal-money --all-targets -- -D warnings: exit=0
cargo test -p calternal-money: exit=0
cargo test -p calternal-fs: exit=0
cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings: exit=0
cargo test -p calternal-plugin-calendar: exit=0
cargo clippy -p calternal-plugin-files --all-targets -- -D warnings: exit=0
cargo test -p calternal-plugin-files: exit=0
cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings: exit=0
cargo test -p calternal-plugin-notes: exit=0
cargo clippy -p calternal-search --all-targets -- -D warnings: exit=0
cargo test -p calternal-search: exit=101
cargo clippy -p calternal-server --all-targets -- -D warnings: exit=0
cargo test -p calternal-server: exit=0
cargo test -p calternal-search follow-up: exit=0

calternal-notes-core:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 25m 36s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 2m 55s
test result: ok. 504 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.54s
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.08s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.18s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.11s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-tags:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 22m 18s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 33m 31s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.41s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-money:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 23s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 57s
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.16s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.49s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

calternal-plugin-calendar:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 19s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 12m 29s
test result: ok. 52 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 38.15s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.46s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-plugin-files:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 5m 29s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 10m 46s
test result: ok. 134 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 385.38s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-plugin-notes:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 53s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 7m 46s
test result: ok. 126 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 142.82s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.61s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

calternal-search:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 12m 58s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 9m 50s
test result: ok. 33 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 17.57s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.46s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s
test result: FAILED. 19 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 64.00s

calternal-server:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 44s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 12m 13s
test result: ok. 85 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 31.36s

calternal-fs:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 43s
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 50s
test result: ok. 41 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 35.08s
test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 45.87s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

The first Search suite failed while polling for the marker in the existing trash race test. The unchanged isolated test passed. The full follow-up passed (exit0) with RUST_TEST_THREADS=1, including every integration binary and doc tests. Both suite results are included above and in the attached raw logs.


running 1 test
test a_file_trashed_during_its_add_never_appears_in_search ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 19 filtered out; finished in 11.37s
test result: ok. 33 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 30.88s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 18.81s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.08s
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 94.93s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s
test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 9.30s
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

bun run check:

$ node scripts/check-type-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Text sizes and UI shape values use shared role tokens.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/merge-check/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings
exit=0

bun run test had one known 5-second font-menu timeout. The original suite and isolated rerun output are preserved. No expectation changed.

Error: Test timed out in 5000ms.
 Test Files  1 failed | 136 passed (137)
      Tests  1 failed | 891 passed (892)
 Test Files  1 passed (1)
      Tests  1 passed (1)

Generated-contract check exited 0; regenerated files have no diff. Parity, sandbox and production capture output:

Parity matrix: 187 web API actions, 113 shortcuts, 2 static commands, 131 menu actions, 30 settings groups, 169 actions with adapter gaps
document input must be a bounded sealed snapshot
document input must be a bounded sealed snapshot
document input must be a bounded sealed snapshot
width: 64
height: 64
PASS: document namespace, private input, inherited limits and network policy
PASS: PDF link/script metadata and bounded SVG references/entities stay isolated
PASS: explicit PDF and SVG loaders produce WebP; PDF action rejects JPEG
x86 policy build: exit=0
arm64 policy build: exit=0
CALENDAR DOC STACK SCREENSHOTS PASSED

The one real-server media round exited 1 for this SLOW-only finding. All other checks reported no finding. It was not repeated; the existing threshold is unchanged.

!! server health after crafted media uploads: status=200, elapsed=6.71s, body=b''
media upload findings: 1

Known gaps: #513 tracks the intermittent Search trash-race marker polling failure; the unchanged isolated test passed. #501 tracks the pre-existing image/video source-inode boundary; no native compromise was reproduced. #507 tracks a scheme preference inconsistency during multi-context review. #510 tracks rendering-kind ambiguity in shared thumbnail cache identity; it is a derived-preview finding. Blocking OS reads cannot be interrupted by async cancellation, although source reads/copies are byte-bounded. arm64 was cross-compiled; real sandbox execution was x86_64.

Decisions: fixed internal PDF/text actions; raw SVG uploads remain outside document mode; PDF snapshots are sealed and capped at 64 MiB; private /tmp is capped at 64 MiB. The complete thumbnail Job has 180 seconds, and document rendering has 60 seconds including snapshot/probe/both sizes. Minimal public addition in calternal-fs keeps all input filesystem work in that crate. Shared dev radius and Caption tokens win over literal values. These choices are recorded in DESIGN §39.

No push or deploy was issued. Issues remain open. Screenshots are attached to #410 for Claude; artifacts are not committed.

Cleanup output, verbatim:

     Removed 21798 files, 13.9GiB total
Job Rust, web and staged test outputs removed; artifacts retained.

Full raw gate output: #410 verbatim gate logs, #473 verbatim gate logs.

Head: `40ccef12818eb27592994a2666021ee34b2b3cf5` on `job/merge-round-0930`. **Built:** merged `job/tag-unicode-473`, then `job/doc-stack`, and merged the one required `origin/dev` refresh. Resolved Calendar token conflicts. Regenerated contracts without a diff. Refreshed the parity inventory for the merged batch Log caller. **Security verdict:** the document renderer boundary passes the defensive audit after hardening. The native decoder has private namespaces, no network, a read-only root, UID/GID 65534, an inherited seccomp policy and bounded resources. Host Homes, data, secret-bearing host directories and extra parent descriptors are absent. The outer environment is cleared before bubblewrap starts, including its PID 1 monitor; monitor host-root and working-directory access are denied by the tested namespace. PDF input is a bounded, fully sealed memory file, not a Home inode. The fixed private symlink names only fd 0. Explicit PDF and generated-SVG actions select loaders. Native tools can create threads only. Complete Jobs and document renders have explicit wall budgets. **Files:** - `deploy/media-sandbox` and `deploy/media-sandbox-dropcaps.c`: isolation, fixed actions, seals and native thread restrictions. - `crates/calternal-fs/src/{lib.rs,thumbnails.rs}`: bounded sealed input helper and regression tests. - `crates/plugins/files/src/thumbnails.rs`: early size/read bounds, shared media slot, fixed loaders, Job/render budgets and failure tests. - `tests/adversarial/{media-sandbox-policy.c,test-document-sandbox.sh,prepare-media-runtime.sh,media_uploads.py,run.sh}`: namespace, descriptor, input and bounded parser checks. - `packages/ui/src/components/{calendar/GridColumn.svelte,files/FileThumb.svelte}`: merged shared radius/Caption tokens. - `apps/web/e2e/calendar-doc-stack.mjs`: production Calendar, Files and Search evidence at 390/820/1440 in light/dark. - `docs/{DESIGN.md,parity-exceptions.json,parity-matrix.md}`: reasoning and the merged parity inventory. **Gates, verbatim:** ```text cargo fmt --check: exit=0 cargo clippy -p calternal-notes-core --all-targets -- -D warnings: exit=0 cargo test -p calternal-notes-core: exit=0 cargo clippy -p calternal-tags --all-targets -- -D warnings: exit=0 cargo test -p calternal-tags: exit=0 cargo clippy -p calternal-fs --all-targets -- -D warnings: exit=0 cargo clippy -p calternal-money --all-targets -- -D warnings: exit=0 cargo test -p calternal-money: exit=0 cargo test -p calternal-fs: exit=0 cargo clippy -p calternal-plugin-calendar --all-targets -- -D warnings: exit=0 cargo test -p calternal-plugin-calendar: exit=0 cargo clippy -p calternal-plugin-files --all-targets -- -D warnings: exit=0 cargo test -p calternal-plugin-files: exit=0 cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings: exit=0 cargo test -p calternal-plugin-notes: exit=0 cargo clippy -p calternal-search --all-targets -- -D warnings: exit=0 cargo test -p calternal-search: exit=101 cargo clippy -p calternal-server --all-targets -- -D warnings: exit=0 cargo test -p calternal-server: exit=0 cargo test -p calternal-search follow-up: exit=0 ``` `calternal-notes-core`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 25m 36s Finished `test` profile [unoptimized + debuginfo] target(s) in 2m 55s test result: ok. 504 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.54s test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.08s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.18s test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.11s test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `calternal-tags`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 22m 18s Finished `test` profile [unoptimized + debuginfo] target(s) in 33m 31s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.41s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `calternal-money`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 23s Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 57s test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.16s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.49s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s ``` `calternal-plugin-calendar`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 19s Finished `test` profile [unoptimized + debuginfo] target(s) in 12m 29s test result: ok. 52 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 38.15s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.46s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `calternal-plugin-files`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 5m 29s Finished `test` profile [unoptimized + debuginfo] target(s) in 10m 46s test result: ok. 134 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 385.38s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `calternal-plugin-notes`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 53s Finished `test` profile [unoptimized + debuginfo] target(s) in 7m 46s test result: ok. 126 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 142.82s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.61s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `calternal-search`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 12m 58s Finished `test` profile [unoptimized + debuginfo] target(s) in 9m 50s test result: ok. 33 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 17.57s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.46s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s test result: FAILED. 19 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 64.00s ``` `calternal-server`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 44s Finished `test` profile [unoptimized + debuginfo] target(s) in 12m 13s test result: ok. 85 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 31.36s ``` `calternal-fs`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11m 43s Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 50s test result: ok. 41 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 35.08s test result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 45.87s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` The first Search suite failed while polling for the marker in the existing trash race test. The unchanged isolated test passed. The full follow-up passed (exit0) with RUST_TEST_THREADS=1, including every integration binary and doc tests. Both suite results are included above and in the attached raw logs. ```text running 1 test test a_file_trashed_during_its_add_never_appears_in_search ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 19 filtered out; finished in 11.37s ``` ```text test result: ok. 33 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 30.88s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 18.81s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.08s test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 94.93s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 9.30s test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `bun run check`: ```text $ node scripts/check-type-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Text sizes and UI shape values use shared role tokens. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/merge-check/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings exit=0 ``` `bun run test` had one known 5-second font-menu timeout. The original suite and isolated rerun output are preserved. No expectation changed. ```text Error: Test timed out in 5000ms. Test Files 1 failed | 136 passed (137) Tests 1 failed | 891 passed (892) Test Files 1 passed (1) Tests 1 passed (1) ``` Generated-contract check exited 0; regenerated files have no diff. Parity, sandbox and production capture output: ```text Parity matrix: 187 web API actions, 113 shortcuts, 2 static commands, 131 menu actions, 30 settings groups, 169 actions with adapter gaps document input must be a bounded sealed snapshot document input must be a bounded sealed snapshot document input must be a bounded sealed snapshot width: 64 height: 64 PASS: document namespace, private input, inherited limits and network policy PASS: PDF link/script metadata and bounded SVG references/entities stay isolated PASS: explicit PDF and SVG loaders produce WebP; PDF action rejects JPEG x86 policy build: exit=0 arm64 policy build: exit=0 CALENDAR DOC STACK SCREENSHOTS PASSED ``` The one real-server media round exited 1 for this SLOW-only finding. All other checks reported no finding. It was not repeated; the existing threshold is unchanged. ```text !! server health after crafted media uploads: status=200, elapsed=6.71s, body=b'' media upload findings: 1 ``` **Known gaps:** #513 tracks the intermittent Search trash-race marker polling failure; the unchanged isolated test passed. #501 tracks the pre-existing image/video source-inode boundary; no native compromise was reproduced. #507 tracks a scheme preference inconsistency during multi-context review. #510 tracks rendering-kind ambiguity in shared thumbnail cache identity; it is a derived-preview finding. Blocking OS reads cannot be interrupted by async cancellation, although source reads/copies are byte-bounded. arm64 was cross-compiled; real sandbox execution was x86_64. **Decisions:** fixed internal PDF/text actions; raw SVG uploads remain outside document mode; PDF snapshots are sealed and capped at 64 MiB; private `/tmp` is capped at 64 MiB. The complete thumbnail Job has 180 seconds, and document rendering has 60 seconds including snapshot/probe/both sizes. Minimal public addition in calternal-fs keeps all input filesystem work in that crate. Shared dev radius and Caption tokens win over literal values. These choices are recorded in DESIGN §39. No push or deploy was issued. Issues remain open. Screenshots are attached to #410 for Claude; artifacts are not committed. **Cleanup output, verbatim:** ```text Removed 21798 files, 13.9GiB total Job Rust, web and staged test outputs removed; artifacts retained. ``` Full raw gate output: [#410 verbatim gate logs](https://git.kayg.org/attachments/2399d8f5-d4b4-4f6c-a27f-b24a11c01e71), [#473 verbatim gate logs](https://git.kayg.org/attachments/80189bd0-1244-45a4-9094-954f887d8484).
Author
Owner

Merged into dev at 8ac92b400 via merge round 1 (all per-crate gates; merged-tree web gates 0 errors, 892/892).

Merged into dev at 8ac92b400 via merge round 1 (all per-crate gates; merged-tree web gates 0 errors, 892/892).
kayg closed this issue 2026-09-30 12:04:45 +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#410
No description provided.