feat(ide): engine lens schema CLI and a schema-driven Steps renderer (BACKLOG #235) - #226
Merged
Conversation
…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
enabled auto-merge (squash)
August 6, 2026 00:40
wshallwshall
added a commit
that referenced
this pull request
Aug 7, 2026
…ted helpers (BACKLOG #337) (#224) * fix(checks): handler-security missed getattr indirection and PHI logged in undecorated helpers (BACKLOG #337) Two execution-verified recall gaps in `_check_handler_security` (ADR 0144), closed and pinned by tests. Gap 1 -- ambient-authority / impure-transform saw only a literal Name/Attribute chain, so a `getattr(os, "system")("id")` indirection (the outer call's func is itself a Call) was never resolved and never flagged. `_dotted_call_name` now splices a *constant* `getattr(mod, "name")` into the chain (`getattr(os, "system")` -> `os.system`), so the ambient-authority deny-list sees it; the same shared resolver flags a `getattr(time, "time")()` wall-clock read for impure-transform. A *dynamic* `getattr(os, name)`, a `globals()["os"]` subscript, and any other call-shaped receiver stay unresolved (None), so benign reflection is not flagged. The opt-in Semgrep leg still carries no getattr pattern (recorded as a residual, not fixed here). Gap 2 -- phi-to-log was decorated-scope only, so it never opened the undecorated `_<feed>_transforms.py` helpers the decompose-by-role convention (docs/CONNECTIONS.md, #226) steers field-level PHI handling into. phi-to-log now scans every function body -- a decorated @router/@handler OR an undecorated helper -- keyed on the first positional parameter as the message symbol. impure-transform stays decorated-scope ONLY: widening it too would re-flag the shipped `_pdf_mdm_transforms.py` ingest-time wall-clock fallback and red the read-only samples/config calibration gate (the trade ADR 0144 records). Proven both directions against the real samples before landing. Severity is unchanged and deliberately conservative -- it holds in BOTH sandbox postures. The lint is advisory by default (required only under --strict-handler-security) and pre-deployment. An evasion reaches only host actions the ADR 0087 sandbox does not confine: DEFAULT_FORBIDDEN_MODULES (pipeline/sandbox.py) blocks socket/ssl/asyncio/multiprocessing, the I/O-bearing messagefoundry.* subpackages and cryptography, but not os or subprocess. So under mode=off the author already holds in-process execution, and under mode=subprocess an evasion still reaches neither the DEK nor the audit chain -- no PHI-exposure path, no runtime-behaviour change ("a filter, not a fix"). To be re-scored upward when ADR 0147 (OS-level default-deny; Proposed, no code) lands. MessageFoundry is a not-deployed beta, so a miss "would slip past a deploying site's CI", never "PHI is exposed". Docs in this commit describe the same change: ADR 0144's Decision-section phi-to-log bullet is corrected to the widened every-function-body scope (it still asserted decorated-scope, contradicting the amendment and the code) and its amendment blockquote is added; the master-test-plan row for this test file is refreshed (line count 987 -> 1,079 and the three scoping regimes). The BACKLOG #337 banner flip is a separate follow-on commit. Tests (tests/test_checks_handler_security.py): amb_pos_getattr_os_system, amb_neg_getattr_dynamic_attr, impure_pos_getattr_time_time, phi_pos_undecorated_helper_logs_msg, plus test_widened_phi_to_log_does_not_widen_impure_transform and test_phi_to_log_undecorated_helper_non_message_local_is_clean. Each was falsified by reverting the fix: the two getattr positives red when the splice is removed; the phi positive reds when the loop reverts to decorated-scope; the impure-transform guard reds when impure-transform is widened (which also reds the real samples calibration); the discrimination test reds when phi-to-log ignores the message symbol; the dynamic-getattr negative reds when the splice over-fires on a non-constant attr. * backlog: flip #337 to done (handler-security getattr + undecorated-helper recall gaps closed) Banner-only edit of docs/BACKLOG.md item #337: the open `[filed]` banner becomes a closed `[done]` banner recording the resolution. Isolated from the code+tests+docs commit per the banner-flip rule. Only #337's banner line changed. The ranked table and the four census distribution lines are untouched, and the census was NOT recomputed -- the coordinator reconciles it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes BACKLOG #235. Adds an engine
lens schemaCLI 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-treeagainstorigin/mainreports 0 conflicts; working tree clean at handoff.