Design — vibeqc.output (unified output, logging, and citation surface)¶
Status (2026-05-18, claude/mystifying-mcnulty-38ab6e):
implemented and landed on main. This document remains the
design contract; the five decisions locked at the bottom of this
section all held through implementation. The phased roadmap below
(O1–O7) plus the pre-v1.0 follow-ups (R1–R7, D1–D5, D7a) have
shipped — see the § Implementation status
section at the end of the doc for the per-phase commit map. The
runner.py dispatch-overhaul shipped as D7a (the OutputWriter
coordinator now owns the .system manifest lifecycle); the
remaining cosmetic conversion of individual writer calls to
dispatch_role (D7b) was assessed and deferred to the v1.0
coordinator rewrite — rationale in the Implementation-status
section.
Goal: consolidate the currently diffuse output, logging, and
citation surface of vibe-qc into a single coordinator package
(vibeqc.output) that
produces a declarative output plan at job start, before any compute, so the
vqqueue knows what files to monitor and copy back and the user can run--vibeqc-dry-runto inspect expected artefacts;carries every existing artefact (
.out,.system,.molden,.scf.jsonl,.perf,.traj,.dump) through a single dispatch layer with zero behaviour change on the first cut;adds new always-on artefacts users expect from a QC program (final-geometry
.xyz, citation files.bibtex+.references) and the periodic / volumetric formats (.cif,POSCAR,.xsf,.cube) in subsequent phases;emits a citation block on every run, assembled from a single TOML-backed reference database, covering vibe-qc itself plus every basis / functional / method / linked library actually exercised by the job.
Non-goal (in this design): the high-blast-radius full
coordinator rewrite scheduled before v1.0. This doc covers the
thin-layer phase landing in v0.8.x onwards. The public API
(OutputPlan, vibeqc.output.citations.assemble, the file-format
identities) is being designed to survive the v1.0 rewrite intact —
internals under output/_adapters/ are explicitly transitional.
Locked decisions¶
Module name:
vibeqc.output. Submodulevibeqc.output.citationsfor the reference database + writers. (§ Module shape)Refactor depth — Phase O1: thin adapter layer over existing writers.
runner.pykeeps its current call sites;OutputPlan+ manifest-status hooks are added; no writer module is relocated. (§ Phased roadmap)Citation database location + enforcement: single source of truth at
python/vibeqc/output/citations/database.toml. CI test fails if any method / functional / basis-set / library used by vibe-qc lacks a route. Dev chats updating a feature own the matching DB update; clause added toAGENTS.md. (§ Citation database)Manifest layout: the existing
{stem}.systemTOML grows two new sections,[plan](declared at job start) and[outputs](filled in as artefacts land). No new sibling file. The fixed-shape rule fromdocs/user_guide/output_files.md§ “Sample manifest” still holds — sections never disappear, keys never get renamed. (§ .system schema extension)basissetdev-conditional citations:
database.tomlonmaincovers only bundled assets. The 87 BSE-fetched basis sets get their own siblingdatabase_basissetdev.tomlthat lives on thebasissetdevbranch and is loaded conditionally. Matches the CLAUDE.md § 4 rule that basissetdev does not merge into main for v0.8.x. (§ basissetdev integration)
Context: where output content lives today¶
python/vibeqc/runner.py:297 is the
single user-facing entry point that emits the full output family. It
dispatches to six separate writer modules with a mix of always-on /
opt-in flags:
Sibling file |
Writer |
Trigger |
|---|---|---|
|
always |
|
|
|
|
|
always |
|
|
|
|
|
|
|
|
|
|
|
exception or non-converged SCF |
Verbosity gates: verbose= / VIBEQC_VERBOSE, progress= /
VIBEQC_LIVE_LOGGING, use_logging=. Live-progress emission is
funnelled through
progress.ProgressLogger.
What is missing:
No plan emitted before compute starts. A
vqdaemon watching a job’s workspace can’t tell “these are the files I should expect” until they land — and a crashed job that wrote nothing is indistinguishable from a successful job that finished in 80 ms.No central registry of “actually written files”. vq currently tar-streams the entire workspace (
vibe-queue/src/vq/fetch.py:170); there is no concept of “fetch only the vibeqc-declared outputs”.No citation surface at runtime. Users reading
output-h2o.outsee the SCF table but no list of papers to cite. Cross-reference todocs/citing.mdis manual.CITATION.cff,docs/user_guide/functionals.md§ Citations, and the.g94basis-set headers all carry their own human-readable copies — three sources of drift.libxc’sxc_func_info_get_referencesis unwrapped. vibe-qc links libxc but does not query the per-functional reference list it exposes.Periodic + crystal formats unevenly covered.
poscar.py,xsf.py,cube.py, andbands.pyexist but are not wired intorun_joband have no per-job artefact convention.
Module shape¶
python/vibeqc/output/
__init__.py # public API: OutputPlan, PlannedFile, OutputWriter,
# register_writer, assemble_citations
plan.py # OutputPlan + PlannedFile dataclasses
writer.py # OutputWriter — owns stem, dispatches to adapters
manifest.py # .system [plan] + [outputs] section emission/update
formats/
__init__.py
xyz.py # {stem}.xyz writer (P1)
cube.py # {stem}.cube wiring around existing cube.py (P2)
crystal.py # {stem}.cif / POSCAR / .xsf wiring (P2)
population.py # {stem}.population.{txt,json} (P2)
fchk.py # placeholder until P3
citations/
__init__.py # public API: assemble(), to_bibtex(), to_plain()
registry.py # CitationRegistry + route walk
database.toml # the single source of truth
bibtex.py # → {stem}.bibtex
plain.py # → {stem}.references
libxc_bridge.py # wraps xc_func_info_get_references()
_adapters/ # TRANSITIONAL — to be inlined in v1.0 rewrite
__init__.py
molden_adapter.py # wraps io.molden.write_molden in OutputWriter shape
scf_log_adapter.py # wraps scf_log.format_scf_trace
system_info_adapter.py # wraps system_info.write_system_manifest
structured_adapter.py # wraps structured_log.StructuredLog
perf_adapter.py # wraps perf.PerfTracker
crash_dump_adapter.py # wraps crash_dump.dump_on_failure
trajectory_adapter.py # wraps io.trajectory
Existing writer modules stay in place during Phase O1. The
_adapters/ shim package provides an OutputWriter-compatible
wrapper for each, so runner.py ends up calling a single dispatcher
instead of six unrelated functions. The adapter shims are explicitly
flagged transitional in their docstrings — they get inlined into
their respective formats/*.py cousins during the pre-v1.0 rewrite.
OutputPlan and PlannedFile¶
Single source of truth for “what does this job produce”. Frozen dataclasses, hashable, JSON-serialisable:
# python/vibeqc/output/plan.py
from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal
OutputRole = Literal[
"log", # .out
"manifest", # .system
"orbitals", # .molden, .fchk
"geometry", # .xyz, .cif, POSCAR, .xsf
"density", # .cube (density)
"orbital_vol", # .cube (per-orbital)
"population", # .population.{txt,json}
"citations", # .bibtex, .references
"trajectory", # .traj
"perf", # .perf
"structured", # .scf.jsonl
"crash", # .dump + .dump.*.npy
"checkpoint", # .h5 (P4)
"bands", # .bands.dat, .bands.gnuplot
]
@dataclass(frozen=True)
class PlannedFile:
role: OutputRole
path: Path
format: str # "text" | "toml" | "json" | "ndjson" | "molden" |
# "xyz" | "cif" | "cube" | "ase-traj" | "bibtex" | ...
always: bool # True = guaranteed; False = conditional
description: str # one-line, surfaced by `vq submit --dry-run`
# Filled in during/after the run by writers:
written: bool = False
bytes: int | None = None
sha256: str | None = None
wall_time_s: float | None = None
@dataclass(frozen=True)
class OutputPlan:
stem: Path
job_kind: Literal["molecular_scf", "periodic_scf", "opt",
"hessian", "post_scf"]
method: str # "RHF", "RKS", "UHF", "UKS", ...
basis: str # name as given to run_job
functional: str | None
files: tuple[PlannedFile, ...]
options_digest: str # short hex hash of the resolved SCF options
# (lets vq detect "did this run change?")
OutputPlan.from_run_job_kwargs(...) is the factory used by
runner.py; it consumes the same kwargs the user passed (output,
write_molden_file, optimize, perf_log, structured_log,
citations, …) plus method/basis/functional and emits the frozen
plan. The plan is serialised into the .system manifest’s [plan]
section before any compute starts.
The conditional files (crash, trajectory) are emitted with
always=False and a description so vq can communicate to the user
“may produce: output-h2o.dump (only on SCF failure)”.
.system schema extension¶
The existing .system shape
(docs/user_guide/output_files.md)
stays a superset — [vibeqc] [host] [cpu] [memory] [python] [libraries] [validation] [run] are unchanged. Two new sections land:
# --- existing sections unchanged ---
[vibeqc]
version = "0.8.0"
codename = "Grimme's Gecko"
git_sha = "..."
# ...
# --- NEW: declared at job start, before any compute ---
[plan]
stem = "output-h2o"
job_kind = "molecular_scf"
method = "RHF"
basis = "6-31g*"
functional = "" # "" not nil for fixed-shape rule
options_digest = "f3a2c1..." # short hex
status = "running" # → "complete" | "crashed"
declared_at_iso = "2026-05-18T10:42:00Z"
[[plan.files]]
role = "log"
path = "output-h2o.out"
format = "text"
always = true
description = "Human-readable SCF log (banner, iter table, orbital block)."
[[plan.files]]
role = "manifest"
path = "output-h2o.system"
format = "toml"
always = true
description = "Runtime manifest (this file) — hardware, libs, plan, outputs."
[[plan.files]]
role = "orbitals"
path = "output-h2o.molden"
format = "molden"
always = true
description = "Molecular orbitals — coefficients, energies, occupations."
[[plan.files]]
role = "geometry"
path = "output-h2o.xyz"
format = "xyz"
always = true
description = "Final geometry in Angstrom."
[[plan.files]]
role = "citations"
path = "output-h2o.bibtex"
format = "bibtex"
always = true
description = "BibTeX entries for every method/basis/library cited."
[[plan.files]]
role = "citations"
path = "output-h2o.references"
format = "text"
always = true
description = "Plain-text reference list (Chicago-ish formatting)."
[[plan.files]]
role = "crash"
path = "output-h2o.dump"
format = "toml"
always = false
description = "Post-mortem snapshot — only written on SCF failure."
# --- NEW: filled in as artefacts land; status flips at job end ---
[outputs]
finished_at_iso = "" # filled at job end
status = "running" # → "complete" | "crashed"
# Per-file rows are appended as each writer reports completion. Same
# path as the matching [[plan.files]] row, plus the runtime fields:
[[outputs.files]]
path = "output-h2o.out"
written = true
bytes = 4231
sha256 = "ab12cd34..."
wall_time_s = 0.082
[[outputs.files]]
path = "output-h2o.system"
written = true
bytes = 1247
sha256 = "..."
wall_time_s = 0.001
# ... etc
Atomicity rule: [plan] is written exactly once, at job start.
[outputs] is rewritten in place after every file lands — the whole
.system file is rewritten atomically (write-to-tmp + rename) to
avoid a half-written manifest if the job is killed mid-update.
vq watch pattern (Phase O4): the daemon polls {stem}.system,
parses [outputs].status. "running" means alive; "complete"
means ready to fetch; absence of either after the job-end timestamp
crosses a threshold means crashed. The [[plan.files]] rows tell vq
which paths to fetch.
vq integration contract¶
Three hooks, all on the vibe-qc side. The vq-side work (Phase O4) is
small: a single new field on JobSpec and a dry-run pre-flight.
Pre-flight dry-run (Phase O3):
python input-h2o.py --vibeqc-dry-run.run_jobshort-circuits afterOutputPlanis built — it writes{stem}.systemwith the[plan]section populated and[outputs].status = "dry_run", prints a one-line summary to stdout, and exits 0. No SCF runs.At job start (real run):
run_jobwrites the.systemmanifest with[plan]+[outputs].status = "running"before any compute. vq’s daemon sees the file appear in the workspace.As artefacts land: each writer reports back to the
OutputWritercoordinator (Phase O1 plumbing); coordinator atomically rewrites.systemwith updated[[outputs.files]]rows. On final return / exception,[outputs].statusflips to"complete"or"crashed"and[outputs].finished_at_isois stamped.
vq side (Phase O4, separate PR in vibe-queue/):
# vibe-queue/src/vq/spec.py — additions
class JobSpec:
...
expected_outputs: list[str] = [] # populated from .system [[plan.files]]
output_stem: str | None = None # for the vq UI
last_output_status: str | None = None # "running" | "complete" | "crashed"
vq submit runs --vibeqc-dry-run when the input is a Python script
that imports vibeqc.run_job. The parsed plan populates
expected_outputs. vq list shows outputs: 5/8 from the
[outputs] section. vq fetch --outputs-only tars only the planned
files, not the whole workspace.
Citation database¶
File location and format¶
Single TOML file at
python/vibeqc/output/citations/database.toml. Hand-maintained.
Sphinx renders docs/citing.md and docs/user_guide/functionals.md
§ Citations from this file via a new vibeqc-cite-block directive
(P3). Editing the rendered docs by hand is disallowed; CI checks the
generated content matches.
Schema sketch (real entries cover v0.8.0-on-main coverage in full — roughly 40–60 references):
# python/vibeqc/output/citations/database.toml
# ---------------------------------------------------------------- #
# REFERENCE ENTRIES
# ---------------------------------------------------------------- #
# Each entry has a stable key (used by routes and bibtex_key).
[entries.vibeqc_software]
kind = "software"
bibtex_key = "peintinger_vibeqc"
authors = ["Peintinger, Michael F."]
title = "vibe-qc: a quantum-chemistry code for molecules and solids"
year = "{{VIBEQC_YEAR}}" # rendered from banner at write time
version = "{{VIBEQC_VERSION}}"
license = "MPL-2.0"
url = "https://vibe-qc.com/"
notes = """Cite this for every vibe-qc calculation. A peer-reviewed
publication is forthcoming; this software citation is the canonical reference
until then. Pulled from CITATION.cff at write time."""
[entries.pob_tzvp_2013]
kind = "article"
bibtex_key = "peintinger_pob_tzvp_2013"
authors = ["Peintinger, Michael F.", "Vilela Oliveira, Daniel", "Bredow, Thomas"]
title = "Consistent Gaussian basis sets of triple-zeta valence with polarization quality for solid-state calculations"
journal = "Journal of Computational Chemistry"
volume = 34
issue = 6
pages = "451--459"
year = 2013
doi = "10.1002/jcc.23153"
[entries.vilela_oliveira_rev2_2019]
kind = "article"
bibtex_key = "vilela_oliveira_pob_rev2_2019"
authors = ["Vilela Oliveira, Daniel", "Laun, Joachim", "Peintinger, Michael F.", "Bredow, Thomas"]
title = "BSSE-correction scheme for consistent Gaussian basis sets of double- and triple-zeta valence with polarization quality for solid-state calculations"
journal = "Journal of Computational Chemistry"
volume = 40
issue = 27
pages = "2364--2376"
year = 2019
doi = "10.1002/jcc.26013"
[entries.libxc_2018]
kind = "article"
bibtex_key = "lehtola_libxc_2018"
authors = ["Lehtola, Susi", "Steigemann, Conrad", "Oliveira, Micael J. T.", "Marques, Miguel A. L."]
title = "Recent developments in libxc — A comprehensive library of functionals for density functional theory"
journal = "SoftwareX"
volume = 7
pages = "1--5"
year = 2018
doi = "10.1016/j.softx.2017.11.002"
[entries.pbe_1996]
kind = "article"
bibtex_key = "perdew_pbe_1996"
authors = ["Perdew, John P.", "Burke, Kieron", "Ernzerhof, Matthias"]
title = "Generalized Gradient Approximation Made Simple"
journal = "Physical Review Letters"
volume = 77
issue = 18
pages = "3865--3868"
year = 1996
doi = "10.1103/PhysRevLett.77.3865"
# ... and so on for every entry currently surfacing in
# docs/citing.md and docs/user_guide/functionals.md § Citations.
# ---------------------------------------------------------------- #
# ROUTING RULES
# ---------------------------------------------------------------- #
# Maps from "what the user requested" to "which entries fire".
# Always-blocks fire unconditionally for the category; per-key blocks
# fire when the matching string is seen.
[routes.software]
always = ["vibeqc_software"]
[routes.integrals]
always = ["libint_valeev"]
[routes.basis_sets]
"pob-tzvp" = ["pob_tzvp_2013"]
"pob-dzvp-rev2" = ["pob_tzvp_2013", "vilela_oliveira_rev2_2019"]
"pob-tzvp-rev2" = ["pob_tzvp_2013", "vilela_oliveira_rev2_2019"]
"6-31g*" = ["pople_6_31g_1972", "hariharan_pople_1973"]
"cc-pvdz" = ["dunning_1989"]
# ... full coverage of every bundled .g94 in basis_library/
[routes.functionals]
# libxc itself is always cited when any DFT functional is used.
_libxc_always = ["libxc_2018"]
"pbe" = ["pbe_1996"]
"pbe0" = ["adamo_barone_1999"]
"b3lyp" = ["becke_1993", "stephens_1994", "vosko_1980"]
"pw91" = ["perdew_pw91_1992"]
# ... full coverage of every functional in functionals.md.
[routes.methods]
"d3bj" = ["grimme_2010", "grimme_2011"]
"d4" = ["caldeweyher_2019"]
"diis" = ["pulay_1980", "pulay_1982"]
"ediis" = ["kudin_2002"]
[routes.libraries]
# Per-linked-library citations that fire whenever vibe-qc links them.
# spglib only fires when periodic; libecpint only when ECPs are used.
"spglib" = ["togo_tanaka_2018"]
"libecpint" = ["shaw_gilbert_libecpint"]
Runtime assembly¶
vibeqc.output.citations.assemble(plan: OutputPlan) -> list[Entry]
walks the routes table and returns an ordered, deduplicated list:
routes.software.always— vibe-qc itself, first.routes.integrals.always— libint.routes.basis_sets[plan.basis.lower()].routes.functionals._libxc_always(if DFT) +routes.functionals[plan.functional.lower()].Method-specific (DIIS / EDIIS / dispersion / spglib for periodic / libecpint when ECPs used).
A miss in routes.basis_sets or routes.functionals raises in
strict mode (CI test fails) and warns in user mode (job still
proceeds; .references carries a # WARNING: no reference for ...
line so the gap is visible).
Writers¶
{stem}.bibtex— BibTeX entries, one per cited reference, in citation order. Plain ASCII, suitable for\bibliography{}use.{stem}.references— plain-text Chicago-ish numbered list (the same content that gets appended to{stem}.out)..out“## References” section — same content, embedded so the text log is self-contained.
Dev-chat contract (AGENTS.md clause to add)¶
Drafted clause:
Citation database ownership: Any merge that adds, removes, or renames a method, functional, basis set, ECP, dispersion model, or third-party linked library to vibe-qc MUST update
python/vibeqc/output/citations/database.tomlin the same merge:
add or remove the
[entries.<key>]block,update the matching
[routes.<category>]row,add a check in
tests/test_citations.pythat the feature triggers the expected references.CI fails if
vibeqc.list_methods()/vibeqc.list_functionals()/ the bundled.g94inventory contains an entry not present in[routes.*].docs/citing.mdanddocs/user_guide/functionals.md§ Citations are auto-rendered fromdatabase.tomlvia thevibeqc-cite-blockSphinx directive — do not hand-edit those sections for new methods. Seedocs/design_output_module.mdfor the full schema.
basissetdev integration¶
Per CLAUDE.md § 4, basissetdev does not merge into main for v0.8.x.
The 87 BSE-fetched basis sets accordingly live in a sibling DB on the
basissetdev branch:
main:python/vibeqc/output/citations/database.tomlonly.basissetdev:database.toml+database_basissetdev.toml. The registry loads both files when present, with basissetdev entries layered on top.
When basissetdev eventually merges (post-v0.8.x, paper-aligned), the two files merge into one — the schema is identical and the only difference is which branch carries the entries. No format change needed at merge time.
Phased roadmap¶
Each phase is a coherent, testable, mergeable chunk. PRs land on
main (or on a feature branch and squash-merge — maintainer’s call
when scope justifies it).
Phase O1 — Foundation (this chat, next PR)¶
Create
python/vibeqc/output/package skeleton.OutputPlan+PlannedFiledataclasses;OutputPlan.from_run_job_kwargs().OutputWritercoordinator with_adapters/shims for every existing writer..systemschema extension:[plan]written at job start;[outputs]updated as files land; status flip at end.runner.pyswitches its writer dispatch to go throughOutputWriter, but every artefact still has bit-identical content to the pre-Phase-O1 output.Tests: round-trip plan, manifest status flips through running → complete → crashed, every existing test in
tests/still passes unchanged.No new artefacts in this phase.
Phase O2 — Citation database + writers¶
python/vibeqc/output/citations/database.tomlpopulated with v0.8.0-on-main coverage (software, libint, libxc, pob-*, B3LYP/PBE/PBE0/PW91, D3BJ, spglib, libecpint, every bundled.g94).assemble()+to_bibtex()+to_plain()implemented.{stem}.bibtexand{stem}.referencesemitted by default.“## References” block appended to
{stem}.out.tests/test_citations.pysmoke-tests every functional / basis / method path exercised by the existing test suite.AGENTS.md clause added.
CHANGELOG entry in
[Unreleased].
Phase O3 — .xyz + dry-run + CLI¶
{stem}.xyz(always-on for molecular jobs; periodic emits a natural-cell.xyzplus the P5 crystal formats).--vibeqc-dry-runCLI flag honoured byrun_job.vibeqc-cite <stem>console-script entry point that re-reads the.systemmanifest + database and reprints citations for already- run jobs.
Phase O4 — vq integration (separate PR in vibe-queue/)¶
JobSpec.expected_outputs+JobSpec.output_stem+JobSpec.last_output_status.vq submitpre-flight: when input is a.pycontainingrun_job, call--vibeqc-dry-runand parse the[plan]section.vq listsurfaces outputs progress (5/8 outputs written).vq fetch --outputs-onlytars only[[plan.files]]paths.vq watch(new verb, optional in this PR): tails the live{stem}.outand prints status transitions from the.systemmanifest.
Phase O5 — Periodic + crystal formats¶
Wire
OutputPlanintoperiodic_runner.py.{stem}.cif/POSCAR/{stem}.xsfalways for periodic jobs; consolidate existing scattered writers underoutput/formats/crystal.py.Periodic-job plan tests.
Phase O6 — Volumetric + population¶
.cubefor orbitals/density on demand (run_job(..., write_cube=...)).{stem}.population.{txt,json}clean separation from.outfor Mulliken / Löwdin / Mayer / dipole. The corresponding block stays in.outfor human reading; the structured file is for downstream parsing.
Phase O7 — Documentation sweep¶
docs/user_guide/output_files.mdupdated phase-by-phase (CLAUDE.md § 5 lightweight cadence — done same-session as each phase).docs/citing.mdanddocs/user_guide/functionals.md§ Citations switched to render fromdatabase.tomlvia the Sphinx directive.
Out of scope for the thin-layer phase¶
.fchk(Gaussian-compat formatted checkpoint) — P3 of the eventual v1.0 sprint.HDF5 full-state checkpoint (
{stem}.h5) — same..bands.dat/ band-structure plot script — relevant once multi-k bands are wired throughrun_job.The relocation of
io/molden.py,system_info.py,structured_log.py,perf.py,crash_dump.pyintooutput/formats/— the v1.0 rewrite.
Pre-v1.0 full refactor (recorded for continuity)¶
The pre-v1.0 sprint will:
Inline the
_adapters/shims into theirformats/*.pycousins.Move
scf_log.py,system_info.py,structured_log.py,perf.py,crash_dump.py,io/molden.py,io/trajectory.pyundervibeqc/output/.Re-point every import across the test suite. High blast radius; must land in a quiet sprint.
Public API (
OutputPlan,vibeqc.output.citations, file-format identities, the.systemschema) survives unchanged. Tests that hit the public surface keep working; tests that import the old module paths are flagged for rewrite as part of that sprint.
Compatibility surface¶
The thin-layer phase is additive on the user-facing surface:
New
[plan]+[outputs]sections in.system— old parsers that only read[vibeqc] [host] [cpu] [memory] [python] [libraries] [validation] [run]keep working (TOML readers ignore unknown sections).Two new default-on artefacts (
{stem}.bibtex,{stem}.references,{stem}.xyz) — opt-out viacitations=False/write_xyz=False.No existing artefact changes shape or path.
No existing kwarg’s default flips. The new kwargs default to values that preserve old behaviour wherever ambiguity exists.
VIBEQC_NO_CITATIONS=1env-var kill switch for batch contexts.
CHANGELOG [Unreleased] carries one entry per phase as it lands.
Implementation status¶
All of the O1–O7 phased roadmap plus the pre-v1.0 follow-ups have
landed on main (2026-05-18). Per-phase commit map:
Phase |
What landed |
Status |
|---|---|---|
O1 |
|
shipped |
O2 |
citation database ( |
shipped |
O3 |
|
shipped |
O4 |
vq integration — |
shipped (vq v0.6.14) |
O5 |
periodic |
shipped |
O6 |
|
shipped |
O7 |
|
shipped |
R1–R7 |
writer modules relocated under |
shipped |
D1 |
role-driven |
shipped |
D2 |
CIF writer + dispatcher registration |
shipped |
D3 |
|
shipped |
D4 |
citation |
shipped |
D5 |
citation routes for the v0.9.0 meta-GGA + RSH functionals |
shipped |
D7a |
|
shipped |
Deviation from the locked decisions: decision 2 (“Phase O1 thin
adapter layer; no writer module is relocated”) held for Phase O1,
but the writer relocation (R1–R7) was then carried out as the
pre-v1.0 follow-up the Non-goal paragraph anticipated. The
output/_adapters/ directory sketched in § Module shape
was not needed — the relocation used in-place backward-compat shims
at the old module paths instead, which preserves import identity
more cleanly than an adapter indirection. The actual layout under
output/formats/ is the relocated writers themselves.
The runner.py dispatch-overhaul — what shipped (D7a) and what
didn’t (D7b): D7a is the load-bearing change. run_job no longer
writes {output}.system with a bare end-of-job
write_system_manifest call — an OutputWriter, constructed
before the SCF, owns the manifest for the whole run: [plan] +
[outputs].status="running" written up front, crash() /
finish() at the exit paths, an end-of-job record-sweep filling
[[outputs.files]]. That removes the genuine “ad-hoc dispatch”
(an uncoordinated manifest write) and gives vq a live status
signal. run_periodic_job already had the equivalent.
D7b — converting each individual write_molden(...) /
write_xyz(...) / write_population(...) / cube / citations call
site to OutputWriter.dispatch_role(...) — was assessed and
deliberately not pursued in the thin-layer phase. Those call
sites are not “ad-hoc dispatch”; they are explicit, well-
instrumented writer calls (each wrapped in its own plog.stage +
PerfScope and emitting a per-writer .out line). Routing them
through dispatch_role as it stands today would: (a) lose that
per-writer perf / progress instrumentation unless the dispatcher
API is widened to thread a perf/progress context; (b) re-run the
population property computation twice, because write_population
is one writer that emits two files and the per-PlannedFile
dispatch model invokes it once per row; (c) need a new
("orbital_vol", "cube") adapter for per-MO cubes. All churn on
the most-contended file in the tree for no gain over D7a’s
record-sweep, which already produces a correct [outputs]
section. The full dispatch_role conversion belongs to the
v1.0 coordinator rewrite, where the writers themselves are
restructured into adapters and the one-writer-many-files case
(population) gets a dispatch model that fits.
See also¶
docs/user_guide/output_files.md— user-facing reference for the output file family.docs/citing.md— citation guidance; the per-feature reference blocks render fromdatabase.tomlvia the Phase-O7bvibeqc-citeSphinx directive.docs/announcement_output_module_2026_05_18.md— the dev-chat memo (citation-DB ownership, the new CLIs).docs/release_process.md— release cadence, documentation cadence (CLAUDE.md § 5).CLAUDE.md§ 4 — basissetdev branch policy.CLAUDE.md§ 10 — external-codes / library-only policy; citation routing must respect the library/program line.AGENTS.md— dev-chat manifest where the citation-DB ownership clause lands.