-
Notifications
You must be signed in to change notification settings - Fork 4
Releases: OPTIMETA/PAIDEIA-codex
PAIDEIA-codex 26.06.04
Update Catalog
26.06.04 update
The lecture-emphasis signal reaches the Codex edition
The Claude edition learned to read a second exam signal this cycle — what the professor emphasized out loud, imported from Exam Radar, OPTIMETA's Alt plugin. Parity is the whole premise of PAIDEIA-codex, so the Codex edition gets the same capability, written the Codex way.
$paideia-alt
Exam Radar reads your lecture recordings and ranks each topic by how hard the professor leaned on it out loud — an exam-probability signal independent of homework density, the one PAIDEIA has always tracked. Triage the topics into three zones, hit copy, and run $paideia-alt with the export pasted after it, or save it to materials/radar.md and run it bare.
Where the Claude edition splits a thin command over a separate skill, the Codex edition ships one self-contained skills/paideia-alt/SKILL.md reached through the $paideia-* invocation — but the work it does, and the files it writes, are identical by design.
Same artifacts, both editions
This is the parity rule from the 26.04.24 release doing its job. $paideia-alt writes the same canonical files the Claude edition writes:
course-index/radar.md— the canonical lecture-emphasis store, overwritten per import.course-index/coverage.md— aLecture emphasiscolumn and a lecture-vs-HW divergence section, folded into drill priority, with the homework-basedExam tierleft untouched. Homework density stays primary; emphasis is a second opinion, surfaced and never substituted.weakmap/weakmap_<ts>.md— a fresh weakmap seeded from the gold zone (lecture-hot and self-weak), never overwriting prior ones.
So a course folder forked between the two editions carries the Exam Radar signal across the boundary the same way it already carries patterns.md and errors/log.md. The divergence cases the signal exists to catch — 🎙 emphasized but never assigned, or homework-frequent but quiet in lecture — read identically on either runner.
An MCP server that boots even when the plugin root doesn't expand
The most important fix in this release has nothing to do with Exam Radar. On Windows, Codex passed the literal, un-expanded ${CODEX_PLUGIN_ROOT} string as PYTHONPATH into the MCP env block, so python3 could not import paideia_mcp.server during MCP initialization. The server never booted — and an MCP server that fails to start is the quietest possible failure: every $paideia-* skill that leans on it just stops working, with no obvious cause.
The launcher no longer trusts the shell to expand anything. It bootstraps paideia_mcp from CODEX_PLUGIN_ROOT when that resolves, and falls back to the local Codex plugin-cache checkout when it doesn't — independent of environment-variable interpolation, which turned out to be the one thing that wasn't portable across platforms. Verified by initializing an MCP ClientSession and calling course_phase through codex exec. Committing the absolute cache path would have "fixed" it on exactly one machine; this keeps it portable across users and plugin versions.
Desktop app install path
The README gains a Codex desktop-app install path and a screenshot grid beside the CLI instructions, matching the dual desktop/CLI split the Claude edition adopted.
PAIDEIA-codex is now an OPTIMETA project
PAIDEIA-codex has moved to the OPTIMETA organization, alongside PAIDEIA and Exam Radar. Stars, forks, and history transferred intact, and old TaewoooPark/PAIDEIA-codex URLs 301-redirect, so existing marketplace add commands and clones keep working. README install URLs and badges now point at OPTIMETA/PAIDEIA-codex.
Mechanics
$paideia-alt— import an Exam Radar export; with no argument, readsmaterials/radar.md.- New:
plugins/paideia/skills/paideia-alt/SKILL.md(self-contained). - MCP launcher hardened to boot without plugin-root variable expansion; dependency bootstrap added for
paideia-mcp. - Plugin and marketplace bumped to 0.3.0 (15 → 16 skills).
- Repository transferred to github.com/OPTIMETA/PAIDEIA-codex; old URLs redirect.
- No course migration. The Exam Radar artifacts are additive and identical to the Claude edition's.
Notes
Pairs with Exam Radar (OPTIMETA's Alt plugin) and the Claude edition of PAIDEIA, both released today.
Assets 2
PAIDEIA-codex 26.04.24
Update Catalog
26.04.24 update
Codex edition reaches study-graph parity
PAIDEIA-codex was born as a sibling of the Claude Code edition, not a fork. The user interface is different (Codex CLI, AGENTS.md instead of CLAUDE.md, a stdio MCP server instead of inline scripts), but the on-disk artifact is supposed to be byte-for-byte the same: course-index/patterns.md, errors/log.md, weakmap/weakmap_<ts>.md, cheatsheet/final.md. You should be able to fork a course folder out of the Claude edition into the Codex edition — or vice versa — and have the new runner pick up without friction.
For that to actually hold, the semantics that read those artifacts have to agree. In 0.1.x the Codex edition quietly disagreed on three of them. paideia_mcp.course_phase treated quizzes/*.md as evidence of drilling even when no problem had been solved. $paideia-blind wrote a parallel YAML schema (pattern_missed_initial: / strategy_error_type:) that every downstream reader silently skipped. And the answer-PDF lifecycle ended at "OCR succeeded" — the original scan kept living in answers/ forever, so the "most recently modified in answers/" heuristic inside $paideia-grade re-picked yesterday's file when the student uploaded a newer one today.
This release closes those disagreements. The phase ladder now advances only when the student has actually graded something. The error log has one shape across every writer — grade, blind, and any future drill. Graded scans are archived after OCR, so the next run sees the next file.
What this fixes
The richest cases are the ones where a course folder would look correct on disk but the Codex edition interpreted it differently from the Claude one:
- Audit a seeded patterns file. Dropping a
course-index/patterns.mdfrom last semester's fork no longer flips the phase todrill.paideia-mcp.course_phasenow gatesdrillon at least one gradedpattern:entry inerrors/log.md. An artifact that was never acted on is not the same signal as one the student produced, so it doesn't move the phase forward. - Audit a
$paideia-blinderror. Running$paideia-blind hw3-p2and failing on the pattern axis now writes the samepattern:/error_type:/source:keys that$paideia-gradewrites. The blind entry appears in the next$paideia-weakmapwithout manual edits, andpaideia-mcp.course_phase's top-miss counter picks it up immediately. In 0.1.x those entries were present on disk but invisible to every consumer, because every consumer pattern-matched onpattern:and blind wrotepattern_missed_initial:. - Audit a mock-exam phase transition.
mockfires only when anerrors/log.mdentry has asource:containingmock— i.e., a mock was actually graded. Seeding an emptymock/<ts>.mdno longer advances the phase, which used to let the display race ahead of the student's real progress. - Audit a second scan of the same assignment. After
$paideia-grade answers/hw3.pdfsucceeds, the MCP movesanswers/hw3.pdfintoanswers/_archive/hw3_<ts>.pdf. Re-running$paideia-gradewith no positional argument no longer re-picks the stalehw3.pdf; it picks the genuinely most recent file. The convertedanswers/converted/hw3.mdstays put and is version-controlled — only the bulky scan is archived. - Audit a cross-course Qwen3-VL prompt. The
qwen3-vlengine used to inject the phrase "math / physics course" into every page prompt regardless of course. It now readsCOURSE_NAMEfrom.course-metaand substitutes it, so a Complex Analysis folder no longer transcribes under a generic framing. The default fallback is unchanged ("math / physics") for folders without a.course-meta.
How it is used
Existing Codex users update by pulling main. Course folders require no migration: the phase detector reads errors/log.md the same way it always did, and nothing in 0.1.x could have written the legacy blind schema into a folder that also had graded entries (the legacy keys only appeared in blind-only folders). A fresh $paideia-init-course in a new course folder seeds the canonical schema directly via bootstrap.py's updated ERRORS_LOG_SEED comment.
In a fresh session:
$paideia-phasecomputessetup → diag → drill → mock → cram → coolfrom three orthogonal signals: artifact-exists, has-graded-entry, mock-was-graded. Create apatterns.mdbut never quiz:diag. Grade one problem:drill. Grade a mock:mock. Drop acheatsheet/final.md:cram. Delete them back: the phase regresses. Unlike 0.1.x, the display tracks what the student did, not what the filesystem declares.$paideia-gradereturns an extraarchived_tofield in itspaideia-mcp.grade_pdfresponse. The skill can surface the archive path in its closing line if the user cares; skipping the line is fine too, the archive happens regardless.$paideia-blindwrites one schema, and that schema is literally the canonical one frompaideia-grade/SKILL.md§6. A student migrating from 0.1.x sees their new blind entries participate in the weakmap immediately — no manual edits, no schema translation pass.
The phase tool owns correctness, not freshness
Claude Code's statusline.py re-renders the phase display on every prompt, which is why that edition introduced an mtime-indexed disk cache: the display has to be cheap enough to recompute hundreds of times per session, and the cache is the only way to keep that bounded on mature course folders. Codex CLI has no persistent statusline slot. The phase is surfaced on demand via $paideia-phase — which calls the MCP tool exactly once per invocation — or by other skills that want to know where the student is in the cycle (also once per call).
Because of that, paideia-mcp.course_phase has no cache layer and does not need one. Freshness is guaranteed by the fact that the tool re-reads errors/log.md, cheatsheet/, mock/, and .course-meta every time it runs. This is the right trade-off under Codex's semantics: the caller decided this was the moment to ask, so we read the disk. Adding a cache would save wall time only if the same skill called the tool repeatedly within a session without the course folder changing — which is not a pattern any existing skill exhibits.
This is also why the "mtime-indexed cache" patch from the Claude edition's 0.6.0 release is explicitly not ported: there is nothing here it would make faster, and introducing it would add an invalidation surface with no compensating win.
What this does not change
The three OCR engines (codex-native / qwen3-vl / tesseract), the ingest_pdfs / grade_pdf / build_course_index / course_phase MCP tool boundaries, the markdown contents of answers/converted/, the 15 skills, the coloring of $paideia-phase's output, and the .mcp.json wiring all behave identically to 0.1.x. A course folder set up under the old version continues to work without migration.
The codex-native engine is also unaffected: its prompt lives in paideia-grade/SKILL.md Step 2a, not in paideia_mcp/ocr/qwen3vl.py, so the course-name parameterization is specific to the Ollama path. Users who stay on the default engine see no prompt-level difference.
The Claude-edition SessionStart hook is intentionally not ported. Codex has no session-start hook mechanism: AGENTS.md is read every turn but it is static markdown, so it cannot inject live phase or top-miss state the way a Python script can. The user-invoked $paideia-phase is the Codex-native equivalent, and it has been the recommended first-turn command since the phase tool shipped.
Technical notes
plugins/paideia/paideia-mcp/paideia_mcp/phase.pygains_has_error_entries(regex-searcheserrors/log.mdfor^\s*pattern:\s*P\d+) and_mock_was_graded(iteratessource:lines and returns true if any containsmock).detect_phaseis split into single-responsibility branches on those helpers. Module docstring is updated to describe the activity-based ladder so future readers don't re-introduce file-existence gating by accident.plugins/paideia/paideia-mcp/paideia_mcp/ocr/qwen3vl.pyrenamesPROMPTtoPROMPT_TEMPLATEwith a{course}placeholder, introducesbuild_prompt(course_name), and hastranscribe_page/transcribe_pagesthread the resolved course name through the Ollama call._is_noise_sentenceis factored out of_dedupe_loopswith a Korean + English hedge-prefix tuple._strip_ngram_tail(text, n=5, min_repeats=3)trims trailing n-gram loops that survive sentence-level dedup._WARMUP_TIMEOUT = 60.0is separated from_PER_PAGE_TIMEOUT = 1800.0so a hung Ollama daemon at startup surfaces quickly instead of hiding under the long per-page ceiling.plugins/paideia/paideia-mcp/paideia_mcp/ocr/__init__.pyadds_resolve_course_name(project_root)which reads.course-meta::COURSE_NAMEviapaideia_mcp.phase.parse_meta.run_ocrplumbs the resolved course name through toqwen3vl.transcribe_pages;tesseractignores it (no prompt to parameterize).plugins/paideia/paideia-mcp/paideia_mcp/grade.pyadds_archive_if_under_answers(pdf, root). Fires only when the resolved PDF path is exactly two components deep under<root>/and the first component isanswers— so absolute-path targets outside the course root are left alone and already-archived files underanswers/_archive/aren't re-archived. Timestamp is UTCYYYYMMDD-HHMMSSZ. Idempotent on missing files (returnsNoneand leaves the source alone). Both response dicts (ocr-completeandrasterize-only) gain anarchived_tofield.plugins/paideia/skills/paideia-grade/SKILL.md§6 is marked canonical — the single source of truth for theerrors/log.mdYAML schema — with a note that downstream readers (paideia-mcp.course_phase,$paideia-phase,$paideia-weakmap) pattern-match onpattern:andsource:, and...