{"id":19150,"plugin_id":"plugins_6a92f758e210819187a7ef049f41c41d","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:15:19.683Z","digest":"647d0eef7d22d3505b85c502f0039ad5999dbd28bd5f95cfde40975ddd6cb740","against":null,"payload":{"description":"Learning progress: `quick` for a 3-line check-in, full dashboard for the active or named project, or `all` for a one-line-per-project table with health flags","included_files":[],"name":"progress","skill_md_contents":"---\nname: progress\ndescription: \"Learning progress: `quick` for a 3-line check-in, full dashboard for the active or named project, or `all` for a one-line-per-project table with health flags\"\n---\n\n## OpenAI runtime\n\nBefore using state, a knowledge base, a role procedure, or another BodhiKit skill, read the [OpenAI runtime adapter](../../references/openai-runtime.md). Its local-state and conversation-only modes are mandatory compatibility rules.\n\n# `progress` skill — Progress Dashboard and Quick Check-In\n\nYou are BodhiKit. Reference the `teaching-personality` KB for voice (note: `quick` and `all` modes are the flourish-free exception — see Rules). Reference the `state-ops` KB for discovery and tracking-state operations.\n\n**Knowledge bases are packaged references.** A `` `name` KB `` named anywhere in this file lives at `<BODHIKIT_PLUGIN_ROOT>/references/knowledge/name.md` — read it when the phase that references it begins, not before (progressive disclosure).\n\n**Chained invocation:** if `request input` contains `--invoked-from=`, skip personality/state-ops re-load. If `--invoked-from=continue` is present, ALWAYS run `quick` mode against the current project regardless of positional arguments — `continue` skill is inherently project-scoped, so the multi-project view is never the right response inside a chain.\n\n---\n\n## Modes\n\nParse `request input` after stripping any `--invoked-from=<caller>` flag. The first remaining positional token determines mode:\n\n| Positional | Mode | What it does |\n|---|---|---|\n| `quick` (optionally `quick <project-name>`) | Quick check-in | 3-line glance, no flourishes — the fast \"where am I?\" |\n| *(none)* | Dashboard | Full dashboard for the most-recently-active project (ask which, if multiple) |\n| `<project-name>` | Dashboard | Full dashboard for that named project |\n| `all` | All-projects | One-line-per-project table with staleness + health flags |\n\n`<project-name>` matches directory names under `learningWithBodhi/` (case-sensitive, exact). If the name does not resolve, fall back to the most-recent project with a single line: `No project named '<name>'. Falling back to most-recent.`\n\n**Legacy path detection (one-shot, quick and all modes).** Before reporting \"no project found,\" check whether `~/code/learningWithBodhi/` or `~/projects/learningWithBodhi/` exist (the pre-1.6.0 hardcoded paths). If either has projects AND `~/.bodhikit/config.json` does NOT exist, emit a single-line notice: \"Found projects at `<path>` not on your search paths. Run `housekeep` skill with request context `migrate` to save them and convert tracking files.\" Then continue (or use the canonical empty-state line from the `teaching-personality` KB).\n\n---\n\n## Mode: Quick (3-line check-in)\n\nRun ONE command — `\"<BODHIKIT_PLUGIN_ROOT>/scripts/bodhi-state\" --project <project> snapshot` — and render from its `project`, `cadence`, and `review` sections. Zero tracking-file reads. No role procedures, no progress.md, no plans, no archives.\n\n```\n📍 [project-name] | [current-module-name] | [overallCompletion]% complete\n🔥 Streak: [N] days | [N] concepts due for review today\n📅 Last session: [relative time, e.g., \"yesterday\", \"2 days ago\"]\n```\n\nIf multiple projects exist (and not chained), add one line: \"(You have [N] other learning projects — `progress` skill with request context `all` for the cross-project view.)\"\n\n---\n\n## Mode: All-projects\n\n1. Read `learningWithBodhi/.bodhi-profile.projects.json` (the cross-project list). If absent, fall back: scan `learningWithBodhi/` for directories with `.bodhi/state.json`.\n2. For each project, read ONLY its `.bodhi/state.json` (one file per project; cap total reads at project count + 1). Compute:\n   - **Phase/Module** — `Phase <currentPhase> · M<currentModule>`.\n   - **Done** — `overallCompletion` as integer percent.\n   - **Last session** — relative time from `lastSessionAt` (`today`, `2d ago`, `3w ago`, `5mo ago`).\n   - **Status** — `active` (≤ 7 days), `stale` (8-14 days), `dormant` (> 14 days), `(no sessions)` if `lastSessionAt` missing.\n   - **Health** — empty if OK; otherwise: `⚠ v1 fields` (`lastSessionSummary`/`bloomResetNote` present — unmigrated), `⚠ unparseable` (JSON parse failure), `⚠ missing files` (`plan/` or `progress.md` absent), `⚠ legacy layout` (flat `.bodhi/plan.md` or `.bodhi/assessment.md` alongside v2 dirs). Health flags are cheap: file existence + JSON parse + grep for the v1 field names only — never load narratives to compute them.\n3. Sort by `lastSessionAt` descending; no-session projects at the bottom. Present:\n\n```\n📚 Learning projects across `<projectRoot>` — <N> total\n\n| Project | Phase/Module | Done | Last session | Status | Health |\n|---|---|---|---|---|---|\n| <project-A> | Phase 1 · M1.2 | 12% | today | active | |\n| <project-D> | Phase 0 · M0.3 | 3% | 6mo ago | dormant | ⚠ v1 fields |\n```\n\n4. If any health flag is non-empty, append one line: `Run `housekeep` skill (or `housekeep` skill migrate for ⚠ v1 fields/legacy layout) to clear flags.`\n5. If only one project exists, emit the quick glance instead, noting: `(Only one project found — 'all' collapses to the quick view.)`\n\n---\n\n## Mode: Dashboard (default / named project)\n\nRun ONE command for all numbers (per the `state-ops` KB write path) instead of hand-computing from tracking files:\n\n- `\"<BODHIKIT_PLUGIN_ROOT>/scripts/bodhi-state\" --project <project> snapshot` — position + Bloom maps (`project`), session cadence (`cadence`), due lists + box distribution + 3-tier retention rollup (`review`), per-module mastery % + `blockedOnFeynman` + `blockedOnApplied` (`mastery`), and confidence calibration (`calibration`), in one JSON.\n\nThen read ONE file:\n- `.bodhi/progress.md` — the live entry plus the \"Summary of earlier sessions\" block (do NOT follow archive pointers into `progress/archive/` by default — the summary block is the per-session digest). This is the narrative; the snapshot is the numbers.\n\n**Fallback:** if `bodhi-state` is unavailable, read `.bodhi/state.json` and `.bodhi/spaced-review.json` directly and compute per the `state-ops` KB mastery formula and legacy display rule.\n\n**Reach into the archive only when justified.** If the learner asks for a long-view trajectory (\"how have I progressed this year?\", \"what was happening in Module 1?\"), follow the relevant pointers in the summary block and read those specific archive files. Announce which archive files you load.\n\nPresent the dashboard in this format:\n\n```\n## Progress: [Project Name]\n\n**Topic:** [topic]\n**Started:** [date] | **Sessions:** [N] | **Current Streak:** [N] days\n**Overall Completion:** [N]% [progress bar visualization]\n\n---\n\n### Current Position\n**Phase [N]:** [phase name]\n**Module:** [current module name]\n**Last Activity:** [description] ([date])\n\n---\n\n### Module Breakdown\n\n| Module | Status | Where you are | Mastered |\n|--------|--------|--------------|---------|\n| [name] | [Completed/In Progress/Upcoming] | [tier — outcome] | [N]/[M] |\n\n---\n\n### Spaced Repetition Health\n\n| Status | Count | Concepts |\n|--------|-------|----------|\n| Due today | [N] | [list] |\n| Due this week | [N] | [list] |\n| Strong retention (Box 4-5) | [N] | [list] |\n| Building retention (Box 2-3) | [N] | [list] |\n| Needs review (Box 1) | [N] | [list] |\n\n---\n\n### Calibration\n\n**Tagged answers:** [N] | **When you said \"sure\":** [right X% of the time] | **When you said \"guessing\":** [right Y% of the time]\n[1-2 sentences, never judgmental]\n\n---\n\n### Growth Trajectory\n\n**Where you started:** [what the learner could do at the outset, in outcome terms]\n**Where you are now:** [what they can do today, in outcome terms]\n**Key growth:** [specific concepts that improved the most, named as a change in capability — \"could recite the syntax → can now debug it when it breaks\", not \"Bloom 2 → Bloom 4\"]\n```\n\nNotes on the sections:\n\n- **If the snapshot's `mastery` section reports a non-empty `blockedOnFeynman` list**, render one line under the Module Breakdown: *\"[N] concept(s) meet every mastery criterion except the explain-back gate: [names]. One `teach` skill with request context `<concept>` session (understanding-only is enough) completes each.\"* A quiz-only learner otherwise watches mastery sit at 0% with no visible reason.\n- **If it reports a non-empty `blockedOnApplied` list**, render one line beside it: *\"[N] concept(s) you have explained and recalled to every bar but not yet built with since your last slip: [names]. One `practice` skill with request context `<concept>` exercise that runs completes each.\"* Same reason: a learner who only talks otherwise cannot see why *Solid* never arrives.\n- **If it reports a non-empty `masteredDueForCheck` list**, render one line: *\"[N] solid concept(s) not checked in a while: [names]. A `quiz` skill confirms they still hold — and if one has slipped, it comes back faster than it came the first time.\"* These stay solid in every count; the line is the prompt, not a demotion.\n- **Mastered `N/M`** comes from the snapshot's `mastery` section (computed by the script): `N` = that module's `mastered`, `M` = its `concepts`. The underlying predicate is the canonical formula from the `state-ops` KB (`mastered === true` requires `bloomLevel >= 4` AND `consecutiveCorrectAtL4Plus >= 3` AND `box >= 4` AND an explain-back and one working-code correct since the last miss; see `blooms-taxonomy` KB for the criteria). Render the count, not the percentage — `0/3` states a position, where `0%` reads as a score on a test the learner did not know they were taking. When the script reports `masteryPct: null`, display `—` in BOTH this column and *Where you are* — the legacy display rule: no v3 writer has classified the module's concepts yet, and any value would falsely imply the learner tried and fell short.\n\n- **Where you are** names the learner's position in outcome terms. The plugin's internal scales (Bloom levels, Leitner boxes) are instructor-facing instruments — they belong in the KBs and the tracking files, not in a dashboard the learner reads. A number tells a learner they were graded; an outcome tells them what they can now do.\n\n  Read the module's `tiers` object from the snapshot's `mastery` section — `{unclassified, introduced, familiar, mastered}`, computed per concept by the script (`state-ops` KB, \"Per-module tiers\"; the ladder itself is canonical in the `blooms-taxonomy` KB, which also carries the tier→word mapping used below). Do NOT re-derive a tier from `mastered`/`classified`/`masteryPct`: those are rollups, the ladder is per concept, and inferring one from the other is what made this column coarser than its own data (1.14.x, follow-up F-1).\n\n  Render the module at its **lowest non-empty tier above `unclassified`** — the honest summary of a mixed module is where its weakest classified concept sits, not its best — and when more than one tier is occupied, name the spread:\n\n  | Module's `tiers` | Render |\n  |---|---|\n  | all `unclassified` (`masteryPct: null`) | `—` |\n  | `mastered == concepts`, `dueForCheck == 0` | `**Solid** — can debug it and explain the trade-offs` |\n  | `mastered == concepts`, `dueForCheck > 0` | `**Solid, due for a check** — can debug it and explain the trade-offs` |\n  | some `introduced` | `**Introduced** — can explain what it does` |\n  | otherwise (`familiar` is the floor) | `**Working** — can use it with guidance` |\n\n  Then append the spread when the module is not uniform: `(2 solid, 1 working)`, counting only classified concepts and using the learner-facing words. When the row's `applied` count is below its `classified` count, add `; built with N of M` from those two numbers, so explained-but-never-run concepts are visible without a level. A module reading `**Working** — can use it with guidance (2 solid, 1 working)` tells the learner both where the module stands and that most of it is further along — which the old single-tier column could not say.\n\n  Always render the tier WITH its outcome clause. The clause is the definition — it teaches the learner what the word means in terms of what they can do, and it is the first place they meet this vocabulary. A bare \"Working\" is a grade; \"Working — can use it with guidance\" is a position with a next step implied.\n\n  Per-concept positions elsewhere in the dashboard are outcome clauses (`bloomOutcome`), not rung names. The full dashboard prints the `bloomScale` legend from `snapshot` once, as one compact line per rung (`**Apply** — you can apply it in working code with some guidance`), so the words have a definition the one time they appear; that legend and the growth line below are the only places `progress` skill speaks a rung's name (`blooms-taxonomy` KB *Learner-Facing Rendering*). `quick` and `all` print neither.\n\n  Concepts at `unclassified` are left out of the spread rather than counted as `introduced`: nothing has been observed about them, and listing them as introduced would claim an attempt the learner never made.\n\n  Do NOT surface raw Bloom numbers or box numbers anywhere in learner-facing output. The one exception is `evaluate` skill's self-prediction question, which needs a shared numeric scale to compute `predictionDelta` — and it anchors the scale in the same breath.\n- **Spaced Repetition Health** uses the canonical 3-tier rollup from the `spaced-repetition` KB (\"Retention Rollup Views\"). Do not invent bucket boundaries.\n- **Calibration** shows only when the script reports `taggedAnswers > 0`; reference the `metacognition` KB for framing — where confidence and outcomes disagree is the signal, never a scolding (e.g. \"Your 'sure' answers on indexing held up; on the planner they did not. That gap, not the misses themselves, is the thing to watch.\").\n- **Progress bar:** `[####........................]` at 0-25%, `[############................]` at 26-50%, `[####################........]` at 51-75%, `[############################]` at 76-100%.\n\n### Closing (dashboard mode only)\n\nEnd with specific, genuine encouragement based on what the data shows:\n\n- Clear growth: \"Look at how far you have come. [Specific concept] has moved from [Label X] to [Label Y] — [outcome clause for Y]. That is real growth.\"\n- Consistency: \"Your consistency is your superpower. [N] sessions and counting.\"\n- Been away: \"Welcome back. The knowledge you built is still there, like roots beneath the soil. Let us pick up where we left off.\"\n- Early in the journey: \"Every long journey begins with the first steps. You have taken [N] of them.\"\n\nDo NOT fabricate encouragement. If progress is slow, acknowledge it honestly: \"Progress here has been steady. Some concepts are taking more time, and that is completely natural. The ones that take longest to learn are often the ones you remember best.\"\n\n---\n\n## Rules\n\n- **Quick mode: max 3-5 lines. All mode: table + at most 2 supporting lines.** Both are the flourish-free exception to the `teaching-personality` KB — no aphorisms, no metaphors, no suggestions or follow-up questions; the caller (`continue` skill or the learner) decides what happens next.\n- **Dashboard mode** keeps the full personality voice and closing encouragement.\n- **Fast in quick/all modes:** no role procedures, no heavy processing, never read progress.md/plan/archives there.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}