Skill v1.0.1
currentAutomated scan100/100+5 new
version: "1.0.1" name: maintain-python-api description: Maintain and extend gbdraw's public Python API while preserving contract snapshots, CLI behavior, rendering outputs, and error semantics. Use when modifying gbdraw/api, public exports or signatures, DiagramOptions or other public dataclasses, interactive SVG builders, table readers, render/export APIs, high-level diagram builders, Python API documentation, or related contract and regression tests.
Maintain the Python API
Keep gbdraw.api thin, truthful, and independent of CLI parsing. Reuse the real implementation owner, make failures observable to library callers, and prove that public contracts and default diagrams remain stable.
Inspect the contract first
- Read
CLAUDE.md,docs/PYTHON_API.md,docs/CLI_Reference.md, the
current public-contract tests, and any related ADR that is present. Do not assume an old plan or ADR still exists.
- Inspect
git statusand the affected diff before editing. In this repository,
dirty in-scope code is not protected: replace it when the requested fix requires a different implementation. Leave unrelated files outside the change.
- Inventory the affected exports, signatures, dataclass fields/defaults, callers,
documentation examples, and contract snapshots.
- Trace each public option through the adapter and assembler to the function that
actually consumes it. Do not assume successful validation means successful forwarding.
- For a cross-surface change, make a matrix of Circular/Linear against the package
root API, gbdraw.api, CLI, Web generation, and saved-session replay. Record the public name, default, validation owner, and final consumer for each affected concept. Distinguish an intentional compatibility alias from the current contract.
Preserve dependency direction
- Put shared behavior in a CLI-independent owner and make both CLI and API call it.
- Keep
gbdraw.apito re-exports and small validation or translation adapters. - Do not import CLI parsers or private CLI helpers from the public API.
- Make Web generation submit the same versioned typed request that sessions
serialize. Do not maintain a second Web-to-CLI argument translation as the rendering contract.
- Reuse existing table validators, renderers, and config models instead of copying
their logic.
- Add a public option class only when it reduces the combined caller and
implementation complexity. Treat release-backed low-level assemble_* functions as compatibility entry points, not as the preferred public workflow.
Keep one resolution path
- A surface adapter translates input shape only. It must not read domain files,
compile rules, select records, or reproduce planner policy.
- Send unresolved inputs through one planner. The planner owns validation,
normalization, and the choice of downstream builder.
- Carry resolved tables, compiled rules, and other reusable values in the prepared
request. Record loading, drawing, analysis, and interactive metadata must consume those same prepared values instead of reopening or recompiling their sources.
- When output names depend on resolved records, expose a reusable plan boundary.
Preflight every diagram and sidecar path from that plan before building, then build the same plan instead of planning twice or assembling a drawing to discover paths.
- Do not inspect implementation source text to discover capabilities, and do not
monkey-patch an internal loader or assembler to inject data. Add an explicit typed boundary owned by the receiving layer.
- Do not trade away a coherent boundary or required regression coverage to save
model tokens. Among complete designs, prefer the smallest durable change.
Apply repository-specific patterns
Options and typed config
- Normalize an option once, then test that the normalized value reaches its real
downstream owner. Include aliases and the unchanged default in forwarding tests.
- Treat
LabelsFilteringConfig.rawas payload, not ordinary nested dataclass state.
A naive dataclasses.asdict() adds another raw layer and can hide label DataFrames from downstream code. Preserve filtering.as_dict() losslessly when applying typed config overrides.
- Accept either an in-memory DataFrame or a file path for a logical table, never
both. Raise ValidationError for ambiguous input.
- Compare DataFrame contents and downstream behavior, not object identity.
Render and export
- Keep the library API strict even if the CLI intentionally warns and skips an
unavailable converter.
- Return only paths that exist when the function returns. Wrap conversion failures
in a typed GbdrawError subclass and preserve the original exception as cause.
- Test stale outputs and overwrite behavior so an old file cannot make a failed
conversion look successful.
- Keep browser/Pyodide conversion distinct from successful local file creation.
Interactive SVG
- Own context construction in a CLI-independent module.
- Re-export the context type, enrichment function, and builder through
gbdraw.api. - Add context as an optional keyword-only argument to byte/file rendering APIs.
- Verify context-free interactive output still works and static SVG bytes do not
change.
- Compare CLI and API metadata schema for feature and match popups.
Public surface and sessions
- Update
gbdraw.api.__all__andtests/fixtures/public_contract.jsontogether.
Build the new contract with the test helper, then review and apply only intended changes; never accept a broad snapshot rewrite blindly.
- Re-export public table models and readers from
gbdraw.api.iowhile sharing the
existing CLI-compatible validation owner.
- Do not expose session regeneration as CLI argument strings. Require CLI-independent
typed request models and pure conversion first; otherwise record the non-public decision in an ADR.
- Keep persisted-session migration in the session compatibility adapter. Fresh
build/render functions accept typed current artifacts, never raw session mappings; expose migration evidence on a session-specific result wrapper.
- Treat compatibility as evidence-based. Support the current writer plus persisted
versions found in the first-parent history of main or a release tag; keep session, request, cache, and metadata schema namespaces distinct.
- For a format bump, retain a representative main-era fixture as a positive
control, regenerate branch-owned fixtures with the current writer, and reject discarded branch-only versions in Python and Web tests. Do not accept or chain migration through an intermediate schema that existed only on the active branch.
Keep documentation executable
- Add capability-oriented recipes to
docs/PYTHON_API.mdusing public imports. - Use
canonicalonly when it is part of a named contract in the code. Otherwise,
state the exact schema version, field, precedence rule, or behavior.
- Do not call a check a
smoke test. Name the feature and expected result, and
report the exact command that checks it.
- Inspect every fixture used by an example. Show only options that affect the
fixture and output; do not list feature types absent from the input record.
- Make optional-tool examples deterministic with fixtures or precomputed data.
- Remember that
tests/test_api_library_usage.pyexecutes every Python code block
sequentially in one namespace. Make each block valid in that execution model.
- Explain intentional low-level or non-public boundaries instead of implying that
an unstable workflow is supported.
- When
docs/CLI_Reference.mdreproduces command help, compare the sorted
--option sets from live Circular and Linear help with the corresponding documented blocks. Report missing and extra options; visual inspection is not enough.
- Check relative Markdown links in every touched document and fail the review for
links to absent repository files.
Verify without contaminating evidence
- Run focused regression and contract tests for the changed boundary.
- For public request or facade work, start with:
pytest tests/test_api_requests.py tests/test_api_request_render.py tests/test_api_library_usage.py tests/test_dead_api_cleanup.py -q.
- For Web request/session work, also run
node tests/web/session-request.test.mjs and the relevant browser test.
- Run
python -m pytest tests/ -v -m "not slow". - Lint touched Python files first, then run the repository lint gate. Distinguish
new failures from a recorded pre-existing baseline.
- Check the working tree and public contract diff after every broad validation run.
Treat reference generation as a write operation. TestGenerateReferences may rewrite tracked SVG fixtures before comparison, which can invalidate the regression evidence. Record the initial status of tests/reference_outputs/, prefer comparison-only tests, and run generation only when intentionally refreshing an approved geometry change. Never discard a reference file that was dirty before the test run, and never mechanically accept reference changes for a non-geometry fix.
Finish the change
- Confirm every new public symbol imports from
gbdraw.api. - Confirm defaults retain previous behavior and non-default values reach the owner.
- Confirm error types and returned artifacts match actual outcomes.
- Confirm CLI behavior remains intentional and uses the shared owner.
- Confirm docs, ADRs, contract snapshots, and tests describe the implemented state.
- Report targeted/full test counts, lint scope, reference-output status, and any
pre-existing failure separately.