Skip to content

Reporting Commands

Relevant Source Files

Reporting commands provide deep visibility into the educational health of your Obsidian vault. They scan topic frontmatter to calculate global mastery metrics, report difficulty distributions, track spaced repetition statistics, and detect structural integrity violations across the prerequisite graph.


1. Dashboard Command (palee dashboard)

The palee dashboard command provides a high-level executive summary of your vault's learning state. It aggregates overall topic mastery percentages, tallies review queues, breaks down progress by difficulty tiers, and surfaces the single most urgent upcoming review.

Syntax & Options

bash
palee dashboard [flags]
FlagTypeDefaultDescriptionExample
--jsonbooleanfalseOutput dashboard metrics as structured JSON (auto-activated in non-TTY environments).palee dashboard --json

Data Aggregation & Metric Calculations

dashboardCommand scans all topic files in the vault and classifies notes using the 4-pillar mastery threshold (M=0.70) src/cli/dashboard.ts#88-114:

  • Mastered: Topics with topic_mastery >= 0.70.
  • Learning: Topics actively in progress (0.0 < topic_mastery < 0.70).
  • New: Unreviewed topics with topic_mastery === 0.0.
  • Reviews Due: Topics where due_at is in the past or equal to the current system time.
  • Difficulty Tiers: Aggregated counts and mastered subtotals across beginner, intermediate, and advanced tiers.

Example Human-Readable Output

bash
$ palee dashboard
╔════════════════════════════════════════════════════════════╗
              PALEE Learning Dashboard
╚════════════════════════════════════════════════════════════╝

Total Topics:      15
Mastered (≥70%):   6 (40.0%)
Learning:          7 (46.7%)
New:               2 (13.3%)
Due for Review:    3

By Difficulty:
  beginner       : 5 topics (4 mastered)
  intermediate   : 7 topics (2 mastered)
  advanced       : 3 topics (0 mastered)

Next Review:
  Memory Ownership (T-20260814T120100-efgh)
  Mastery: 45.0% | Reps: 2

─────────────────────────────────────────────────────────────
Run "palee next" to start reviewing
Run "palee plan" to see today's learning plan

2. Progress Command (palee progress)

The palee progress command offers granular analytics into learning retention, historical repetitions, lapse counts, and topic-specific mastery scores.

Syntax & Options

bash
palee progress [flags]
FlagTypeDefaultDescriptionExample
--topic <id>stringundefinedInspect a specific topic by ID (e.g. T-20260814T120000-abcd) or unique title substring.palee progress --topic "Recursion"
--jsonbooleanfalseOutput progress metrics as structured JSON (auto-activated in non-TTY environments).palee progress --json

Vault-Wide vs Topic-Specific Modes

1. Vault-Wide Mode (Default)

Aggregates all active topics across the vault src/cli/progress.ts#140-233:

  • Archived Topic Exclusion: Notes with status: archived in frontmatter are tracked separately and excluded from global_mastery and active_topic_count.

  • Global Average Mastery: Calculates the true mathematical mean of mastery across all active topics:

    text
    global_mastery = sum(active_topics.mastery) / active_topic_count
  • Mastery Status: Classifies vault overall state as 'no_data' (active_count === 0), 'learning' (< 0.70), or 'mastered' (>= 0.70).

  • Total Repetitions & Lapses: Aggregates lifetime review repetitions and memory lapses across all active topics.

2. Topic-Specific Mode (--topic <id>)

Surfaces comprehensive SRS metadata for an individual topic note:

  • topic_mastery percentage
  • difficulty tier
  • repetition count and lapses count
  • assessed_at and last_reviewed_at timestamps

Example Outputs

Vault-Wide Human-Readable Summary

bash
$ palee progress
=== Learning Progress ===

Active Topics: 14 (1 archived)
  Mastered (≥70%): 6 (42.9%)
  Learning: 6 (42.9%)
  New: 2 (14.3%)

Average Mastery: 54.3% (learning)
Total Reviews: 28
Total Lapses: 3

By Difficulty:
  beginner: 5 topics, avg mastery 78.0%
  intermediate: 6 topics, avg mastery 48.3%
  advanced: 3 topics, avg mastery 26.7%

Topic Lookup

bash
$ palee progress --topic "Recursion"
Progress for: Recursion and Backtracking
ID: T-20260814T120000-abcd
Path: DSA/Recursion.md

Mastery: 80.0%
Difficulty: advanced
Repetitions: 5
Lapses: 0
Last Assessed: 2026-08-25
Last Reviewed: 2026-08-25

3. Validate Command (palee validate)

The palee validate command performs static analysis on the entire Obsidian vault to verify data model integrity and prerequisite graph acyclicity.

Syntax & Options

bash
palee validate [flags]
FlagTypeDefaultDescriptionExample
--fixbooleanfalseAttempt automated repairs for detected validation errors (Phase 1 diagnostic flag).palee validate --fix
--jsonbooleanfalseOutput validation diagnostics as structured JSON (auto-activated in non-TTY environments).palee validate --json
--strictbooleanfalseEscalate warnings to a non-zero exit code: a warnings-only vault exits 3 like an errors vault (default: warnings never gate the exit code).palee validate --strict

Vault Structural Integrity Rules

palee validate runs a nineteen-rule validation framework (rules live under src/validation/rules/, registered in src/cli/validate.ts, exported through the src/validation/ barrel): eleven error-default rules (the graph integrity ports minus missing-dependency, the schema/identity rules, the assessment, review-state, session-schema, and vault-path-boundary rules) and eight warning-default rules — the two snapshot rules that explain gaps in the collected topic set (read-failure covers walked notes, session notes, and memory-subsystem components alike — a partial snapshot is reported wherever it was discovered), the mastery-drift rule, missing-dependency findings (the engine quarantines the dependent topic instead of failing the scan; roadmap --from pre-validation keeps its own separate error path), the ambiguous-kind rule for managed notes, the session-unknown-topic rule, the session-index rule, and the hot-memory rule. valid-dependency-list is error-default but emits its duplicate-entry findings as warnings. Errors exit 3; warnings exit 0 unless --strict escalates them to 3. The memory subsystem (.palee/sessions/*, .palee/index.md, .palee/hot.md) is collected in the same single-read snapshot as the topic vault; a fresh vault with no memory subsystem validates clean.

  1. Malformed Frontmatter (parse-frontmatter, warning): A note whose YAML frontmatter cannot be parsed (including unclosed --- fences whose body reads like YAML). The scan always continues — one bad note is a finding, never a dead validation.

  2. Read Failures (read-failure, warning): A file that could not be read at all (locked or deleted mid-scan). Validation ran on an incomplete snapshot; the warning appears alongside any graph findings so transient conditions are visible without downgrading them.

  3. Schema Version (valid-palee-schema, error): Every PALEE-managed note must declare palee_schema: 1; missing, non-integer, and unsupported versions are errors so mutations can refuse to guess at unknown data. Non-managed user notes are never reported.

  4. Topic ID Format (valid-topic-id-format, error): Topic IDs must match the centralized policy in src/engine/topic-id.tsT- plus lowercase kebab-case segments; the exact legacy adopt-generated format stays valid.

  5. Topic Status (valid-topic-status, error): Status must be one of not_started | learning | paused | archived; missing status is tolerated as the adopt default.

  6. Duplicate Topic IDs (no-duplicate-topic-id, error): Multiple Markdown notes sharing the same palee_id in their frontmatter.

  7. Dependency List Shape (valid-dependency-list, error): depends_on must be an array of non-empty topic-ID strings — a bare string, a non-string item (numbers, booleans, nulls), or an empty-string slot is a shape error, and a self-reference (T-a depending on T-a) is a structural error. Duplicate entries are a separate warning: the loader dedupes them, so scheduling is unaffected. Missing or null depends_on is the documented empty-list default and never reports. The rule runs before the graph rules and reads raw frontmatter (pre-normalization) so defects the loader's coercion would hide are exposed.

  8. Missing Dependencies (no-missing-dependency, warning): A topic referencing a prerequisite ID in depends_on that does not exist anywhere in the vault. A warning in vault scans (the engine quarantines the dependent topic from plan/next instead of failing the scan — incrementally-written vaults are the norm, and a note mid-flight must not fail a whole validation run); roadmap --from pre-validation keeps its own separate hard-error path, so an import-time dangling reference still blocks the import. Findings never depend on unrelated vault state; if a dependency target was itself unreadable, the read-failure warning appears alongside explaining the transient condition, and re-running settles it. --strict escalates the warning for CI use.

  9. Dependency Cycles (no-dependency-cycle, error): Circular dependency chains (e.g. ABCA) detected by the dependency engine src/engine/dependency.ts (iterative Tarjan SCC analysis with a lexicographic-first cycle search; the rule reports the exact path).

  10. Assessment Fields (valid-assessment-fields, error): Assessment scores (conceptual, practical, debug, feynman) must be finite numbers within [0.0, 1.0] as stored on disk, and assessed_at must be null or a real calendar date — date-only strings (YYYY-MM-DD) and ISO timestamps (2026-02-30T12:00:00Z) alike are rejected when their written calendar rolls over (2026-02-30 is not normalized into March). The rule reads raw frontmatter values — the loader clamps and coerces during normalization, so this rule exposes real vault corruption instead of silently blessing it. Missing assessment fields follow the documented default policy (they are the newly-adopted state) and pass.

  11. Topic Mastery Drift (valid-topic-mastery, warning): When a topic's assessment fields are shape-valid, stored topic_mastery must equal the engine formula round((conceptual + practical + debug + 2*feynman) / 5, 4) — recomputed with computeTopicMastery from src/engine/mastery.ts. Drift reports a warning with details.actual (stored) and details.expected (computed); a present-but-malformed stored value (non-numeric, non-finite) is itself a mismatch — the loader would coerce it to 0 at runtime, so the rule reads the raw value to expose that. Topics whose assessment fields fail rule 10 are skipped (no double-reporting), topics with all four pillars absent are the newly-adopted default state and never report, missing assessment data never crashes the rule, and archived topics are still checked — internal consistency matters for any stored topic. A warning only, so --strict gates it.

  12. Review Fields (valid-review-fields, error): SM-2 review state must match the engine contract as stored on disk: ease_factor a finite number >= 1.3 (the SM-2 floor), interval_days an integer >= 1, repetition/lapses integers >= 0, and last_quality null or an integer 0-5. The rule reads raw frontmatter (the loader's parseNumber/parseInteger coercion would silently accept stringified or fractional values), so values no PALEE writer could produce — stringified numbers, null numeric state, fractional counters — are exposed as errors. Missing keys are the adopt-default state and pass.

  13. Review Dates (valid-review-dates, error): last_reviewed_at and due_at must be null (newly adopted) or strict zero-padded date-only YYYY-MM-DD strings naming real calendar dates — the exact shape formatLocalDateOnly persists. Full ISO timestamps fail (date-only is the product contract: review scheduling compares local calendar days), impossible calendars fail (2026-02-31 is not normalized into March), and when both fields are valid, due_at earlier than last_reviewed_at reports the inversion. Validation is pure component analysis shared with the assessed_at policy — no timezone-dependent parsing.

  14. Managed Note Kind (valid-managed-note-kind, warning): A note declaring palee_schema asserts PALEE owns part of its frontmatter — it must carry exactly one recognizable identity: palee_id (topic), session_id (session), memory_id (hot memory), or type: session_index (index). A versioned note with no identity, or one claiming two kinds at once (e.g. palee_id + session_id), reports a warning — a human decides what the note is; guessing is unsafe for validation and migration. Runs before the schema rules so kind-specific errors are read with the kind in hand. Internal notes (.palee/sessions/*, index.md, hot.md) never reach the vault walker, so the rule classifies them from the collected memory snapshot too: a session note also carrying palee_id, or the index also carrying session_id, reports the same conflict — their kind is fixed by location, so only conflicting-identity claims are findings there (shape defects stay with the memory rules).

  15. Session Schema (valid-session-schema, error): Canonical session notes under .palee/sessions/ — the durable learning history that hot.md and index.md rebuild from — must carry the full schema: required fields (session_id, topic_id, started_at, ended_at), status of completed or draft coherent with the S-/DRAFT-S- filename convention, session_id matching the filename stem (the rebuild paths key on it), ISO 8601 timestamps carrying an explicit timezone designator (Z or ±HH:MM — offset forms pass; date-only and timezone-less strings fail) with ended_at not preceding started_at, ended_at: null for drafts and a timestamp for completed sessions. Every session note must also declare a supported palee_schema version — the canonical writers emit palee_schema: 1 and the rebuild paths default missing versions silently, so validation is where a foreign or missing version on canonical data surfaces (the same policy as valid-palee-schema, session side). Malformed YAML is one error, never a silently skipped note; a note that could not be read at all (locked or deleted mid-scan) is the read-failure rule's finding, never a schema error. Reads raw frontmatter — the rebuild paths cast with as string, so shape drift is silent at runtime and validation is the only place it surfaces.

  16. Session Unknown Topic (no-session-unknown-topic, warning): A session's topic_id must reference a topic that exists in the vault — a session pointing at a missing topic cannot be connected back to the learning graph. The historical T-general phantom sessions (from the pre-hot-memory session end fallback) are reported like any other unknown topic unless a real topic with that ID exists; no special case hides the bug the rule exists to surface. Drafts follow the same policy; sessions whose frontmatter failed valid-session-schema are skipped (no double-reporting). A warning while existing vaults may still contain legacy T-general sessions; --strict escalates.

  17. Session Index (valid-session-index, warning): The derived .palee/index.md must parse, and its [[S-…]] session references must point at existing confirmed session notes. A stale or broken entry reports the missing session ID; the index is a rebuildable projection (canonical session notes are the source of truth — ADR-0008 decision 4), so findings never become errors and never gate the exit code by default; --strict escalates them like every other warning (ADR-0008 decision 3), and a rebuild restores correctness. Only session-shaped [[S-…]]/[[DRAFT-S-…]] links are index entries — a topic link or hand-added note link in the editable body is never treated as a session reference. A missing index never reports (fresh vaults have none), and an empty index is legal even when sessions exist (staleness-by-omission is deferred until the index format is finalized per the issue). An unreadable index (locked mid-scan) is reported by read-failure, not here.

  18. Hot Memory (valid-hot-memory, warning): The derived .palee/hot.md — the note that orients session start when the learner resumes — must carry the writer's identity (memory_id: H-active), its body must stay within the 250-word cap (MAX_HOT_WORDS, counted with the same whitespace-delimited countWords the writer's truncation uses, frontmatter excluded), and its last_session/active_topic references must point at existing session/topic notes. A missing hot memory never reports (fresh vault), an unparsable one is rebuilt rather than diagnosed field-by-field, and reference findings are suppressed on a read-incomplete snapshot (the referenced note may be exactly the one that failed to read). Findings never become errors and never gate the exit code by default — hot memory is a rebuildable projection (ADR-0008 decision 4); --strict escalates them like every other warning (decision 3), and a rebuild restores correctness.

  19. Safe Vault Paths (safe-vault-paths, error): Every PALEE-managed path in the collected snapshot — topic notes, scanned notes, and session notes — must resolve inside the configured vault. Paths normalize (\/) before validation so Windows separators validate identically; parent-directory traversal (../outside.md, including mid-path a/../../escape.md), absolute POSIX paths, and absolute Windows drive paths (C:/…) are boundary escapes and report as errors. This mirrors the containment policy the vault walker and roadmap import already enforce, making the write boundary explicit in validation output; symlink resolution itself stays owned by the walker.

Example Human-Readable Output (Failures Detected)

bash
$ palee validate
Validating vault: /Users/dev/ObsidianVault

Found 18 PALEE topics in 24 files

 Found 1 validation error(s):

 Dependency cycle detected: T-topic-a -> T-topic-b -> T-topic-a
    Rule: no-dependency-cycle

 Found 1 validation warning(s):

 Topic T-cloud-native depends on missing topic T-docker-missing
    Rule: no-missing-dependency

Assessment-Review Independence (enforced by regression tests, #40)

Assessment fields (conceptual, practical, debug, feynman, assessed_at, topic_mastery) and SM-2 review fields (last_quality, last_reviewed_at, due_at, ease_factor, interval_days, repetition, lapses) are independent state. Per the #40 design, independence is not a static vault rule — command-level mutation tests are the enforcement mechanism, because independence is about what commands write, not what the vault looks like. test/e2e/assessment-review-independence.test.ts pins the contract in both directions: palee review updates only SM-2 fields and preserves assessment data (including non-zero topic_mastery) byte-for-byte; palee roadmap import — the curriculum write path — preserves all seven SM-2 review fields byte-for-byte on reviewed topics, so an assessment-path mutation never clobbers review state unless an explicit confirmed review mutation is added.


4. Machine-Readable Output & Non-TTY Detection

All reporting commands support the PALEE automated JSON streaming contract (isJsonOutput()):

bash
# Direct JSON piping to jq for CI/CD checks
$ palee validate | jq .valid
true

# Extract total reviews due from dashboard
$ palee dashboard | jq .reviews_due
3

Structured Error Handling

When an error occurs (such as an unconfigured vault or a missing topic query in --topic), PALEE emits a structured error JSON object on stderr and exits with code 2:

json
{"error": "Topic not found: NonExistentTopic"}

5. Exit Codes for Reporting Commands

CommandExit Code 0Exit Code 1Exit Code 2Exit Code 3Exit Code 4Exit Code 5
palee dashboardSuccessfully displayed dashboard metrics or empty vault onboarding.N/AVault path not configured or directory does not exist.N/AN/AUnexpected runtime exception or calculation failure.
palee progressSuccessfully displayed vault progress summary, topic detail (--topic), or empty vault state.N/AVault path unconfigured, or topic query not found for --topic.N/AN/AUnexpected runtime exception or file read failure.
palee validateVault validation passed with 0 structural errors.N/AVault path not configured or invalid directory.Any validation error (malformed schema, topic ID, status, duplicate palee_id, missing dependency, cycle, or assessment-field shape); warnings also exit 3 under --strict.N/AUnexpected runtime exception or directory walk failure.

Released under the MIT License.