Skip to content

Fix the quality gates, the dead pytest module, and conventions.json packaging (#1) - #2

Merged
jstet merged 7 commits into
mainfrom
chore/green-quality-gates
Aug 19, 2026
Merged

Fix the quality gates, the dead pytest module, and conventions.json packaging (#1)#2
jstet merged 7 commits into
mainfrom
chore/green-quality-gates

Conversation

@jstet

@jstet jstet commented Aug 19, 2026

Copy link
Copy Markdown
Member

Why

The tree was clean but every quality gate was red, one pytest module had been silently dead since a fixture move, and a consumer hit a real packaging bug (#1). This branch fixes all of it and adds the handover documents for the three downstream repos.

Fixes

  • Quality gates — 7 committed TS files were unformatted, ARCHITECTURE.md had 9 markdownlint errors, knip reported @registry/* (a docs Vite alias, not a package) as an unlisted dependency, and ruff check . failed on three C901 complexity errors. --max-warnings tightened from 12 to 0, which the tree already satisfied.
  • conventions.json packaging (fixes tsc build does not emit dist/generated/conventions.json — prepare script leaves dist incomplete #1) — the library imported generated JSON at runtime with an import attribute, so whether dist/generated/conventions.json existed depended on tsc copying it as a side effect, and whether a consumer could bundle it depended on their bundler understanding with { type: 'json' }. codegen now emits src/generated/conventions.ts alongside the JSON and the library imports that; npm run build copies the JSON into dist/ regardless.
  • test_xlsform_pyxform.py — looked for <exampleDir>/xlsform.xlsx, which 7a892c3 moved to <exampleDir>/generated/xlsform.xlsx. The parametrisation collapsed to zero cases, so its guard failed and none of the 14 fixtures were validated. The guard now skips when no generated/ directory exists at all, since that is gitignored codegen output.
  • markdownlint-clean skill output — the two files under skills/cdl-survey-types/references/ are generated, so their lint errors were fixed in codegen/emit_skill.py rather than by hand: warnings emit one >-joined blockquote (MD028), a blank line precedes the naming list (MD032), and a new _tidy() collapses seam blank lines (MD012). The two emitters were split to clear C901 at the same time. Regenerated output is byte-identical apart from the whitespace fixes.

Documents

HANDOVER_FORMTRANSFORM_APP.md, HANDOVER_QWAC.md and HANDOVER_FORMULAID.md — one per downstream repo, each linking a sequenced set of issues filed in that repo (formtransform-app #4#10, qwac #9#12, formulaid #9#12). They record the verified API surface each consumer needs and the two real gaps: no response-data CSV emitter (keeps Pyodide alive in the app), and no labelled machine-readable type catalogue (prefLabel/useWhen live in registry/, which "files": ["dist"] keeps out of consumers' node_modules).

Verification

npm run validate (7 gates), 707 vitest, 33 pytest, uv run ruff check ., ruff format --check, and scripts/check-drift.sh all pass. For #1 specifically: pristine clone → npm run builddist/generated/ holds both conventions.js and conventions.json; no import attributes remain in dist/; bun build dist/index.js --target=browser bundles 55 modules; npm pack --dry-run lists both files.

Not verified here: a fresh npm install of the package end to end, because it fetches xlsx from cdn.sheetjs.com and remote tarballs are blocked in the environment this was built in.

🤖 Generated with Claude Code

jstet added 7 commits August 19, 2026 10:05
The committed tree failed several `npm run validate` gates:

- prettier: seven src files were committed unformatted
- markdownlint: ARCHITECTURE.md had list-spacing and trailing-newline errors
- knip: `@registry/*` (a docs vite alias, not a package) was reported as an
  unlisted dependency, and the entry/project config belonged under a
  `workspaces` block now that the repo has one

Also drop `docker-compose` and `ruff` from knip's ignoreBinaries (neither is
invoked anywhere) and tighten the lint gate to `--max-warnings 0`, which the
tree already satisfies.
The two files under skills/cdl-survey-types/references/ are codegen output, so
their markdownlint errors could only be fixed at the source:

- consecutive per-type warnings were emitted as separate blockquotes separated
  by a blank line (MD028); they are now one `>`-joined blockquote
- the names/choice-codes list had no blank line above it (MD032)
- blocks each carry a trailing newline and are then joined with another, which
  left double blank lines at the seams (MD012); a new `_tidy()` normalises the
  whole document once instead of making every emitter track blank lines

Splitting the two emitters keeps them under the C901 complexity limit that
`ruff check` enforces: `_question_types_md` now delegates to `_variants_by_base`,
`_constraint_lines` and `_type_section`, and `_syntax_md` walks a
(rule, section-builder) table instead of one long chain of `if` blocks.

Regenerated output is byte-identical apart from the whitespace fixes above.
7a892c3 restructured registry/entities into definition/fixtures/generated and
moved each entity's xlsform.xlsx into generated/, but test_xlsform_pyxform.py
still looked for it at the exampleDir root. The parametrisation collapsed to
zero cases, so its guard test failed and none of the 14 fixtures were actually
validated. `generated/` is gitignored codegen output, so the guard now skips
(rather than fails) when no generated/ directory exists at all, and still fails
if the directories are there but hold no xlsx.

Also extract helpers from test_testB_question_order_in_groups to clear its C901
complexity error: dict-or-object field access, the gid/question groupings, the
per-group order assertion, and the ordered-titles lookup were all inline.
formtransform-app still runs two unrelated conversion engines: the old
xlsform2lstsv npm package for the TSV path, and Pyodide plus the survey2ddi
Python wheel for the DDI paths. This library covers all of that except the
response-data CSV, so the app can drop both engines for a single dependency.

The document records the verified API surface the app needs, the one gap that
blocks deleting Pyodide (buildDdiXml uses `submissions` for caseQnty only —
there is no TS equivalent of survey2ddi's build_data_csv), and links the
sequenced issues filed in CorrelAid/formtransform-app (#4-#10).

It also carries the scope notice the app's site is missing: the site presents
FormTransform as an XLSForm converter, while the library deliberately rejects
anything outside the registered CDL subset. Stating that on the page turns a
confusing error into an expected one.
Neither repo runs a competing converter, so unlike formtransform-app this is a
drift problem rather than a dependency one: both carry hand-maintained copies of
knowledge the registry owns.

qwac renders qwacback data — whose types come from here — using labels derived by
string surgery on the type name, and encodes the type list a second time in its
preview components, with nothing to catch a registry type that has no component.
formulaid's skill teaches ~1000 lines of general XLSForm specification, well
beyond what the pipeline accepts, and its app builds workbooks with xlsx and
never validates them.

The document records what the package can hand over today, the one thing it
cannot (no labelled type catalogue: prefLabel/useWhen live in registry/, which
"files": ["dist"] keeps out of consumers' node_modules), and links the sequenced
issues filed in CorrelAid/qwac (#9-#12) and CorrelAid/formulaid (#9-#12). It also
flags the three details that went stale in qwacback's own untracked brief.
Fixes #1.

Every module that needed the generated conventions imported them as
`generated/conventions.json` with an import attribute. That made the published
package depend on two things outside its control: tsc copying the JSON into
`dist/` (it only does so as a side effect of some .ts file importing it), and the
consumer's bundler understanding `with { type: 'json' }`. A consumer installing
from GitHub reported `dist/generated/conventions.json` missing and the bundle
failing to resolve it.

codegen now emits `src/generated/conventions.ts` alongside the JSON — same
payload, same inferred types — and the library imports that. tsc always emits it
and every bundler resolves it, so neither failure mode is reachable any more.

The JSON stays as the artifact for non-TypeScript consumers, and `npm run build`
copies it into `dist/generated/` after tsc so it is present in an installed
package regardless of what imports it.

Verified: clean `npm run build` produces dist/generated/{conventions.js,
conventions.json}, no import attributes remain in dist, and `bun build
dist/index.js --target=browser` bundles 55 modules without error.
The combined document made a reader of either repo skim past half of it, and the
two plans have little in common beyond one shared upstream gap: qwac needs a
labelled type catalogue for its UI, formulaid needs it for a type union.

HANDOVER_QWAC.md now carries the label/preview-component drift, the DDI upload
page's silence about what it validates, and the qwacback notes (Go-fmt-serialised
DDI fields, structured validation findings, the stale details in that repo's own
brief). HANDOVER_FORMULAID.md carries the skill's over-broad XLSForm reference,
the unvalidated workbook output, and the cdl-survey-types bundling contract.

Each document repeats the allowlist framing and the missing-catalogue constraint
so it stands alone, and cross-links the other. The eight issues in the two repos
were edited to point at their own document.
@jstet
jstet merged commit f26b07f into main Aug 19, 2026
3 checks passed
@jstet
jstet deleted the chore/green-quality-gates branch August 19, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tsc build does not emit dist/generated/conventions.json — prepare script leaves dist incomplete

1 participant