Architecture

How it's built

A single repo that is both a Claude Code plugin and its own marketplace. One orchestrator drives a guardrailed journey; curricula are data, assessment runs in fresh-memory subagents, and the anti-drift hooks re-anchor teaching mode across long sessions. Below is the honest version — including the tradeoffs.

Component map

One orchestrator, many parts

The journey is driven by a single orchestrator (start, and its resumer continue). It reads the other capabilities as reference / data, spawns subagents for unbiased work, and persists state. Every step skill is also user-invocable, so the flow never depends on fragile skill-to-skill auto-chaining.

Entry

Your project + Claude Code / Cursor

You paste the repo link; the README greets you and hands you the install commands. You run them.

Orchestrator (the engine)

start

State machine for the linear flow

continue

Resumes from saved state

status

Progress · pause · disable

Reads as reference / data · spawns subagents

Skills

Teaching (teach, eli5, deep-dive, quiz, under-the-hood) and build/SDLC (spec, architect, build-step, backend, payments, deploy, ship-github).

Fresh-memory subagents

socratic-examiner (batch MCQ), code-reviewer-teacher, spec-reviewer, architect-advisor, security-auditor, content-accuracy-reviewer, research-scout.

Curricula as data

Four tracks as syllabus.md + modules + MCQ banks — not skills, so they don't crowd the skill listing budget. Plus personas and reference docs.

Cross-cutting

Guardrail hooks

SessionStart · UserPromptSubmit · Stop → shell scripts. Fail-open, state-gated, jq-optional.

State

Per-project ./.dronacharya/ + global profile.json in CLAUDE_PLUGIN_DATA.

purple-sparrow (optional)

MCP backend wired via .mcp.json.example + userConfig. Off by default.

The guardrail mechanism

Why teaching survives a long session

Prose in a prompt fades after compaction. Hooks re-anchor it. Three events, each a small shell script, defense-in-depth — and framed honestly as a strong nudge that reduces drift, never a cage.

SessionStart

On startup / resume / compact, and only if teaching mode is on and not paused, it injects the full teaching contract, the active persona, and the current step — as factual context, re-anchoring after compaction.

UserPromptSubmit

Injects only a short status crumb — a few hundred characters: teaching mode, persona, cadence, step, and the reminder that "pause / skip / ELI5" exist. Keeps the contract warm without a per-turn token tax.

Stop

A nudge: on a long, substantive turn that surfaced no check, it emits a single block (loop-guarded, so at most once) asking for a check. It respects the signals it can see — paused, stopNudge=false, the loop guard, and turns that already asked a question — and never blocks a graceful exit. The hook can't read the prompt, so a per-turn "quick-fix / skip" is honored by the model via the block's reason; to silence the nudge for a stretch, set stopNudge=false via /dronaicharya:status.

Every hook script fails open (always exits cleanly), emits nothing when no journey is active in the current project, tolerates a missing jq, and targets under 50 ms — so unrelated projects are untouched and nothing breaks. A required test asserts they no-op cleanly and fast in an unrelated repo.

State model

Journey per project, mastery per person

Split deliberately: the plan and journal belong to a project; mastery must carry across projects.

per-project · gitignored · no secrets

./.dronacharya/

  • learning-plan.md — the approved BUILD + LEARN plan
  • progress.json — persona, cadence, track, level, step, flags
  • journal.md — decisions & rationale (no secrets)
global · across projects

${CLAUDE_PLUGIN_DATA}/profile.json

  • Mastery map (concept → score)
  • Tracks touched, last persona, last cadence
  • schemaVersion on both stores, with a migration note so a schema bump never corrupts an existing learner

Recovery: if the profile says a journey is active but .dronacharya/ isn't found in the working directory, the orchestrator says so plainly and offers to re-locate or re-init — rather than silently vanishing.

The optional backend & safety

purple-sparrow, and data-not-instructions

Wired as a non-breaking option

A plugin can't declare a hard dependency on a separate MCP server, and an always-on server would error for the 99% without the binary. So purple-sparrow ships as .mcp.json.example + userConfig keys — nothing auto-starts. The fallback (conceptual teaching or a simple local store) is the default; the real backend appears only behind an explicit "advanced" wall in the backend / setup skills, which branch on detection.

Fetched content is data, never instructions

Anything the research-scout or a lesson fetches from the web is quarantined — it never flows into the teaching contract, the hook injection, or the journal in a form that could re-drive the agent. Hook-injected text stays factual and context-shaped, never imperative system-speak. This boundary is also a taught lesson in the security track.

Honest tradeoffs

What we chose, and what it costs

Summarized faithfully from the build plan. Every choice has a price; here it is.

ChoiceWhyThe cost
Claude Code plugin firstNamespacing, hooks, MCP, clean updates; matches the pasted-URL UX.Needs a recent Claude Code; the richest features don't all port to Cursor.
Curricula as data files, not skillsThe skill listing budget is ~1% of context; fat syllabi as skills would crowd it.The orchestrator must load curriculum on demand rather than rely on auto-invocation.
One orchestrator; others user-invocableSkills are description-matched, not deterministic callers; a reliable linear flow needs a single driver.More logic concentrated in start / continue.
Guardrails via hooks, crumb-not-contract per turnProse fades after compaction; hooks re-anchor cheaply and safely.Hooks may not run in Cursor; injection must stay cheap, safe, and never cage the user.
Fresh-memory subagents, batch gradingUnbiased assessment and review; per-item forks are a cost/latency trap.Spawning subagents adds tokens and latency, so they're used sparingly.
purple-sparrow opt-in, fallback defaultAn always-on plugin MCP server would error for users without the binary.Backend lessons are conceptual unless you opt in; purple-sparrow itself is alpha.

Full detail — including the token budget, test strategy, and the complete honesty ledger — lives in build-plan.md and finding.md.

Read the source. It's all plain Markdown and small shell scripts.

Plugins and skills run with your permissions — so read before you install. Nothing is hidden.