Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
186 commits
Select commit Hold shift + click to select a range
d28a672
docs(project): add KB management POC implementation plan
rschlaefli Jul 15, 2026
ffecea3
docs(project): record PR 5174 id in KB POC plan filename
rschlaefli Jul 15, 2026
b8e1442
docs(project): revise KB POC plan to package architecture on v3-ai
rschlaefli Jul 15, 2026
09a464f
feat(packages/prisma): add slim KB and KBResource models for KB POC
rschlaefli Jul 15, 2026
6b86f9e
enhance(packages/prisma): support blob and web/URL KB resource kinds
rschlaefli Jul 15, 2026
6906734
feat(packages/kb-management): scaffold KB management package mounted …
jabbadizzleCode Jul 17, 2026
546d48c
feat(kb): knowledge base CRUD across graphql and kb-management package
jabbadizzleCode Jul 17, 2026
651a9bb
feat(kb): blob upload and web-resource registration for KB resources
jabbadizzleCode Jul 17, 2026
fbd4dcd
feat(kb): hatchet ingestion dispatch task and ingest trigger mutation
jabbadizzleCode Jul 17, 2026
fec1f4a
feat(kb): signed ingestion webhook receiver updating resource status
jabbadizzleCode Jul 17, 2026
2174fae
enhance(kb): status badges, live polling, and responsive polish
jabbadizzleCode Jul 17, 2026
4eefc28
chore(kb): final checks and dev docs for KB POC
jabbadizzleCode Jul 17, 2026
5df4394
fix(kb): protect claimed blobs during upload confirmation
jabbadizzleCode Jul 17, 2026
cac0fa1
docs(kb): record finish-gate review outcomes
jabbadizzleCode Jul 17, 2026
f0a109b
docs(kb): design external Hatchet ingestion bridge
jabbadizzleCode Jul 20, 2026
226cb1c
docs(kb): plan external Hatchet ingestion bridge
jabbadizzleCode Jul 20, 2026
d74ef2b
feat(kb): track latest external ingestion attempt
jabbadizzleCode Jul 20, 2026
b4e84e5
feat(kb): correlate signed ingestion callbacks
jabbadizzleCode Jul 20, 2026
cacba15
feat(kb): add correlated speed-aware ingestion attempts
jabbadizzleCode Jul 20, 2026
31b588d
feat(kb): dispatch selected resources to external Hatchet
jabbadizzleCode Jul 20, 2026
b93d0eb
feat(kb): monitor external ingestion runs with one cron
jabbadizzleCode Jul 20, 2026
64b1b9f
feat(kb): select ingestion speed per resource
jabbadizzleCode Jul 20, 2026
9007e84
chore(kb): configure external Hatchet ingestion bridge
jabbadizzleCode Jul 20, 2026
c722f52
fix(kb): close external ingestion review findings
jabbadizzleCode Jul 20, 2026
b291d71
fix(kb): bound serialized blob deletion
jabbadizzleCode Jul 20, 2026
b4a9989
docs(kb): record external ingestion bridge verification
jabbadizzleCode Jul 20, 2026
49481a3
docs(kg): add chatbot graph visualization design
jabbadizzleCode Jul 20, 2026
b9bcbbd
docs(kg): add visualization implementation plan
jabbadizzleCode Jul 20, 2026
c91d35e
feat(kg): add shared graph contracts
jabbadizzleCode Jul 20, 2026
70b94eb
feat(kg): persist chatbot graph lifecycle
jabbadizzleCode Jul 20, 2026
e161740
feat(kg): manage chatbot graph resources
jabbadizzleCode Jul 20, 2026
88b5e07
feat(kg): add bounded FalkorDB reader
jabbadizzleCode Jul 20, 2026
009e6b7
feat(kg): dispatch chatbot graph builds
jabbadizzleCode Jul 20, 2026
4a72019
feat(kg): monitor chatbot graph builds
jabbadizzleCode Jul 20, 2026
1ea9328
feat(kg): add interactive graph viewer
jabbadizzleCode Jul 20, 2026
f67ee13
feat(kg): expose lecturer graph preview
jabbadizzleCode Jul 20, 2026
9f61905
feat(chat): serve authorized knowledge graphs
jabbadizzleCode Jul 20, 2026
82fd2fe
chore(kg): wire FalkorDB configuration
jabbadizzleCode Jul 20, 2026
6198749
feat(manage): configure chatbot knowledge graphs
jabbadizzleCode Jul 20, 2026
7b4489c
feat(chat): add knowledge graph workspace
jabbadizzleCode Jul 20, 2026
101709d
fix(kg): validate recovered Hatchet runs
jabbadizzleCode Jul 20, 2026
ea9681a
fix(chat): stabilize knowledge graph viewer
jabbadizzleCode Jul 20, 2026
576969e
refactor(kg): extract cytoscape presentation
jabbadizzleCode Jul 20, 2026
edf3a5a
docs(kg): add browser verification screenshots
jabbadizzleCode Jul 20, 2026
f8e4ffc
docs: design Hatchet worker dev startup ordering
jabbadizzleCode Jul 21, 2026
b525942
fix(hatchet): stabilize worker startup in development
jabbadizzleCode Jul 21, 2026
98a0ad5
fix(chat): restore knowledge graph runtime
jabbadizzleCode Jul 21, 2026
f2145bc
fix(ci): build knowledge graph before graphql
jabbadizzleCode Jul 21, 2026
6d90ca8
docs(kg): design model selection
jabbadizzleCode Jul 21, 2026
99645a2
feat(kg): select ingestion models
jabbadizzleCode Jul 21, 2026
9b5fc7a
feat(hatchet): update KB ingestion model IDs and add Swiss Foundry mo…
jabbadizzleCode Jul 22, 2026
8df7777
docs(project): add KB production v1 roadmap plan from PR 5182/5078 re…
rschlaefli Jul 24, 2026
dcbdbe3
docs(project): record KB v1 grill rulings Q1-Q11 and finalize roadmap
rschlaefli Jul 25, 2026
cbb6780
docs(project): move KB roadmap onto integration branch
rschlaefli Jul 26, 2026
b66ae01
feat(kb): add management and ingestion POC
jabbadizzleCode Jul 26, 2026
d898fad
chore(kb): sync v3-ai integration line
rschlaefli Jul 26, 2026
c8ab80b
enhance(kb): align dispatch with ingestion API
rschlaefli Jul 26, 2026
fb39440
fix(kb): preserve ingestion retry identity
rschlaefli Jul 26, 2026
e7a3df8
enhance(kb): align ingestion status webhook
rschlaefli Jul 26, 2026
b646f9c
enhance(kb): finish ingestion contract cleanup
rschlaefli Jul 26, 2026
3ca3490
fix(kb): harden ingestion reconciliation
rschlaefli Jul 26, 2026
0d28539
fix(kb): allow worker startup without ingestion config
rschlaefli Jul 26, 2026
d520150
docs(kb): start W3 status history work
rschlaefli Jul 27, 2026
89f2a4e
feat(kb): track ingestion history and serving state
rschlaefli Jul 27, 2026
07bd7f2
feat(kb): show ingestion history and serving state
rschlaefli Jul 27, 2026
519aa2a
docs(kb): close W3 status history milestone
rschlaefli Jul 27, 2026
bc368ea
docs(kb): start W4 chatbot retrieval work
rschlaefli Jul 27, 2026
0491fd0
feat(kb): connect chatbots to scoped retrieval
rschlaefli Jul 27, 2026
cd4c806
fix(kb): harden scoped retrieval boundary
rschlaefli Jul 27, 2026
69c2a49
docs(kb): recapture W4 chatbot evidence
rschlaefli Jul 27, 2026
8a061fc
docs(kb): plan W5 deletion and retention work
rschlaefli Jul 27, 2026
c21f25b
docs(kb): address W5 plan review
rschlaefli Jul 27, 2026
3b8f743
feat(kb): add asynchronous deletion tombstones
rschlaefli Jul 27, 2026
c7e1789
feat(kb): add retention cleanup
rschlaefli Jul 28, 2026
7e90684
refactor(kb): simplify retention cleanup
rschlaefli Jul 28, 2026
7ef33aa
fix(kb): close deletion cleanup races
rschlaefli Jul 28, 2026
db1e1a2
fix(kb): retry superseded deletions
rschlaefli Jul 28, 2026
4cd1a83
test(kb): cover terminal deletion statuses
rschlaefli Jul 28, 2026
281cac9
docs(kb): record W5 verification
rschlaefli Jul 28, 2026
569a148
docs(kb): plan W6 and W7 execution
rschlaefli Jul 28, 2026
5e8b02f
enhance(kb): enforce per-KB quotas
rschlaefli Jul 28, 2026
262a355
enhance(kb): add scalable resource operations
rschlaefli Jul 28, 2026
57193ac
enhance(kb): scale lecturer workspace
rschlaefli Jul 28, 2026
24f1e2f
docs(kb): record W6 and W7 verification
rschlaefli Jul 28, 2026
bef1b1f
fix(kb): close W7 review findings
rschlaefli Jul 28, 2026
925eea6
fix(kb): preserve loaded resource state
rschlaefli Jul 28, 2026
891ae41
docs(kb): record senior review and plan W8-interim package
rschlaefli Jul 29, 2026
92d85e6
fix(kb): reserve unknown-size quota at URL creation
rschlaefli Jul 29, 2026
507448a
fix(kb): keep mutation success distinct from refresh failures
rschlaefli Jul 29, 2026
00bcc63
refactor(kb): deduplicate unknown-size seeding in quota tests
rschlaefli Jul 29, 2026
e766f4e
refactor(kb): centralize best-effort refresh handling
rschlaefli Jul 29, 2026
f69b858
fix(kb): recover stranded ingestion dispatches
rschlaefli Jul 29, 2026
1a32d5c
refactor(kb): deduplicate invalid retry-payload logging
rschlaefli Jul 29, 2026
60db6a9
enhance(kb): gate KB workspace behind private preview
rschlaefli Jul 29, 2026
de58ad1
fix(kb): surface preview-access errors in the workspace shell
rschlaefli Jul 29, 2026
7f844dd
enhance(kb): bound active polling and align filter controls
rschlaefli Jul 29, 2026
7b99329
fix(kb): harden bounded polling against window drift
rschlaefli Jul 30, 2026
86226c0
fix(kb): reject tombstoned gateway sources
rschlaefli Jul 30, 2026
3e2c805
refactor(kb): simplify gateway coverage fixtures
rschlaefli Jul 30, 2026
b4541c6
fix(kb): stabilize loaded-window polling
rschlaefli Jul 30, 2026
b355c25
fix(kb): preserve invalid polling bookkeeping
rschlaefli Jul 30, 2026
95ef8a8
docs(kb): update wiki for interim gating and remediation
rschlaefli Jul 30, 2026
dc63178
test(kb): align ingestion fixtures with preview gate
rschlaefli Jul 30, 2026
00ab85b
fix(kb): serialize polling with load more
rschlaefli Jul 30, 2026
d9e3e1b
fix(kb): queue refresh after load more
rschlaefli Jul 30, 2026
885622a
docs(kb): record W8 local finish gate
rschlaefli Jul 30, 2026
f9196d9
docs(kb): record W8 publication readback
rschlaefli Jul 30, 2026
3ba6a54
fix(kb): support local Blob uploads with Azurite
rschlaefli Jul 30, 2026
38625cb
fix(dev): use low-entropy Azurite test key
rschlaefli Jul 30, 2026
ed2cba5
docs(kb): plan KB-owned knowledge graph stack
rschlaefli Jul 31, 2026
532e409
feat(kb): accept platform refresh ingestion events
rschlaefli Aug 1, 2026
35988b8
fix(kb): preserve current ingestion status
rschlaefli Aug 1, 2026
38a8cef
docs(kb): record L1 review completion
rschlaefli Aug 1, 2026
5a8fc28
feat(kg): add bounded FalkorDB reader and graph contracts
jabbadizzleCode Jul 31, 2026
8452528
refactor(kg): re-home the knowledge graph from chatbot to KB
rschlaefli Jul 31, 2026
a7ef9bf
fix(kb): preserve active content in graph digest
rschlaefli Aug 1, 2026
fc06696
fix(kb): guard published graph builds
rschlaefli Aug 1, 2026
4218033
docs(kb): record L2 review completion
rschlaefli Aug 1, 2026
bf652a3
feat(kb-graph): take over KB graph lifecycle
rschlaefli Aug 1, 2026
b1b760c
fix(kb-graph): harden retention reconciliation
rschlaefli Aug 1, 2026
a486449
fix(kb-graph): close digest reconciliation race
rschlaefli Aug 1, 2026
bc6262f
Merge branch 'feat/kb-knowledge-graph-parked' into feat/kb-graph-life…
rschlaefli Aug 1, 2026
de99b64
chore(devcontainer): wire local KB service boundaries
rschlaefli Aug 1, 2026
36f6c32
test(kb-graph): align pinned manifest digest fixture
rschlaefli Aug 1, 2026
b348ade
docs(project): record current graph verification
rschlaefli Aug 2, 2026
da02707
docs(kb-graph): record roadmap corrections
rschlaefli Aug 15, 2026
eb4c0fd
docs(project): extend KB graph plan for M1
rschlaefli Aug 15, 2026
567cad0
enhance(kb-graph): complete quota and lifecycle seams
rschlaefli Aug 15, 2026
a52d1cb
enhance(kb-graph): present quota and settlement state
rschlaefli Aug 15, 2026
a326487
fix(kb-graph): protect held cost reservations
rschlaefli Aug 15, 2026
7ca0ff2
fix(kb-graph): preserve accounting invariants
rschlaefli Aug 15, 2026
18dce8e
refactor(kb-graph): rely on terminal contract validation
rschlaefli Aug 15, 2026
c1358a2
fix(kb-graph): close final lifecycle review gaps
rschlaefli Aug 15, 2026
013843d
docs(kb-graph): refresh lifecycle plan evidence
rschlaefli Aug 15, 2026
810ce4e
fix(kb-graph): close lifecycle contract gaps
rschlaefli Aug 15, 2026
371a782
docs(kb-graph): refresh lifecycle review evidence
rschlaefli Aug 15, 2026
22a1357
fix(kb-graph): close dispatch and late-settlement gaps
rschlaefli Aug 15, 2026
2107999
docs(kb-graph): reconcile current target evidence
rschlaefli Aug 15, 2026
045c01c
fix(kb-graph): wire terminal settlement
rschlaefli Aug 15, 2026
1245ba6
fix(kb-graph): fence ambiguous dispatches
rschlaefli Aug 15, 2026
e851e1d
docs(kb-graph): record M1 evidence
rschlaefli Aug 15, 2026
5070813
docs(kb-graph): record final review
rschlaefli Aug 15, 2026
7882bcc
merge: integrate v3 through #5420 into kb-graph lifecycle
rschlaefli Aug 17, 2026
919d22f
fix(graphql): restore Chatbot knowledge-base field and regen after v3…
rschlaefli Aug 17, 2026
7d4157f
merge(v3-ai): synchronize kb-graph lifecycle through 198747502
rschlaefli Aug 17, 2026
dda345f
docs(kb): record M1 runtime verification and remediation state
rschlaefli Aug 17, 2026
e843091
fix(kb): format test files with biome
rschlaefli Aug 17, 2026
8e0d47f
fix(ci): allowlist devcontainer compose and ship knowledge-graph dist…
rschlaefli Aug 17, 2026
e309190
docs(kb): renumber KB ADRs to 0009-0016 to avoid collisions
rschlaefli Aug 17, 2026
bf4ac24
fix(ci): keep the KB graph integration unconfigured for tests
rschlaefli Aug 17, 2026
c65ad86
test(chat): scope manage-assistant starter assertions to their section
rschlaefli Aug 17, 2026
012a1a6
fix(kb): apply final-review fixes to graph retention, dispatch recove…
rschlaefli Aug 18, 2026
bfe858f
fix(ci): build knowledge-graph and graphql before chat tests
rschlaefli Aug 18, 2026
8244896
docs(adr): record the graph-build ledger as the canonical version record
rschlaefli Aug 18, 2026
4353f8a
fix(ci): build markdown before chat tests
rschlaefli Aug 18, 2026
1a80d78
docs(adr): record the provider-launcher, consumer-E2E seam
rschlaefli Aug 19, 2026
e8e115d
docs(roadmap): close W3 at the merged delivery layer
rschlaefli Aug 20, 2026
599ffcd
fix(frontend-manage): register knowledge base detail route
rschlaefli Aug 21, 2026
bc4c8a1
docs(kb): record v3-ai finalization plan
rschlaefli Aug 23, 2026
4d8bef8
Merge remote-tracking branch 'origin/v3-ai' into rs/kb-v3-ai-finaliza…
rschlaefli Aug 23, 2026
6f68c67
fix(kb): restore ingestion HTTP routes
rschlaefli Aug 23, 2026
a64a76a
fix(chat): keep KB scope on thread identity
rschlaefli Aug 23, 2026
f914683
fix(kb): bound graph reconciliation
rschlaefli Aug 23, 2026
b4c3748
fix(kb): complete local graph ingestion wiring
rschlaefli Aug 23, 2026
8ba4f9e
test(kb): add graph interaction selectors
rschlaefli Aug 23, 2026
5bf20cb
docs(kb): align finalization contracts
rschlaefli Aug 23, 2026
ed8e369
fix(kb): satisfy strict graph monitor checks
rschlaefli Aug 23, 2026
c396406
fix(kb): preserve source content type
rschlaefli Aug 23, 2026
4319bb4
fix(kb): rotate monitor pages without gaps
rschlaefli Aug 23, 2026
7c27637
fix(chat): resolve graph runtime under turbopack
rschlaefli Aug 23, 2026
0387560
docs(kb): record final review evidence
rschlaefli Aug 23, 2026
30c3b0e
style(kb): fix test file formatting
rschlaefli Aug 23, 2026
90c0a18
docs(kb): record exact-head CI evidence
rschlaefli Aug 23, 2026
8eee6f0
docs(kb): record runtime lifecycle closure
rschlaefli Aug 23, 2026
703d026
chore(v3-ai): reconcile KB graph lifecycle with target
rschlaefli Aug 24, 2026
30562c6
docs(kb): record v3-ai conflict reconciliation
rschlaefli Aug 24, 2026
c92d89c
fix(kb-graph): fence provider timeouts and loopback hosts
rschlaefli Aug 24, 2026
ce9001d
docs(kb): record final conflict review
rschlaefli Aug 24, 2026
77ab853
docs(kb): record final review pass
rschlaefli Aug 24, 2026
65a209e
merge: reconcile knowledge graph lifecycle with current v3-ai
rschlaefli Aug 25, 2026
9fb54d4
fix(kb-graph): abort timed-out provider requests
rschlaefli Aug 25, 2026
789bc72
fix(chat): preserve async MCP server actions
rschlaefli Aug 25, 2026
c546f16
test(prisma-data): preserve combined package test coverage
rschlaefli Aug 25, 2026
f85ffe5
refactor(prisma): consolidate KB management migration
rschlaefli Aug 25, 2026
f85a3dd
fix(kb): preserve rollback-safe MCP authentication
rschlaefli Aug 25, 2026
1d57f4f
fix(mcp): include util in lecturer runtime image
rschlaefli Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .agents/skills/klicker-data-model/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ Provenance: steps 2 requires a database; on a machine without one running, write
- **Decimal fields**: Python client needs `enable_experimental_decimal = true` in `apps/analytics/prisma/schema/py.prisma` (already set — don't remove); TS side never truthy-checks Decimals.
- **Don't touch synced Analytics model files by hand** — `prisma:sync` overwrites them while preserving Analytics-owned `py.prisma` and `datasource.prisma`.
- **Participant email uniqueness is per auth mode** (`@@unique([email, isSSOAccount])`) — cross-mode duplicate prevention lives in service logic, not the schema.
- **KB ingestion has two state axes** — `KBResource` holds the latest operation plus active serving identity; `KBIngestionRun` is append-only. Lecturer runs use the ingestion attempt/idempotency UUID; a signed `resource.content_refreshed` webhook uses its event UUID and persists the platform operation ID. A platform refresh may advance only serving identity and must not overwrite a current lecturer operation; resource-list operation status and filtering must resolve through the stored attempt, not the newest ledger row; a failed replacement must not erase the active version.
- **KB graph pointers are untrusted state** — `KB.publishedGraphBuildId` is a plain UUID rather than a relation. A reader must verify that the resolved `KBGraphBuild` belongs to the requested KB and is `SUCCEEDED` before using its graph name; retain old per-build graphs through a grace period and never sweep the active or published graph.
- **KB graph cost state is a locked ledger** — `KBGraphQuota` is unique per owner and semester, `KBGraphBuild.costStatus` fences reservation settlement by build id, and `dispatchClaimedAt` durably distinguishes an unattempted dispatch from an accepted-but-uncorrelated provider run. All monetary and persisted usage counters stay within PostgreSQL `INTEGER` bounds. Use the `ON CONFLICT DO NOTHING` insert followed by a row lock for concurrent first reservations; valid metered non-success terminal results settle actual usage without publishing, while invalid, mismatched, overflowed, or cleanup-fenced results become `NEEDS_HUMAN_REVIEW` and retain the reservation. A timed-out success may publish only after locked no-newer-build and current-digest reconciliation; stale or superseded late results settle without publication.
- **KB/chatbot activation is a database invariant** — `KBChatbot` may keep disabled history, but a SQL partial unique index permits only one enabled KB per chatbot. Prisma cannot express that index; preserve and verify it in the migration SQL.
- **KB deletion retains correlation state** — `KB`/`KBResource` tombstones hide owner reads immediately, while `KBIngestionOperation.DELETE` runs remain until external serving is empty and storage cleanup succeeds. Preserve the restrictive `KBUploadTicket` relation so pending uploads block parent hard deletion.
- **KB quotas count retained allocations** — include every resource row (including tombstones) plus every upload ticket for the 100-item limit, and sum resource plus ticket `sizeBytes` for the 500 MiB limit. Allocate under the parent-KB row lock; confirmation consumes a reservation rather than claiming quota again. An unknown-size URL row reserves the full 25 MiB source limit until the worker records its measured size.
- **KB scale operations preserve stable order** — resource cursors use immutable `createdAt` plus UUID, and bulk mutations lock the live parent before sorted resource ids. Do not make `createdAt` mutable or introduce a competing child-first lock order.
- **KB owner cascade is not a cleanup mechanism** — `KB.owner` currently uses `onDelete: Cascade`; deleting a User would remove resource/run correlation before external and Blob cleanup. Any future account-deletion or GDPR flow must complete the KB tombstone lifecycle before deleting the User.

## Seeds — two independent paths

Expand Down
6 changes: 4 additions & 2 deletions .agents/skills/klicker-environment-doctor/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ If `apps/analytics` complains about schema drift or a schema edit isn't visible:
lsof -nP -iTCP:5432 -sTCP:LISTEN # repeat for 6379 6380 6381 7077 8888 80 443
```

`Bind for :::5432 failed: port is already allocated` means another stack holds the port — stop it or don't start the colliding service. Plain localhost and legacy host-based paths publish fixed ports, so only one such stack runs per machine. Parallel devcontainer worktrees use the port-free base compose file plus `.devcontainer/docker-compose.devrouter.yml`; the one-at-a-time fallback uses `.devcontainer/docker-compose.localhost.yml`. If manage media uploads fail with an Azure Blob CORS error while GraphQL auth still works, check the storage account before changing app CORS. The media library uploads directly from the browser to Azure Blob Storage via SAS, so its CORS rule must allow the actual local origin. Use exact origins for production/staging accounts and dev-only localhost rules for a dedicated dev storage account.
`Bind for :::5432 failed: port is already allocated` means another stack holds the port — stop it or don't start the colliding service. Plain localhost and legacy host-based paths publish fixed ports, so only one such stack runs per machine. Parallel devcontainer worktrees use the port-free base compose file plus `.devcontainer/docker-compose.devrouter.yml`; the one-at-a-time fallback uses `.devcontainer/docker-compose.localhost.yml`.

The managed DevPod needs no Azure credentials for media or KB uploads: it starts a Blob-only Azurite service, routes `blob.klicker[.<workspace>].localhost`, and configures the exact Manage origin as local Blob CORS during `post-start.sh`. `Blob storage is not configured` means the app process predates that environment; run `devrouter ensure .` and retry against the printed Blob route. A browser CORS failure with working GraphQL should first be reproduced as an `OPTIONS` request to that route using the exact Manage origin. Do not print the SAS query. Production and staging Azure accounts still require exact deployed origins; never add localhost CORS to them.

## Check 6 — infra bring-up / server status (headless-safe)

Expand All @@ -69,7 +71,7 @@ devrouter exec . -- cat /tmp/devrouter-process-klicker-dev.state
devrouter exec . -- tail -n 50 /tmp/dev.log
```

`devrouter ensure` delivers its matching process helper to the exact validated container. Released `0.0.35` fingerprints the workspace, command, adapter bytes, and declared non-secret origin allowlist. The helper replaces a stale owned process group and leaves unknown processes untouched. Host-side ensure checks all routes and can recreate one stale or unhealthy exact-path DevPod once.
`devrouter ensure` delivers its matching process helper to the exact validated container. Released `0.0.35` fingerprints the workspace, command, adapter bytes, and declared non-secret origin allowlist. The helper replaces a stale owned process group and leaves unknown processes untouched. Host-side ensure checks all eleven routes, including Blob Storage, and can recreate one stale or unhealthy exact-path DevPod once.

`devrouter doctor --repo .` provides static diagnostics. `devrouter ensure .` resolves the checkout-specific overlay and is the authoritative runtime proof.

Expand Down
14 changes: 14 additions & 0 deletions .agents/skills/klicker-frontend-ui/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,33 @@ Conventions (design system, Tailwind v4, Apollo, i18n, CSP): [docs/frontend-conv
services.
- Forms: Formik + Yup. Conditional classes: `twMerge`. Feature flags gate alone — never `flag && count > 0`.
- No Next.js middleware for CSP/headers — that belongs at the proxy layer.
- KB graph panel: show the per-KB opt-in and localized billing/quota states before the rebuild control. Format current estimates, maxima, and quota values with the current persisted quota currency, and historical settled build cost with its recorded currency; treat persisted quota currency/limit drift as unavailable. Keep rebuild disabled when opt-in, cost configuration, or active-build conditions fail; display actual cost and usage only after settlement; keep provider credentials out of the browser; and map billing enums to localized text.
- Assessment comparison charts use equal-width categorical bars and a
labelled 0–100 percentile ruler; keep the exact range/count table and
highlight the student's containing range.
3. **Verify in the browser — mandatory, not optional.** Depending on your environment path:

- **Inside Devcontainer:** Dev servers auto-start in the background. No need to start/stop them. View logs via `tail -f /tmp/dev.log`.
- **Host-based Setup:** You are authorized to start the dev servers needed for this verification, and must clean up after with `./_down.sh`. Bring-up per [docs/getting-started.md](../../../docs/getting-started.md) (localhost `dev:raw` path works without secrets).
- On bring-up / server failure → `klicker-environment-doctor`.
- Open the changed pages with `npx agent-browser` (never bare `agent-browser`), log in via **delegated** access with the AGENTS.md test credentials (not Edu-ID).
- Capture before/after screenshots of every changed state (including error/empty states you touched); check both locales if strings changed.
- Iterate on issues you see yourself; hand to the user for manual verification only after your own pass succeeds.

4. **Pre-PR** — `klicker-testing-verification` checklist; attach the screenshots to the PR description.

## App boundaries

- `frontend-manage` (lecturer), `frontend-pwa` (student; also has a localforage offline side-channel for live-quiz answers — don't bypass `storageHelpers.ts`), `frontend-control` (mobile controller), `auth` (login flows — auth changes also need [docs/auth-model.md](../../../docs/auth-model.md)).
- Knowledge-base management is a reusable package mounted by `frontend-manage`: edit `packages/kb-management`, not duplicate app-local components. Verify `/resources/knowledgeBases` plus the detail route at desktop and mobile widths, both locales, and every changed empty/active/success/failure state.
- The KB navigation item is an interim `user.privatePreview` discovery gate. Direct catalog/detail URLs must render the localized `KB_PREVIEW_ACCESS_REQUIRED` service error for a non-preview lecturer; never rely on hidden navigation as authorization.
- The knowledge-resource Ingest action accepts only the resource identifier. Do not expose transport tuning in the UI unless the GraphQL and ingestion-platform contracts add a real user-controlled setting.
- Keep full KB attempt history out of the two-second detail poll. Load the bounded, owner-checked history query only when a lecturer expands a resource, while the parent query carries only the latest run needed for operation status.
- Localize KB failure detail from stable status/error codes. Do not render raw ingestion-platform status text into the EN/DE lecturer UI.
- Replacing a chatbot's enabled KB requires an explicit warning state; verify attach, replace, detach, linked-KB, and no-KB states in both locales and at desktop/mobile widths.
- KB delete copy must distinguish immediate removal from background external/blob cleanup; never claim that asynchronous cleanup completed in the mutation success toast.
- KB uploads expose only PDF, TXT, and MD up to 25 MiB while the ingestion bridge supports PDF/plain text; map Markdown to `text/plain` and do not advertise DOCX/PPTX prematurely. Localize stable quota codes rather than raw service messages.
- KB catalog/detail scale uses server-backed search/filter connections, design-system `SelectField` filters, and explicit load-more controls. While one row is active, poll page zero plus known active pages every two seconds and run a full loaded-window walk every tenth tick; fall back immediately on cursor/page-length drift. Preserve the latest loaded window across action refreshes, use `no-cache` promise queries for background polls, and invalidate in-flight refreshes when filters change. Use indeterminate operation progress, not fabricated percentages. Keep selection bounded to 50 and remove rows from selection when they become active. Keep bulk deletion behind a named confirmation, and expose source, operation-versus-serving state, contextual actions, and lazy history in the keyboard-accessible inspector.
- Treat a KB mutation and its follow-up query refresh as separate outcomes. After mutation success, close/reset and show success even when a best-effort refresh fails; log the refresh failure without surfacing a mutation error or encouraging a duplicate retry.
- KB metrics must distinguish visible data from quota usage, reservations, pending cleanup, unknown-size conservative claims, and linked consumers. Verify these states in EN/DE at desktop and 390 px widths.
- **`apps/chat` is out of scope here** — app router, zustand, assistant-ui; read [docs/chat-platform.md](../../../docs/chat-platform.md) and follow its local conventions instead.
22 changes: 20 additions & 2 deletions .agents/skills/klicker-graphql-api/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Facts (auth ladder, layering, error conventions): [docs/graphql-api-layer.md](..

1. **Service function** — `packages/graphql/src/services/<area>.ts`. All logic, Prisma, Redis, pubSub here. Signature `(args, ctx: ContextWithUser) => …`. Errors: `GraphQLError` with `extensions.code` (grep `LIVE_QUIZ_PIN_INVALID` for the pattern) — not bare `Error`.
2. **Schema field** — `packages/graphql/src/schema/query.ts` / `mutation.ts` / `subscription.ts` (+ new object types in the area file). The resolver is a **one-liner** delegating to the service.
3. **Auth on the field** — copy the existing composition exactly (real shape from `deleteCourse` in `mutation.ts`; `withPermission` WRAPS the resolver):
3. **Auth on the field** — always declare the role with `t.withAuth(...)`. For shareable aggregates represented by `PermissionCheck`, copy the existing composition exactly (real shape from `deleteCourse` in `mutation.ts`; `withPermission` WRAPS the resolver):

```ts
deleteCourse: t.withAuth(asUser).field({
Expand All @@ -30,7 +30,7 @@ Facts (auth ladder, layering, error conventions): [docs/graphql-api-layer.md](..
})
```

Participant-facing fields usually need only `t.withAuth(asParticipant)`. Note `withPermission` returns `null` on failure (client sees a null field, not an error) — don't "fix" that.
Participant-facing fields usually need only `t.withAuth(asParticipant)`. Note `withPermission` returns `null` on failure (client sees a null field, not an error) — don't "fix" that. Owner-only aggregates that have no `PermissionCheck` key, including `KB`, keep the role gate on the schema field and must resolve the persisted owner relation inside every service entry point. Do not invent a permission key or make an owner-only aggregate shareable only to reuse this wrapper.

Multi-object batch fields are the deliberate exception: `withPermission`
accepts one object selector and can only return one nullable field. Protect
Expand All @@ -57,6 +57,24 @@ Facts (auth ladder, layering, error conventions): [docs/graphql-api-layer.md](..
7. **Frontend wiring** — `import { <Name>Document } from '@klicker-uzh/graphql/dist/ops'`; `useQuery`/`useMutation` (+ `refetchQueries`) per [docs/frontend-conventions.md](../../../docs/frontend-conventions.md).
8. **Tests** — graphql vitest for service logic (`pnpm --filter @klicker-uzh/graphql test:local`; see the heavy pattern in `38c92d035`); route further via `klicker-testing-verification`.

Do not nest full history under a frequently polled parent list. The KB detail query loads only each resource's latest run; the separate owner-checked history query returns at most the five newest runs and is called on expansion.

Every KB service query and mutation must start with `assertKbPreviewAccess(ctx)`, which reads the current `User.privatePreview` value instead of trusting a JWT claim. Apply the separate `assertKbIngestionEnabled()` kill switch only to upload-ticket issue, URL-resource creation, and Ingest/Retry/Re-ingest; reads, confirmation, deletion, and chatbot binding must stay available while ingestion is disabled.

Knowledge-graph mutations add a distinct `KB_GRAPH_DISABLED` generation switch and require the persisted per-KB `knowledgeGraphEnabled` opt-in. Before dispatch, reserve the configured estimate in the owner-semester `KBGraphQuota`; recheck the complete reservation and linked quota identity at the worker effect boundary; claim `dispatchClaimedAt` before the provider call; and hold an accepted-but-uncorrelated run instead of retrying an ambiguous external effect. Keep its reservation and active KB build slot fenced until recovery, cancellation, settlement, or manual resolution, and refuse a rebuild mutation that would start a second external run. Expose cost and quota state without credentials. Provider status is not a GraphQL success proof: wire a versioned terminal result through `settleKbKnowledgeGraphResult`, validate build/KB/owner/run/digest/artifact/currency/bounded-counter/metering identity, settle valid metered non-success results without publication, and let `KBGraphBuild.costStatus` make settlement idempotent. The production backend and general worker explicitly pass `getKBGraphTerminalResult` and `settleKbKnowledgeGraphResult` into `prepareHatchetTasks`; both adapters are required for the supported runtime composition. A timed-out success requires locked no-newer-build and current-digest reconciliation before publication; stale or superseded late results settle without publication. The config query selects the newest graph attempt for lifecycle and cost fields, while it resolves `isStale` only from a verified successful published build, so a held or charged rebuild remains visible without changing the served pointer. Report persisted quota currency/limit drift as unavailable and keep historical build-cost currency separate from quota display.

KB/chatbot attach and detach must lock both owner rows, replace the enabled link atomically, and reconcile only the `tutor` and `explainer` KB MCP configurations. Do not expose a configuration without the matching scoped-retrieval runtime support.

KB child creation and deletion share a KB-first lock order. Upload-ticket issue, confirmation, URL creation, resource deletion, and whole-KB deletion must require a live parent under that lock. Deletion keeps hidden tombstones and queues the external operation after commit; queue failure must remain hidden and retryable.

KB resource-count and byte quotas use that same parent lock. Reserve unknown-size URL resources at the full 25 MiB source limit until the worker records their measured size. Return stable GraphQL codes for quota and ticket mismatches, and derive every ingestion `kb_id` from the persisted owner-checked KB/resource relationship rather than client-supplied scope text.

The source gateway is system-to-system, not caller-owner authorization: one `KB_SOURCE_GATEWAY_KEY` may fetch any owner's exact eligible BLOB resource. Keep the non-tombstoned BLOB/digest/QUEUED-or-PROCESSING database predicate ahead of Blob Storage access, derive the container from the persisted owner relation, and never describe the shared key as tenant-scoped.

For scalable KB lists, use the existing owner/filter-bound opaque keyset connections: `(updatedAt, id)` for KBs and immutable `(createdAt, id)` for resources, both descending, bounded to 50. Do not reintroduce the former unbounded `getUserKbs` or nested `KB.resources` fields. Keep exact metrics derived with grouped queries, keep full run history in its separate five-row query, filter resources by their latest ingestion-run status, and reset cursors when normalized search/type/status filters change.

Bulk resource deletion accepts at most 50 unique ids from one owned KB. Lock the parent then sorted children, reject the selection atomically for missing/foreign/active rows, create one fenced delete run per row before commit, and treat post-commit task dispatches independently.

For pagination changes, test both finite `take`/`skip` values and omitted
values in the service, and verify that the generated operation variables and
public schema make the arguments optional. Do not emulate an unbounded query
Expand Down
Loading
Loading