lilleman
9b52b7e5ff
Record the comprehension check: PR #223 paired ruling, better
CI / full-gate (push) Successful in 3m25s
Mirror / github-mirror (push) Successful in 4s
2026-11-08 20:50:55 +01:00
lilleman
903dde8a24
Answer product-owner round 1: 0 🔴 , 1 🟡 (0 misread); relative links in docs/ci-cd.md resolve again
2026-11-08 20:47:52 +01:00
lilleman
9b1b201f65
Split conflict detection out of plugin.ts and the CI/CD section out of the README
CI / full-gate (push) Successful in 3m22s
2026-11-08 20:39:49 +01:00
lilleman
b4579131a2
Split handleRequest into named stages, so the stage order is written once
2026-11-08 19:55:26 +01:00
lilleman
b7e9780437
Record the comprehension check: PR #220 paired ruling same
2026-11-08 19:34:58 +01:00
lilleman
777c0bd86d
Let a plugin add links to the profile menu, and list the setting conflict kind
CI / full-gate (push) Failing after 1m17s
2026-11-08 19:17:57 +01:00
lilleman
14a07dd879
Record the comprehension check: PR #216 paired ruling better
CI / full-gate (push) Successful in 3m21s
Mirror / github-mirror (push) Successful in 4s
2026-11-08 15:10:53 +01:00
lilleman
996eef2dc5
Record reading cost
2026-11-08 14:55:47 +01:00
lilleman
6ca63c6c0f
Record the comprehension check: PR #215 paired ruling better
CI / full-gate (push) Successful in 3m30s
Mirror / github-mirror (push) Successful in 4s
2026-11-08 14:45:06 +01:00
lilleman
404f2584ac
Add buildPaginationModel and ctx.assetHref, and bump the plugin contract to 0.5.0
CI / full-gate (push) Failing after 5s
2026-11-08 14:32:05 +01:00
lilleman
01f368d663
Record the comprehension check: PR #214 paired ruling better
CI / full-gate (push) Successful in 3m23s
Mirror / github-mirror (push) Successful in 5s
2026-11-08 14:22:51 +01:00
lilleman
5aa62e2742
Record reading cost for item 51's chunk
2026-11-08 14:09:20 +01:00
lilleman
190d12532d
Record the reading cost
CI / full-gate (push) Failing after 1m4s
2026-11-03 21:44:05 +01:00
lilleman
c2389f871d
Record the comprehension check: overall 6.50 then 6.38, under the 7.0 floor; the README's REQUIRE_SECURE_SECRETS drift is filed as item 56
CI / full-gate (push) Successful in 3m24s
Mirror / github-mirror (push) Successful in 4s
2026-10-31 14:09:54 +01:00
lilleman
217a36252e
Keep a URL config value out of the boot error that rejects it
CI / full-gate (push) Failing after 1m6s
2026-10-31 13:48:43 +01:00
lilleman
8f7bc59b38
Keep a secret setting's raw value out of the boot error that rejects it
CI / full-gate (push) Successful in 3m23s
2026-10-31 13:38:27 +01:00
lilleman
622059101f
Record the comprehension check: PR #200 ruled same, scored 6.50 and 6.25; filed items 46-54
CI / full-gate (push) Successful in 3m20s
Mirror / github-mirror (push) Successful in 4s
2026-10-31 13:31:41 +01:00
lilleman
4cb21d8909
Link the admin's recovery-code notice to the flow the code was minted on
CI / full-gate (push) Failing after 1m1s
2026-10-31 13:05:06 +01:00
lilleman
7cf37acb1b
Record the reading cost of the last chunk
2026-10-31 12:41:11 +01:00
lilleman
d0e1de96b8
Show which filter segment or chip is selected and focused without :has()
CI / full-gate (push) Failing after 1m3s
2026-10-31 12:19:21 +01:00
lilleman
7ff8140e07
Fail the gate on a stylesheet feature the browser floor lacks, unless an @supports guards it
CI / full-gate (push) Successful in 4m45s
2026-10-31 11:52:17 +01:00
lilleman
b6afdf1ecc
Push auto-release tags as renovate-bot so release.yml fires, and give the Docker Hub page its short description
CI / full-gate (push) Successful in 3m20s
2026-10-31 11:12:23 +01:00
lilleman
fcc5e04e0d
Record the reading cost of the chunk before item 3
2026-10-31 10:47:47 +01:00
lilleman
71b12a6c0a
Split src/http/app.test.ts into one file per concern, and reset the reading-cost trend
CI / full-gate (push) Successful in 3m20s
2026-10-31 10:35:08 +01:00
lilleman
3d757d4726
item-34 - Serve every /public/ asset under a content-versioned URL with an immutable Cache-Control
2026-10-31 10:00:03 +01:00
lilleman
f4969d5c1f
Refuse a stale permission save, confirm or report it, show View to read-only holders, keep an absolute return_to beside a locale
CI / full-gate (push) Failing after 2m53s
2026-10-31 09:34:05 +01:00
lilleman
2a4ed30e93
Redirect an anonymous hit on /settings to sign-in with the page as return_to
CI / full-gate (push) Successful in 3m25s
2026-10-31 09:04:55 +01:00
lilleman
2be8ebfa67
Record the reading cost of the previous chunk
2026-10-31 08:51:49 +01:00
lilleman
de4eb7d9ee
Record the reading cost of the previous chunk
2026-10-31 08:37:45 +01:00
lilleman
a8a9744744
Record the comprehension check: paired ruling better for PR #187
CI / full-gate (push) Successful in 3m19s
Mirror / github-mirror (push) Successful in 5s
2026-10-31 08:26:23 +01:00
lilleman
520d2c928d
Visual E2E at five viewports, typecheck e2e-tests in the gate, drop unused icons
CI / full-gate (push) Successful in 3m29s
2026-10-31 08:20:16 +01:00
lilleman
b336dee6d7
Record the comprehension check: paired ruling better for PR #184
CI / full-gate (push) Successful in 3m5s
2026-10-31 02:19:51 +01:00
lilleman
16da74f03f
Record the reading cost of the last chunks
2026-10-31 02:14:39 +01:00
lilleman
bb8e6bc8e8
Record the comprehension check: paired ruling better for PR #183
CI / full-gate (push) Successful in 3m2s
Mirror / github-mirror (push) Successful in 4s
2026-10-31 02:09:41 +01:00
lilleman
2ab482e774
Keep the mobile nav reachable on engines without :has(), and fix the stylesheet header
CI / full-gate (push) Successful in 3m8s
2026-10-31 01:49:13 +01:00
lilleman
364b04795c
Record the comprehension check: paired ruling better for PR #182
2026-10-31 01:43:26 +01:00
lilleman
635006b5fc
Show the whole nav tree without :has(), raise text contrast to 4.5:1, drop unused auth rules
CI / full-gate (push) Successful in 3m1s
2026-10-31 01:24:34 +01:00
lilleman
3e70768433
Record the comprehension check: paired ruling better for PR #181
CI / full-gate (push) Successful in 3m2s
Mirror / github-mirror (push) Successful in 4s
2026-10-31 01:16:05 +01:00
lilleman
7e3e8acb28
Give every palette colour a hex value an engine without oklch can read
CI / full-gate (push) Successful in 3m5s
2026-10-31 01:00:42 +01:00
lilleman
d8cf257940
Move plugin-contract.md into README's Building plugins section; remove docs/, repoint all references
2026-06-26 23:12:40 +02:00
lilleman
de22f51c12
Organize src/ into concern folders (http, auth, admin, plugin-host, ui); co-locate tests, move plugin-api barrel into plugin-host, sync docs + AGENTS layout
2026-06-24 00:23:55 +02:00
lilleman
913bd6813a
Consolidate E2E into e2e-tests/ (Dockerfile + compose.{visual,auth,oauth,full,devstack}.yml, WORKDIR /e2e-tests); move ci.sh to repo root
2026-06-23 23:48:05 +02:00
lilleman
a9f25a7692
Remove completed todo.md + html-css-foundation mockups; strip dead §N phase refs from comments/docs (simplify visual E2E to drop the mockup-comparison oracle)
2026-06-23 22:49:28 +02:00
lilleman
e22d24aa8a
§10 - one menu everywhere (buildPluginChrome) + shell on every page; instructional starter dashboard; Kratos-native email docs
...
Collapse the three nav builders into buildPluginChrome: chrome.bestHref does longest-prefix matching so deep admin routes mark their leaf. Delete adminNav; buildConfirmModel and the admin model builders take the resolved nav. The same role-filtered sidebar now renders signed in or out, collapsing to a burger on narrow screens.
shell.ejs gains menu (default true; menu:false -> single-column .app-bare), docTitle (separate <title> from the topbar, so the body keeps the single <h1>), and hideSignIn (suppress the footer Sign-in on auth pages to avoid a login loop). auth/home/landing now render inside the shell.
Dashboard is a replaceable instructional starter (definePlugin snippet, no mock data). Email stays delegated to Kratos: documented its built-in courier.template_override_path instead of adding web-side SMTP.
2026-06-23 21:26:00 +02:00
lilleman
58398481ca
§10 review pass: address the architecture + product reviewers (todo §10); hide the gated Dashboard nav node from anonymous visitors in buildPluginChrome (a no-permission link to /dashboard only dead-ended them at /login) and dedup it into a shared DASHBOARD_NAV (admin-nav.ts, reused by chrome + adminNav). New chrome.signInHref bakes the current page in as return_to for the shell's anonymous Sign-in link (shell.ejs + reference overview.ejs), mirrored as optional ShellModel.signInHref so the typed builder is complete. ctx.chrome is now a lazy, memoized getter (context.ts chrome option = a factory) so a json/redirect handler or the public "/" with a standalone home never composes the global menu — app.ts passes the app-level memoized factory at every site. Default /dashboard prints a "Starter dashboard" note framing the mock-data home as a replaceable demo (signals its inert affordances); stale "until §4" comments fixed. RESERVED_PLUGIN_IDS drift-guard test derives the built-in segments from AUTH_FLOWS + ADMIN_*_BASE + host literals (home stays deliberately unreserved). Refreshed the stale plugin-contract status blurb and documented the chrome.*→partials/shell mapping. Reviewers: architecture + product APPROVE (no addressable findings remain), stability APPROVE (no Critical/High/Medium). typecheck + 356 units + visual(10) + full-flow(7) E2E green.
2026-06-21 01:19:40 +02:00
lilleman
7bdeb24b7f
§10 public pages + menu items, the blessed explicit alias (todo §10); a plugin may mark a page and its menu option public. A no-permission route/nav node is already anonymous-reachable, so per the human's pick this BLESSES that as a first-class, explicit choice (keep the default; add an explicit alias — not a secure-by-default flip). New optional public?: boolean on Route (src/plugin.ts) + NavNode (src/nav.ts) = "open to everyone, signed in or not", honored outright in isAuthorized (router.ts) + filterByRoles (nav.ts), and MUTUALLY EXCLUSIVE with permission — discovery shapeError recursively rejects a route/nav node setting both, failing the boot loud (never silently picks one). public is filter-only (toRenderNode never emits it). The shell (views/partials/shell.ejs) now renders a Sign in link instead of the profile/sign-out block for an anonymous visitor, so a public page in the native shell (ctx.chrome; ctx.user may be null) isn't a broken "Guest / Sign out". Reference plugin demos it: a public /scheduling Overview route + a public "Overview" nav child (the "Scheduling" header now shows for everyone), the shifts list still behind scheduling:read. Hardened the latent gap the shell newly leans on: claimsToUser rejects an empty email like it does an empty sub. Tests-first (348 → 354 units): router/nav/discovery (public open + reject-both + loads), shell (anon → Sign in, no logout form), app (public route anon-200), shifts (overview handler), jwt-middleware (empty email). Docs: plugin-contract.md ("Public pages & menu items" + route shape + shape-error note) + README (menu system + reference snippet). E2E: visual.spec asserts the public Overview is anon-200 + shown in the member's nav while the gated Shifts redirects/filters. stability-reviewer: APPROVE, no Critical/High/Medium (addressed its one Low — the empty-email hardening). typecheck + 354 units + full scripts/ci.sh gate (visual 10 · auth 1 · oauth 2 · full 7) green.
2026-06-20 18:12:46 +02:00
lilleman
7787ed4ea4
§10 split landing into a public "/" + gated "/dashboard", both plugin-replaceable (todo §10 follow-up); per human feedback, "/" is now an ungated public landing (default views/home.ejs: brand + intro + prominent Log in / Create account links, or "go to dashboard" when signed in) and "/dashboard" is the gated post-login app home (anonymous → /login?return_to=/dashboard). Both are fully replaceable via two optional RouteHandlers on PluginManifest — home? (public /) and dashboard? (gated /dashboard) — rendered against the plugin's own views with the native shell via ctx.chrome (full route parity: HEAD, void-return, response hooks, fresh CSRF cookie; a home handler is public so ctx.user may be null). Single-slot + loud: findConflicts errors on >1 owner of either slot (new "home"/"dashboard" kinds), discovery rejects a non-function handler, and "dashboard" is reserved so a plugin folder can't shadow it ("/" can't be shadowed — route paths carry the /<id> prefix). Post-login + already-signed-in redirects and the global Dashboard/People nav hrefs moved to /dashboard. Tests-first (348 units): public-/ + gated-/dashboard + dual plugin-override in app.test; per-slot conflict in plugin.test; non-function/reserved/two-owners in discovery.test. Docs: plugin-contract "The landing pages" section + README. E2E: visual.spec plants a session for /dashboard design-system tests + a cookie-free public-landing test; full-flow repointed to /dashboard. stability-reviewer: APPROVE, no Critical/High/Medium. typecheck + 348 units + visual(10) + full-flow(7) green.
2026-06-20 17:43:01 +02:00
lilleman
2eb5b84ccf
§10 gate the dashboard + make "/" replaceable by a plugin (todo §10); "/" is now gated to a signed-in session (anonymous → /login via loginRedirect, query preserved as return_to) and fully replaceable via a new optional home?: RouteHandler on PluginManifest — a handler with the same signature as any route (the most ergonomic shape). The app.ts "/" branch gates first, then renders the single home plugin's handler against its own views/ with the native shell via ctx.chrome (HEAD / void-return / response-hook parity with a plugin route), else the built-in mock-data People list. home mounts at the root above the /<id> namespace, so it can't shadow or be shadowed by a built-in route. Single-slot + loud: findConflicts errors on >1 home (new "home" kind), discovery rejects a non-function home — never last-write-wins. Tests-first (338 → 344 units): app.test.ts gate + home-override; plugin.test.ts home conflict; discovery.test.ts home validation. Docs: plugin-contract.md (manifest table + "The dashboard (home)" section + conflict row), README. E2E: visual.spec plants a dev-signed session (the anonymous plugin-gate probe uses the cookie-free request fixture); all e2e web/gateway healthchecks repointed from the gated "/" to /public/css/styles.css. stability-reviewer: APPROVE, no Critical/High/Medium. typecheck + 344 units + visual(9) + full-flow(7) E2E green.
2026-06-20 17:18:30 +02:00
lilleman
66ea68a91b
§9 whole-project arch+product review pass (todo §9); ran systems-architect + product-owner on the whole project (no Critical/High — a converged scaffold) and addressed the in-scope §9 customer-facing/security findings. (1) return_to deep-link login, open-redirect-safe: a gated request hit while signed out (plugin-route gate, requireSession, requireAdmin) bounces to /login?return_to=<host-relative path> via new loginRedirect(ctx) (GET/HEAD, skips /); /login bakes it into the Kratos flow — a host-relative target is wrapped through <origin>/auth/complete?return_to=<path> so the JWT mints before landing, an absolute target (§6 OAuth2 login challenge) passes to Kratos as-is; /auth/complete redirects to the requested page. (2) safeUrl()+localPath() in new pure src/safe-url.ts: safeUrl sanitises an untrusted href/src to relative-or-http(s) (else "#"), exported via plugin-api.ts (closes the contract's "planned for §9" pointer); localPath is the host-relative redirect-allowlist guard for return_to, re-checked at both /login and /auth/complete. (3) honest 503 on Ory-unreachable sign-in (views/503.ejs) instead of the misattributed catch-all 500; expired-flow 4xx still restarts. Tests-first throughout; stability-reviewer APPROVE (addressed its Medium — scoped the 503 catch so a template bug hits the 500 with a stack, not a 503). typecheck + 339 units + full scripts/ci.sh gate green. Deferred with justification: the app.ts route-table refactor (standalone change + §10 prereq), mock dashboard + public-page blessing (§10 lines 139/140), success-flash (known).
2026-06-20 16:39:09 +02:00
lilleman
bea9a71d6f
§9 trace all fetch + ENV service name + leveled logging (todo §9 follow-up); route every outbound fetch through the request logger, make the OTLP service name implementer-configurable, and add proper leveled logging throughout. An AsyncLocalStorage<Log> makes the per-request logger ambient (runWithLog/currentLog), so all outbound fetch traces with no signature churn: tracedFetch (a typeof fetch) routes through the active request log (client span + propagated W3C traceparent) for string/URL inputs, else plain fetch; server.ts wires it under the Ory timeout into every Kratos/Keto/Hydra + JWKS call (timeout still honoured — log.fetch spreads {...init,headers}). RequestContext gained ctx.log (request logger; additive/contract-stable, silent default) so a handler/plugin logs in-trace and ctx.log.fetch(url) traces upstream calls; the reference plugin's createUpstream defaults to tracedFetch and its handlers log via ctx.log; plugin-api.ts exports tracedFetch + the Log class. SERVICE_NAME (config + createLogger({serviceName})) brands the OTLP service.name. Leveled logging: who-did-what audit info lines on every admin write (user/group/role/client create·delete·assign — actor/target, no secrets), info on login (session mint) + logout, warn on missing-role 403 + CSRF rejections + Ory-unreachable, debug on a JWKS kid-miss reload. app.ts's handler body was extracted to handleRequest run inside runWithLog; end() now fires exactly once after BOTH the handler unwinds AND the response closes, so a client abort mid-handler can't end the log out from under a still-running ctx.log/tracedFetch (regression-tested) and the happy-path access line is never dropped. bootstrap.ts wraps main in runWithLog + traces the seed calls. Tests extended (logger: serviceName/runWithLog/currentLog/tracedFetch-continues-trace; config: SERVICE_NAME; context: ctx.log default+passthrough; app: ctx.log in-trace + ctx.log.fetch propagation + the abort race; plugin-api: tracedFetch+Log). Stability-reviewer: APPROVE, no Critical/High (fixed the abort-race end(); green nits addressed). docs/plugin-contract.md (ctx.log/ctx.log.fetch/tracedFetch) + README (config, Observability tracing/serviceName, plugin note, Layout) updated. typecheck + 333 units + the full scripts/ci.sh E2E gate green (326 → 333).
2026-06-20 15:46:48 +02:00