Commit Graph
26 Commits
Author SHA1 Message Date
Lucas WintherandClaude Opus 5.5 b5e00af4f7 fix(web,learner): navigation swaps at once; content-only entrance, loading line, focus (R19)
Drops document-level View Transitions and the lesson hold: they dropped clicks for 120-180 ms, froze the screen
while route code loaded, and let the dock act on a stale lesson. Navigation now swaps immediately; only the
content region enters (opacity and a 6 px rise, 160 ms); a 2 px loading line appears when code or data takes over
150 ms; the dock works from the outline and the URL, so a second Next advances again; focus moves to the new h1
and a polite live region announces it. Route chunks of a feature preload together so the sidebar never drops.
Scroll restore stops on user input; pdf.js never starts a load after cleanup.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 21:49:49 +01:00
Lucas WintherandClaude Opus 5.5 305c262ff1 docs: record motion plan deviations
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 21:26:15 +01:00
Lucas WintherandClaude Opus 5.5 eed59d4ef2 docs: v1.1 M navigation motion plan
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 20:31:36 +01:00
Lucas WintherandClaude Opus 5.5 fd5c72afc2 docs: v1.1 plan (motion, studio import editing, perf, AI schema)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 20:21:39 +01:00
Lucas WintherandClaude Opus 5.5 8045c80d93 fix: final-review wave (CSRF guard, fail-closed owner bootstrap, streamed zips, e2e visibility suite)
- Critical 1 (CSRF): core wrap refuses every POST/PUT/PATCH/DELETE whose Origin
  (Referer as fallback) is not publicUrl's origin or STC_ALLOWED_ORIGINS, and
  any body that is not application/json (multipart only on declared upload
  routes). Better Auth routes go through the same guard; only declared
  identity-provider callbacks (SAML ACS/SLO, OAuth callbacks) are exempt.
  Better Auth trustedOrigins use the same list. Session cookie stays Lax
  (Strict breaks OAuth/OIDC/SAML returns); documented.
- Important 4 / learner minor 4: the first-owner claim and the role row are
  one transaction guarded by "no owner yet"; only a unique violation means
  learner; any other error fails the sign-up with 500 and removes the
  half-created user. Serialised per DB handle (Bun's SQLite connection).
- Important 2 / learner minor 12: code zips stream entry by entry (large
  files stored in 256 KB chunks with a data descriptor and incremental
  CRC-32), so memory stays at one chunk; the cap is now 1 GiB (ZIP32).
- Important 3 / learner minor 10: the e2e runs the production wiring
  (productFeatures order, AI drafts through the real sink) against a
  restricted course and asserts 404/401 for learner and anonymous on
  course, lesson, media, cover, code, zip, notes, progress, attempts, AI
  chat, library, search and AI status; a real Range request (206, 416);
  forged writes (foreign Origin, text/plain, no Origin) are 403 and the MCP
  test command never runs.
- Minors 7-9: compose passes every documented variable; the health check
  uses STC_PORT; productFeatures checks the studio-before-AI order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 15:42:17 +01:00
Lucas WintherandClaude Opus 5.5 69aaedcb43 build: docker image and compose
Dockerfile on oven/bun:1.3 with ffmpeg, git, unzip and python3-pymupdf, a
production install, non-root user, /data volume and a health check;
docker-compose.yml (project stc) with an optional postgres:17 profile and
read-only course and import mounts; docs/operations.md with configuration,
mounts, security headers and the measured idle memory (101.4 MiB for app plus
Postgres after 5 minutes idle).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 15:17:58 +01:00
Lucas WintherandClaude Opus 5.5 427eda6e93 merge: ai
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 14:44:06 +01:00
Lucas WintherandClaude Opus 5.5 2854c26a7d merge: studio
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 14:42:51 +01:00
Lucas WintherandClaude Opus 5.5 627ebe0029 merge: import
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 14:41:36 +01:00
Lucas WintherandClaude Opus 5.5 0da35ec6f8 fix(learner): remove the SPA proxy; CSP as a meta tag in index.html (ruling R16)
The proxy, its internal server, Unix socket and NO_PROXY changes are gone; main.ts serves the HTML
import directly again. apps/web/index.html carries the SPA policy as <meta http-equiv=CSP> ahead of every
script (script-src 'self' plus the hash of Bun's dev-server helper, no unsafe-inline/eval; no
frame-ancestors, which meta cannot carry). Dev HMR connects under it. The runner frame keeps its header CSP.

Tests: the served index.html (prod and dev, in a subprocess) has the meta policy and every inline script
is hash-covered; the XSS browser test fails with the sanitizer disabled and passes with it, and the meta
CSP alone blocks unsanitized handlers (with a no-CSP control); no source assigns proxy env vars, and the
feature leaves them untouched at runtime. Contract request 5 asks core for frame-ancestors/X-Frame-Options.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 14:38:04 +01:00
Lucas WintherandClaude Opus 5.5 c7b9e1f98e fix(learner): review round 1: sanitize-html, sandboxed runner frame, one visibility rule, no Tab capture
C1: lesson HTML goes through a sanitize-html allowlist on the fully rendered output (no on* attributes, no
style, no svg/math/iframe/object/embed/form; URLs limited to http(s), mailto, relative and anchors). Raw tags
outside the allowlist are escaped as text. The SPA document is served with a CSP (script-src 'self', no inline
script) through spaWithCsp in main.ts; contract request §5 asks core to own it.

C2: exercises run in <iframe sandbox="allow-scripts"> (opaque origin, no cookies) loaded from
/api/learner/runner-frame, whose CSP allows only its own script (by hash) and blob: workers, and connect-src
'none'. postMessage only, both sides check event.source; the timeout removes the frame. The worker stubs now
also cover Worker, SharedWorker and caches.

I1: learner visibility calls core canView with per-course authorship only, like /api/media (contract request §6).
I2: Tab and Shift+Tab move focus; Enter keeps the previous line's indentation.

Tests: XSS unit cases, headless-Chrome checks for the exploit lesson and for fetch, import, Cache.add, nested
Worker, img, EventSource, WebSocket, XHR and sendBeacon from learner and test code (plus a negative control),
visibility on both dialects, SPA and runner-frame CSP headers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 05:20:43 +01:00
Lucas WintherandClaude Opus 5.5 061ae7a2d9 feat(learner): search results show lesson matches cleanly; contract notes for the browser entry and LessonView; plan marked done
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 04:54:53 +01:00
Lucas WintherandClaude Opus 5.5 f3ce2e34bf fix(ai): lessonUrl builder in one place; authoring follows core canView
- Learner links go through lessonUrl/courseUrl (src/web/urls.ts).
- Generation, the studio course list and file listing require an authoring
  right and core canView, so a global author needs a grant for a restricted
  course. Contract requests 8 (global author in canView) and 9
  (STC_AI_ALLOWED_BASE_URLS in AppConfig).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 04:49:48 +01:00
Lucas WintherandClaude Opus 5.5 2d162a4282 fix(studio): review round 1 (symlink-safe writes, remote allowlist, upload cap/types/no-overwrite, owner rule at publish/sync/resolve, studio/repos layout and read-only imports, side-effect-free list)
C1: writes lstat every path component and refuse symlinks (dangling included),
check the parent's real path after mkdir, and open with O_NOFOLLOW.
I1: only https://, ssh:// and git@host: remotes; protocol.file/ext/fd=never on
every fetch and push; global/system git config ignored.
I2: STC_UPLOAD_MAX_MB cap (default 100) by Content-Length and while streaming;
extension allowlist (415); stcourse.yaml refused; 409 unless overwrite=true;
sandboxing CSP on draft raw files.
I3: owner-only visibility/source.kind rule checked on the merged result at
publish and sync, and at resolve; publish also validates the merged tree.
I4: bare repos in <dataDir>/studio/repos/<id>.git; imported courses read-only.
I5: list status comes from the bare repo; drafts are created lazily.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 04:43:53 +01:00
Lucas WintherandClaude Opus 5.5 0ff8e6a5de docs(ai): mark plan tasks done
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 04:40:02 +01:00
Lucas WintherandClaude Opus 5.5 1f111b80db docs(ai): contract requests; dev fake provider returns a draft file
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 04:39:58 +01:00
Lucas WintherandClaude Opus 5.5 2849d3c911 docs(studio): contract requests (ctx feature factories, worktrees course dir, LessonView swap, web subpath, nav roles, DraftWriter)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:26:58 +01:00
Lucas WintherandClaude Opus 5.5 7adf9765b1 fix(import): allowed import roots, hostile zip symlink removal, tests
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:26:22 +01:00
Lucas WintherandClaude Opus 5.5 c4df51f7ca feat(import): UI polish, contract requests, report
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:22:22 +01:00
Lucas WintherandClaude Opus 5.5 668ae379bf feat(learner): Better Auth sessions, owner bootstrap, claim groups; server takes feature factories
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:20:06 +01:00
Lucas WintherandClaude Opus 5.5 0b117159cd docs: WP-A learner package plan
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:15:48 +01:00
Lucas WintherandClaude Opus 5.5 c2a3464937 docs(ai): WP-D implementation plan
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:13:33 +01:00
Lucas WintherandClaude Opus 5.5 10cf93c27e docs(import): WP-B plan
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:12:03 +01:00
Lucas WintherandClaude Opus 5.5 fc37da9789 docs(studio): WP-C implementation plan
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 03:11:41 +01:00
Lucas WintherandClaude Opus 5.5 29ba043de7 Add STCourses implementation plan
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 02:46:28 +01:00
lucasw89andClaude Opus 5.5 ab96addbab Add STCourses design, research report and approved mockup
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 02:37:38 +01:00