Known issues & documented failure routes
Repo-wide register. This page is a register of known, deliberately-deferred
behaviors and documented failure routes: behaviors we have discovered and
understood but are (for now) choosing not to prevent, plus quirks worth keeping
in mind. Each entry carries a stable KI-NN id — a permanent identifier
(e.g. KI-01) that is referenced from code, tests, and docs (e.g. “pass-through
per KI-01”) so a behavior can be pointed to from a single canonical place.
Scope. Entries describe bugs, tripping hazards, codebase/convention oddities, and in general issues that need dedicated development work (refactoring, cleanup, new features) or a standing decision. Small transient issues do not belong here — e.g. doc-sweep leftovers or other nits are fixed directly rather than registered (keeps the register lean).
Conventions:
IDs are permanent and never reused. Add new issues with the next free
KI-NN; do not renumber existing ones.Status vocabulary:
accepted= known, intentionally not prevented now;planned-fix= slated for a named branch;watch= quirk to keep in mind, no action yet;implemented= addressed by a shipped change (kept for history);blocker= must be resolved before the relevant branch can merge.Severity vocabulary (impact if encountered, orthogonal to status):
critical= silent wrong/empty output or data loss;major= breaks a real flow or compounds technical debt;minor= works but fragile / incorrect-in-edge-cases;nit= cleanup. Anitis registered only when fixing it now is genuinely out of scope — otherwise fix it directly instead of filing it.Entry headings carry status and severity:
`status · severity`.Each entry states: what, evidence (
file:linewhere applicable), current decision, revisit trigger. Reference related issues by id.
KI-01 — Malformed fixed cell computes a wrong pattern silently accepted · critical
What. A metrically symmetry-illegal cell (e.g. cubic phase with a≠b≠c) that
is not refined is used verbatim: GSAS-II computes peak positions from the raw,
non-symmetric reciprocal metric tensor and returns a “successful” refinement with
no error or warning.
Evidence. Verified against the pinned GSAS-II: a distorted cubic cell with cell-refine off → d-spacings follow the distorted metric (Rwp 65.8%, reported as a successful refinement).
Decision. Pass-through, do not prevent. Preventing symmetry-illegal cell values is the future recipe builder’s job. This behavior is also arguably useful — e.g. deliberately simulating symmetry-breaking/strain effects with a fixed distorted cell.
Revisit. When a recipe builder exists, or if a cell-vs-symmetry metric validation option is added. If added, it should be opt-in / warn so the legitimate symmetry-breaking-simulation use survives.
KI-02 — Oblique (monoclinic/triclinic) per-parameter cell holds are limited implemented (0.26), limitation inherent · minor
What. Holding a reciprocal-metric A-term equals holding a direct cell
parameter only for orthogonal cells. For monoclinic, only the unique-axis length
is independently holdable; a, c, and the oblique angle are coupled. For
triclinic, no direct parameter is individually holdable.
Evidence. Verified against the pinned GSAS-II: holding A2 in a monoclinic
cell → c still moves (6.000→6.032).
Decision (implemented in schema 0.26). PowderLine models the cell as
Laue-class DOF-groups (coupled oblique parameters form a single group) and
applies group-OR: a group refines if any member is requested, and only
whole unrefined groups are held (powderline/constraints.py,
cell_dof_groups). PowderLine does not validate or reject mixed flags
within a group — it is the engine/translator; symmetry-consistent flags are
the upstream recipe builder’s responsibility. Consequence for a malformed
recipe: a false flag inside a group with a true member is not honored
(deterministic, documented here). Not a GSAS-II limitation we can work around:
a direct-parameter hold on an oblique cell is a nonlinear constraint outside
GSAS-II’s linear constraint system.
Revisit. Transparency (which groups refined together) arrives with the provenance work. True independent oblique direct-parameter holds would require a fundamentally different (nonlinear) constraint approach — unlikely to change.
KI-03 — Site symmetry & multiplicity are derived, and failures are swallowed planned-fix · major
What. PowderLine derives each atom’s site symmetry and multiplicity at load
via G2spc.SytSym, inside a bare except Exception: pass, defaulting to ""
and 1 on failure. The recipe never states these, and a derivation failure is
invisible.
Evidence. kicker.py:1170-1176.
Decision. Candidate for dedicated work: make site symmetry and multiplicity explicit, validated recipe fields, with an immediate safety patch to stop swallowing the exception. Improves resilience and decouples structural interpretation from GSAS-II for future multi-engine support.
Revisit. At minimum, stop swallowing the exception when the surrounding area is next touched.
KI-04 — Recipe→output provenance is implicit; no round-trip planned-fix · major
What. Outputs are CSV/txt keyed by GSAS-II parameter names. Which parameters
were actually varied vs held-by-user vs held-by-symmetry vs frozen-by-limit is
implicit (inferred from absence in varyList). No recipe-shaped output exists, so
sequential/multi-step refinement needs external output→input mapping.
Evidence. GSAS-II outputs are keyed by parameter name only; per-parameter
disposition is inferred from absence in varyList, with no recipe-shaped output.
Decision. Add explicit per-parameter disposition and a JSON recipe-style output enabling round-trip / sequential refinement. Deferred to dedicated work.
Revisit. When designing sequential-refinement support, or when a downstream data-management consumer needs machine-readable provenance.
KI-05 — GSAS-II refinement failures & constraint warnings don’t raise planned-fix · major
What. proj.refine() does not raise on refinement failure (singular matrix,
etc.); constraint cascades/conflicts and frozen-variable notices go to stdout /
Rvals['msg'] only. PowderLine currently inspects none of these, inferring
success solely from hist.residuals['wR'] being non-None.
Evidence. Verified against GSAS-II internals: proj.refine() does not raise
on failure, and constraint/frozen-variable notices go to stdout / Rvals['msg']
only, which PowderLine does not currently inspect.
Decision. Surface diagnostics via proj.data structures + captured console
text, without adopting do_refinements. Minimal failure-detection may land
early; fuller provenance later.
Revisit. Bundle with the provenance work.
KI-06 — GSAS-II parameter-limit (min/max) semantics are clamp-and-freeze watch · minor
What. GSAS-II enforces parmMin/parmMaxDict not as in-loop box constraints
but by resetting out-of-range variables to the limit after a refine and
freezing them from the next one. Frozen state persists in the .gpx. Coordinate
limits must target Ax/Ay/Az even though the varied variable is dAx/dAy/dAz.
Evidence. Verified against GSAS-II internals (parmMin/parmMaxDict
clamp-and-freeze semantics; coordinate limits target Ax/Ay/Az while the varied
variable is dAx/dAy/dAz).
Decision. min/max deferred to the general constraints & limits work. When implemented, document the semantics honestly and surface frozen variables; do not imply hard bounds.
Revisit. With the general constraints & limits work.
KI-07 — Space-group setting (hexagonal vs rhombohedral, origin choice) is unvalidated watch · critical
What. R -3 c (hexagonal axes) and R -3 c R (rhombohedral axes) are both
accepted but imply different cell parameterizations and A-term equivalence sets.
Two-origin space groups default to origin 2; a recipe with origin-1 coordinates
under such a symbol is silently wrong (no CIF symops exist to trigger GSAS-II’s
auto-shift, since PowderLine is file-less).
Evidence. Verified against GSAS-II internals: two-origin space groups default to origin 2, and the CIF-only auto-shift never fires because PowderLine is file-less (no CIF symops).
Decision. Candidate for the convention-validation work: at minimum echo the inferred Laue class / setting back to the user. Origin-1 detection is hard without symmetry operators and better handled in the recipe builder.
Revisit. Convention-validation branch, or recipe-builder work.
KI-08 — Sphinx build emits reST warnings from RecipeModel docstring fixed · nit
What. A clean pixi run docs (which runs sphinx-build -W --keep-going,
so this did already fail the build) printed 266 warnings
(re-measured 2026-08-27), all pre-existing or cosmetic:
~230 autodoc cross-reference warnings (
py:class/py:objreference target not found) for pydantic internals —PlainSerializer,ConfigDict, and the fully-expandedAnnotated[..., PlainSerializer(lambda ...)]field types inschema.pythat autodoc can’t resolve (the renderer additionally mis-splits thePlainSerializer(func=..., return_type=..., when_used=...)repr into several bogus sub-references);~15 docutils WARNING/ERROR lines from the
RecipeModeldocstring, whose embedded markdown-style ```json migration examples don’t parse as reST (unexpected indentation, inline-literal start without end);~19 myst warnings (header-level jumps in
known_issues.md, and slug-style anchor cross-refs incross-platform-guide.md’s table of contents);~12 Pygments highlighting-failure warnings from ```json fences in
DEVELOPMENT.md/TROUBLESHOOTING.mdcontaining///#comments and...ellipses (illustrative, not strictly valid JSON);one stray
pandas.core.frame.DataFramecross-reference (pandas’ own intersphinx inventory only indexes the publicpandas.DataFramepath);one
html_static_path entry '_static' does not existwarning.
Evidence. Reproduced by pixi run docs-clean && pixi run docs.
Decision. Fixed:
added
docs/_static/.gitkeepso the configuredhtml_static_pathexists;reworded the
RecipeModeldocstring to use a proper.. code-block:: javascriptdirective and double-backtick literals instead of markdown fences/single-backticks;relabeled the illustrative, comment-bearing
json fences asjavascript (tolerant of//comments and...) inDEVELOPMENT.mdandTROUBLESHOOTING.md;fixed a handful of docstrings in
kicker.pywhoseReturns:sections (e.g.is_template_file,extract_refined_params_from_project,calculate_cell_esds_from_A_matrix,_extract_fit_profile) were misparsed by Napoleon as bogusname (type):pairs;docs/conf.pynitpick_ignore_regex/nitpick_ignorefor the unresolvable pydantic-PlainSerializerandpandas.core.frame.DataFramenoise; fixed thepydandticintersphinx key typo; setmyst_heading_anchors = 4socross-platform-guide.md’s TOC anchors resolve; and promotedknown_issues.md’s### KI-NNheaders to##(the file has no other H2, so H1→H3 was a level skip).
Revisit. Closed; kept for history.
KI-09 — Withdrawn
Filed during the PR #23 review; withdrawn 2026-07-16 (the reported behavior was intentional, not a defect). Id retained so it is never reused.
KI-10 — stop_server() force-kill escalation raises on Windows and is untested implemented · minor
What. stop_server() escalates to os.kill(pid, signal.SIGKILL) when the
server has not exited 5 s after SIGTERM. signal.SIGKILL does not exist on
Windows, so the escalation branch raises AttributeError there instead of
force-killing. The branch is rarely reached on Windows (Python maps
os.kill(pid, SIGTERM) to TerminateProcess, a hard kill, so the graceful
loop usually succeeds), and no test on any platform drives the timeout path —
the unit tests cover only the graceful sequence
(tests/test_gsas_server_unit.py, alive_states = iter([True, False])).
Evidence. src/powderline/gsas_server.py:371;
tests/test_gsas_server_unit.py:157.
Decision. Fixed: the escalation now uses
getattr(signal, "SIGKILL", signal.SIGTERM) — on Windows,
os.kill(pid, SIGTERM) maps to TerminateProcess, the correct hard kill — and
unit tests drive the escalation path (tests/test_gsas_server_unit.py).
Revisit. Closed; kept for history.
KI-11 — Server discovery is split between PID file and port probe; an orphaned server is unmanageable watch · minor
What. gsas-server status|stop trust the PID file
(<tempdir>/powderline_gsas_server.pid), while GSASClient trusts an HTTP
/health probe on the port-file/default port (19471). If the PID file is lost
(crash, tempdir cleanup, or the starting process group being killed) the server
keeps serving: clients silently use it while status reports “not running” and
stop cannot stop it (there is no HTTP shutdown endpoint). A related latent
bug in the auto-start path — the child env joined PYTHONPATH with a
hardcoded ':' instead of os.pathsep (wrong separator on Windows; benign in
the pixi env, where powderline is pip-installed) — has since been fixed; the
discovery/lifecycle mismatch below remains open.
Evidence. Reproduced live: /health on 19471 answered
(uptime_seconds≈13261) with no
.pid/.port files present and status reporting “not running”.
src/powderline/gsas_server.py:332-347 (is_server_running = PID file),
src/powderline/gsas_client.py:40-49 (is_server_available = port probe),
gsas_client.py:180 (the former hardcoded ':', now os.pathsep).
Decision. File for a server-lifecycle robustness pass: align status/
stop with the client’s probe (e.g. status also checks /health; add an
HTTP shutdown endpoint, or have stop fall back to the PID reported by
/health).
Revisit. Next branch touching gsas_server.py/gsas_client.py.