diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index bc5e418..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "node .claude/hooks/gsd-check-update.cjs" - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-activation-prompt.sh" - } - ] - } - ], - "PostToolUse": [ - { - "matcher": "Edit|MultiEdit|Write", - "hooks": [ - { - "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-use-tracker.ts" - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-build-check-enhanced.sh" - }, - { - "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/error-handling-reminder.sh" - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "node .claude/hooks/gsd-statusline.js" - }, - "enabledPlugins": { - "agent-sdk-dev@claude-plugins-official": true, - "claude-md-management@claude-plugins-official": true - } -} diff --git a/.dev/notes..txt b/.dev/notes..txt deleted file mode 100644 index a576f1c..0000000 --- a/.dev/notes..txt +++ /dev/null @@ -1,32 +0,0 @@ -/gsd:discuss-phase — phases where plans assumed “vision” decisions - -/gsd:discuss-phase 2 (Setup Automation): scripts/automation, minimal UX - -Discuss first (highest risk / most UX ambiguity): -- /gsd:discuss-phase 3 (Onboarding Wizard) - - Why: User-facing interactive CLI - - Decide: question flow, presentation style, validation behavior, help text, branching vs linear - -- /gsd:discuss-phase 8 (Rolling Tracker & Hedge Sizer) - - Why: Most complex phase (6 plans); CLI design heavy - - Decide: subcommand naming, output format (status / suggest-roll / history), roll suggestion display - -- /gsd:discuss-phase 10 (Template Engine) - - Why: Visual interactive product - - Decide: explorer layout, node appearance, interactions (click/hover/drag), color scheme - -- /gsd:discuss-phase 11 (Self-Assessment & Topics) - - Why: Interactive UX choices - - Decide: 4-state cycling behavior, guided vs standard vs yolo differences, topic content depth - -- /gsd:discuss-phase 12 (Maya/CLI/Landing): landing page design + CLI ergonomics (templates exist; builds on 10–11) -- /gsd:discuss-phase 9 (SQQQ vs Puts): mostly presentation (math/formulas fixed by research) -- /gsd:discuss-phase 7 (Total Return): mainly CLI output formatting (narrow scope; follows existing patterns) - - -Skip (infrastructure; minimal gray areas): -- Phase 1 (Git Scrub): tool-dictated (git-filter-repo, gitleaks) -- -- Phase 4 (Polish & Hooks): mostly technical (SIGINT, Bun migration) -- Phase 5 (Agent Readiness): needs planning, but scope is lint/templates/coverage (low UX ambiguity) -- Phase 6 (Config & Models): internal infrastructure for downstream CLIs \ No newline at end of file diff --git a/.dev/notes.md b/.dev/notes.md new file mode 100644 index 0000000..deff17c --- /dev/null +++ b/.dev/notes.md @@ -0,0 +1,81 @@ +## /gsd:discuss-phase — “vision decision” checklist + +These phases need your input because the plans had to assume UX/presentation details. + +### Discuss first (highest risk / most UX ambiguity) + +- **Phase 3 — Onboarding Wizard** (`/gsd:discuss-phase 3`) + - **Why**: user-facing interactive CLI + - **Decide**: question flow, presentation style, validation behavior, help text, branching vs linear + +- **Phase 8 — Rolling Tracker & Hedge Sizer** (`/gsd:discuss-phase 8`) + - **Why**: most complex phase (6 plans); CLI design heavy + - **Decide**: subcommand naming, output format (`status` / `suggest-roll` / `history`), how roll suggestions display + +- **Phase 10 — Template Engine** (`/gsd:discuss-phase 10`) + - **Why**: visual interactive product + - **Decide**: explorer layout, node appearance, interaction patterns (click / hover / drag), color scheme + +- **Phase 11 — Self-Assessment & Topics** (`/gsd:discuss-phase 11`) + - **Why**: interactive UX choices + - **Decide**: 4-state cycling behavior, guided vs standard vs YOLO differences, topic content depth + +- **Phase 12 — Maya / CLI / Landing** (`/gsd:discuss-phase 12`) + - Landing page design + CLI ergonomics (templates exist; builds on phases 10–11) +- **Phase 9 — SQQQ vs Puts** (`/gsd:discuss-phase 9`) + - Mostly presentation (math/formulas fixed by research) +- **Phase 7 — Total Return** (`/gsd:discuss-phase 7`) + - Mainly CLI output formatting (narrow scope; follows existing patterns) + +### Skip (infrastructure; minimal gray areas) + +- **Phase 1 — Git Scrub**: tool-dictated (git-filter-repo, gitleaks) +- **Phase 2 — Setup Automation**: scripts/automation, minimal UX +- **Phase 4 — Polish & Hooks**: mostly technical (SIGINT, Bun migration) +- **Phase 5 — Agent Readiness**: needs planning, but scope is lint/templates/coverage (low UX ambiguity) +- **Phase 6 — Config & Models**: internal infrastructure for downstream CLIs + +--- + +## PR #8 status + +**Status:** Ready to merge (pending checks) + +| Item | Status | +| --- | --- | +| Merge conflicts | Resolved — rebased clean branch with only Phase 11 commits | +| Mergeable | Yes | +| CodeRabbit | Re-reviewing (triggered by force push) | +| GitGuardian | In progress | +| Claude Review | Queued | + +### Codex GPT-5.2 review findings + +#### Good + +- All prior CodeRabbit issues addressed +- Plan docs are well-structured with consistent frontmatter and verification blocks +- Persistence architecture is sound (per-topic keying, version-aware, migration-aware) + +#### Issues to note (non-blocking; plan docs, not code) + +- **"7 knowledge values" but only 6 listed** (low) + - Detail: Plan says 7, but the enum lists 6: `know`, `fuzzy`, `unknown`, `familiar`, `confident`, `mastered`. Fix during execution. +- **"Yolo" vs "YOLO" casing mismatch** (low) + - Detail: Context says "Yolo", plans use "YOLO". Cosmetic — resolve at implementation. +- **Color mapping inconsistency** (low) + - Detail: Context says gray/blue/green/gold; plans hardcode red/amber/blue/green. Plans are more specific; context is aspirational. +- **`src/explorer/` doesn’t exist on branch** (expected) + - Detail: Phase 10 creates those files. Plans reference future paths correctly. +- **localStorage fallback is export-only** (noted) + - Detail: No import path — acceptable for v1; can add in a later phase. + +None of these block merge. They’re plan-level inconsistencies that get resolved during Phase 11 execution. + +### Learn (7/7) + +**Summary:** PR #8 merge conflicts resolved via branch rebase. Now mergeable, awaiting automated checks (CodeRabbit, GitGuardian, Claude Review). Codex review found no blocking issues. + +**Next:** Wait for checks to pass, then merge. You can resume Codex with `codex resume` for follow-up analysis. + +**Obi:** PR #8 is now mergeable. Checks are running — once they pass, it’s ready to merge. Codex found only cosmetic plan-doc inconsistencies, nothing blocking. \ No newline at end of file diff --git a/.planning/phases/02-setup-automation/02-02-SUMMARY.md b/.planning/phases/02-setup-automation/02-02-SUMMARY.md index 6c9cfc5..52f583c 100644 --- a/.planning/phases/02-setup-automation/02-02-SUMMARY.md +++ b/.planning/phases/02-setup-automation/02-02-SUMMARY.md @@ -77,7 +77,7 @@ Each task was committed atomically: 3. **Task 3: Update integration test for new setup.sh behavior** - `1ef6a53` (test) ## Files Created/Modified -- `setup.sh` - Added 8 new functions: is_step_complete, mark_step_complete, show_progress, create_dir, create_directory_structure, verify_directory_structure, scaffold_file, scaffold_config_files, install_python_deps, print_summary. Updated main flow with step-based execution. +- `setup.sh` - Added 10 new functions: is_step_complete, mark_step_complete, show_progress, create_dir, create_directory_structure, verify_directory_structure, scaffold_file, scaffold_config_files, install_python_deps, print_summary. Updated main flow with step-based execution. - `.gitignore` - Added .setup-progress exclusion in Family Office section - `tests/integration/test_setup_onboarding_integration.sh` - Complete rewrite: 46 assertions across 5 test groups, removed all old onboarding/symlink/MCP references diff --git a/.planning/phases/10-template-engine-dividend-topic-port/10-CONTEXT.md b/.planning/phases/10-template-engine-dividend-topic-port/10-CONTEXT.md new file mode 100644 index 0000000..5840c04 --- /dev/null +++ b/.planning/phases/10-template-engine-dividend-topic-port/10-CONTEXT.md @@ -0,0 +1,82 @@ +# Phase 10: Template Engine & Dividend Topic Port - Context + +**Gathered:** 2026-02-03 +**Status:** Ready for planning + + +## Phase Boundary + +Build a pipeline that converts topic JSON + HTML template into standalone interactive knowledge explorers using Cytoscape.js, with the dividend strategy topic as the first output. Feature parity with the existing prototype (`.dev/playgrounds/dividend-strategy-explorer.html`). Knowledge state persistence, learning modes, additional topics, and Maya integration are Phases 11-12. + + + + +## Implementation Decisions + +### Graph visualization engine +- Use Cytoscape.js library instead of the prototype's custom canvas renderer +- Force-directed layout (Cytoscape's `cose` or similar) to match the prototype's organic feel +- Dark theme as default with a light mode toggle (both themes) +- Node colors based on category (Foundation=blue, Strategy=purple, etc.) — knowledge state shown via badge or border indicator, not node fill color +- Template defines a global color palette for category slots; topics assign category names and the template maps them to colors by order + +### Explorer interaction model +- Clicking a node opens a bottom drawer detail panel (slides up from bottom, graph stays full width) +- Bottom drawer shows: concept description, related concepts, generated learning prompt, and a "Copy prompt" button +- Knowledge state cycling available via a button inside the detail panel (not by clicking the node directly) +- Preset category filter buttons that highlight/dim nodes by category group +- Text search box to find specific concepts by name (in addition to preset filters) +- Hovering a node highlights its connected edges and adjacent nodes (Cytoscape built-in) + +### Topic data structure +- Full topic envelope: JSON includes topic title, description, version, author, categories array, concepts array, edges array +- Each concept: id, name, category, description, difficulty (beginner/intermediate/advanced), prerequisites (array of concept IDs) +- Edges use typed relationships: each edge has {source, target, type, label?} where type is one of (enables, requires, protects, funds, etc.) — type determines edge styling (color, dash pattern) +- Zod schema validates the entire topic JSON structure at build time +- Category colors defined in the template as a global palette, not in topic JSON — topics name their categories, template assigns colors by order + +### Build pipeline output +- HTML + separate JS bundle per topic (not single-file inline) +- Cytoscape.js bundled into the JS output during build (works offline, no CDN dependency) +- Bun as the build tool (consistent with project's existing Bun usage) +- Strict Zod validation before build — malformed JSON fails with specific error messages, no broken HTML output +- Output directory: `fin-guru-private/explorers/` (private output, consistent with existing conventions) +- Build command produces: `{topic-slug}.html` + `{topic-slug}.js` per topic + +### Claude's Discretion +- Exact Cytoscape.js layout parameters and animation settings +- Bottom drawer animation and sizing details +- Edge type-to-style mapping (which colors/dashes for each relationship type) +- Search implementation details (fuzzy vs exact matching) +- Bun build script internals and file watching for dev mode +- Dark/light theme color palettes and toggle mechanism + + + + +## Specific Ideas + +- The existing prototype at `.dev/playgrounds/dividend-strategy-explorer.html` is the authoritative reference for feature parity — 21 concepts, 6 categories, 22 edges, same graph structure +- The prototype uses a GitHub-dark aesthetic — the dark theme should feel similar +- Generated learning prompts should be concept-specific and useful when pasted into an AI chatbot +- The detail panel should feel contextual but not obstructive — bottom drawer keeps the graph visible above it + + + + +## Deferred Ideas + +- Knowledge state persistence via localStorage — Phase 11 (EXPL-05) +- Learning mode selector (guided/standard/yolo) — Phase 11 (EXPL-06) +- Options-greeks and risk-management topic content — Phase 11 (EXPL-09a, EXPL-09b) +- Mobile/touch polish — Phase 11 (EXPL-08) +- Maya learner profile export — Phase 12 (EXPL-07) +- Topic selector landing page — Phase 12 (EXPL-10) +- CLI launcher (`fin_guru.py explore`) — Phase 12 (EXPL-12) + + + +--- + +*Phase: 10-template-engine-dividend-topic-port* +*Context gathered: 2026-02-03* diff --git a/.planning/phases/11-self-assessment-persistence-topics/11-01-PLAN.md b/.planning/phases/11-self-assessment-persistence-topics/11-01-PLAN.md index bc4568a..b5653be 100644 --- a/.planning/phases/11-self-assessment-persistence-topics/11-01-PLAN.md +++ b/.planning/phases/11-self-assessment-persistence-topics/11-01-PLAN.md @@ -12,42 +12,42 @@ autonomous: true must_haves: truths: - - "Options-greeks topic JSON passes Zod schema validation at build time" - - "Risk-management topic JSON passes Zod schema validation at build time" - - "Zod schema accepts all 7 knowledge values (3 legacy + 4 new) with default 'unknown'" - - "Each topic has exactly 20 concepts across 5 categories with exactly 22 edges" - - "Every node ID appears in at least one edge (no orphan nodes)" - - "Concept descriptions are practitioner-level with concrete numbers, formulas, or fund tickers" + - "Zod schema accepts all 7 knowledge values (know, fuzzy, unknown, familiar, confident, mastered) with default 'unknown'" + - "Options-greeks topic JSON validates against schema with 30 nodes, 37 edges, 7 categories, 6 presets, and a prompt template" + - "Risk-management topic JSON validates against schema with 27 nodes, 35 edges, 6 categories, 6 presets, and a prompt template" + - "Every node in both topic files appears in at least one edge (no orphan nodes)" + - "Existing dividend-strategy.json still validates against the updated schema (backward compatible)" artifacts: - path: "src/explorer/schemas/topic-schema.ts" - provides: "Updated Zod schema with backward-compatible knowledge enum" - contains: "familiar, confident, mastered" + provides: "Updated Zod schema accepting 4 new knowledge state values" + contains: "familiar" - path: "src/explorer/topics/options-greeks.json" - provides: "Options Greeks topic data for knowledge explorer" - contains: "delta, gamma, theta, vega, rho" + provides: "Options Greeks topic data with 30 concepts across 7 categories" + contains: "delta" - path: "src/explorer/topics/risk-management.json" - provides: "Risk Management topic data for knowledge explorer" - contains: "var, cvar, sharpe-ratio, position-sizing" + provides: "Risk Management topic data with 27 concepts across 6 categories" + contains: "sharpe-ratio" key_links: - - from: "src/explorer/topics/options-greeks.json" - to: "src/explorer/schemas/topic-schema.ts" - via: "Zod schema validation at build time" + - from: "src/explorer/schemas/topic-schema.ts" + to: "src/explorer/topics/options-greeks.json" + via: "Schema validates topic JSON at build time" pattern: "TopicSchema.safeParse" - - from: "src/explorer/topics/risk-management.json" - to: "src/explorer/schemas/topic-schema.ts" - via: "Zod schema validation at build time" + - from: "src/explorer/schemas/topic-schema.ts" + to: "src/explorer/topics/risk-management.json" + via: "Schema validates topic JSON at build time" pattern: "TopicSchema.safeParse" - from: "src/explorer/schemas/topic-schema.ts" to: "src/explorer/topics/dividend-strategy.json" - via: "Backward compatibility -- legacy 'fuzzy' default still validates" - pattern: "z.enum.*fuzzy" + via: "Updated schema must still validate existing topic data" + pattern: "TopicSchema.safeParse" --- -Create two new topic data JSON files (options-greeks and risk-management) and update the Zod schema to support the Phase 11 4-state knowledge model while preserving backward compatibility with Phase 10's 3-state model. +Update the Zod topic schema to support the 4-state knowledge model and create two new topic JSON data files (options-greeks, risk-management) with curated concept graphs. -Purpose: Deliver the content layer for EXPL-09a and EXPL-09b (curated financial concept graphs) AND the schema foundation that Plan 11-02's 4-state knowledge cycling requires. The schema update must happen here because the new topic JSONs use "unknown" as default and must pass validation. -Output: Two validated topic JSON files and an updated Zod schema that accepts both legacy and new knowledge values. +Purpose: The new topic data files are the content that Phase 11's enhanced explorer will render. The schema update enables the build pipeline to validate both legacy 3-state data and new 4-state data. Without this, the build pipeline will reject topic JSONs that use "familiar", "confident", or "mastered" knowledge values. + +Output: Updated topic-schema.ts with 7-value knowledge enum, options-greeks.json (30 nodes, 37 edges), risk-management.json (27 nodes, 35 edges), all validated against the schema. @@ -59,177 +59,480 @@ Output: Two validated topic JSON files and an updated Zod schema that accepts bo @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md +@.planning/phases/11-self-assessment-persistence-topics/11-CONTEXT.md @.planning/phases/11-self-assessment-persistence-topics/11-RESEARCH.md -@.planning/phases/10-template-engine-dividend-topic-port/10-RESEARCH.md - -Reference the Phase 10 Zod schema in `src/explorer/schemas/topic-schema.ts` and existing `dividend-strategy.json` as the format exemplar. - -Research provides: -- Complete curated concept graphs for both topics (20 nodes, 5 categories, 22 edges each) -- Schema migration strategy (Open Question 1): extend Zod enum to 7 values with `.default("unknown")` -- Pitfall 1: knowledge schema mismatch between Phase 10 and 11 -- must update schema before new JSONs validate -- Pitfall 7: disconnected concept graphs -- every node must have at least one edge +@src/explorer/schemas/topic-schema.ts +@src/explorer/topics/dividend-strategy.json - Task 1: Update Zod schema knowledge enum for 4-state model - src/explorer/schemas/topic-schema.ts + Task 1: Update Zod schema knowledge enum and create options-greeks.json + + src/explorer/schemas/topic-schema.ts + src/explorer/topics/options-greeks.json + -Read the Phase 10 Zod schema in `src/explorer/schemas/topic-schema.ts`. Find the knowledge enum field (expected: `z.enum(["know", "fuzzy", "unknown"])` or similar). - -Update it to accept ALL 7 values -- 3 legacy + 4 new: - -```typescript -z.enum(["know", "fuzzy", "unknown", "familiar", "confident", "mastered"]).default("unknown") -``` - -Why 7 values, not just 4: Backward compatibility. The existing `dividend-strategy.json` has nodes with `"knowledge": "fuzzy"` as default. If the schema only accepts the 4 new states, the dividend topic build breaks. By accepting all 7, both old and new topic data validates. - -Why `.default("unknown")`: New topics (options-greeks, risk-management) default to "unknown" which is the first state in the Phase 11 4-state cycle. Existing dividend topic keeps its "fuzzy" defaults explicitly set in its JSON. - -This is a one-line change. Do NOT modify any other part of the schema file. Do NOT rename fields, change types, or alter the schema structure. - -After editing, verify the existing dividend-strategy.json still conforms by checking that "fuzzy" is in the accepted enum values. + **Step 1: Update Zod schema** + + In `src/explorer/schemas/topic-schema.ts`, find the `NodeSchema` knowledge field and extend the enum to accept all 7 values: + + Change from: + ```typescript + knowledge: z.enum(["know", "fuzzy", "unknown"]).default("fuzzy") + ``` + + Change to: + ```typescript + knowledge: z.enum(["know", "fuzzy", "unknown", "familiar", "confident", "mastered"]).default("unknown") + ``` + + Note: The default changes from "fuzzy" to "unknown" because Phase 11's 4-state model starts at "unknown". The 3 legacy values (know, fuzzy, unknown) remain for backward compatibility with dividend-strategy.json. + + **Step 2: Verify backward compatibility** + + Run the existing dividend-strategy.json through the updated schema to confirm it still validates: + ```bash + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const data = await Bun.file('./src/explorer/topics/dividend-strategy.json').json(); + const r = TopicSchema.safeParse(data); + console.log(r.success ? 'BACKWARD COMPAT: OK' : 'BACKWARD COMPAT: FAIL'); + if (!r.success) console.error(r.error.format()); + " + ``` + + **Step 3: Create options-greeks.json** + + Create `src/explorer/topics/options-greeks.json` with the following structure. All data comes from the Phase 11 RESEARCH.md "Topic Data: Options Greeks (EXPL-09a)" section. + + **metadata:** + ```json + { + "title": "Options Greeks Explorer", + "slug": "options-greeks", + "version": "1.0.0", + "description": "Interactive knowledge graph for understanding options Greeks, pricing mechanics, and volatility effects" + } + ``` + + **categories (7):** + | ID | Label | Color | + |----|-------|-------| + | core-greeks | Core Greeks | #58a6ff | + | option-fundamentals | Option Fundamentals | #3fb950 | + | time-effects | Time Effects | #d29922 | + | volatility-effects | Volatility Effects | #f85149 | + | strategy-impact | Strategy Impact | #bc8cff | + | second-order-greeks | Second-Order Greeks | #f778ba | + | volatility-surface | Volatility Surface | #ff9f43 | + + **nodes (30):** The first 20 from RESEARCH.md, plus 10 additional nodes for comprehensive coverage. Each node: + - `id`: kebab-case identifier (e.g., "delta", "gamma", "option-premium") + - `label`: Display name with `\n` for multi-line (e.g., "Delta\n(Price Sensitivity)") + - `category`: Category ID from the table above + - `description`: Full description from RESEARCH.md + - `knowledge`: "unknown" (default for new topics) + - `tags`: Relevant tags for preset filtering. Assign tags based on the research's preset filters: + - Nodes in "core-greeks" category: tag `["core"]` + - Nodes in "time-effects" or "volatility-effects": tag `["vol-time"]` + - Nodes in "strategy-impact": tag `["strategy"]` + - Nodes in "option-fundamentals": tag `["fundamentals"]` + - Nodes in "second-order-greeks": tag `["second-order"]` + - Nodes in "volatility-surface": tag `["vol-surface"]` + + The 20 nodes from research (create ALL of these): + 1. delta (core-greeks) - "Measures how much option price changes per $1 move in underlying..." + 2. gamma (core-greeks) - "Rate of change of delta..." + 3. theta (core-greeks) - "Daily time decay..." + 4. vega (core-greeks) - "Sensitivity to implied volatility..." + 5. rho (core-greeks) - "Interest rate sensitivity..." + 6. option-premium (option-fundamentals) - "Total price paid for an option..." + 7. intrinsic-value (option-fundamentals) - "What the option is worth if exercised now..." + 8. extrinsic-value (option-fundamentals) - "Time value + volatility premium..." + 9. moneyness (option-fundamentals) - "ITM/ATM/OTM status..." + 10. implied-volatility (option-fundamentals) - "Market's forecast of future volatility..." + 11. time-decay-acceleration (time-effects) - "Theta increases exponentially as expiration approaches..." + 12. expiration-dynamics (time-effects) - "At expiration: extrinsic value goes to zero..." + 13. dte-impact (time-effects) - "Days to expiration affects ALL Greeks..." + 14. iv-crush (volatility-effects) - "Sharp drop in implied volatility after known events..." + 15. iv-skew (volatility-effects) - "OTM puts typically have higher IV than OTM calls..." + 16. vol-regime (volatility-effects) - "Low-vol vs high-vol environments..." + 17. delta-hedging (strategy-impact) - "Maintaining delta-neutral positions..." + 18. gamma-risk (strategy-impact) - "Short gamma positions can suffer catastrophic losses..." + 19. theta-harvesting (strategy-impact) - "Selling options to collect time decay..." + 20. vega-trading (strategy-impact) - "Trading volatility rather than direction..." + 21. charm (second-order-greeks) - "Rate of change of delta over time (dDelta/dTime). Also called delta decay. Shows how delta erodes as expiration approaches, critical for maintaining delta-hedged positions over multiple days." + 22. vanna (second-order-greeks) - "Cross-Greek measuring sensitivity of delta to implied volatility changes (dDelta/dVol). When IV rises, vanna tells you how much your delta shifts — essential for understanding how vol moves affect directional exposure." + 23. volga-vomma (second-order-greeks) - "Second derivative of option price with respect to volatility (d²Price/dVol²). Measures convexity of vega — how vega itself changes as IV moves. Important for volatility-of-volatility risk in exotic options and large vol moves." + 24. color (second-order-greeks) - "Rate of change of gamma over time (dGamma/dTime). Also called gamma decay. Shows how gamma exposure shifts as expiration approaches — critical for understanding how near-expiry gamma risk evolves day by day." + 25. volatility-smile (volatility-surface) - "The U-shaped pattern of implied volatility across strike prices at a single expiration. Deep OTM puts and calls tend to have higher IV than ATM options, reflecting market demand for tail-risk protection." + 26. term-structure (volatility-surface) - "The pattern of implied volatility across different expiration dates at the same strike. Normally upward-sloping (longer-dated options have higher IV), but inverts during crisis periods when near-term fear spikes." + 27. implied-vol-surface (volatility-surface) - "The complete 3D surface mapping implied volatility across both strike prices and expirations. Combines the smile and term structure into a unified view. Professional traders monitor surface dynamics for relative value opportunities." + 28. skew-dynamics (volatility-surface) - "How the volatility skew changes over time and with market moves. Skew steepens in selloffs (put IV rises faster) and flattens in rallies. Understanding skew dynamics is essential for spread pricing and risk management." + 29. pin-risk (strategy-impact) - "The risk that an underlying closes exactly at or near a short option's strike at expiration, creating uncertainty about assignment. Gamma explodes near the pin, making delta-hedging nearly impossible in the final hours." + 30. gamma-scalping (strategy-impact) - "Active trading strategy that exploits long gamma positions by delta-hedging at intervals. Buy options (long gamma), then sell underlying when it rises and buy when it falls. Profits from realized volatility exceeding implied volatility." + + Use the FULL descriptions from RESEARCH.md for nodes 1-20. Use the descriptions above for nodes 21-30. + + **edges (37):** The first 22 from RESEARCH.md, plus 15 new edges connecting the expanded nodes. Each edge has source, target, and label: + 1. delta -> gamma: "accelerated by" + 2. gamma -> delta-hedging: "necessitates" + 3. theta -> time-decay-acceleration: "manifests as" + 4. vega -> implied-volatility: "measures sensitivity to" + 5. option-premium -> intrinsic-value: "composed of" + 6. option-premium -> extrinsic-value: "composed of" + 7. moneyness -> delta: "determines" + 8. moneyness -> gamma: "peaks at ATM" + 9. implied-volatility -> iv-crush: "collapses in" + 10. implied-volatility -> iv-skew: "shapes" + 11. implied-volatility -> vol-regime: "defines environment" + 12. time-decay-acceleration -> expiration-dynamics: "culminates in" + 13. dte-impact -> theta: "scales" + 14. dte-impact -> vega: "scales" + 15. dte-impact -> gamma: "scales inversely" + 16. gamma-risk -> gamma: "drives" + 17. theta-harvesting -> theta: "exploits" + 18. vega-trading -> vega: "targets" + 19. delta-hedging -> gamma-risk: "mitigates" + 20. iv-crush -> vega-trading: "threatens" + 21. vol-regime -> theta-harvesting: "determines effectiveness of" + 22. rho -> dte-impact: "amplified by longer" + 23. charm -> delta: "time decay of" + 24. charm -> theta: "cross-derivative with" + 25. vanna -> delta: "vol sensitivity of" + 26. vanna -> vega: "cross-derivative with" + 27. volga-vomma -> vega: "convexity of" + 28. color -> gamma: "time decay of" + 29. volatility-smile -> implied-volatility: "shape of" + 30. term-structure -> implied-volatility: "temporal pattern of" + 31. implied-vol-surface -> volatility-smile: "extends" + 32. implied-vol-surface -> term-structure: "combines" + 33. skew-dynamics -> iv-skew: "evolution of" + 34. skew-dynamics -> vol-regime: "influenced by" + 35. pin-risk -> gamma-risk: "extreme case of" + 36. pin-risk -> expiration-dynamics: "peaks at" + 37. gamma-scalping -> delta-hedging: "refinement of" + + **presets (6):** + ```json + [ + { "id": "all", "label": "All", "filter": "all" }, + { "id": "core-greeks", "label": "Core Greeks", "filter": { "categories": ["core-greeks"] } }, + { "id": "second-order", "label": "Second-Order Greeks", "filter": { "categories": ["second-order-greeks"] } }, + { "id": "vol-surface", "label": "Volatility Surface", "filter": { "categories": ["volatility-surface", "volatility-effects"] } }, + { "id": "vol-time", "label": "Volatility & Time", "filter": { "categories": ["time-effects", "volatility-effects"] } }, + { "id": "strategy", "label": "Strategy Applications", "filter": { "categories": ["strategy-impact"] } } + ] + ``` + + **promptTemplate:** + ```json + { + "intro": "I'm studying options Greeks and their role in options pricing, hedging, and strategy construction.", + "knowPrefix": "I understand these concepts well:", + "fuzzyPrefix": "I'm somewhat familiar with but need clarity on:", + "unknownPrefix": "I need to learn these from scratch:", + "edgePrefix": "Key relationships to explore:", + "outro": "Explain with real-world examples from options trading. Reference specific strategies (covered calls, protective puts, iron condors, straddles) and show how Greeks change with market conditions. Include how Greeks apply to portfolio-level hedging and the SQQQ vs puts comparison. Use concrete numbers." + } + ``` + + **Step 4: Validate options-greeks.json** + ```bash + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const data = await Bun.file('./src/explorer/topics/options-greeks.json').json(); + const r = TopicSchema.safeParse(data); + if (!r.success) { console.error(r.error.format()); process.exit(1); } + const nodeIds = new Set(r.data.nodes.map(n => n.id)); + for (const e of r.data.edges) { + if (!nodeIds.has(e.source)) { console.error('Bad source:', e.source); process.exit(1); } + if (!nodeIds.has(e.target)) { console.error('Bad target:', e.target); process.exit(1); } + } + // Check no orphan nodes + const edgeNodeIds = new Set([...r.data.edges.map(e => e.source), ...r.data.edges.map(e => e.target)]); + const orphans = r.data.nodes.filter(n => !edgeNodeIds.has(n.id)); + if (orphans.length) { console.error('Orphan nodes:', orphans.map(n => n.id)); process.exit(1); } + console.log('options-greeks.json: OK -', r.data.nodes.length, 'nodes,', r.data.edges.length, 'edges,', r.data.categories.length, 'categories,', r.data.presets.length, 'presets'); + if (r.data.nodes.length < 30) { console.error('Expected 30+ nodes, got', r.data.nodes.length); process.exit(1); } + " + ``` -Read the updated schema file and confirm: -1. The knowledge enum includes all 7 values: "know", "fuzzy", "unknown", "familiar", "confident", "mastered" -2. The default is "unknown" -3. No other changes were made to the schema - -```bash -cd /Users/ossieirondi/Documents/Irondi-Household/family-office -grep -n "enum\|knowledge" src/explorer/schemas/topic-schema.ts -``` + ```bash + # Schema compiles + bun build src/explorer/schemas/topic-schema.ts --outdir /tmp/schema-check --target node 2>&1 | head -5 + + # Backward compatibility: dividend-strategy still validates + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const data = await Bun.file('./src/explorer/topics/dividend-strategy.json').json(); + const r = TopicSchema.safeParse(data); + console.log(r.success ? 'dividend-strategy: PASS' : 'dividend-strategy: FAIL'); + " + + # New topic validates with correct counts + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const data = await Bun.file('./src/explorer/topics/options-greeks.json').json(); + const r = TopicSchema.safeParse(data); + if (!r.success) { console.error(r.error.format()); process.exit(1); } + const nodeIds = new Set(r.data.nodes.map(n => n.id)); + const edgeNodeIds = new Set([...r.data.edges.map(e => e.source), ...r.data.edges.map(e => e.target)]); + const orphans = r.data.nodes.filter(n => !edgeNodeIds.has(n.id)); + if (orphans.length) { console.error('ORPHANS:', orphans.map(n => n.id)); process.exit(1); } + for (const e of r.data.edges) { + if (!nodeIds.has(e.source) || !nodeIds.has(e.target)) { console.error('BAD EDGE:', e); process.exit(1); } + } + if (r.data.nodes.length < 30) { console.error('Expected 30+ nodes, got', r.data.nodes.length); process.exit(1); } + console.log('options-greeks: OK -', r.data.nodes.length, 'nodes,', r.data.edges.length, 'edges'); + " + ``` + Must output: `dividend-strategy: PASS` and `options-greeks: OK - 30 nodes, 37 edges` - Zod schema accepts all 7 knowledge values with "unknown" as default. Existing dividend-strategy.json remains valid (its "fuzzy" defaults are in the accepted enum). + + Zod schema updated to accept 7 knowledge values. options-greeks.json created with 30 nodes (7 categories), 37 edges, 6 presets, prompt template. Includes core 5 Greeks, second-order Greeks (charm, vanna, volga/vomma, color), volatility surface concepts, and strategy applications. All nodes connected (no orphans). Schema validates both new and existing topic data. + - Task 2: Create options-greeks and risk-management topic JSONs - src/explorer/topics/options-greeks.json, src/explorer/topics/risk-management.json + Task 2: Create risk-management.json and cross-validate all topics + src/explorer/topics/risk-management.json -Create both topic JSON files following the Phase 10 TopicSchema format. Use the EXACT curated concept graphs from 11-RESEARCH.md. - -**For `src/explorer/topics/options-greeks.json`:** - -Read the existing `src/explorer/topics/dividend-strategy.json` as the format exemplar. Match its structure exactly. - -Structure: -- `metadata`: title "Options Greeks Explorer", slug "options-greeks", version "1.0.0", description about options Greeks concept map -- `categories`: 5 categories with IDs and colors from research: - - core-greeks (#58a6ff), option-fundamentals (#3fb950), time-effects (#d29922), volatility-effects (#f85149), strategy-impact (#bc8cff) -- `nodes`: All 20 nodes from research with fields: id (kebab-case), label (human-readable), category (category ID), description (practitioner-level, 2-3 sentences with concrete numbers/formulas), knowledge ("unknown"), tags (array of relevant keywords) - - Node IDs: delta, gamma, theta, vega, rho, option-premium, intrinsic-value, extrinsic-value, moneyness, implied-volatility, time-decay-acceleration, expiration-dynamics, dte-impact, iv-crush, iv-skew, vol-regime, delta-hedging, gamma-risk, theta-harvesting, vega-trading -- `edges`: All 22 edges from research with fields: source, target, label (relationship verb), type (optional) - - Edge list: delta->gamma, gamma->delta-hedging, theta->time-decay-acceleration, vega->implied-volatility, option-premium->intrinsic-value, option-premium->extrinsic-value, moneyness->delta, moneyness->gamma, implied-volatility->iv-crush, implied-volatility->iv-skew, implied-volatility->vol-regime, time-decay-acceleration->expiration-dynamics, dte-impact->theta, dte-impact->vega, dte-impact->gamma, gamma-risk->gamma, theta-harvesting->theta, vega-trading->vega, delta-hedging->gamma-risk, iv-crush->vega-trading, vol-regime->theta-harvesting, rho->dte-impact -- `presets`: 4 presets: "All" (all nodes), "Core Greeks" (core-greeks category), "Volatility & Time" (time-effects + volatility-effects), "Strategy Applications" (strategy-impact) -- `promptTemplate`: Options-domain-specific intro/prefixes/outro. Use verbs appropriate for the domain (e.g., "I need to understand how [concept] affects my options positions"). - -**For `src/explorer/topics/risk-management.json`:** - -Same format. Use the research's risk-management curated graph. - -- `metadata`: title "Risk Management Explorer", slug "risk-management", version "1.0.0" -- `categories`: 5 categories from research: - - risk-measurement (#f85149), performance-metrics (#58a6ff), position-management (#3fb950), portfolio-diversification (#d29922), risk-control-strategies (#bc8cff) -- `nodes`: All 20 nodes from research with IDs: var, cvar, volatility, beta, max-drawdown, sharpe-ratio, sortino-ratio, alpha, calmar-ratio, position-sizing, kelly-criterion, stop-loss, concentration-risk, diversification, correlation, asset-allocation, rebalancing, hedging, vol-regime-awareness, tail-risk -- `edges`: All 22 edges from research -- `presets`: 5 presets: "All", "Risk Measurement", "Performance & Metrics", "Position & Portfolio" (position-management + portfolio-diversification), "Risk Controls" (risk-control-strategies) -- `promptTemplate`: Risk-management-domain-specific intro/prefixes/outro - -Key quality rules: -1. Descriptions MUST be practitioner-level, not textbook definitions. Include real numbers ("SPY's worst: -56% in 2008"), formulas ("f* = (bp - q) / b"), and actionable context. Copy descriptions verbatim from research where provided. -2. ALL edge source/target must reference valid node IDs. Cross-validate before saving. -3. Do NOT invent concepts or edges beyond what the research provides. Use EXACTLY 20 nodes and 22 edges per topic. -4. All node knowledge defaults to "unknown" (uses the updated schema default). + **Step 1: Create risk-management.json** + + Create `src/explorer/topics/risk-management.json` with data from the Phase 11 RESEARCH.md "Topic Data: Risk Management (EXPL-09b)" section. + + **metadata:** + ```json + { + "title": "Risk Management Explorer", + "slug": "risk-management", + "version": "1.0.0", + "description": "Interactive knowledge graph for portfolio risk measurement, performance metrics, position management, and risk control strategies" + } + ``` + + **categories (6):** + | ID | Label | Color | + |----|-------|-------| + | risk-measurement | Risk Measurement | #f85149 | + | performance-metrics | Performance Metrics | #58a6ff | + | position-management | Position Management | #3fb950 | + | portfolio-diversification | Portfolio Diversification | #d29922 | + | risk-control-strategies | Risk Control Strategies | #bc8cff | + | advanced-risk | Advanced Risk Analytics | #f778ba | + + **nodes (27):** The first 20 from RESEARCH.md, plus 7 additional nodes for comprehensive coverage. Each node: + - `id`: kebab-case (e.g., "var", "cvar", "sharpe-ratio") + - `label`: Display name with `\n` for multi-line (e.g., "VaR\n(Value at Risk)") + - `category`: Category ID from table above + - `description`: Full description from RESEARCH.md + - `knowledge`: "unknown" (default) + - `tags`: Assign based on preset filters: + - risk-measurement: `["measurement"]` + - performance-metrics: `["metrics"]` + - position-management: `["position"]` + - portfolio-diversification: `["portfolio"]` + - risk-control-strategies: `["controls"]` + - advanced-risk: `["advanced"]` + + The 20 nodes from research (create ALL): + 1. var (risk-measurement) - "Value at Risk: maximum expected loss over a time period..." + 2. cvar (risk-measurement) - "Conditional VaR (Expected Shortfall)..." + 3. volatility (risk-measurement) - "Standard deviation of returns..." + 4. beta (risk-measurement) - "Systematic risk relative to market..." + 5. max-drawdown (risk-measurement) - "Largest peak-to-trough decline..." + 6. sharpe-ratio (performance-metrics) - "Risk-adjusted return: (return - risk-free rate) / volatility..." + 7. sortino-ratio (performance-metrics) - "Like Sharpe but only penalizes DOWNSIDE volatility..." + 8. alpha (performance-metrics) - "Excess return above benchmark after adjusting for risk..." + 9. calmar-ratio (performance-metrics) - "Annualized return divided by max drawdown..." + 10. position-sizing (position-management) - "Determining how much capital to allocate per position..." + 11. kelly-criterion (position-management) - "Mathematical formula for optimal bet size..." + 12. stop-loss (position-management) - "Exit rule to limit losses on a position..." + 13. concentration-risk (position-management) - "Over-exposure to a single position, sector, or factor..." + 14. diversification (portfolio-diversification) - "Spreading investments to reduce unsystematic risk..." + 15. correlation (portfolio-diversification) - "How assets move together..." + 16. asset-allocation (portfolio-diversification) - "Strategic portfolio mix across asset classes..." + 17. rebalancing (portfolio-diversification) - "Adjusting portfolio back to target weights..." + 18. hedging (risk-control-strategies) - "Using offsetting positions to reduce specific risks..." + 19. vol-regime-awareness (risk-control-strategies) - "Markets alternate between low-volatility and high-volatility regimes..." + 20. tail-risk (risk-control-strategies) - "Risk of extreme, rare events..." + 21. drawdown-duration (risk-measurement) - "Time spent in drawdown from peak to recovery. Measures not just HOW MUCH you lose, but HOW LONG recovery takes. A 20% drawdown lasting 3 years is psychologically and financially worse than one lasting 3 months. Critical for retirement planning and margin accounts." + 22. risk-adjusted-return (performance-metrics) - "Return per unit of risk taken, typically measured as CAGR divided by volatility or max drawdown. Goes beyond raw returns to answer: was the risk worth it? Enables fair comparison between conservative and aggressive strategies. Key context for Sharpe, Sortino, and Calmar ratios." + 23. margin-risk (position-management) - "Risk of forced liquidation due to margin calls when leveraged positions decline. Margin amplifies both gains and losses — a 50% decline on 2x margin wipes out the account. Must account for maintenance margin requirements, concentration limits, and broker-specific rules." + 24. stress-testing (advanced-risk) - "Simulating portfolio performance under extreme historical or hypothetical scenarios (2008 crash, COVID crash, rate shock, correlation breakdown). Goes beyond VaR by testing specific disaster scenarios rather than statistical distributions. Essential for understanding tail-risk exposure." + 25. factor-exposure (advanced-risk) - "Decomposing portfolio risk into underlying factors (market, size, value, momentum, quality, volatility). Reveals hidden concentrations — a portfolio of 20 tech stocks may appear diversified but have extreme factor concentration. Used in factor-based risk budgeting." + 26. regime-detection (advanced-risk) - "Identifying whether markets are in low-volatility (trending) or high-volatility (crisis) regimes. Different strategies perform differently across regimes — momentum works in trends, mean-reversion works in ranges. Regime-aware allocation adjusts position sizing and hedging dynamically." + 27. conditional-correlation (advanced-risk) - "How asset correlations change under stress conditions. In normal markets, stocks and bonds may have low correlation, but during a liquidity crisis correlations spike toward 1.0. Undermines naive diversification assumptions. Critical for building truly resilient portfolios." + + Use the FULL descriptions from RESEARCH.md for nodes 1-20. Use the descriptions above for nodes 21-27. + + **edges (35):** The first 22 from RESEARCH.md, plus 13 new edges connecting the expanded nodes: + 1. var -> cvar: "extended by" + 2. volatility -> var: "input to" + 3. volatility -> sharpe-ratio: "denominator of" + 4. beta -> volatility: "component of systematic" + 5. max-drawdown -> calmar-ratio: "denominator of" + 6. sharpe-ratio -> sortino-ratio: "refined by" + 7. alpha -> sharpe-ratio: "contextualized by" + 8. position-sizing -> concentration-risk: "prevents" + 9. kelly-criterion -> position-sizing: "optimizes" + 10. stop-loss -> max-drawdown: "limits" + 11. diversification -> correlation: "depends on low" + 12. correlation -> asset-allocation: "informs" + 13. asset-allocation -> rebalancing: "maintained by" + 14. hedging -> volatility: "reduces portfolio" + 15. hedging -> tail-risk: "protects against" + 16. vol-regime-awareness -> hedging: "triggers" + 17. vol-regime-awareness -> position-sizing: "adjusts" + 18. tail-risk -> cvar: "measured by" + 19. concentration-risk -> diversification: "solved by" + 20. rebalancing -> concentration-risk: "prevents drift toward" + 21. stop-loss -> position-sizing: "complements" + 22. beta -> alpha: "adjusted for in" + 23. drawdown-duration -> max-drawdown: "temporal dimension of" + 24. drawdown-duration -> calmar-ratio: "context for" + 25. risk-adjusted-return -> sharpe-ratio: "contextualized by" + 26. risk-adjusted-return -> alpha: "component of" + 27. margin-risk -> position-sizing: "constrains" + 28. margin-risk -> concentration-risk: "amplified by" + 29. stress-testing -> var: "validates" + 30. stress-testing -> tail-risk: "simulates" + 31. factor-exposure -> beta: "decomposes" + 32. factor-exposure -> diversification: "reveals hidden" + 33. regime-detection -> vol-regime-awareness: "quantifies" + 34. conditional-correlation -> correlation: "extends" + 35. conditional-correlation -> tail-risk: "reveals in" + + **presets (6):** + ```json + [ + { "id": "all", "label": "All", "filter": "all" }, + { "id": "risk-measurement", "label": "Risk Measurement", "filter": { "categories": ["risk-measurement"] } }, + { "id": "performance", "label": "Performance & Metrics", "filter": { "categories": ["performance-metrics"] } }, + { "id": "position-portfolio", "label": "Position & Portfolio", "filter": { "categories": ["position-management", "portfolio-diversification"] } }, + { "id": "risk-controls", "label": "Risk Controls", "filter": { "categories": ["risk-control-strategies"] } }, + { "id": "advanced-risk", "label": "Advanced Analytics", "filter": { "categories": ["advanced-risk"] } } + ] + ``` + + **promptTemplate:** + ```json + { + "intro": "I'm studying risk management concepts for portfolio construction, protection, and performance evaluation.", + "knowPrefix": "I'm confident with these risk concepts:", + "fuzzyPrefix": "I'm somewhat familiar with but need clarity on:", + "unknownPrefix": "I'm unfamiliar with these concepts:", + "edgePrefix": "Key risk relationships to understand:", + "outro": "Explain with practical portfolio examples using real scenarios (2008 crash, COVID crash, correlation breakdown). Show how these concepts connect to Finance Guru CLI tools (risk_metrics_cli, volatility_cli, correlation_cli). Include specific calculations where applicable. Reference common risk thresholds (Sharpe > 1.0, max position 5-10%, 95% VaR)." + } + ``` + + **Step 2: Cross-validate ALL topic files** + + Run validation across all 3 topic JSON files to confirm schema compliance, edge integrity, and no orphan nodes: + ```bash + for topic in dividend-strategy options-greeks risk-management; do + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const data = await Bun.file('./src/explorer/topics/${topic}.json').json(); + const r = TopicSchema.safeParse(data); + if (!r.success) { console.error('${topic}: FAIL', r.error.format()); process.exit(1); } + const nodeIds = new Set(r.data.nodes.map(n => n.id)); + const edgeNodeIds = new Set([...r.data.edges.map(e => e.source), ...r.data.edges.map(e => e.target)]); + const orphans = r.data.nodes.filter(n => !edgeNodeIds.has(n.id)); + if (orphans.length > 0) { console.error('${topic}: ORPHANS', orphans.map(n => n.id)); process.exit(1); } + for (const e of r.data.edges) { + if (!nodeIds.has(e.source) || !nodeIds.has(e.target)) { console.error('${topic}: BAD EDGE', e); process.exit(1); } + } + console.log('${topic}:', r.data.nodes.length, 'nodes,', r.data.edges.length, 'edges,', r.data.categories.length, 'categories - OK'); + " + done + ``` -Validate both JSONs are well-formed and structurally correct: - -```bash -cd /Users/ossieirondi/Documents/Irondi-Household/family-office - -python3 -c " -import json, sys - -for topic in ['options-greeks', 'risk-management']: - path = f'src/explorer/topics/{topic}.json' - with open(path) as f: - d = json.load(f) - - nodes = len(d['nodes']) - edges = len(d['edges']) - cats = len(d['categories']) - presets = len(d['presets']) - node_ids = {n['id'] for n in d['nodes']} - cat_ids = {c['id'] for c in d['categories']} - - # Check edge integrity - bad_edges = [e for e in d['edges'] if e['source'] not in node_ids or e['target'] not in node_ids] - - # Check all nodes reference valid categories - bad_cats = [n['id'] for n in d['nodes'] if n['category'] not in cat_ids] - - # Check orphan nodes (no edges) - edge_nodes = set() - for e in d['edges']: - edge_nodes.add(e['source']) - edge_nodes.add(e['target']) - orphans = node_ids - edge_nodes - - # Check knowledge defaults - bad_knowledge = [n['id'] for n in d['nodes'] if n.get('knowledge', 'unknown') not in ['unknown', 'fuzzy', 'know', 'familiar', 'confident', 'mastered']] - - print(f'=== {topic} ===') - print(f'Nodes: {nodes}, Edges: {edges}, Categories: {cats}, Presets: {presets}') - print(f'Bad edges (dangling refs): {len(bad_edges)}') - print(f'Bad categories: {bad_cats}') - print(f'Orphan nodes: {orphans}') - print(f'Bad knowledge values: {bad_knowledge}') - - errors = [] - if nodes != 20: errors.append(f'Expected 20 nodes, got {nodes}') - if edges != 22: errors.append(f'Expected 22 edges, got {edges}') - if cats != 5: errors.append(f'Expected 5 categories, got {cats}') - if bad_edges: errors.append(f'Dangling edges: {bad_edges}') - if bad_cats: errors.append(f'Invalid categories: {bad_cats}') - if orphans: errors.append(f'Orphan nodes: {orphans}') - if bad_knowledge: errors.append(f'Invalid knowledge: {bad_knowledge}') - - if errors: - for e in errors: - print(f' FAIL: {e}') - sys.exit(1) - else: - print(' All checks PASSED') -" -``` + ```bash + # Risk management validates with correct counts + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const data = await Bun.file('./src/explorer/topics/risk-management.json').json(); + const r = TopicSchema.safeParse(data); + if (!r.success) { console.error(r.error.format()); process.exit(1); } + const nodeIds = new Set(r.data.nodes.map(n => n.id)); + const edgeNodeIds = new Set([...r.data.edges.map(e => e.source), ...r.data.edges.map(e => e.target)]); + const orphans = r.data.nodes.filter(n => !edgeNodeIds.has(n.id)); + if (orphans.length) { console.error('ORPHANS:', orphans.map(n => n.id)); process.exit(1); } + for (const e of r.data.edges) { + if (!nodeIds.has(e.source) || !nodeIds.has(e.target)) { console.error('BAD EDGE:', e); process.exit(1); } + } + if (r.data.nodes.length < 27) { console.error('Expected 27+ nodes, got', r.data.nodes.length); process.exit(1); } + console.log('risk-management: OK -', r.data.nodes.length, 'nodes,', r.data.edges.length, 'edges'); + " + + # All 3 topics validate + PASS=0; FAIL=0 + for topic in dividend-strategy options-greeks risk-management; do + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const d = await Bun.file('./src/explorer/topics/${topic}.json').json(); + const r = TopicSchema.safeParse(d); + console.log('${topic}:', r.success ? 'PASS' : 'FAIL'); + if (!r.success) process.exit(1); + " && PASS=$((PASS+1)) || FAIL=$((FAIL+1)) + done + echo "Results: $PASS passed, $FAIL failed" + ``` + Must output: `risk-management: OK - 27 nodes, 35 edges` and `Results: 3 passed, 0 failed` - Both topic JSONs exist with 20 nodes, 22 edges, 5 categories each, zero dangling edge references, zero orphan nodes, valid knowledge defaults, and practitioner-level descriptions matching research. + + risk-management.json created with 27 nodes (6 categories), 35 edges, 6 presets, prompt template. Includes full risk framework with VaR, Sharpe, Beta, correlation, diversification, drawdown + advanced risk analytics (stress-testing, factor-exposure, regime-detection, conditional-correlation), margin risk, and drawdown duration. All 3 topic files (dividend-strategy, options-greeks, risk-management) validate against the updated schema. No orphan nodes. No dangling edge references. + -All three data files pass: -1. Zod schema accepts 7 knowledge values with "unknown" default -2. Both JSONs are valid JSON (python3 -m json.tool) -3. Both JSONs have exactly 20 nodes, 22 edges, 5 categories -4. Zero dangling edge references (all source/target IDs reference valid nodes) -5. Zero orphan nodes (every node appears in at least one edge) -6. All node categories reference valid category IDs -7. Descriptions are practitioner-level (concrete numbers, formulas, actionable context) -8. Existing dividend-strategy.json remains valid against updated schema +Complete plan verification: +```bash +# Schema compiles +bun build src/explorer/schemas/topic-schema.ts --outdir /tmp/verify-11-01 --target node 2>&1 + +# All 3 topics validate +for topic in dividend-strategy options-greeks risk-management; do + bun -e " + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const d = await Bun.file('./src/explorer/topics/${topic}.json').json(); + const r = TopicSchema.safeParse(d); + if (!r.success) { console.error('${topic}: FAIL'); process.exit(1); } + console.log('${topic}: PASS -', d.nodes.length, 'nodes'); + " +done + +# New knowledge values accepted by schema +bun -e " + import { z } from 'zod'; + import { TopicSchema } from './src/explorer/schemas/topic-schema'; + const testNode = { id: 'test', label: 'Test', category: 'cat', description: 'Desc', knowledge: 'mastered', tags: [] }; + // Check schema accepts 'mastered' + console.log('mastered accepted:', TopicSchema.shape ? 'schema loaded' : 'schema missing'); +" + +# File counts +echo "Topic files: $(ls src/explorer/topics/*.json | wc -l) (expect 3)" +``` -- Zod schema backward-compatible: existing dividend-strategy.json validates, new topics validate -- Both topic JSONs provide 40 curated financial concepts ready for explorer generation -- Content quality matches the dividend-strategy prototype's practitioner-level tone -- Schema change is minimal (one-line enum extension) with no side effects +1. Zod schema knowledge enum includes all 7 values: know, fuzzy, unknown, familiar, confident, mastered +2. Schema default changed from "fuzzy" to "unknown" +3. options-greeks.json: 30 nodes, 37 edges, 7 categories, 6 presets, prompt template -- validates +4. risk-management.json: 27 nodes, 35 edges, 6 categories, 6 presets, prompt template -- validates +5. dividend-strategy.json still validates (backward compatible) +6. Zero orphan nodes in any topic file +7. Zero dangling edge references in any topic file diff --git a/.planning/phases/11-self-assessment-persistence-topics/11-02-PLAN.md b/.planning/phases/11-self-assessment-persistence-topics/11-02-PLAN.md index 74acd8b..a55e59d 100644 --- a/.planning/phases/11-self-assessment-persistence-topics/11-02-PLAN.md +++ b/.planning/phases/11-self-assessment-persistence-topics/11-02-PLAN.md @@ -5,59 +5,56 @@ type: execute wave: 1 depends_on: [] files_modified: - - src/explorer/template/explorer.ts - - src/explorer/template/styles.css - src/explorer/template/template.html + - src/explorer/template/styles.css + - src/explorer/template/explorer.ts autonomous: true must_haves: truths: - - "Clicking a concept node cycles through 4 knowledge states: unknown -> familiar -> confident -> mastered -> unknown" - - "Knowledge states and learning mode persist in localStorage and survive page refresh" - - "Learning mode selector shows 3 options (guided/standard/yolo) and changes prompt complexity and vocabulary" + - "Clicking a node cycles through 4 knowledge states (unknown -> familiar -> confident -> mastered) with distinct border colors (red -> amber -> blue -> green)" + - "Knowledge state and learning mode persist in localStorage keyed by topic slug and survive page refresh" + - "Legacy 3-state values (know/fuzzy/unknown) from Phase 10 are migrated to 4-state equivalents on load" + - "Learning mode selector (Guided | Standard | YOLO) changes prompt complexity, vocabulary, and relationship count" - "Copy prompt button copies text to clipboard on Chrome, Safari, and Firefox including file:// protocol" - - "Canvas interactions work with touch (tap to cycle, drag nodes) and mouse equally" - - "Sidebar collapses to bottom drawer on screens narrower than 768px" - - "localStorage failure (private browsing, quota, file:// security) degrades silently without breaking the app" - - "Legacy 3-state localStorage data migrates to 4-state model: know->mastered, fuzzy->familiar, unknown->unknown" + - "Progress bar shows confident + mastered count as fraction and visual fill" + - "Mobile viewport has responsive sidebar collapse and touch-action: none on graph container" + - "JSON export fallback is available when localStorage is unavailable" artifacts: - path: "src/explorer/template/explorer.ts" - provides: "Enhanced explorer application with persistence, 4-state cycling, learning modes, clipboard, and JSON export" - contains: "saveState, loadState, KNOWLEDGE_STATES, LEARNING_MODES, copyPrompt, fallbackCopy, exportStateAsJSON, debouncedSave" - - path: "src/explorer/template/styles.css" - provides: "Enhanced styles with responsive layout, 4 knowledge state colors, mode selector, and touch-action" - contains: "@media (max-width: 768px), .mode-selector, touch-action: none" + provides: "Enhanced explorer application with persistence, 4-state cycling, learning modes, clipboard, mobile support" + min_lines: 350 - path: "src/explorer/template/template.html" - provides: "Updated HTML shell with learning mode selector, export button, and viewport meta" - contains: "mode-selector, export-btn, viewport" + provides: "Updated HTML template with mode selector, progress bar, export button, viewport meta" + contains: "mode-selector" + - path: "src/explorer/template/styles.css" + provides: "Updated styles with 4-state colors, responsive layout, mode selector, progress bar" + contains: "touch-action: none" key_links: - from: "src/explorer/template/explorer.ts" to: "localStorage" - via: "saveState/loadState with version-keyed storage per topic slug" - pattern: "localStorage\\.(get|set)Item.*fin-guru-explorer" - - from: "src/explorer/template/explorer.ts" - to: "navigator.clipboard" - via: "copyPrompt with mandatory textarea fallback for file:// protocol" - pattern: "navigator\\.clipboard\\.writeText" - - from: "src/explorer/template/explorer.ts" - to: "Cytoscape.js" - via: "cy.on('tap', 'node') for knowledge cycling, cy.batch() for bulk updates" - pattern: "cy\\.on.*tap.*node" + via: "Safe wrapper with version-keyed storage per topic slug" + pattern: "fin-guru-explorer" - from: "src/explorer/template/explorer.ts" - to: "TOPIC_DATA.promptTemplate" - via: "updatePrompt() reads promptTemplate.intro/outro for topic context; learning mode verbs replace knowPrefix/fuzzyPrefix/unknownPrefix; mode suffix appended after outro; maxRelationships overrides topic default" - pattern: "TOPIC_DATA\\.promptTemplate" - - from: "src/explorer/template/styles.css" to: "src/explorer/template/template.html" - via: "CSS classes for mode selector, knowledge badges, and responsive breakpoint" - pattern: "mode-selector|@media.*768" + via: "DOM element IDs for mode selector, progress bar, export button" + pattern: "getElementById" + - from: "src/explorer/template/template.html" + to: "src/explorer/template/styles.css" + via: "CSS classes for mode-btn, progress-bar, knowledge state colors" + pattern: "mode-btn" + - from: "src/explorer/template/explorer.ts" + to: "navigator.clipboard.writeText" + via: "Clipboard API with textarea fallback for file:// protocol" + pattern: "clipboard.writeText" --- -Enhance the Phase 10 template engine with localStorage persistence, 4-state knowledge cycling, learning mode selector, cross-browser clipboard copy, and responsive mobile layout. +Enhance the Phase 10 template engine with interactive self-assessment features: 4-state knowledge cycling with localStorage persistence, learning mode selector, cross-browser clipboard copy, progress visibility, mobile responsive support, and JSON export fallback. + +Purpose: This transforms the explorer from a single-session tool into a persistent learning tracker. Users can track their understanding of concepts across sessions, choose prompt complexity via learning modes, and use the explorer on mobile devices. These enhancements apply to ALL topics (dividend-strategy, options-greeks, risk-management) through the shared template engine. -Purpose: Deliver the core interaction loop for the knowledge explorer -- users can self-assess, persist progress, choose learning depth, copy prompts, and use the explorer on mobile devices. Covers EXPL-05 (persistence), EXPL-06 (learning modes), EXPL-08 (touch/mobile), EXPL-15 (clipboard), EXPL-16 (zero external deps). -Output: Enhanced template files that produce interactive explorers with persistence when built by the Phase 10 pipeline. +Output: Updated explorer.ts (major feature additions), template.html (new UI elements), and styles.css (responsive layout and state colors). @@ -69,306 +66,869 @@ Output: Enhanced template files that produce interactive explorers with persiste @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md +@.planning/phases/11-self-assessment-persistence-topics/11-CONTEXT.md @.planning/phases/11-self-assessment-persistence-topics/11-RESEARCH.md -@.planning/phases/10-template-engine-dividend-topic-port/10-RESEARCH.md - -Phase 11 research provides verified code patterns for ALL features. Use them directly: -- Pattern 1: Safe localStorage wrapper with version-keyed storage -- Pattern 2: 4-state knowledge cycling via Cytoscape data -- Pattern 3: Learning mode prompt generation (guided/standard/yolo) -- Pattern 4: Cross-browser clipboard with mandatory fallback -- Pattern 5: Responsive sidebar with mobile touch -- Pattern 6: Debounced save utility -- Pattern 7: JSON export fallback - -Key Phase 10 integration points: -- explorer.ts: Cytoscape.js app code with cy.on("tap", "node", ...), node.data("knowledge"), updateSidebar(), updatePrompt(), updateStats() -- template.html: DOM structure with #cy, #node-list, #prompt-text, #copy-btn, #stat-*, #preset-buttons -- styles.css: Dark theme CSS with CSS variables (--bg, --border, --accent) -- TOPIC_DATA: Global constant injected at build time with metadata.slug, nodes, edges, presets, promptTemplate - -Phase 10 research confirms: Cytoscape.js 3.33.x Canvas renderer, CoSE layout, data-driven styling via selectors like node[knowledge = "know"] - -Research Open Question 3 -- state migration: When loading localStorage data from Phase 10's 3-state model, map: "know" -> "mastered", "fuzzy" -> "familiar", "unknown" -> "unknown". Use STORAGE_VERSION to detect old formats. +@.planning/phases/10-template-engine-dividend-topic-port/10-02-PLAN.md +@src/explorer/template/explorer.ts +@src/explorer/template/template.html +@src/explorer/template/styles.css - Task 1: Add localStorage persistence, 4-state knowledge cycling, and state migration - src/explorer/template/explorer.ts, src/explorer/template/styles.css + Task 1: Update template.html and styles.css with Phase 11 UI elements + + src/explorer/template/template.html + src/explorer/template/styles.css + -Modify Phase 10 template files to add persistence and upgrade the knowledge model. All code patterns are from 11-RESEARCH.md -- use them directly, do not invent alternatives. - -**In explorer.ts, add these features IN ORDER:** - -1. **Constants and types** (add near top, after imports): - - `STORAGE_PREFIX = 'fin-guru-explorer'` and `STORAGE_VERSION = 'v1'` - - `KNOWLEDGE_STATES = ['unknown', 'familiar', 'confident', 'mastered'] as const` - - `KNOWLEDGE_COLORS` record mapping each state to ring/badge/text colors from research Pattern 2: unknown=#f85149, familiar=#d29922, confident=#58a6ff, mastered=#3fb950 - - `ExplorerState` interface: { version, topicId, knowledge: Record, learningMode, updatedAt } - - `currentLearningMode` variable initialized to `'standard'` - -2. **Safe localStorage wrapper** (from research Pattern 1): - - `storageKey(topicId)` returns `${STORAGE_PREFIX}.${STORAGE_VERSION}.${topicId}` - - `isStorageAvailable()` with try/catch test write/read/remove -- catches ALL failure modes - - `const storageAvailable = isStorageAvailable()` evaluated once at module load - - `saveState(topicId, knowledge, learningMode)` serializes to localStorage, returns boolean, never throws - - `loadState(topicId)` deserializes, checks version, returns ExplorerState | null, never throws - - ALL localStorage access in try/catch -- zero throws on any storage failure - -3. **State migration** in loadState (from research Open Question 3): - - After parsing JSON, check each knowledge value in the state - - Map legacy values: "know" -> "mastered", "fuzzy" -> "familiar" - - "unknown" stays "unknown" - - Any unrecognized value maps to "unknown" - - This handles users who used the Phase 10 explorer and have localStorage with old values - -4. **Debounced save** (from research Pattern 6): - - `debounce(fn, delay)` generic utility - - `debouncedSave = debounce(() => { collect all node knowledge, call saveState }, 500)` - -5. **4-state knowledge cycling** (from research Pattern 2): - - Replace Phase 10's existing 3-state cycle with the new 4-state array - - Find the existing `cy.on('tap', 'node', ...)` handler and update it: - - Get current knowledge from `node.data('knowledge')` - - Find index in KNOWLEDGE_STATES, advance by 1 (modulo length) - - Set via `node.data('knowledge', next)` -- Cytoscape auto-refreshes styles - - Call `debouncedSave()`, `updateSidebar()`, `updatePrompt()`, `updateStats()` - - Update Cytoscape stylesheet to use 4 data-driven selectors instead of 3: - - `node[knowledge = "unknown"]` -> border-color: #f85149 - - `node[knowledge = "familiar"]` -> border-color: #d29922 - - `node[knowledge = "confident"]` -> border-color: #58a6ff - - `node[knowledge = "mastered"]` -> border-color: #3fb950 - - Also add backward compatibility selectors for legacy data: - - `node[knowledge = "know"]` -> border-color: #3fb950 (same as mastered) - - `node[knowledge = "fuzzy"]` -> border-color: #d29922 (same as familiar) - -6. **Bulk state operations** (for preset buttons like "Set All Unknown"): - - Wrap bulk `node.data()` calls in `cy.batch(() => { ... })` to limit to 1 redraw - - Add "Set All" buttons for each knowledge state or an "All Unknown"/"All Mastered" pair - -7. **State restore on load:** - - After Cytoscape init + layout complete, call `loadState(TOPIC_DATA.metadata.slug)` - - If state exists: apply knowledge to nodes in `cy.batch()`, set learning mode - - If null: keep defaults (nodes start as whatever their JSON default is) - - CRITICAL: Render graph first, apply persisted state after (research Pitfall 6) - -8. **Stats display for 4 states:** - - Update `updateStats()` to count and display: N unknown, N familiar, N confident, N mastered - - Use the KNOWLEDGE_COLORS for stat badges - -9. **JSON export fallback** (from research Pattern 7, EXPL-05): - - `exportStateAsJSON()` function: collect knowledge map, create Blob, trigger download as `{slug}-progress.json` - - Uses Blob + URL.createObjectURL + anchor click pattern - - Always available as secondary action; more prominent when `!storageAvailable` - -**In styles.css, add:** - -10. **4 knowledge state CSS utility classes:** - - `.knowledge-unknown`, `.knowledge-familiar`, `.knowledge-confident`, `.knowledge-mastered` - - Each sets `--state-color` CSS variable to the corresponding color - - Badge styling for sidebar concept list items - -**CRITICAL: Do NOT break Phase 10 functionality.** This is additive. The graph, CoSE layout, sidebar, presets, tooltips, and basic prompt generation must all continue working. Do NOT remove any existing event handlers or DOM manipulation -- extend them. - -**Anti-patterns from research to AVOID:** -- Do NOT save on every pointer move -- only on knowledge change + debounce -- Do NOT block initial render on localStorage load -- Do NOT use topic-specific JavaScript -- all code works generically via TOPIC_DATA -- Do NOT add custom pointer event listeners on #cy -- Cytoscape handles its own events + **Step 1: Update template.html** + + Modify `src/explorer/template/template.html` with the following additions. Preserve ALL existing structure -- only ADD new elements and modify the stats section. + + 1. **Viewport meta tag** -- Ensure this exists in ``: + ```html + + ``` + + 2. **Learning mode selector** -- Add AFTER the stats div inside `.header-controls`: + ```html +
+ + + +
+ ``` + Note: Default active mode is "guided" in HTML; the explorer.ts init will read from localStorage and may switch to a different mode. + + 3. **Progress bar** -- Add BEFORE `.main` div (between header and main content): + ```html +
+
+
+
+ 0/0 concepts progressed + + +
+ ``` + + 4. **Update stats section** -- Replace the 3-state stats with 4-state: + ```html +
+ 0 unknown + 0 familiar + 0 confident + 0 mastered +
+ ``` + + 5. **Update sidebar instructions** -- Change the cycling description: + ```html + Click a node to cycle knowledge: Unknown → Familiar → Confident → Mastered. + Drag nodes to arrange. Use presets to focus. Choose a learning mode above. + The prompt below updates live based on your gaps and selected mode. + ``` + + 6. **Update action buttons** -- Replace 3-state set-all buttons with 4-state + Reset All: + ```html +
+ + + + + +
+ ``` + + 7. **Update prompt panel copy button** -- Add an id if it doesn't have one. Also add the `onclick` will be wired in explorer.ts. Keep existing structure. + + 8. **Last studied timestamp** -- Add below the prompt panel header: + ```html + + ``` + + **Step 2: Update styles.css** + + Add the following CSS rules to `src/explorer/template/styles.css`. Preserve ALL existing styles -- only ADD new rules. + + 1. **4-state knowledge badge colors** (update or add these): + ```css + .stat-unknown { color: #f85149; } + .stat-familiar { color: #d29922; } + .stat-confident { color: #58a6ff; } + .stat-mastered { color: #3fb950; } + + .unknown-bg { background: rgba(248,81,73,0.15); color: #f85149; } + .familiar-bg { background: rgba(210,153,34,0.15); color: #d29922; } + .confident-bg { background: rgba(88,166,255,0.15); color: #58a6ff; } + .mastered-bg { background: rgba(63,185,80,0.15); color: #3fb950; } + ``` + + 2. **Touch-action on Cytoscape container**: + ```css + #cy { + touch-action: none; + } + ``` + Add to the existing `#cy` rule (do not create a duplicate selector -- merge into the existing one). + + 3. **Learning mode selector**: + ```css + .mode-selector { + display: flex; + gap: 4px; + align-items: center; + } + .mode-btn { + background: var(--surface); + color: var(--text-secondary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 4px 12px; + font-size: 12px; + cursor: pointer; + transition: all 0.15s ease; + } + .mode-btn:hover { + border-color: var(--text-secondary); + } + .mode-btn.active { + background: var(--accent); + color: #fff; + border-color: var(--accent); + } + ``` + + 4. **Progress bar**: + ```css + .progress-container { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 16px; + border-bottom: 1px solid var(--border); + flex-wrap: wrap; + } + .progress-bar { + flex: 1; + min-width: 100px; + max-width: 300px; + height: 8px; + background: var(--surface); + border-radius: 4px; + overflow: hidden; + } + .progress-fill { + height: 100%; + background: linear-gradient(90deg, #58a6ff, #3fb950); + border-radius: 4px; + transition: width 0.3s ease; + width: 0%; + } + .progress-text { + font-size: 12px; + color: var(--text-secondary); + white-space: nowrap; + } + .progress-expand-btn { + background: none; + border: none; + color: var(--text-secondary); + cursor: pointer; + font-size: 10px; + padding: 2px 4px; + } + .progress-breakdown { + width: 100%; + display: flex; + gap: 8px; + padding-top: 4px; + } + .breakdown-item { + font-size: 11px; + padding: 2px 8px; + border-radius: 4px; + } + ``` + + 5. **Last studied timestamp**: + ```css + .last-studied { + font-size: 11px; + color: var(--text-secondary); + margin-left: auto; + } + ``` + + 6. **Responsive layout (mobile)**: + ```css + @media (max-width: 768px) { + .main { + flex-direction: column; + } + .sidebar { + width: 100%; + max-height: 40vh; + border-right: none; + border-bottom: 1px solid var(--border); + order: 1; + } + .graph-container { + order: 0; + min-height: 50vh; + } + .prompt-panel { + order: 2; + } + .header-controls { + flex-wrap: wrap; + gap: 4px; + } + .mode-selector { + width: 100%; + justify-content: center; + } + .progress-container { + flex-wrap: wrap; + } + .progress-bar { + min-width: 80px; + } + } + ```
-Read explorer.ts and confirm these functions/constants exist: -- `KNOWLEDGE_STATES` with 4 entries -- `KNOWLEDGE_COLORS` with 4 entries -- `isStorageAvailable`, `saveState`, `loadState` -- `debouncedSave` -- `exportStateAsJSON` -- State migration mapping in loadState ("know" -> "mastered", "fuzzy" -> "familiar") -- `cy.batch()` usage for bulk operations -- Cytoscape stylesheet has 4 knowledge selectors + 2 legacy selectors - -Build check (TypeScript compiles): -```bash -cd /Users/ossieirondi/Documents/Irondi-Household/family-office && bun build src/explorer/template/explorer.ts --outdir /tmp/build-check --format iife --target browser 2>&1 -``` + ```bash + # Verify new elements exist in template + grep -q "mode-selector" src/explorer/template/template.html && echo "MODE_SELECTOR: OK" || echo "MODE_SELECTOR: MISSING" + grep -q "progress-fill" src/explorer/template/template.html && echo "PROGRESS_BAR: OK" || echo "PROGRESS_BAR: MISSING" + grep -q "stat-unknown" src/explorer/template/template.html && echo "4_STATE_STATS: OK" || echo "4_STATE_STATS: MISSING" + grep -q "stat-mastered" src/explorer/template/template.html && echo "MASTERED_STAT: OK" || echo "MASTERED_STAT: MISSING" + grep -q "btn-reset-all" src/explorer/template/template.html && echo "RESET_ALL: OK" || echo "RESET_ALL: MISSING" + grep -q "btn-export" src/explorer/template/template.html && echo "EXPORT_BTN: OK" || echo "EXPORT_BTN: MISSING" + grep -q "last-studied" src/explorer/template/template.html && echo "LAST_STUDIED: OK" || echo "LAST_STUDIED: MISSING" + grep -q "viewport" src/explorer/template/template.html && echo "VIEWPORT: OK" || echo "VIEWPORT: MISSING" + + # Verify new CSS rules + grep -q "touch-action: none" src/explorer/template/styles.css && echo "TOUCH_ACTION: OK" || echo "TOUCH_ACTION: MISSING" + grep -q "mode-btn" src/explorer/template/styles.css && echo "MODE_CSS: OK" || echo "MODE_CSS: MISSING" + grep -q "progress-fill" src/explorer/template/styles.css && echo "PROGRESS_CSS: OK" || echo "PROGRESS_CSS: MISSING" + grep -q "max-width: 768px" src/explorer/template/styles.css && echo "RESPONSIVE: OK" || echo "RESPONSIVE: MISSING" + grep -q "stat-mastered" src/explorer/template/styles.css && echo "STATE_COLORS: OK" || echo "STATE_COLORS: MISSING" + + # Verify injection points still exist (not accidentally removed) + grep -q "__INJECT_CSS__" src/explorer/template/template.html && echo "INJECT_CSS: OK" || echo "INJECT_CSS: MISSING" + grep -q "__INJECT_TOPIC_DATA__" src/explorer/template/template.html && echo "INJECT_DATA: OK" || echo "INJECT_DATA: MISSING" + grep -q "__INJECT_APP_JS__" src/explorer/template/template.html && echo "INJECT_JS: OK" || echo "INJECT_JS: MISSING" + ``` + All checks must print OK. -- 4-state knowledge cycling works via Cytoscape tap events with auto-style refresh -- localStorage persistence saves/loads state with version-keyed keys per topic slug -- Legacy 3-state data migrates to 4-state (know->mastered, fuzzy->familiar, unknown->unknown) -- Storage failures degrade silently (app works without persistence) -- Debounced save prevents excessive writes (500ms) -- Bulk updates use cy.batch() for single-redraw performance -- JSON export fallback exists for environments without localStorage -- Stats display shows all 4 knowledge states with colors -- TypeScript compiles without errors + template.html updated with: viewport meta, mode selector (Guided/Standard/YOLO), progress bar with expandable breakdown, 4-state stats, updated action buttons (All Mastered, All Unknown, Reset All, Export JSON), last studied timestamp. styles.css updated with: 4-state colors, touch-action: none, mode selector styles, progress bar styles, responsive @media queries for mobile. All injection points preserved.
- Task 2: Add learning mode selector, clipboard copy, and mobile responsive layout - src/explorer/template/explorer.ts, src/explorer/template/styles.css, src/explorer/template/template.html + Task 2: Update explorer.ts with persistence, 4-state cycling, learning modes, and clipboard + src/explorer/template/explorer.ts -Add the remaining three features. Use research patterns directly. - -**In template.html:** - -1. **Learning mode selector** (place in header-controls, between presets and action buttons): - ```html -
- - - -
- ``` - -2. **Export button** (near the existing copy button): - ```html - - ``` - -3. **Viewport meta** (if not already present in head): - ```html - - ``` - -**In explorer.ts:** - -4. **LEARNING_MODES object** (from research Pattern 3): - - Three tiers: guided, standard, yolo - - Each has: label, description, promptStyle object - - Guided promptStyle: prefix "I'm a beginner learning about", unknownVerb "Please explain from scratch", familiarVerb "Please clarify with simple examples", confidentVerb "Just confirm my understanding of", suffix about analogies and step-by-step, maxRelationships 4 - - Standard promptStyle: prefix "I'm studying", unknownVerb "I need to learn", familiarVerb "I need deeper understanding of", confidentVerb "I want to verify my knowledge of", suffix about practical examples, maxRelationships 8 - - YOLO promptStyle: prefix "Advanced study of", unknownVerb "Cover comprehensively", familiarVerb "Deepen my understanding of", confidentVerb "Challenge my assumptions about", suffix about edge cases and mathematical foundations, maxRelationships 12 - -5. **setLearningMode(mode):** - - Updates `currentLearningMode` - - Toggles `.active` class on mode buttons - - Calls `debouncedSave()` to persist - - Calls `updatePrompt()` to regenerate with new mode - -6. **initModeSelector():** - - `document.querySelectorAll('.mode-btn').forEach(btn => btn.addEventListener('click', ...))` - - On click: read `data-mode`, call `setLearningMode(mode)` - - Called after DOMContentLoaded - -7. **Prompt generation integration with learning modes:** - - Modify the existing `updatePrompt()` function: - - Use the active learning mode's `promptStyle` for vocabulary - - For concepts in "unknown" state: use `unknownVerb` - - For concepts in "familiar" state: use `familiarVerb` (or Phase 10's fuzzyPrefix as fallback) - - For concepts in "confident" state: use `confidentVerb` - - For "mastered" concepts: exclude from the learning prompt entirely (user has confirmed mastery). The prompt focuses only on unknown/familiar/confident concepts that need learning. - - Use `maxRelationships` to limit how many edges are included in the prompt - - **Precedence rules for learning mode vs TOPIC_DATA.promptTemplate:** - 1. Learning mode vocabulary (`unknownVerb`, `familiarVerb`, `confidentVerb`) REPLACES `TOPIC_DATA.promptTemplate`'s `knowPrefix`/`fuzzyPrefix`/`unknownPrefix` - 2. `TOPIC_DATA.promptTemplate.intro` provides topic-specific opening context and is PRESERVED (prepended before concept list) - 3. Learning mode `suffix` is APPENDED after `promptTemplate.outro` (both are included, mode suffix comes last) - 4. `maxRelationships` from learning mode controls edge count in the prompt, overriding any topic default - - This is the KEY integration: learning mode vocabulary + topic-specific context = personalized learning prompt - -8. **copyPrompt() async function** (from research Pattern 4): - - CRITICAL: navigator.clipboard.writeText DOES NOT WORK on file:// protocol - - Try `navigator.clipboard.writeText(text)` first inside try/catch - - On failure, call `fallbackCopy(text)` - - `fallbackCopy(text)`: create hidden textarea, position fixed off-screen, select, execCommand('copy'), remove - - On success: update copy button text to "Copied!" for 1500ms - - IMPORTANT: Call copyPrompt DIRECTLY from click handler, not from nested async chain, to preserve user gesture context - - Wire: `document.getElementById('copy-btn')?.addEventListener('click', () => copyPrompt())` - -9. **Wire export button:** - - `document.getElementById('export-btn')?.addEventListener('click', exportStateAsJSON)` - -**In styles.css:** - -10. **Mode selector styles:** - ```css - .mode-selector { display: flex; gap: 2px; background: var(--surface, #21262d); border-radius: 6px; padding: 2px; } - .mode-btn { padding: 4px 10px; font-size: 12px; border: none; background: transparent; color: var(--text-muted, #8b949e); border-radius: 4px; cursor: pointer; transition: all 0.2s; } - .mode-btn:hover { color: var(--text, #c9d1d9); } - .mode-btn.active { background: var(--accent, #58a6ff); color: #000; font-weight: 600; } + Modify `src/explorer/template/explorer.ts` to add ALL Phase 11 features. This is the core logic task. Reference the RESEARCH.md architecture patterns for verified code examples. + + **IMPORTANT: Preserve all 14 Phase 10 features. Phase 11 ENHANCES -- it does not replace. The following changes are ADDITIVE except where explicitly noted as replacements (knowledge cycling, stats, prompt generation).** + + **Addition 1: Constants and types** + + Add near the top of the file: + ```typescript + // Phase 11: 4-state knowledge model + const KNOWLEDGE_STATES = ['unknown', 'familiar', 'confident', 'mastered'] as const; + type KnowledgeState = typeof KNOWLEDGE_STATES[number]; + + const KNOWLEDGE_COLORS: Record = { + unknown: { ring: '#f85149', badge: 'rgba(248,81,73,0.15)', text: '#f85149' }, + familiar: { ring: '#d29922', badge: 'rgba(210,153,34,0.15)', text: '#d29922' }, + confident: { ring: '#58a6ff', badge: 'rgba(88,166,255,0.15)', text: '#58a6ff' }, + mastered: { ring: '#3fb950', badge: 'rgba(63,185,80,0.15)', text: '#3fb950' }, + }; + + // Legacy state migration map (Phase 10 -> Phase 11) + const STATE_MIGRATION: Record = { + 'know': 'mastered', + 'fuzzy': 'familiar', + 'unknown': 'unknown', + }; + + // Learning modes + const LEARNING_MODES = { + guided: { + label: 'Guided', + promptStyle: { + prefix: "I'm a beginner learning about", + unknownVerb: "Please explain from scratch", + familiarVerb: "Please clarify with simple examples", + confidentVerb: "Just confirm my understanding of", + masteredVerb: "I've mastered", + suffix: "Use analogies, concrete numbers, and build up step-by-step. Define any technical terms before using them.", + maxRelationships: 4, + }, + }, + standard: { + label: 'Standard', + promptStyle: { + prefix: "I'm studying", + unknownVerb: "I need to learn", + familiarVerb: "I need deeper understanding of", + confidentVerb: "I want to verify my knowledge of", + masteredVerb: "I'm confident about", + suffix: "Explain with practical examples and show how concepts connect. Include relevant formulas where applicable.", + maxRelationships: 8, + }, + }, + yolo: { + label: 'YOLO', + promptStyle: { + prefix: "Advanced study of", + unknownVerb: "Cover comprehensively", + familiarVerb: "Deepen my understanding of", + confidentVerb: "Challenge my assumptions about", + masteredVerb: "Push beyond mastery of", + suffix: "Be technical and precise. Include edge cases, mathematical foundations, and real-world failure modes. Skip introductory context.", + maxRelationships: 12, + }, + }, + } as const; + + let currentLearningMode = 'standard'; + ``` + + **Addition 2: Safe localStorage wrapper (Pattern 1 from RESEARCH.md)** + + Add the complete localStorage wrapper: + ```typescript + const STORAGE_PREFIX = 'fin-guru-explorer'; + const STORAGE_VERSION = 'v1'; + + function storageKey(topicId: string): string { + return `${STORAGE_PREFIX}.${STORAGE_VERSION}.${topicId}`; + } + + function isStorageAvailable(): boolean { + const testKey = '__storage_test__'; + try { + localStorage.setItem(testKey, 'test'); + localStorage.removeItem(testKey); + return true; + } catch { + return false; + } + } + + const storageAvailable = isStorageAvailable(); + + interface ExplorerState { + version: string; + topicId: string; + knowledge: Record; + learningMode: string; + updatedAt: number; + } + + function saveState(topicId: string, knowledge: Record, learningMode: string): boolean { + if (!storageAvailable) return false; + try { + const data: ExplorerState = { version: STORAGE_VERSION, topicId, knowledge, learningMode, updatedAt: Date.now() }; + localStorage.setItem(storageKey(topicId), JSON.stringify(data)); + return true; + } catch { return false; } + } + + function loadState(topicId: string): ExplorerState | null { + if (!storageAvailable) return null; + try { + const raw = localStorage.getItem(storageKey(topicId)); + if (!raw) return null; + const data = JSON.parse(raw) as ExplorerState; + if (data.version !== STORAGE_VERSION) return null; + return data; + } catch { return null; } + } + ``` + + **Addition 3: Debounce utility (Pattern 6 from RESEARCH.md)** + + ```typescript + function debounce void>(fn: T, delay: number): T { + let timer: ReturnType; + return ((...args: any[]) => { + clearTimeout(timer); + timer = setTimeout(() => fn(...args), delay); + }) as T; + } + + const debouncedSave = debounce(() => { + const knowledge: Record = {}; + cy.nodes().forEach((node: any) => { + knowledge[node.id()] = node.data('knowledge'); + }); + saveState(TOPIC_DATA.metadata.slug, knowledge, currentLearningMode); + }, 500); + ``` + + **Replacement 4: Update Cytoscape style selectors for 4 knowledge states** + + In the Cytoscape initialization stylesheet array, REPLACE the 3-state knowledge border styles with 4-state: + ```typescript + { selector: 'node[knowledge = "unknown"]', style: { 'border-color': '#f85149', 'border-width': 2.5 } }, + { selector: 'node[knowledge = "familiar"]', style: { 'border-color': '#d29922', 'border-width': 2.5 } }, + { selector: 'node[knowledge = "confident"]', style: { 'border-color': '#58a6ff', 'border-width': 2.5 } }, + { selector: 'node[knowledge = "mastered"]', style: { 'border-color': '#3fb950', 'border-width': 2.5 } }, + ``` + Also keep the legacy selectors for the initial render before migration runs: + ```typescript + { selector: 'node[knowledge = "know"]', style: { 'border-color': '#3fb950', 'border-width': 2.5 } }, + { selector: 'node[knowledge = "fuzzy"]', style: { 'border-color': '#d29922', 'border-width': 2.5 } }, + ``` + + **Replacement 5: Update knowledge cycling from 3-state to 4-state** + + REPLACE the existing `cy.on('tap', 'node', ...)` handler with 4-state cycling: + ```typescript + cy.on('tap', 'node', (evt: any) => { + const node = evt.target; + const current = node.data('knowledge') as string; + const idx = KNOWLEDGE_STATES.indexOf(current as any); + // If current state is legacy or unknown, start at 'unknown' (index 0) + const currentIdx = idx >= 0 ? idx : 0; + const next = KNOWLEDGE_STATES[(currentIdx + 1) % KNOWLEDGE_STATES.length]; + node.data('knowledge', next); + debouncedSave(); + updateSidebar(); + updatePrompt(); + updateStats(); + updateProgress(); + }); ``` -11. **Responsive sidebar** (from research Pattern 5): - ```css - #cy { touch-action: none; } - - @media (max-width: 768px) { - .main { flex-direction: column; } - .sidebar { width: 100%; max-height: 40vh; border-right: none; border-bottom: 1px solid var(--border); order: 1; } - .graph-container { order: 0; min-height: 50vh; } - .prompt-panel { order: 2; } - .header-controls { flex-wrap: wrap; gap: 4px; } - .mode-selector { width: 100%; justify-content: center; } + **Replacement 6: Update updateStats() for 4 states** + + REPLACE the existing `updateStats()` with: + ```typescript + function updateStats(): void { + const visible = cy.nodes(':visible'); + const counts = { unknown: 0, familiar: 0, confident: 0, mastered: 0 }; + visible.forEach((node: any) => { + const k = node.data('knowledge') as string; + if (k in counts) counts[k as keyof typeof counts]++; + }); + const el = (id: string) => document.getElementById(id); + const setCount = (id: string, count: number) => { + const e = el(id); + if (e) e.querySelector('strong')!.textContent = String(count); + }; + setCount('stat-unknown', counts.unknown); + setCount('stat-familiar', counts.familiar); + setCount('stat-confident', counts.confident); + setCount('stat-mastered', counts.mastered); } ``` -**Anti-patterns from research to AVOID:** -- Do NOT mix touch + pointer event listeners on same element -- Do NOT call clipboard.writeText from inside a promise chain -- call directly in click handler -- Do NOT add custom pointerdown/pointermove on #cy -- Cytoscape manages its own input -- Do NOT reference specific topic concept IDs -- everything works generically via TOPIC_DATA + **Addition 7: updateProgress() function** + + ```typescript + function updateProgress(): void { + const allNodes = cy.nodes(':visible'); + const total = allNodes.length; + let progressed = 0; + allNodes.forEach((node: any) => { + const k = node.data('knowledge'); + if (k === 'confident' || k === 'mastered') progressed++; + }); + const pct = total > 0 ? (progressed / total) * 100 : 0; + const fill = document.getElementById('progress-fill'); + const text = document.getElementById('progress-text'); + if (fill) fill.style.width = `${pct}%`; + if (text) text.textContent = `${progressed}/${total} concepts progressed`; + + // Update breakdown counts + const counts = { unknown: 0, familiar: 0, confident: 0, mastered: 0 }; + allNodes.forEach((node: any) => { + const k = node.data('knowledge') as string; + if (k in counts) counts[k as keyof typeof counts]++; + }); + ['unknown', 'familiar', 'confident', 'mastered'].forEach(state => { + const el = document.getElementById(`breakdown-${state}`); + if (el) el.textContent = String(counts[state as keyof typeof counts]); + }); + } + ``` + + **Addition 8: Progress breakdown expand/collapse** + + In the init function, wire the expand button: + ```typescript + const expandBtn = document.getElementById('progress-expand-btn'); + const breakdown = document.getElementById('progress-breakdown'); + if (expandBtn && breakdown) { + expandBtn.addEventListener('click', () => { + const visible = breakdown.style.display !== 'none'; + breakdown.style.display = visible ? 'none' : 'flex'; + expandBtn.innerHTML = visible ? '▼' : '▲'; + }); + } + ``` + + **Addition 9: Learning mode selector initialization and wiring** + + ```typescript + function initModeSelector(): void { + document.querySelectorAll('.mode-btn').forEach(btn => { + btn.addEventListener('click', () => { + const mode = (btn as HTMLElement).dataset.mode; + if (mode && mode in LEARNING_MODES) { + setLearningMode(mode); + } + }); + }); + } + + function setLearningMode(mode: string): void { + currentLearningMode = mode; + document.querySelectorAll('.mode-btn').forEach(btn => { + btn.classList.toggle('active', (btn as HTMLElement).dataset.mode === mode); + }); + debouncedSave(); + updatePrompt(); + } + ``` + + **Replacement 10: Update updatePrompt() for learning modes and 4 states** + + REPLACE the existing `updatePrompt()` with mode-aware prompt generation: + ```typescript + function updatePrompt(): void { + const promptEl = document.getElementById('prompt-text'); + if (!promptEl) return; + + const mode = LEARNING_MODES[currentLearningMode as keyof typeof LEARNING_MODES] || LEARNING_MODES.standard; + const style = mode.promptStyle; + const visible = cy.nodes(':visible'); + + const groups: Record = { unknown: [], familiar: [], confident: [], mastered: [] }; + visible.forEach((node: any) => { + const k = node.data('knowledge') as string; + const label = node.data('label').replace(/\n/g, ' '); + if (k in groups) groups[k].push(label); + else groups['unknown'].push(label); // Legacy fallback + }); + + // Use topic intro if available + const intro = TOPIC_DATA.promptTemplate?.intro || `${style.prefix} this topic.`; + + const sections: string[] = [intro, '']; + + if (groups.mastered.length > 0) { + sections.push(`${style.masteredVerb}: ${groups.mastered.join(', ')}`); + } + if (groups.confident.length > 0) { + sections.push(`${style.confidentVerb}: ${groups.confident.join(', ')}`); + } + if (groups.familiar.length > 0) { + sections.push(`${style.familiarVerb}: ${groups.familiar.join(', ')}`); + } + if (groups.unknown.length > 0) { + sections.push(`${style.unknownVerb}: ${groups.unknown.join(', ')}`); + } + + // Add relevant edges (limited by mode) + const relevantEdges: string[] = []; + cy.edges(':visible').forEach((edge: any) => { + const srcK = edge.source().data('knowledge'); + const tgtK = edge.target().data('knowledge'); + if (srcK !== 'mastered' || tgtK !== 'mastered') { + const srcLabel = edge.source().data('label').replace(/\n/g, ' '); + const tgtLabel = edge.target().data('label').replace(/\n/g, ' '); + relevantEdges.push(`${srcLabel} ${edge.data('label')} ${tgtLabel}`); + } + }); + + if (relevantEdges.length > 0) { + const edgePrefix = TOPIC_DATA.promptTemplate?.edgePrefix || 'Key relationships:'; + sections.push(''); + sections.push(edgePrefix); + relevantEdges.slice(0, style.maxRelationships).forEach(e => { + sections.push(`- ${e}`); + }); + } + + // Use topic outro + mode suffix + const outro = TOPIC_DATA.promptTemplate?.outro || ''; + sections.push(''); + sections.push(style.suffix); + if (outro) sections.push(outro); + + promptEl.textContent = sections.join('\n'); + } + ``` + + **Replacement 11: Update clipboard copy with fallback (Pattern 4 from RESEARCH.md)** + + REPLACE the existing copy handler with the dual clipboard approach: + ```typescript + function fallbackCopy(text: string): boolean { + const textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.select(); + textarea.setSelectionRange(0, 99999); + let success = false; + try { success = document.execCommand('copy'); } catch { success = false; } + document.body.removeChild(textarea); + return success; + } + + async function copyPrompt(): Promise { + const text = document.getElementById('prompt-text')?.textContent || ''; + const btn = document.getElementById('copy-btn'); + if (!btn) return; + let copied = false; + try { + if (navigator.clipboard && navigator.clipboard.writeText) { + await navigator.clipboard.writeText(text); + copied = true; + } + } catch {} + if (!copied) { copied = fallbackCopy(text); } + if (copied) { + btn.textContent = 'Copied!'; + setTimeout(() => { btn.textContent = 'Copy'; }, 1500); + } + } + ``` + Wire the copy button: `document.getElementById('copy-btn')?.addEventListener('click', () => copyPrompt());` + IMPORTANT: Call `copyPrompt()` directly from the click handler (not wrapped in additional async chains) to preserve user gesture context for Clipboard API permission. + + **Addition 12: JSON export fallback (Pattern 7 from RESEARCH.md)** + + ```typescript + function exportStateAsJSON(): void { + const knowledge: Record = {}; + cy.nodes().forEach((node: any) => { + knowledge[node.id()] = node.data('knowledge'); + }); + const state = { + topicId: TOPIC_DATA.metadata.slug, + knowledge, + learningMode: currentLearningMode, + exportedAt: new Date().toISOString(), + }; + const blob = new Blob([JSON.stringify(state, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${TOPIC_DATA.metadata.slug}-progress.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } + ``` + Wire: `document.getElementById('btn-export')?.addEventListener('click', exportStateAsJSON);` + + **Addition 13: State migration + restore on init** + + Add a `migrateKnowledgeState()` function and call it after Cytoscape init: + ```typescript + function migrateKnowledgeState(value: string): string { + if (KNOWLEDGE_STATES.includes(value as any)) return value; + return STATE_MIGRATION[value] || 'unknown'; + } + + function restoreState(): void { + // First: migrate initial TOPIC_DATA knowledge values (Phase 10 legacy) + cy.batch(() => { + cy.nodes().forEach((node: any) => { + const current = node.data('knowledge'); + const migrated = migrateKnowledgeState(current); + if (migrated !== current) { + node.data('knowledge', migrated); + } + }); + }); + + // Second: apply persisted localStorage state (overrides JSON defaults) + const state = loadState(TOPIC_DATA.metadata.slug); + if (state) { + cy.batch(() => { + for (const [nodeId, knowledge] of Object.entries(state.knowledge)) { + const node = cy.getElementById(nodeId); + if (node.length > 0) { + const migrated = migrateKnowledgeState(knowledge); + node.data('knowledge', migrated); + } + } + }); + + if (state.learningMode && state.learningMode in LEARNING_MODES) { + setLearningMode(state.learningMode); + } + + // Update last studied timestamp + if (state.updatedAt) { + updateLastStudied(state.updatedAt); + } + } + + updateSidebar(); + updatePrompt(); + updateStats(); + updateProgress(); + } + ``` + + **Addition 14: Last studied timestamp** + + ```typescript + function updateLastStudied(timestamp: number): void { + const el = document.getElementById('last-studied'); + if (!el) return; + const diff = Date.now() - timestamp; + const minutes = Math.floor(diff / 60000); + const hours = Math.floor(diff / 3600000); + const days = Math.floor(diff / 86400000); + let text = ''; + if (days > 0) text = `Last studied: ${days} day${days > 1 ? 's' : ''} ago`; + else if (hours > 0) text = `Last studied: ${hours} hour${hours > 1 ? 's' : ''} ago`; + else if (minutes > 0) text = `Last studied: ${minutes} min ago`; + else text = 'Last studied: just now'; + el.textContent = text; + } + ``` + + **Replacement 15: Update action button handlers** + + Replace the old set-all and reset handlers: + ```typescript + document.getElementById('btn-all-mastered')?.addEventListener('click', () => { + cy.batch(() => { cy.nodes(':visible').forEach((n: any) => n.data('knowledge', 'mastered')); }); + debouncedSave(); updateSidebar(); updatePrompt(); updateStats(); updateProgress(); + }); + + document.getElementById('btn-all-unknown')?.addEventListener('click', () => { + cy.batch(() => { cy.nodes(':visible').forEach((n: any) => n.data('knowledge', 'unknown')); }); + debouncedSave(); updateSidebar(); updatePrompt(); updateStats(); updateProgress(); + }); + + document.getElementById('btn-reset-all')?.addEventListener('click', () => { + cy.batch(() => { cy.nodes().forEach((n: any) => n.data('knowledge', 'unknown')); }); + // Clear localStorage for this topic + if (storageAvailable) { + try { localStorage.removeItem(storageKey(TOPIC_DATA.metadata.slug)); } catch {} + } + // Reset learning mode to standard + setLearningMode('standard'); + // Show all nodes, re-run layout + cy.nodes().show(); + cy.edges().show(); + cy.layout({ name: 'cose', animate: false, fit: true, padding: 40, nodeDimensionsIncludeLabels: true, randomize: true, idealEdgeLength: () => 120, nodeRepulsion: () => 8000, gravity: 0.25, numIter: 2000 }).run(); + updateSidebar(); updatePrompt(); updateStats(); updateProgress(); + }); + ``` + + **Addition 16: Update sidebar node list for 4-state knowledge badges** + + Update the `updateSidebar()` function so knowledge badges reflect the 4-state model. The badge text and color should use `KNOWLEDGE_COLORS` for the current state. Clicking the badge should cycle through the 4 states (same as tapping the node). + + **Modification 17: Update initialization flow** + + Update the `DOMContentLoaded` handler to include new initialization steps. The sequence MUST be: + 1. Build category color map from TOPIC_DATA + 2. Transform TOPIC_DATA nodes/edges to Cytoscape elements + 3. Initialize Cytoscape with container, elements, style (including 4-state selectors), layout + 4. Render category legend + 5. Render preset buttons + 6. Render sidebar node list + 7. Bind event handlers (node tap, mouseover, mouseout, button clicks) + 8. **Initialize mode selector** (NEW) + 9. **Restore persisted state** (NEW -- MUST be after Cytoscape init completes) + 10. Update stats, prompt, and **progress** (includes new progress bar) + + CRITICAL: `restoreState()` MUST be called AFTER Cytoscape initialization and initial layout. This prevents the state-before-ready pitfall (RESEARCH.md Pitfall 6).
-Read explorer.ts and confirm: -- `LEARNING_MODES` object with guided/standard/yolo tiers -- `setLearningMode` function toggles active class and regenerates prompt -- `copyPrompt` async function with try/catch around clipboard API -- `fallbackCopy` function using textarea approach -- `updatePrompt()` integrates learning mode vocabulary + maxRelationships -- `updatePrompt()` excludes mastered concepts from prompt output -- `updatePrompt()` preserves TOPIC_DATA.promptTemplate.intro, replaces knowPrefix/fuzzyPrefix/unknownPrefix with mode verbs, appends mode suffix after promptTemplate.outro -- Mode button + export button click handlers wired - -Read template.html and confirm: -- Mode selector div with 3 buttons exists -- Export button exists -- Viewport meta tag present - -Read styles.css and confirm: -- `.mode-selector` and `.mode-btn` styles -- `#cy { touch-action: none; }` -- `@media (max-width: 768px)` responsive rules - -Build check: -```bash -cd /Users/ossieirondi/Documents/Irondi-Household/family-office && bun build src/explorer/template/explorer.ts --outdir /tmp/build-check-2 --format iife --target browser 2>&1 -``` + ```bash + # File exists and has substantial content + LINES=$(wc -l < src/explorer/template/explorer.ts) + test "$LINES" -ge 350 && echo "LINES: OK ($LINES)" || echo "LINES: INSUFFICIENT ($LINES, need 350+)" + + # Compiles as IIFE + bun build src/explorer/template/explorer.ts --format iife --target browser --outdir /tmp/explorer-check-11 2>&1 && echo "COMPILE: OK" || echo "COMPILE: FAIL" + + # Key patterns present + grep -q "KNOWLEDGE_STATES" src/explorer/template/explorer.ts && echo "4_STATE: OK" || echo "4_STATE: MISSING" + grep -q "localStorage" src/explorer/template/explorer.ts && echo "PERSISTENCE: OK" || echo "PERSISTENCE: MISSING" + grep -q "LEARNING_MODES" src/explorer/template/explorer.ts && echo "MODES: OK" || echo "MODES: MISSING" + grep -q "clipboard" src/explorer/template/explorer.ts && echo "CLIPBOARD: OK" || echo "CLIPBOARD: MISSING" + grep -q "fallbackCopy" src/explorer/template/explorer.ts && echo "FALLBACK: OK" || echo "FALLBACK: MISSING" + grep -q "migrateKnowledgeState" src/explorer/template/explorer.ts && echo "MIGRATION: OK" || echo "MIGRATION: MISSING" + grep -q "debouncedSave" src/explorer/template/explorer.ts && echo "DEBOUNCE: OK" || echo "DEBOUNCE: MISSING" + grep -q "exportStateAsJSON" src/explorer/template/explorer.ts && echo "EXPORT: OK" || echo "EXPORT: MISSING" + grep -q "updateProgress" src/explorer/template/explorer.ts && echo "PROGRESS: OK" || echo "PROGRESS: MISSING" + grep -q "fin-guru-explorer" src/explorer/template/explorer.ts && echo "STORAGE_KEY: OK" || echo "STORAGE_KEY: MISSING" + grep -q "restoreState" src/explorer/template/explorer.ts && echo "RESTORE: OK" || echo "RESTORE: MISSING" + grep -q "TOPIC_DATA" src/explorer/template/explorer.ts && echo "TOPIC_DATA: OK" || echo "TOPIC_DATA: MISSING" + ``` + All checks must print OK. COMPILE: FAIL means TypeScript errors that must be fixed. -- Learning mode selector with 3 tiers renders in header and persists selection -- Prompt generation uses mode vocabulary (unknownVerb, familiarVerb, confidentVerb), respects maxRelationships, and excludes mastered concepts entirely -- Prompt precedence: mode verbs replace topic prefixes, topic intro preserved, mode suffix appended after topic outro -- Copy button uses navigator.clipboard.writeText with mandatory textarea fallback for file:// -- Export button triggers JSON download of progress -- Responsive sidebar collapses to bottom on mobile (< 768px) -- Cytoscape container has touch-action: none for gesture conflict prevention -- All features work generically with any topic JSON (no topic-specific code) -- TypeScript compiles without errors + explorer.ts enhanced with all Phase 11 features: 4-state knowledge cycling (unknown/familiar/confident/mastered), safe localStorage persistence with version-keyed storage, legacy state migration, learning mode selector (guided/standard/yolo) with per-topic persistence, mode-aware prompt generation, cross-browser clipboard copy with file:// fallback, progress bar with expandable breakdown, JSON export fallback, debounced save, last studied timestamp. All Phase 10 features preserved. Compiles as IIFE without errors.
-After both tasks: -1. TypeScript compiles: `bun build src/explorer/template/explorer.ts --format iife --target browser` succeeds -2. No topic-specific references: `grep -r "delta\|sharpe\|dividend" src/explorer/template/` returns zero matches (all topic references are in topic JSONs, not template code) -3. All 7 research patterns implemented: localStorage wrapper, 4-state cycling, learning modes, clipboard copy+fallback, responsive sidebar, debounced save, JSON export -4. State migration handles Phase 10 legacy data: know->mastered, fuzzy->familiar -5. Requirements covered: EXPL-05 (localStorage + JSON export fallback), EXPL-06 (learning modes), EXPL-08 (touch + responsive), EXPL-15 (clipboard + fallback), EXPL-16 (zero external runtime deps) +Complete plan verification: +```bash +# All 3 files modified +test -f src/explorer/template/explorer.ts && echo "explorer.ts: EXISTS" || echo "explorer.ts: MISSING" +test -f src/explorer/template/template.html && echo "template.html: EXISTS" || echo "template.html: MISSING" +test -f src/explorer/template/styles.css && echo "styles.css: EXISTS" || echo "styles.css: MISSING" + +# explorer.ts compiles +bun build src/explorer/template/explorer.ts --format iife --target browser --outdir /tmp/verify-11-02 2>&1 + +# Template injection points preserved +grep -c "__INJECT_" src/explorer/template/template.html # Must be >= 3 + +# Feature count check (key patterns) +echo "Features present:" +for pattern in "KNOWLEDGE_STATES" "localStorage" "LEARNING_MODES" "clipboard" "fallbackCopy" "migrateKnowledgeState" "debouncedSave" "exportStateAsJSON" "updateProgress" "restoreState" "mode-selector" "touch-action" "progress-fill" "max-width: 768px"; do + grep -rq "$pattern" src/explorer/template/ && echo " $pattern: YES" || echo " $pattern: NO" +done +``` -- Template engine produces explorers with persistent knowledge tracking across page refreshes -- 4 knowledge states cycle correctly via Cytoscape tap events with auto-style refresh -- 3 learning modes produce meaningfully different prompts (vocabulary, structure, relationship limits) -- Clipboard copy works on HTTPS, localhost, AND file:// protocol (via fallback) -- Explorer is usable on mobile (responsive layout, touch interactions via Cytoscape) -- Legacy Phase 10 localStorage data migrates correctly to Phase 11 4-state model -- Zero new external runtime dependencies added +1. explorer.ts compiles as IIFE targeting browser without errors, 350+ lines +2. 4-state knowledge cycling works: unknown -> familiar -> confident -> mastered -> unknown (wrapping) +3. localStorage wrapper with version-keyed storage per topic slug +4. State migration maps legacy values: know->mastered, fuzzy->familiar, unknown->unknown +5. Learning mode selector with 3 modes affecting prompt complexity and vocabulary +6. Clipboard copy uses navigator.clipboard.writeText with textarea fallback for file:// +7. Progress bar shows confident+mastered count vs total with expandable breakdown +8. Mobile responsive layout with touch-action: none and @media queries +9. JSON export fallback available +10. Template injection points preserved (no regression on Phase 10 build pipeline) diff --git a/.planning/phases/11-self-assessment-persistence-topics/11-03-PLAN.md b/.planning/phases/11-self-assessment-persistence-topics/11-03-PLAN.md index 5f055ea..d361026 100644 --- a/.planning/phases/11-self-assessment-persistence-topics/11-03-PLAN.md +++ b/.planning/phases/11-self-assessment-persistence-topics/11-03-PLAN.md @@ -4,47 +4,48 @@ plan: 03 type: execute wave: 2 depends_on: ["11-01", "11-02"] -files_modified: - - src/explorer/dist/dividend-strategy-explorer.html - - src/explorer/dist/options-greeks-explorer.html - - src/explorer/dist/risk-management-explorer.html +files_modified: [] autonomous: false must_haves: truths: - - "All 3 explorers (dividend, options-greeks, risk-management) build successfully from the enhanced template + topic JSONs" - - "Each built explorer is a single standalone HTML file with zero external script or stylesheet references" - - "Each explorer loads in under 1 second from local filesystem (EXPL-16)" - - "Knowledge state cycling persists after page refresh in each explorer (EXPL-05)" - - "Learning mode selector changes prompt vocabulary and relationship count (EXPL-06)" - - "Copy prompt button copies text to clipboard including on file:// protocol (EXPL-15)" - - "Explorer layout is responsive and usable on mobile viewport (EXPL-08)" + - "Build pipeline produces 3 standalone HTML files: dividend-strategy-explorer.html, options-greeks-explorer.html, risk-management-explorer.html" + - "All 3 explorers have zero external dependencies (no CDN links, no script src, no link href)" + - "All 3 explorers show 4-state knowledge cycling with correct colors and persistence" + - "All 3 explorers have learning mode selector, progress bar, and copy prompt button" + - "All 3 explorers load in under 1 second with zero external runtime dependencies" + - "Human verification confirms all Phase 11 features work correctly across all topics" artifacts: - path: "src/explorer/dist/dividend-strategy-explorer.html" - provides: "Rebuilt dividend explorer with Phase 11 enhancements (4-state, persistence, modes)" - min_lines: 100 + provides: "Rebuilt dividend explorer with Phase 11 features" + contains: "KNOWLEDGE_STATES" - path: "src/explorer/dist/options-greeks-explorer.html" - provides: "Options Greeks knowledge explorer with 20 financial concepts" - min_lines: 100 + provides: "New options Greeks explorer with 30 concept nodes" + contains: "options-greeks" - path: "src/explorer/dist/risk-management-explorer.html" - provides: "Risk Management knowledge explorer with 20 financial concepts" - min_lines: 100 + provides: "New risk management explorer with 27 concept nodes" + contains: "risk-management" key_links: - from: "src/explorer/build.ts" - to: "src/explorer/topics/*.json" - via: "Build pipeline reads topic JSON, validates against schema, injects into template, outputs standalone HTML" - pattern: "build\\.ts.*\\.json" + to: "src/explorer/topics/options-greeks.json" + via: "Build pipeline validates and bundles options-greeks topic" + pattern: "TopicSchema.safeParse" + - from: "src/explorer/build.ts" + to: "src/explorer/topics/risk-management.json" + via: "Build pipeline validates and bundles risk-management topic" + pattern: "TopicSchema.safeParse" - from: "src/explorer/dist/*.html" - to: "localStorage" - via: "Embedded explorer.ts code persists knowledge state per topic slug" - pattern: "fin-guru-explorer\\.v1" + to: "src/explorer/template/explorer.ts" + via: "All features bundled into each standalone HTML" + pattern: "KNOWLEDGE_STATES" --- -Build all 3 knowledge explorers using the Phase 10 build pipeline with Phase 11 enhanced template and topic data, then verify all success criteria through automated checks and human visual verification. +Build all 3 knowledge explorers using the Phase 10 build pipeline with Phase 11's enhanced template and new topic data, then verify all Phase 11 features through automated checks and human visual verification. + +Purpose: This is the integration and verification plan. It confirms that the schema updates (Plan 01) and template enhancements (Plan 02) work together correctly through the existing build pipeline. Building all 3 explorers proves the template engine is truly reusable -- changing only the topic JSON produces a different explorer with all the same features. -Purpose: Integration proof -- confirm that topic data (11-01) + enhanced template (11-02) produce working explorers with all Phase 11 features. This plan proves the phase delivers on its 5 success criteria. -Output: 3 standalone HTML explorer files in dist/, verified working by both automated checks and human testing. +Output: 3 standalone HTML explorer files in dist/, verified for correctness and performance. @@ -56,242 +57,304 @@ Output: 3 standalone HTML explorer files in dist/, verified working by both auto @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md +@.planning/phases/11-self-assessment-persistence-topics/11-CONTEXT.md +@.planning/phases/11-self-assessment-persistence-topics/11-RESEARCH.md @.planning/phases/11-self-assessment-persistence-topics/11-01-SUMMARY.md @.planning/phases/11-self-assessment-persistence-topics/11-02-SUMMARY.md -@.planning/phases/10-template-engine-dividend-topic-port/10-RESEARCH.md - -Plan 11-01 SUMMARY: Two topic JSONs (options-greeks, risk-management) created + Zod schema updated for 4-state model -Plan 11-02 SUMMARY: Template engine enhanced with localStorage persistence, 4-state cycling, learning modes, clipboard, responsive +@src/explorer/build.ts +@src/explorer/schemas/topic-schema.ts +@src/explorer/template/explorer.ts +@src/explorer/template/template.html +@src/explorer/template/styles.css Task 1: Build all 3 explorers and run automated verification - - src/explorer/dist/dividend-strategy-explorer.html - src/explorer/dist/options-greeks-explorer.html - src/explorer/dist/risk-management-explorer.html - + -Run the Phase 10 build pipeline for all 3 topic JSON files. The build script validates topic data against the Zod schema and produces standalone HTML. - -1. **Build all 3 explorers:** -```bash -cd /Users/ossieirondi/Documents/Irondi-Household/family-office - -# Build dividend (rebuilt with Phase 11 template enhancements) -bun run src/explorer/build.ts src/explorer/topics/dividend-strategy.json - -# Build options-greeks (new) -bun run src/explorer/build.ts src/explorer/topics/options-greeks.json - -# Build risk-management (new) -bun run src/explorer/build.ts src/explorer/topics/risk-management.json -``` - -If any build fails, read the error output carefully: -- "Schema validation failed": check that topic JSON matches the Zod schema (knowledge values must be in the 7-value enum) -- "File not found": check file paths -- "TypeScript error": check explorer.ts for syntax issues introduced in 11-02 -- Fix the root cause and re-run the failing build - -2. **Verify all outputs exist:** -```bash -ls -la src/explorer/dist/*.html -``` - -3. **Verify standalone constraint (EXPL-16: zero external runtime dependencies):** -```bash -for f in src/explorer/dist/*.html; do - echo "=== $(basename $f) ===" - ext_scripts=$(grep -c '