docs: update documentation to OKF v0.1 format

This commit is contained in:
OpenVelo Agent
2026-07-23 04:06:46 +00:00
parent 5a6e832cca
commit c874e96879
4 changed files with 11 additions and 9 deletions
+3 -2
View File
@@ -3,7 +3,7 @@ type: architecture
title: Key Files Index title: Key Files Index
description: One-line responsibility for important source and contract-test files, including strict event-window validation, the public bootstrap SSE listener, the challenges full-replace import flow, the authenticated player-facing challenges board, and the per-user `solvedByMe` reset on the session boundary. description: One-line responsibility for important source and contract-test files, including strict event-window validation, the public bootstrap SSE listener, the challenges full-replace import flow, the authenticated player-facing challenges board, and the per-user `solvedByMe` reset on the session boundary.
tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations, challenges, import, board, notifications, per-user, session] tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations, challenges, import, board, notifications, per-user, session]
timestamp: 2026-07-23T03:45:00Z timestamp: 2026-07-23T04:05:00Z
--- ---
# Backend # Backend
@@ -77,7 +77,7 @@ timestamp: 2026-07-23T03:45:00Z
| `frontend/src/app/features/challenges/challenges.service.ts` | HTTP service for `/api/v1/challenges/{board,status,:id,:id/solves}` returning typed `ApiErrorEnvelope`s. `getDetail` always requests `?include=solvers` so the modal can render the solvers list in one round-trip; `getBoard` only attaches `include=solvers` when the caller opts in. | | `frontend/src/app/features/challenges/challenges.service.ts` | HTTP service for `/api/v1/challenges/{board,status,:id,:id/solves}` returning typed `ApiErrorEnvelope`s. `getDetail` always requests `?include=solvers` so the modal can render the solvers list in one round-trip; `getBoard` only attaches `include=solvers` when the caller opts in. |
| `frontend/src/app/features/challenges/challenges.pure.ts` | Pure types and helpers (`BoardCard`, `SolverRow`, `SolveEventPayload`, `parseSolveEvent`, `mergeSolveEventIntoSolvers`, `messageForSolveError`, `formatDdHhMm`). | | `frontend/src/app/features/challenges/challenges.pure.ts` | Pure types and helpers (`BoardCard`, `SolverRow`, `SolveEventPayload`, `parseSolveEvent`, `mergeSolveEventIntoSolvers`, `messageForSolveError`, `formatDdHhMm`). |
| `frontend/src/app/features/challenges/category-column.component.ts` | Renders a single category column (header + cards) on the challenges board. | | `frontend/src/app/features/challenges/category-column.component.ts` | Renders a single category column (header + cards) on the challenges board. |
| `frontend/src/app/features/challenges/challenge-card.component.ts` | Renders a single challenge card on the board (difficulty, live points, solve count, `✓` when solved by the player). | | `frontend/src/app/features/challenges/challenge-card.component.ts` | Renders a single challenge card on the board as a semantic `<button>` (difficulty, live points, solve count, `✓` when solved by the player). Exposes `data-testid="challenge-card-<id>"`, `data-solved`, `aria-pressed`, an accessible name (`Challenge <name>, solved|not solved`), and an inner `data-testid="challenge-check-<id>"` for the solved glyph. |
| `frontend/src/app/features/challenges/challenge-modal.component.ts` | Challenge detail modal: description, flag form, awarded banner, solvers list, live-solve updates. | | `frontend/src/app/features/challenges/challenge-modal.component.ts` | Challenge detail modal: description, flag form, awarded banner, solvers list, live-solve updates. |
| `frontend/src/app/core/services/notification.service.ts` | Root-provided signal-backed store of `{ id, kind, message, ts }` records; `error()` / `info()` push, `dismiss(id)` / `clear()` remove. | | `frontend/src/app/core/services/notification.service.ts` | Root-provided signal-backed store of `{ id, kind, message, ts }` records; `error()` / `info()` push, `dismiss(id)` / `clear()` remove. |
| `frontend/src/app/core/interceptors/error-notification.interceptor.ts` | Translates `HttpErrorResponse` into friendly messages and pushes them to `NotificationService` (with suppression for `/api/v1/auth/{login,csrf,register}` and `/api/v1/challenges/status`). | | `frontend/src/app/core/interceptors/error-notification.interceptor.ts` | Translates `HttpErrorResponse` into friendly messages and pushes them to `NotificationService` (with suppression for `/api/v1/auth/{login,csrf,register}` and `/api/v1/challenges/status`). |
@@ -86,6 +86,7 @@ timestamp: 2026-07-23T03:45:00Z
| `tests/frontend/challenges.store.spec.ts` | Signal store: board mutation, live solve merge, listeners, `stop()`. The "marks solvedByMe" and "does not double-count" tests now call `setMyUserId('me-1')` *before* `load()` to exercise the realistic page-mount sequence. | | `tests/frontend/challenges.store.spec.ts` | Signal store: board mutation, live solve merge, listeners, `stop()`. The "marks solvedByMe" and "does not double-count" tests now call `setMyUserId('me-1')` *before* `load()` to exercise the realistic page-mount sequence. |
| `tests/frontend/challenges.store.reset.spec.ts` | Per-user `solvedByMe` regression suite: `reset()` clears board/detail/myUserId/SSE; switching user id flushes the previous board; **orphan board** (cached with no recorded user id) is cleared when PlayerB's id is set; a non-mine solve never flips `solvedByMe` from `false` to `true`; `applySubmitResponse` takes the server value verbatim. | | `tests/frontend/challenges.store.reset.spec.ts` | Per-user `solvedByMe` regression suite: `reset()` clears board/detail/myUserId/SSE; switching user id flushes the previous board; **orphan board** (cached with no recorded user id) is cleared when PlayerB's id is set; a non-mine solve never flips `solvedByMe` from `false` to `true`; `applySubmitResponse` takes the server value verbatim. |
| `tests/frontend/notification-interceptor.spec.ts` | Interceptor suppression rules and friendly message mapping. | | `tests/frontend/notification-interceptor.spec.ts` | Interceptor suppression rules and friendly message mapping. |
| `tests/frontend/challenge-card-accessibility.spec.ts` | Challenge-card accessibility contract: keyboard activation, `aria-pressed` toggling, accessible-name composition, and the `data-solved` / `challenge-check-*` test selectors. |
| `tests/backend/challenges-board.spec.ts` | Board query shape, ordering, `?include=solvers`. | | `tests/backend/challenges-board.spec.ts` | Board query shape, ordering, `?include=solvers`. |
| `tests/backend/challenges-status-rest.spec.ts` | `/api/v1/challenges/status` snapshot shape. | | `tests/backend/challenges-status-rest.spec.ts` | `/api/v1/challenges/status` snapshot shape. |
| `tests/backend/challenges-events-sse.spec.ts` | `/api/v1/events` SSE: status + solve frames, dedup, ordering. | | `tests/backend/challenges-events-sse.spec.ts` | `/api/v1/events` SSE: status + solve frames, dedup, ordering. |
+5 -4
View File
@@ -3,7 +3,7 @@ type: guide
title: Challenges Board title: Challenges Board
description: How a signed-in player navigates the `/challenges` page, opens a challenge modal, submits a flag, and watches live solve updates. description: How a signed-in player navigates the `/challenges` page, opens a challenge modal, submits a flag, and watches live solve updates.
tags: [guide, challenges, board, flag, score, sse, tester] tags: [guide, challenges, board, flag, score, sse, tester]
timestamp: 2026-07-23T03:09:00Z timestamp: 2026-07-23T04:05:00Z
--- ---
# When this view is available # When this view is available
@@ -57,11 +57,11 @@ category.
|----------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------| |----------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| Category column | `[data-testid="category-column-CR"]` (or any abbreviation) | Header shows the category `icon`, uppercase `abbreviation`, and full `name`; below it is a vertical list of cards. | | Category column | `[data-testid="category-column-CR"]` (or any abbreviation) | Header shows the category `icon`, uppercase `abbreviation`, and full `name`; below it is a vertical list of cards. |
| Category column with no challenges | Same | The header renders but the body shows `No challenges yet.` | | Category column with no challenges | Same | The header renders but the body shows `No challenges yet.` |
| Challenge card | `[data-testid="challenge-card-<uuid>"]` | Click anywhere on the card to open the modal. | | Challenge card | `[data-testid="challenge-card-<uuid>"]` | Rendered as a real `<button type="button">`. Keyboard activatable (Tab + Enter/Space), exposes `aria-pressed` and an accessible name `Challenge <name>, solved` / `Challenge <name>, not solved`, and `data-solved="true"\|"false"`. Click anywhere (or press Enter/Space) to open the modal. |
| Difficulty pill | `.diff.diff-LOW` / `.diff-MEDIUM` / `.diff-HIGH` | Green / yellow / red pill matching the challenge difficulty. | | Difficulty pill | `.diff.diff-LOW` / `.diff-MEDIUM` / `.diff-HIGH` | Green / yellow / red pill matching the challenge difficulty. |
| Live points | `[data-testid="points-<uuid>"]` | `livePoints` from the board payload; updates live as `solve` frames arrive. | | Live points | `[data-testid="points-<uuid>"]` | `livePoints` from the board payload; updates live as `solve` frames arrive. |
| Solve count | text `N solve` / `N solves` | Number of distinct players who have solved this challenge. | | Solve count | text `N solve` / `N solves` | Number of distinct players who have solved this challenge. |
| Solved-by-me check | `.check` (the `` glyph) | Appears on the card once the player has solved it; the card border also flips to the success color. | | Solved-by-me check | `[data-testid="challenge-check-<uuid>"]` (the `` glyph, `aria-hidden="true"`) | Appears on the card once the player has solved it; the card border also flips to the success color. The button's `aria-pressed` flips to `true` and the accessible name gains the trailing `, solved`. |
Columns are sorted alphabetically by `abbreviation`; cards within a Columns are sorted alphabetically by `abbreviation`; cards within a
column are sorted `LOW → MEDIUM → HIGH` then by name (lowercased). column are sorted `LOW → MEDIUM → HIGH` then by name (lowercased).
@@ -196,7 +196,8 @@ destruction of the page the SSE source is closed via
| `frontend/src/app/features/challenges/challenges.service.ts` | HTTP service: `getBoard`, `getDetail`, `getEventState`, `submit` (returns `ApiErrorEnvelope`). | | `frontend/src/app/features/challenges/challenges.service.ts` | HTTP service: `getBoard`, `getDetail`, `getEventState`, `submit` (returns `ApiErrorEnvelope`). |
| `frontend/src/app/features/challenges/challenges.pure.ts` | Pure types (`BoardCard`, `SolverRow`, `SolveEventPayload`, etc.) + helpers (`sortColumnsByAbbrev`, `formatDdHhMm`, `parseSolveEvent`, `messageForSolveError`). | | `frontend/src/app/features/challenges/challenges.pure.ts` | Pure types (`BoardCard`, `SolverRow`, `SolveEventPayload`, etc.) + helpers (`sortColumnsByAbbrev`, `formatDdHhMm`, `parseSolveEvent`, `messageForSolveError`). |
| `frontend/src/app/features/challenges/category-column.component.ts` | Renders one category column (header + cards). | | `frontend/src/app/features/challenges/category-column.component.ts` | Renders one category column (header + cards). |
| `frontend/src/app/features/challenges/challenge-card.component.ts` | Renders one challenge card on the board. | | `frontend/src/app/features/challenges/challenge-card.component.ts` | Renders one challenge card on the board as a semantic `<button>` with `aria-pressed`, `aria-label`, `data-testid="challenge-card-<id>"`, `data-solved`, and an inner `data-testid="challenge-check-<id>"` for the solved glyph. |
| `tests/frontend/challenge-card-accessibility.spec.ts` | Keyboard activation (Enter/Space), `aria-pressed` toggling, accessible-name composition, and the `data-solved`/`challenge-check-*` selectors. |
| `frontend/src/app/features/challenges/challenge-modal.component.ts` | Renders the challenge detail modal, flag form, solvers list, and live-solve updates. | | `frontend/src/app/features/challenges/challenge-modal.component.ts` | Renders the challenge detail modal, flag form, solvers list, and live-solve updates. |
| `frontend/src/app/core/services/event-status.store.ts` | Anchor-based clock + 4-state event store; reused here for the gate logic. | | `frontend/src/app/core/services/event-status.store.ts` | Anchor-based clock + 4-state event store; reused here for the gate logic. |
| `frontend/src/app/core/services/authenticated-event-source.service.ts` | Fetch-based SSE transport used to open `/api/v1/events`. | | `frontend/src/app/core/services/authenticated-event-source.service.ts` | Fetch-based SSE transport used to open `/api/v1/events`. |
+2 -2
View File
@@ -3,7 +3,7 @@ type: guide
title: Per-User `solvedByMe` Across the Session Boundary title: Per-User `solvedByMe` Across the Session Boundary
description: How the `ChallengesStore` signal cache guarantees that PlayerA's solved-by-me flags never leak into PlayerB's view across logout/login, including SSE-unauthorized and peer-logout invalidation. description: How the `ChallengesStore` signal cache guarantees that PlayerA's solved-by-me flags never leak into PlayerB's view across logout/login, including SSE-unauthorized and peer-logout invalidation.
tags: [guide, challenges, solvedByMe, session, logout, store, tester] tags: [guide, challenges, solvedByMe, session, logout, store, tester]
timestamp: 2026-07-23T03:45:00Z timestamp: 2026-07-23T04:05:00Z
--- ---
# What this feature does # What this feature does
@@ -117,7 +117,7 @@ are the regression targets:
| Element | Selector | After user-switch | | Element | Selector | After user-switch |
|---|---|---| |---|---|---|
| Solved check | `.check` inside `[data-testid="challenge-card-<uuid>"]` | **Absent** for PlayerB if PlayerA solved it. | | Solved check | `[data-testid="challenge-check-<uuid>"]` (`.check` glyph, `aria-hidden="true"`) inside `[data-testid="challenge-card-<uuid>"]` | **Absent** for PlayerB if PlayerA solved it. The button's `aria-pressed` is also `false` and `data-solved="false"`. |
| Solved border | `[data-testid="challenge-card-<uuid>"]` (border color) | Default border color, not success. | | Solved border | `[data-testid="challenge-card-<uuid>"]` (border color) | Default border color, not success. |
| Modal solved banner | `[data-testid="modal-solved-banner"]` | **Absent** for PlayerB. | | Modal solved banner | `[data-testid="modal-solved-banner"]` | **Absent** for PlayerB. |
| Awarded banner | `[data-testid="modal-awarded-banner"]` | **Absent** (no award yet for PlayerB). | | Awarded banner | `[data-testid="modal-awarded-banner"]` | **Absent** (no award yet for PlayerB). |
+1 -1
View File
@@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin
controls. controls.
The docs below are organized by purpose so agents can pull just the slice The docs below are organized by purpose so agents can pull just the slice
they need. Last regenerated 2026-07-23T03:45:00Z. they need. Last regenerated 2026-07-23T04:05:00Z.
# Architecture # Architecture