@meronq/practice
v0.1.0
Published
Deliberate-practice skill tracker: calibration (prediction vs actual), root-caused error logs, feedback-loop speed. Standalone repo; consumed by Meronq desktop as a mounted tab.
Readme
Practice (working title)
A deliberate-practice tracker for engineering skills (algorithms, languages, frameworks, systems, clusters) — built around calibration (prediction → actual), root-caused error logs, and feedback-loop speed, not hours logged.
This is a standalone repository, separate from
meronq. It is not a submodule, not
an npm workspace member of meronq, and meronq's git history/index does not
contain it. See "Why a separate repo" below for the reasoning.
Relationship to Meronq
Practice renders inside Meronq-desktop as a mounted tab, not as a
separate app window. It depends on @meronq/core for CEM primitives but does
not call into Meronq internals directly — only through @meronq/core and its
own store. See the "Invariant" section below.
The mount point on the meronq side is a single explicit line in
apps/desktop/src/main.ts (calling a stub in
apps/desktop/src/plugins/practice-tab.ts). Today that's a static mount —
no plugin host exists yet, and none should be built for a single consumer.
When a second plugin shows up, the static mount generalizes into
discovery + a real ctx. Until then, resist building the generic host.
Why a separate repo (not nested in meronq, not a submodule)
Two options were on the table for keeping this out of meronq's git tree:
- Nested folder inside meronq's working copy +
.gitignore+ scanner exclusion. - Git submodule inside meronq.
- Sibling repo, own directory, own remote. (chosen)
Went with (3): it's the option with the fewest moving parts. A submodule
still couples the two repos' commit graphs and adds submodule-update
ceremony to every clone/pull. A gitignored nested folder works, but it's a
foot-gun — one git add -A or a scanner path that doesn't respect
.gitignore and Practice's code silently ends up inside meronq's index or
its CEM memory anyway. A fully separate repo makes "not indexed by meronq"
true by construction, not by convention. It costs nothing here since Practice
isn't an npm workspace member either way (meronq's pnpm-workspace.yaml
only globs apps/* and packages/*), so there's no workspace tooling to
lose by living outside the tree.
Local path: C:\Users\freim\source\repos\practice — a sibling of
C:\Users\freim\source\repos\meronq, not nested inside it.
GitHub: private repo, created under the same account as meronq.
Why this schema (research basis, condensed)
Skill grows from practice at the edge of ability with fast, unambiguous feedback (Ericsson; Hogarth's kind-vs-wicked learning environments) — not from raw hours. Experience without that feedback loop often just inflates confidence, not competence (Meehl, Kahneman, Tetlock, Goldberg).
So every practice record requires four fields, not a duration:
- Target sub-skill — a narrow ability at the edge of current competence, not a topic.
- Prediction → actual — forecast before, outcome after (calibration).
- Error log with root cause — category + analysis, not just "got it wrong."
- Feedback source and latency — tests / contest judge / AI / human, and how fast it came back.
Success metric for the tracker itself: convergence of prediction↔actual over time, and shrinking repeat-rate of error categories — not cumulative hours.
The full theory track — Veritasium citations grouped by what they imply for the tracker — is in docs/product/literature.md. Review should point at those papers; it should not grow an hours field.
What's actually being practiced right now — domain, motivation, pace, and the theory + practice resource stack per layer — is in docs/product/direction.md. It's a snapshot, set by questionnaire, revisited when the domain shifts.
Invariant (holds from the first line of code)
Practice's UI never calls Meronq internals directly. It only talks to
@meronq/core for CEM data, and to its own store for domain data.
Storage — ADR-0001 + ADR-0002
Practice records live outside this project's meronq graph. The
namespace may be a plugin-owned CEM instance (same seven layers, own
types and store) — not an eighth meronq layer, not Action/Result.
See ADR-0001
and ADR-0002.
v0 persist is .practice/store.json. @meronq/core / embeddings are
libraries, used only when search or vitality is actually needed.
Anti-decisions (carried over from the handoff, still binding)
- No generic plugin host built for a single consumer.
- No
ctxdefined ahead of knowing what Practice actually calls on@meronq/core. - No forking the anchor-scheduler — consume it as a package.
- No exporting
@meronq/coresurface by guesswork. - No hour-tracking.
- Practice never calls Meronq internals directly (see Invariant).
Status
Sprint 3 closed: session loop is practice_start → practice_complete,
plus practice_next (open attempt, repeating error, miscalibration,
then least-recent — not a scheduler). practice_record stays for
backfilling attempts where both halves are already known.
Clients spawn the server themselves via an MCP config
(PRACTICE_ROOT selects the store). .mcp.json is
tracked for Claude Code. Cursor users need their own local
.cursor/mcp.json — it's gitignored, because Cursor rewrites it with
absolute local paths and, at least once, injected a live API key/token
from an unrelated connected MCP connector; never commit it. Do not
leave npm run mcp running in a terminal — that is stdio, not a
daemon.
Records live outside this project's meronq graph
(ADR-0001,
ADR-0002).
No real UI yet, and no anchor-scheduler wiring — but mountPracticeTab
now has a first real version (src/tab.ts, a thin text render, no
styling), backed by an actual @meronq/core call rather than a guess.
The full session loop (practice_start → practice_complete →
practice_next) has now been dogfooded end to end on a real topic
(a GitHub Actions trigger/concurrency policy, verified against real CI
runs, not just reviewed as code) — see the dogfood retro in
docs/roadmap.md. That dogfood run
is also what motivated actually calling @meronq/core for real: Sprint
4's gate is now passed, and it turned up a real finding —
@meronq/core is thinner than assumed (a stub Project, no store, no
search) and has no distribution path outside the meronq monorepo yet.
See ADR-0003.
Sequence (for orientation, not a commitment)
- ~~v1 MCP record + one review tool.~~ Done in Sprint 2 (
npm run mcp). - ~~Resolve the storage fork.~~ ADR-0001: outside CEM.
- ~~Session loop.~~ Done in Sprint 3 (
practice_start/practice_complete/practice_next). - Watch what a desktop client actually calls on
@meronq/core— that becomes the futurectx(Sprint 4).
