Amos 0baa00e418 UI iteration 2: toggleable sidebar, floating composer blob, pending-question morph
Sidebar (replaces the modal drawer):
- Inline, toggleable, no backdrop/focus theft — can stay open while working
- Open state persists across pages (provider in root layout) and reloads
  (localStorage; starts closed server-side to avoid hydration mismatch,
  stored value applied post-mount; default open on first run)
- Hamburger in page headers only when closed; close button in sidebar header

Floating composer blob (per reference screenshot):
- Floats over the transcript (absolute overlay), persists at bottom while
  scrolling, content fades behind it (gradient mask); pb-44 scroll clearance
- Concentric corner radii: blob rounded-2xl(16px) − p-1.5(6px) = send
  rounded-[10px] = 25% of the 40px button; CornerDownLeft icon per reference
- Stop moved from header into the blob (busy → square icon)
- prompt-input.tsx + 7 orphan ui files pruned (attachments/command palette
  unused); hand-rolled Composer is ~100 lines and render-tested

Pending-question morph (one text field on screen, never two):
- Quiz/ask-select pending → blob becomes the note field (send hidden, hint
  line above); card's internal note input removed; Submit consumes it
- Ask free-text pending → blob becomes the answer field; its send submits
  via the card's registered submit api (ref bridge); Enter works
- Chat owns the single text state; card clears it on submit
- canSend for answer mode mirrors the card predicate directly — reading the
  ref during render lagged one commit (found in browser testing)

Widths: one shared column (max-w-3xl + px-4) for transcript, cards, and
composer — all three measured pixel-identical (left 416, width 736).
Assistant Message max-w-full/MessageContent w-full (was max-w-[95%]/w-fit —
the 'cards are a bit shorter' bug).

User messages render plain text again — no math pipeline on learner-authored
text (currency hazard accepted for the model only, guarded by the prompt).

Tests: 58 pass (5 new composer pins; card tests updated to controlled
props). Verified live: note flow, answer flow, sidebar persistence, float,
fade, zero console errors — plus the live model spontaneously honoring the
$ currency escape contract in a continuation.
2026-08-22 04:28:13 +03:00

learning-harness

A standalone learning harness built on the pi agent SDK: a teacher agent, durable session artifacts, and a derived knowledge graph of what the learner knows, what they only think they know, and what to learn next.

Design + architecture: docs/architecture.md. Lineage: built up from the Obsidian-based POC (~/Desktop/Obsidian/vault/π/learn) — same teaching methodology, own interface and data pipeline.

Commands

npm install        # once

# ── interface ──────────────────────────────────────────────────────────
npm run dev        # web UI: Fastify server (port 8787) + Vite dev server (5173)
npm start          # build the web UI and serve it from the API server
npm run teach      # terminal TUI teaching session (alternative interface)

# ── health ─────────────────────────────────────────────────────────────
npm run doctor     # smoke test of the terminal (TUI) teacher wiring
npm run doctor:web # smoke test of the web (dispatch-mode) teacher wiring

# ── tests ──────────────────────────────────────────────────────────────
npm test           # unit + scripted integration (deterministic, no LLM, fast)
npm run test:unit  # just the pure-logic unit tests
npm run e2e:live   # LIVE smoke test against a real LLM (server must be running;
                   # costs tokens, has model variance — manual, never a gate)

# ── pipeline (later phases) ────────────────────────────────────────────
npm run ingest -- <session.jsonl>   # Stage 1: normalize a session into the canonical doc
npm run graph                        # ledger stats

Testing strategy

Three layers, LLM only at the top:

  1. Unit (tests/question-core.test.ts, tests/bridge.test.ts) — pure logic: grading, exact-set matching, option shuffling/index resolution, answer formatting, pending-question re-arm, the bridge state machine. No pi, no network.
  2. Scripted integration (tests/integration-quiz.test.ts, tests/server.test.ts) — the full pi agent loop with real extensions and real session persistence, but the LLM replaced by a scripted fake model (src/testing/scripted-model.ts). Deterministically verifies the quiz dispatch contract: question persisted → tool returns immediately → agent ends turn → answer graded → [Quiz answer] user message → agent continues → resume re-arms pending questions.
  3. Live smoke (scripts/e2e.ts) — real model over real WebSocket. For verifying actual model behavior (does it follow the end-your-turn convention?) — which is a prompt-engineering question, verified by using the app, not by CI.

Layout

teacher/system-prompt.md        the teacher's entire behavior (teach methodology + tool guidelines)
extensions/
  core/question-core.ts         pure quiz/ask logic (shared by terminal + web)
  core/bridge.ts                in-process bridge: server ↔ extensions (answer/dismiss/re-arm)
  quiz.ts, ask-user-question.ts terminal mode (blocking pi-tui components)
  quiz-web.ts, ask-web.ts       web dispatch mode (non-blocking; answer arrives as user message)
src/
  teacher/create-session.ts     pi SDK bootstrap (hermetic agent; tui | web extension sets)
  server/index.ts               Fastify + WebSocket server embedding the teacher agent
  shared/protocol.ts            WS/REST protocol types (server ↔ browser)
  testing/scripted-model.ts     deterministic fake LLM for integration tests
  schema.ts                     knowledge-graph data model
  ledger.ts                     append-only JSONL ledgers
  extract/                      normalize (✅) · extract · merge (stubs, specified)
  derive.ts                     derived state (known-set now; understanding parked)
  queries/                      what-next (stub)
web/                            Vite + React frontend (home view, session view, quiz cards)
data/agent/                     hermetic pi agentDir (auth + model store are symlinks to ~/.pi/agent)
data/sessions/                  session JSONLs (canonical source of truth)
data/graph/                     ledgers + canonical documents

The web dispatch shape (why sessions are resumable mid-quiz)

In web mode, quiz/ask_user_question never block: the tool validates, shuffles, and grades-armed, persists an lh_question custom entry (excluded from LLM context, so the correct answer is safe there), and returns immediately with "end your turn". The learner's answer comes back through the server → bridge → extension, which grades it, persists lh_question_answered (gold-standard data for the future extractor), and sends the graded result into the session as a user message. Because nothing blocks, a session can be exited and resumed mid-question: pending state lives in the session file itself and is re-armed on resume.

Pi is a library dependency here (pinned in package.json) — this is a separate pi from any global install. Credentials and the model catalog are shared read-mostly via symlinks; everything else is the harness's own. Nothing under data/ is ever tracked by git.

S
Description
Independent writable copy of amosblomqvist/learnharness; GitHub remains upstream.
Readme AGPL-3.0
15 MiB
2026-10-31 23:43:36 +00:00
Languages
TypeScript 84.8%
Python 9.8%
JavaScript 4.2%
HTML 0.8%
CSS 0.4%