Skip to content

feat(ide): engine lens schema CLI and a schema-driven Steps renderer (BACKLOG #235) - #226

Open
wshallwshall wants to merge 5 commits into
mainfrom
plan-steps-param-schema
Open

feat(ide): engine lens schema CLI and a schema-driven Steps renderer (BACKLOG #235)#226
wshallwshall wants to merge 5 commits into
mainfrom
plan-steps-param-schema

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Closes BACKLOG #235. Adds an engine lens schema CLI and an IDE renderer driven by it, including OQ1 enum dropdowns.

The schema is emitted by the engine rather than duplicated in the IDE, so the two cannot drift -- a second hand-maintained copy of a parameter schema is a divergence waiting to happen, and the renderer reading the engine's own output removes the possibility rather than documenting against it.

BACKLOG #248 is queued behind this merging -- brief at .git/mefor-coord/brief-248-NOTE-ROW.md.

Per-lane notes, owner decisions and merge notes: .git/mefor-coord/handoff/LANE-HANDOVER-backlog-session-2e228e.md (not published).

Verified: 11 files; merge-tree against origin/main reports 0 conflicts; working tree clean at handoff.

…t widgets (BACKLOG #235)

The Steps editor's per-op input widgets are now derived from the transform
vocabulary's own Python type hints, instead of a hand-maintained IDE-side table
that drifts from the signatures.

Engine (new messagefoundry/lens_schema.py + a `lens schema [--json]` CLI):
- op_param_schema() derives {op -> [param]} from actions.__all__ and
  diagnostics.__all__ via stdlib inspect + typing (eval_str=True) -- no new
  runtime dependency (ADR 0076 section 6.5). Covers BOTH sources, so no row kind
  bypasses the schema-driven renderer (the diagnostic ops log_note/checkpoint
  included).
- The `lens schema` subcommand lazy-imports the module and starts no server,
  mirroring `hl7schema --json`.
- Per-param contract: {name, kind, required, keyword_only} plus optional choices
  (enum), nullable (X | None), and default (JSON scalar only -- a non-serializable
  sentinel like code_lookup's _UNSET is omitted so json.dumps never raises).

IDE (cli.ts / stepsView.ts / stepsModel.ts / stepsWebview.js):
- cli.ts shells `lens schema` (execFile, the same path as `lens parse`); stepsView
  fetches it once and threads an optional schema through the render chain (undefined
  keeps byte-identical text inputs, so every existing caller is unchanged).
- A pure resolveWidget() maps kind -> widget for EDITABLE params only (gating
  unchanged, ADR 0076 section 5): int/float -> number input, enum -> select,
  everything else and any unmapped op -> the current text input. code/control rows
  stay read-only.
- Retype fix: a number field posts a JS number, preserved through buildEditRequest
  to the engine, so `lens rewrite` renders an int literal (6) rather than retyping
  it to a string ("6") -- a latent corruption on the currently-editable int args.

Tests: tests/test_lens_schema.py (derivation, dual-source coverage, CLI smoke, and
the committed-fixture sync guard) and ide steps-schema.test.ts (renderer +
widget-resolver + edit round-trip), each falsified against a planted defect. Full
pytest -q green (10346 passed, 843 skipped); IDE Extension Host suite green (648).

Deliberate scope: did NOT touch ADD_MENU_CATALOG/TOOLBAR_INSERT_DEFAULTS (structural
insert templates with no Python signature to derive from); did NOT narrow the four
closed-set str params to Literal[...] (owner-gated -- so enum -> dropdown is built
but dormant until an op emits an enum kind).

NOT-DEPLOYED beta: no prior schema contract to preserve, so no compatibility shim.
…t (BACKLOG #235)

Flip #235's status banner only, from not-started to in-progress: the
engine-emitted param schema (the `lens schema` CLI) and the schema-driven IDE
renderer are built and committed (64a0e12). The item is NOT closed -- the
enum-to-dropdown widget is dormant until the owner rules on OQ1 (narrowing the
four closed-set str params to Literal).

Scope: only #235's banner line under its own heading changed. The ranked table
and the four census distribution lines were NOT touched, and the census was NOT
recomputed (not-started -> in-progress is open -> open, so the distribution is
unchanged anyway).
…ps renders enum dropdowns (BACKLOG #235)

OQ1 follow-on to the engine-emitted Steps param schema: narrow the closed-set
string arguments to Literal[...] so `lens schema` projects them as `enum` (with
their choice list) and the IDE renders a dropdown instead of a text input --
the item's headline widget, previously built but dormant.

- actions.py: convert_case mode -> Literal["upper","lower","title"]; pad_field
  side -> Literal["left","right"]; arith_field op -> Literal["+","-","*","/"];
  date_diff_field unit -> Literal["days","years","hours","minutes"]. The runtime
  if/elif ValueError guards are kept: the Literal defends the author-time typed
  caller and drives the dropdown; the guard still catches a dynamically-supplied
  bad value at run time. The intentional two-layer overlap is documented once in
  the module docstring.
- Regenerated ide/src/test/fixtures/lens-schema/op-schema.json (the four ops now
  carry kind "enum" + choices); test_ide_fixture_in_sync keeps it faithful.
- New test_closed_set_param_becomes_enum pins the enum derivation (falsified:
  reverting a Literal flips kind enum->str and reds the test).

No test_actions.py change needed: CI mypy is `mypy messagefoundry
messagefoundry_webconsole` (tests/ is not type-checked) and Literal is not
enforced at run time, so the negative-guard tests still raise ValueError and
pass unchanged. stepsModel.ts already prepends an out-of-set literal as the
selected <option>, so a hand-authored value outside the choice set displays
correctly.

Verify: ruff + ruff format clean; mypy messagefoundry clean (the 21 remaining
are pre-existing optional-extra import errors, none in touched files); IDE
Extension Host suite 649 passing; full pytest 10346 passed / 843 skipped. The
one full-suite failure (test_installed_coord_hooks push_guard.py:
installed-vs-committed hash mismatch) is PRE-EXISTING and environmental -- a
concurrent edit of the shared coord hook by another session; proven independent
of this change by a control arm (it fails identically with all OQ1 changes
stashed, and its hashes moved between two runs 22 min apart).
…wing (BACKLOG #235)

Flip #235's status banner only, from in-progress to closed. With OQ1 landed
(48b829b), all the item's widgets are live: int-to-number, the retype-trap fix,
and enum-to-dropdown for the four closed-set ops. The code-set picker is N/A (no
editable code-set literal to attach to) and code/control rows stay read-only, so
the item's scope is complete.

Scope: only #235's banner line under its own heading changed. The ranked table
and the four census distribution lines were NOT touched, and the census was NOT
recomputed (in-progress -> closed; the coordinator reconciles the distribution).
@wshallwshall
wshallwshall enabled auto-merge (squash) August 6, 2026 00:40
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.

1 participant