From 7866f6dcb3b7cefbda3ea870089a3c89c8881fa3 Mon Sep 17 00:00:00 2001 From: user Date: Sun, 26 Apr 2026 18:41:13 +0900 Subject: [PATCH] feat: show learning progress in web app --- .planning/REQUIREMENTS.md | 6 +- .planning/STATE.md | 8 ++- .../008-learning-progress-view/008-CONTEXT.md | 34 +++++++++++ .../008-learning-progress-view/008-PLAN.md | 26 +++++++++ .../008-RESEARCH.md | 20 +++++++ .../008-learning-progress-view/008-SUMMARY.md | 33 +++++++++++ .../008-VERIFICATION.md | 24 ++++++++ internal/webapp/assets_test.go | 3 + internal/webapp/static/app.js | 57 +++++++++++++++++++ internal/webapp/static/index.html | 10 ++++ internal/webapp/static/styles.css | 28 +++++++++ openspec/changes/frontend-mvp/tasks.md | 2 +- 12 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 .planning/phases/008-learning-progress-view/008-CONTEXT.md create mode 100644 .planning/phases/008-learning-progress-view/008-PLAN.md create mode 100644 .planning/phases/008-learning-progress-view/008-RESEARCH.md create mode 100644 .planning/phases/008-learning-progress-view/008-SUMMARY.md create mode 100644 .planning/phases/008-learning-progress-view/008-VERIFICATION.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 1cbd119..a26be2e 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -74,7 +74,7 @@ interview-ready after each short practice loop. - [x] **WEB-02**: User can create a diagnostic interview session from the web app. - [x] **WEB-03**: User can answer a diagnostic question and see rubric feedback. -- [ ] **WEB-04**: User can see learner memory, readiness, and next challenge +- [x] **WEB-04**: User can see learner memory, readiness, and next challenge after answering. - [ ] **WEB-05**: Operator can ingest source material from the web app. - [ ] **WEB-06**: Operator can inspect ontology candidate concepts, edges, and @@ -117,7 +117,7 @@ interview-ready after each short practice loop. | ONTO-01..ONTO-04 | Phase 5 | Complete | | ASSET-01..ASSET-03 | Phase 6 | Complete | | WEB-01..WEB-03 | Phase 7 | Complete | -| WEB-04 | Phase 8 | Pending | +| WEB-04 | Phase 8 | Complete | | WEB-05..WEB-08 | Phase 9 | Pending | **Coverage:** @@ -128,4 +128,4 @@ interview-ready after each short practice loop. --- *Requirements defined: 2026-04-26* -*Last updated: 2026-04-26 after Phase 7 execution.* +*Last updated: 2026-04-26 after Phase 8 execution.* diff --git a/.planning/STATE.md b/.planning/STATE.md index 3e2a0d7..b6acb3b 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -7,7 +7,7 @@ See: `.planning/PROJECT.md` (updated 2026-04-26) **Core value:** The user should feel and prove that they are becoming more interview-ready after each short practice loop. -**Current focus:** Phase 8 planning: Learning Progress View. +**Current focus:** Phase 9 planning: Material and Asset Workspace. ## Current Decisions @@ -38,10 +38,11 @@ interview-ready after each short practice loop. - v2 Frontend MVP milestone selected to turn the backend learning loop into a usable web service. - Phase 7 web app shell and diagnostic start UI is implemented and verified. +- Phase 8 learning progress view is implemented and verified. ## Next Actions -1. Plan and execute Phase 8: Learning Progress View. +1. Plan and execute Phase 9: Material and Asset Workspace. 2. Verify the production OpenAI image model identifier before real image generation calls. 3. Add standardized SUMMARY frontmatter or Nyquist validation files if future @@ -80,6 +81,9 @@ interview-ready after each short practice loop. - 2026-04-26: Phase 7 implementation verified with `go test ./...`, OpenSpec validation, root/asset HTTP smoke, and diagnostic API smoke through the server used by the web app. +- 2026-04-26: Phase 8 implementation verified with `go test ./...`, OpenSpec + validation, app script smoke, and learner memory/readiness/next-challenge API + smoke after an answer. --- *State initialized: 2026-04-26.* diff --git a/.planning/phases/008-learning-progress-view/008-CONTEXT.md b/.planning/phases/008-learning-progress-view/008-CONTEXT.md new file mode 100644 index 0000000..d5ccd9e --- /dev/null +++ b/.planning/phases/008-learning-progress-view/008-CONTEXT.md @@ -0,0 +1,34 @@ +# Phase 8 Context: Learning Progress View + +**Status:** Ready for execution +**Started:** 2026-04-26 + +## Goal + +Show evidence-backed learning progress in the web app after diagnostic +practice. + +## Requirements + +- WEB-04: User can see learner memory, readiness, and next challenge after + answering. + +## Inputs + +- Phase 7 web app shell. +- Existing backend endpoints: + - `GET /api/v1/learners/{userID}/memory` + - `GET /api/v1/learners/{userID}/readiness-map` + - `GET /api/v1/learners/{userID}/next-challenge` + +## UX Direction + +Keep progress in the right-side context column so the answer workspace remains +centered. The user should see the loop close immediately: answer, feedback, +memory, readiness, next challenge. + +## Out of Scope + +- Full graph visualization. +- Historical readiness timeline. +- Editing learner memory. diff --git a/.planning/phases/008-learning-progress-view/008-PLAN.md b/.planning/phases/008-learning-progress-view/008-PLAN.md new file mode 100644 index 0000000..41e66d8 --- /dev/null +++ b/.planning/phases/008-learning-progress-view/008-PLAN.md @@ -0,0 +1,26 @@ +# Phase 8 Plan: Learning Progress View + +**Status:** Ready for execution +**Phase Goal:** Close the practice loop with visible learning progress. + +## Tasks + +### 1. Add progress UI region + +- Add learner progress section to the right context pane. +- Include manual refresh affordance. + +### 2. Fetch progress after answer + +- Fetch learner memory, readiness map, and next challenge after grading. +- Render empty/error states when progress is unavailable. + +### 3. Verify + +- Add/update web app tests for progress asset content. +- Run Go tests, OpenSpec validation, line-count check, and live smoke. + +## Out of Scope + +- Charts. +- Historical progress storage. diff --git a/.planning/phases/008-learning-progress-view/008-RESEARCH.md b/.planning/phases/008-learning-progress-view/008-RESEARCH.md new file mode 100644 index 0000000..22cf93d --- /dev/null +++ b/.planning/phases/008-learning-progress-view/008-RESEARCH.md @@ -0,0 +1,20 @@ +# Phase 8 Research: Learning Progress View + +## Findings + +The backend already provides progress projection after answer submission. The +frontend only needs to fetch and summarize the three endpoints after grading. + +Useful MVP display: + +- profile target role and stack +- top concept mastery states +- readiness percentage +- next challenge concept, ladder level, and question + +## Recommendation + +- Refresh progress automatically after successful answer submission. +- Add a manual refresh button for recovery. +- Use empty state before the first answer. +- Keep evidence labels compact so they do not overwhelm the practice surface. diff --git a/.planning/phases/008-learning-progress-view/008-SUMMARY.md b/.planning/phases/008-learning-progress-view/008-SUMMARY.md new file mode 100644 index 0000000..7b770bc --- /dev/null +++ b/.planning/phases/008-learning-progress-view/008-SUMMARY.md @@ -0,0 +1,33 @@ +# Phase 8 Summary + +**Status:** Complete +**Completed:** 2026-04-26 + +## Delivered + +- Added learning progress region to the web app right context pane. +- Added manual progress refresh action. +- After answer submission, the app fetches learner memory, readiness map, and + next challenge. +- Rendered readiness percentage, concept mastery states, and recommended next + challenge. +- Added frontend asset test coverage for progress API wiring. + +## Verification + +```powershell +gofmt -w cmd internal +go test ./... +openspec validate frontend-mvp --strict +``` + +Additional smoke check: + +- Submitted a diagnostic answer, then verified learner memory, readiness, and + next challenge APIs returned progress consumed by the app script. + +## Deferred + +- Browser screenshot audit. +- Charts and historical progress. +- Editable learner memory. diff --git a/.planning/phases/008-learning-progress-view/008-VERIFICATION.md b/.planning/phases/008-learning-progress-view/008-VERIFICATION.md new file mode 100644 index 0000000..f3aba28 --- /dev/null +++ b/.planning/phases/008-learning-progress-view/008-VERIFICATION.md @@ -0,0 +1,24 @@ +# Phase 8 Verification + +## Verdict + +PASS + +## Requirement Coverage + +- WEB-04: PASS. The web app can fetch and render learner memory, readiness, and + next challenge after an answer. + +## Evidence + +- `go test ./...` passed. +- `openspec validate frontend-mvp --strict` passed. +- Static app script includes the readiness API integration. +- Live smoke confirmed memory mastery, readiness percentage, and next challenge + after diagnostic answer submission. + +## Residual Risk + +The UI is still verified through code and HTTP/API smoke rather than browser +screenshots. Phase 9 should add visual/browser validation after the workspace +surface is complete. diff --git a/internal/webapp/assets_test.go b/internal/webapp/assets_test.go index f653ed5..c860041 100644 --- a/internal/webapp/assets_test.go +++ b/internal/webapp/assets_test.go @@ -33,4 +33,7 @@ func TestHandlerServesAsset(t *testing.T) { if !strings.Contains(rec.Body.String(), "diagnostic-sessions") { t.Fatal("expected app script content") } + if !strings.Contains(rec.Body.String(), "readiness-map") { + t.Fatal("expected progress API content") + } } diff --git a/internal/webapp/static/app.js b/internal/webapp/static/app.js index f466e8e..39ca69b 100644 --- a/internal/webapp/static/app.js +++ b/internal/webapp/static/app.js @@ -2,6 +2,7 @@ const state = { session: null, selectedQuestion: null, lastAnswer: null, + progress: null, }; const els = { @@ -11,6 +12,8 @@ const els = { answerButton: document.querySelector("#answer-button"), questions: document.querySelector("#questions"), feedback: document.querySelector("#feedback"), + progress: document.querySelector("#progress"), + refreshProgress: document.querySelector("#refresh-progress"), status: document.querySelector("#status-line"), error: document.querySelector("#error-line"), title: document.querySelector("#session-title"), @@ -45,6 +48,11 @@ els.sessionForm.addEventListener("submit", async (event) => { } }); +els.refreshProgress.addEventListener("click", async () => { + clearError(); + await refreshProgress(); +}); + els.answerForm.addEventListener("submit", async (event) => { event.preventDefault(); clearError(); @@ -63,6 +71,7 @@ els.answerForm.addEventListener("submit", async (event) => { }); state.lastAnswer = answer; renderFeedback(); + await refreshProgress(); setStatus(`Answer graded as ${answer.grade.overall}`); } catch (error) { showError(error.message); @@ -96,6 +105,54 @@ function renderSession() { els.answerButton.disabled = !state.selectedQuestion; } +async function refreshProgress() { + if (!state.session) return; + setStatus("Refreshing learning progress..."); + + try { + const userID = encodeURIComponent(state.session.user_id); + const [memory, readiness, challenge] = await Promise.all([ + request(`/api/v1/learners/${userID}/memory`), + request(`/api/v1/learners/${userID}/readiness-map`), + request(`/api/v1/learners/${userID}/next-challenge`), + ]); + state.progress = { memory, readiness, challenge }; + renderProgress(); + setStatus("Learning progress updated"); + } catch (error) { + showError(error.message); + renderProgress(); + } +} + +function renderProgress() { + els.refreshProgress.disabled = !state.session; + if (!state.progress) { + els.progress.className = "feedback empty-state"; + els.progress.textContent = "Answer once to update learner memory and readiness."; + return; + } + + const { memory, readiness, challenge } = state.progress; + const mastery = memory.mastery || []; + els.progress.className = "feedback"; + els.progress.innerHTML = ` +
+
${readiness.readiness_percentage}%
+

${escapeHTML(memory.profile.target_role)} readiness

+
+
+

Concept memory

+
${mastery.map((item) => `${escapeHTML(item.concept.label)} · ${escapeHTML(item.state)}`).join("")}
+
+
+

Next challenge

+

${escapeHTML(challenge.concept.label)} · ${escapeHTML(challenge.ladder_level)}

+

${escapeHTML(challenge.question)}

+
+ `; +} + function renderFeedback() { if (!state.lastAnswer) { els.feedback.className = "feedback empty-state"; diff --git a/internal/webapp/static/index.html b/internal/webapp/static/index.html index 403c8cb..63485d9 100644 --- a/internal/webapp/static/index.html +++ b/internal/webapp/static/index.html @@ -61,6 +61,16 @@
Submit an answer to see grade, evidence, and follow-up.
+
+
+

Progress

+

Learning state

+
+ +
+
+ Answer once to update learner memory and readiness. +
diff --git a/internal/webapp/static/styles.css b/internal/webapp/static/styles.css index ec96632..3d4cbf5 100644 --- a/internal/webapp/static/styles.css +++ b/internal/webapp/static/styles.css @@ -199,6 +199,16 @@ button:disabled { margin-top: 22px; } +.progress-heading { + margin-top: 34px; +} + +.small-button { + min-height: 34px; + padding: 0 12px; + font-size: 12px; +} + .metric-row { display: grid; grid-template-columns: 1fr auto; @@ -213,6 +223,24 @@ button:disabled { font-weight: 800; } +.readiness-value { + color: var(--accent); + font-size: 48px; + font-weight: 850; + line-height: 1; +} + +.concept-pill { + display: inline-flex; + margin: 4px 6px 4px 0; + border: 1px solid var(--line); + border-radius: 999px; + padding: 6px 9px; + color: var(--muted); + font-size: 12px; + font-weight: 650; +} + .small-list { margin: 0; padding-left: 18px; diff --git a/openspec/changes/frontend-mvp/tasks.md b/openspec/changes/frontend-mvp/tasks.md index 2af4955..8b3454c 100644 --- a/openspec/changes/frontend-mvp/tasks.md +++ b/openspec/changes/frontend-mvp/tasks.md @@ -2,7 +2,7 @@ - [x] 1. Implement web app shell served by the Go backend. - [x] 2. Implement diagnostic session start and answer submission UI. -- [ ] 3. Implement learner memory, readiness, and next challenge UI. +- [x] 3. Implement learner memory, readiness, and next challenge UI. - [ ] 4. Implement material ingestion and ontology inspection UI. - [ ] 5. Implement teaching asset prompt candidate UI. - [ ] 6. Validate frontend MVP with tests, smoke checks, and OpenSpec.