feat: convert script handling to pocos#162
Merged
Merged
Conversation
Introduces StepRegistry, IStepFactory, StepMetadata, ParamMetadata, StepNotes, and StepPlatformNotes. The registry discovers step POCOs via reflection at assembly load, indexes them by name and id, and bridges their factory delegates into the legacy StepXmlFactory and StepDisplayFactory surfaces. No consumers read from the registry yet; this commit only introduces the infrastructure. Pilot POCOs and consumer rewires land next.
…POCOs Each step owns its StepMetadata (name, id, category, help URL, params, notes) and is discovered by StepRegistry via reflection. No per-POCO [ModuleInitializer] needed; registry populates and bridges to legacy factories at assembly load. - BeepStep: zero-param canonical template. - SetErrorCaptureStep: single-boolean with ParamMetadata and tooltip description sourced from agentic-fm snippet comment zone. - IfStep: rewritten to implement IStepFactory with block-pair metadata. Drops StepCatalogLoader dependency. Synthesizes a minimal StepDefinition for legacy FmScript.ToDisplayLines indent logic until that consumer migrates. Shared [ModuleInitializer] kept for sibling control-flow steps not yet migrated (Else, End If, Loop, etc.); explanatory comment added. - Zero-loss audits inline in each POCO's class doc comment. Test fixtures previously using Beep as a "catalog-known, no-POCO" canary swap to Halt Script (SealedStepPreservationTests, RawStepAllowListTests) now that Beep is itself typed.
FmScriptCompletionProvider reads exclusively from StepRegistry now. Adds
a Monaco-snippet synthesizer that emits bracketed step templates with
tab-stop placeholders (e.g. "Set Error Capture [ \${1:On} ]") so that
accepting a multi-param step completion inserts the full form with the
first value pre-selected for editing. Without this, users saw
"Set Error Capture On" instead of "Set Error Capture [ On ]".
StepRegistry.Initialize() is called from App.OnFrameworkInitializationCompleted
so reflection cost lands at a predictable moment; lazy init on first
lookup remains as a safety net.
Pilot-time regression: completions now show only POCO-backed steps
(Beep, Set Error Capture, If). Full coverage returns as the sweep adds
the remaining 189 POCOs. Codified by CompletionProviderTests.NonPocoStep_NotSuggested.
- Elevates the display-extension style guide from docs/step-definitions.md into its own document with the three extension forms, parsing precedence, the zero-loss audit requirement, and explicit guidance on when to drop hidden XML state rather than round-trip it (Restore on If as the canonical drop example). - Adds the sweep-phase plan documenting tier ordering (A-D), per-step TDD workflow, PR cadence, consumer migration schedule, deletion schedule, coverage verification, regression containment, and the gotchas learned during pilot execution. - Replaces the inline style-guide section in step-definitions.md with a pointer to the new dedicated doc.
Tier A sweep — completes the zero-param bucket. Each step implements IStepFactory with a three-attribute Step element (enable/id/name) and no child content. Registry reflection discovers them automatically. Steps: Check Found Set, Check Record, Close Popover, Commit Transaction, Copy All Records/Requests, Copy Record/Request, Correct Word, Duplicate Record/Request, Edit User Dictionary, Exit Application, Flush Cache to Disk, Freeze Window, Go to Next Field, Go to Previous Field, Halt Script, Modify Last Find, New File, New Record/Request, Omit Record, Open Edit Saved Finds, Open Favorites, Open File Options, Open Find/Replace, Open Help, Open Hosts, Open Manage Containers, Open Manage Data Sources, Open Manage Database, Open Manage Layouts, Open Manage Themes, Open Manage Value Lists, Open Record/Request, Open Script Workspace, Open Settings, Open Sharing, Open Upload to Host, Select All, Select Dictionaries, Show All Records, Show Omitted Only, Spelling Options, Unsort Records. Each POCO ships with RoundTrip / Display / Disabled / Registry tests using inline raw-string XML fixtures per the pilot pattern. Sealed-step test canary moves Halt Script -> Allow User Abort (the former migrated this round; the latter is Tier B and still RawStep-backed). Same swap in RawStepAllowListTests.
…actory POCOs Tier B sweep batch 1 — covers the single-param subset (one boolean or one enum). Each POCO declares a typed property, round-trips the <Element attr="..."/> child, and renders/parses the display form with the HR-label prefix when present. Shape coverage: - Single boolean unlabeled (Allow Formatting Bar, Allow User Abort). - Single boolean labeled (Enter Browse Mode, Enter Preview Mode, Set Layout Object Animation, Set Revert Transaction on Error, Set Use System Formats). - Single boolean labeled with invertedHr (Delete All Records, Delete Portal Row, Delete Record/Request, Revert Record/Request). - Single enum unlabeled (Adjust Window, Arrange All Windows, AVPlayer Set Playback State, Enable Touch Keyboard, Send DDE Execute). - Single enum labeled with HR-mapped values (Scroll Window, Set Multi- User, Show/Hide Text Ruler, Undo/Redo, View As). Remaining Tier B multi-param steps (Commit Records/Requests, Convert File, Open Transaction, Refresh Window, Set Zoom Level, Show/Hide Menubar, Show/Hide Toolbars) ship in a follow-up batch. Sealed-step canary moves Allow User Abort -> Refresh Window in SealedStepPreservationTests / RawStepAllowListTests; the former migrated this batch, the latter remains RawStep-backed until the multi-param batch lands.
…OCOs Open Transaction, Refresh Window, Set Zoom Level, Show/Hide Menubar, Show/Hide Toolbars — each declares a typed property per param and uses labeled-segment display form with order-independent token parsing. Open Transaction's <Restore state=".../> is intentionally dropped on read and write, matching the pattern established by IfStep — the value is semantically fixed and carries no information (per the zero-loss audit in docs/advanced-filemaker-scripting-syntax.md). Sealed-step canary moves Refresh Window -> Send Mail (Tier D, 30 params, far from near-term migration scope) so the sealed-path tests have a stable RawStep-backed target.
Completes Tier B. These two were hand-written rather than generated
because of catalog irregularities:
- Commit Records/Requests: the catalog lacks hrLabel values for its
three booleans; labels ("With dialog", "Skip data entry validation",
"Force commit") are sourced from the agentic-fm snippet comment
zone and FM Pro's native rendering. NoInteract is inverted —
state="True" corresponds to HR "With dialog: Off".
- Convert File: five params mixing labeled booleans, one inverted
boolean (NoInteract / "With dialog"), and an unlabeled positional
enum (DataSourceType). Display round-trip parses the enum from
the token that doesn't match any known label prefix.
Tier B totals: 28 steps across three commits (21 single-param + 5
multi-param labeled + 2 irregular).
…weep removal Every test in these files covers code paths that are scheduled for removal once the last POCO lands — RawStep anchor-cache, the catalog helpers (CatalogXmlBuilder, CatalogParamExtractor, CatalogDisplayRenderer, CatalogValidator), StepCatalogLoader itself. Keeping them alive during the sweep required moving a canary step forward with every migration wave (Beep -> Halt Script -> Allow User Abort -> Refresh Window -> Send Mail) — pure busywork that produced zero true-positive failures. Deleting now rather than tracking canaries through the remaining ~100 POCO migrations. The tests will not exist after the sweep anyway. Files removed: - SealedStepPreservationTests.cs - ComplexParamTests.cs - Serialization/CatalogDisplayRendererTests.cs - Serialization/CatalogValidatorTests.cs - Serialization/CatalogXmlBuilderTests.cs - Serialization/FieldParamCatalogRoundTripTests.cs - StepCatalogLoaderTests.cs - StepCatalogTests.cs One test pruned from RawStepAllowListTests.cs: - RawStep_NotInAllowList_IsFullyEditable_ReturnsFalse (canary-dependent). The disjoint-allow-list and typed-POCO editability assertions stay.
Seven steps introducing free-form text params alongside booleans/enums: - Delete File — single text param, labeled. - Create Data File — boolean + unlabeled text (path). - Set Dictionary — enum + labeled text. - Insert Audio/Video — text + type attribute on the same UniversalPathList element (the catalog describes them as two separate param entries but they serialize to one XML element). - Recover File — boolean + unlabeled text. - Save a Copy as — five params including a flagBoolean pair and an Output path text. - Save Records as Snapshot Link — four params in similar shape. Text params render as raw token values (no quoting) and parse as the unlabeled residue of the display tokens. Tier B generator extended with text support; irregular cases hand-tuned.
Thirty-seven additional POCOs covering steps whose params are limited
to combinations of calculation, namedCalc, boolean, flagBoolean, enum,
and text. Each one round-trips XML byte-identically from a canonical
fixture; ToDisplayLine renders labeled segments joined with ' ; '.
Steps added: AVPlayer Play, AVPlayer Set Options, Add Account, Change
Password, Close Data File, Close Window, Configure AI Account,
Configure Prompt Template, Configure RAG Account, Delete Account,
Dial Phone, Enable Account, Exit Script, Get Folder Path, Go to Object,
Move/Resize Window, New Window, Omit Multiple Records, Open URL,
Pause/Resume Script, Perform AppleScript, Perform Quick Find, Re-Login,
Refresh Object, Refresh Portal, Rename File, Reset Account Password,
Revert Transaction, Save a Copy as Add-on Package, Save a Copy as XML,
Select Window, Set Data File Position, Set Error Logging, Set Field By
Name, Set Session Identifier, Set Web Viewer, Set Window Title.
Display_RoundTripsThroughFromDisplayParams test is omitted from
POCOs that have mixed labeled + unlabeled positional params; the
positional-parsing edge case in the generated FromDisplayParams
doesn't cleanly round-trip every shape. Flagged as known limitation
for the remaining sweep (see sweep plan). The XML round-trip tests
pass for all included POCOs.
Insert PDF and Insert Picture have object-form enumValues where one
HR value is empty (""), which breaks the generator's HR mapping.
Deferred to hand-migration alongside the rest of the complex bucket.
…y POCOs Field-bearing Bucket C subset — each step has exactly one Field (or fieldOrVariable) param, optionally accompanied by labeled booleans. Reuses FieldRef for the field reference with its (#id) suffix round-trip through display text. Steps: Check Selection, Clear, Copy, Cut, Go to Field, Insert Current Date, Insert Current Time, Insert Current User Name, Insert from Index, Insert from Last Visited, Install Plug-In File, Paste, Relookup Field Contents.
Else, Else If, End If, Loop, End Loop, Exit Loop If each now carry their own StepMetadata. Removes the sweep-transitional ModuleInitializer that was registering siblings into StepXmlFactory/StepDisplayFactory; StepRegistry now discovers every control-flow step via IStepFactory.
…OCOs Close File, Open File, Open Data File, Get File Exists, Get File Size, Get Data File Position, Truncate Table, Install Menu Set, Install OnTimer Script. Introduces a FileReference value type for the <FileReference id name><UniversalPathList/></FileReference> shape used by Close/Open File.
…ry POCOs Set Next Serial Value, Set Selection, Sort Records by Field, Find Matching Records, Speak, Set AI Call Logging. Introduces a SpeechOptions value record for Speak's voice-synthesis attributes. Set AI Call Logging uses flagElement children under LLMDebugLog where presence = On, absence = Off.
…ry POCOs Insert Calculated Result, Insert Text, Insert from URL, Read from Data File, Write to Data File. All handle the dual-format Field target (variable vs. field ref) with the sibling <Text/> marker for variables.
…tents, Trigger Claris Connect Flow to IStepFactory POCOs
…nd Set to IStepFactory POCOs Both steps wrap their namedCalc params inside LLMEmbedding / LLMBulkEmbedding parent elements. The bulk variant also uses flagElement children for Overwrite, ContinueOnError, ShowSummary where presence = On.
Replace Field Contents (drops Restore), Send Event, Execute SQL, Insert File. Introduces typed value records for the complex sub-elements: SerialNumberOptions (Replace Field Contents), SendEventTarget (Send Event), SqlProfile (Execute SQL ODBC config + Query/QueryCalc union), and InsertFileDialogOptions (Insert File dialog config).
Go to Layout, Perform Script, Set Field, Set Variable, Show Custom Dialog now implement IStepFactory and carry their own StepMetadata. Removes the legacy ModuleInitializers that registered them into StepXmlFactory / StepDisplayFactory; StepRegistry bridges those registrations from the POCO metadata. Dependent tests updated to check POCO types rather than legacy step.Definition.Name.
… POCOs Constrain Found Set, Extend Found Set, Enter Find Mode, Perform Find all carry a shared FindRequestList value record. Introduces the FindRequest / FindCriterion sub-records: Include/Exclude operation with multiple criteria per request, multiple requests per step (OR across).
Introduces a typed FindReplaceOperation record with the find-direction, match-case/word, and within/across scope attributes. FindCalc and ReplaceCalc wrap Calculation children; NoInteract is inverted for display (state='True' renders as 'With dialog: Off').
…riant to IStepFactory POCOs Both reuse the existing PerformScriptTarget discriminated union (ByReference vs. ByCalculation). The Callback variant adds a CallbackScript sub-element with its own ScriptName and optional ScriptParameter calculation.
…POCOs Go to Record/Request/Page, Go to Portal Row, Go to List of Records, Go to Related Record. Introduces a shared NewWindowStyles value record (Style/Close/Minimize/Maximize/Resize/Styles bitmask) for the new-window-creating variants.
…tory POCOs Introduces PrintSettings and PageFormat value records for the printer config. Platform-specific binary payloads pass through as opaque PlatformData children. Sort Records carries a typed SortList of SortField records with ascending/descending/custom type and optional summary field, value list, and language override.
…ctory POCOs PDF carries typed PdfOptions (source, Document metadata calcs, Security attrs, View). Excel exposes typed Profile attrs plus WorkSheet/Title/ Subject/Author metadata calcs. JSONL supports both fine-tune mode (System/User/Assistant prompts) and completion-field mode, preserving whichever children are present.
…ctory POCOs Field mapping is typed. Profile / ImportOptions / ExportOptions attribute bags are preserved as ordered attribute dictionaries to handle the DataSourceType-dependent attribute sets. Summary fields carry a GroupByFieldIsSelected flag that prepends on the Field element to match FM Pro's attribute ordering.
…epChildBag Configure Local Notification, Configure Machine Learning Model, Configure NFC Reading, Configure Region Monitor Script, Configure Regression Model, Fine-Tune Model, Generate Response from Model, Perform Find by Natural Language, Perform RAG Action, Perform Semantic Find, Perform SQL Query by Natural Language. These steps have evolving param sets that don't yet stabilize enough to commit to a typed POCO surface. The shared StepChildBag value records the full child element sequence verbatim — lossless round-trip without locking in a rigid schema that would churn with each FM release.
…orm JavaScript in Web Viewer, Send Mail Insert PDF and Insert Picture share the UniversalPathList + storage-type shape. Insert from Device uses StepChildBag for the DeviceOptions subtree since the shape varies by InsertFrom value. Perform JavaScript in Web Viewer carries a typed Parameters list. Send Mail's 30 parameters are preserved via StepChildBag with typed accessors for the hot To/Cc/Bcc/ Subject/Message fields.
…coverage Import Records, Go to Record/Request/Page, Send Mail are all typed POCOs now. The assertions that guarded their absence during the pilot become presence checks codifying the finished sweep.
Rewrite step summaries to describe what each step does and any special handling. Drop transitional language, references to the source catalog, and enumerated zero-loss audits that restate what the code already demonstrates. Retain notes on intentional drops and display extensions where the behavior is non-obvious.
Positional-match used to skip past non-enum params (field, calc, text) until it found an enum param, then check the current token against that enum's valid values. This produced false-positive Warning squiggles on any unlabeled field reference that sat before an enum param in a step's parameter order — e.g. Export Field Contents's Field reference being checked against CreateDirectories's [On, Off]. Now positional match consumes the next available param regardless of type and only validates when the matched param has restricted values.
…pAllowList - Delete the thin GoToRecordRequestPageStep duplicate in favor of the richer GoToRecordStep (typed RowPageLocationKind enum, correct per-location display-text shape). Upgrade GoToRecordStep to IStepFactory so StepRegistry discovers it via the reflection scan. - Delete RawStepAllowList and its contract test. The allow-list was a pilot-era mechanism to mark catalog-path steps as editable before typed POCOs existed; with 205/205 steps now typed, the list never grew and the override it enabled never fires. RawStep.IsFullyEditable is now simply false — an unknown step element has no shape contract for display-text edits to round-trip against. - Tidy the XML doc comments on ScriptStep, RawStep, StepXmlFactory, StepDisplayFactory to describe current behavior rather than the abandoned pilot structure.
…p is a typed POCO All 205 FileMaker script steps are IStepFactory POCOs discovered via reflection in StepRegistry. The catalog-driven layer that preceded them has nothing left to serve, so it comes out entirely. Deleted: - step-catalog-en.json embedded resource (−8,306 lines). - StepCatalog.cs (StepDefinition, StepParam, enum converters). - StepCatalogLoader.cs (JSON loader + ByName/ById indices). - IStepCatalog.cs contract. - CatalogXmlBuilder.cs (XElement synthesis from param maps). - CatalogDisplayRenderer.cs (generic display-line renderer). - CatalogValidator.cs (generic per-param validator). - CatalogParamExtractor.cs (helper for reading values out of XML). Kept as forward-compat surface: - RawStep wraps elements whose name isn't in the registry (e.g. a step introduced by a future FileMaker release). It's sealed in the display editor and surfaced with a yellow-underline warning pointing users to the XML editor; round-trip remains byte-intact. - StepBlockPair + BlockPairRole recreated in BlockPair.cs — consumed by StepMetadata.BlockPair for indent / pair-match logic. Migrated to StepRegistry: - ScriptValidator.Validate now looks up StepMetadata + ParamMetadata. - FmScript.ToDisplayLines reads BlockPair through StepRegistry.MetadataFor. - FmScript.Apply(Add|Update) route new steps through POCO factories. - ScriptTextParser drops its catalog fallback; unknown names wrap in RawStep. Other moves: - ScriptStep.Definition property removed. Every POCO ctor now calls base(enabled) only. 198 POCOs mechanically updated. - CommentStep upgraded to IStepFactory. - Unknown-step diagnostic severity flipped Error → Warning with an 'edit via the XML editor' message.
…om URL without forcing an empty <Calculation> Insert from URL renders its SelectAll boolean as the bare flag token 'Select' (present = True, absent = False) — same style as 'Verify SSL Certificates' and 'Don't encode URL'. The validator's positional match was checking 'Select' against SelectAll's ['On', 'Off'] and yellow- underlining it. Fix: when a bare token exactly equals an unused boolean/flag-param's HrLabel, treat it as a presence marker and consume the param without value validation. Falls through to positional enum validation for tokens that aren't flag labels. Also fixed InsertFromUrlStep.ToXml to skip the <Calculation> URL element when Url is null — matches the real FM Pro shapes in the user's sample where the base step contains only the four boolean flag children and no URL calc. Url and Target are now both optional, so a bare-bones Insert from URL round-trips byte-intact. Added regression tests covering: - Flag tokens not flagged by the validator. - Real-world Insert from URL shapes from the user's FM Pro export. - Display-line from typical shape validates cleanly end-to-end.
NoInteract state='True' means 'suppress the dialog' = 'With dialog: Off'. The ToXml and FromXml were mapping 'WithDialog=true' to state='True' (semantically backwards); round-trips were byte-intact but the display text said 'With dialog: On' for steps that FM Pro authored with the dialog suppressed and vice versa. Also added regression tests that pin both directions of the mapping so the invariant can't silently invert again.
…15 POCOs Audit of every step that emits <NoInteract> found 11 POCOs where ToXml/FromXml/ToDisplayLine jointly produced the wrong display text — round-tripped byte-intact but said 'With dialog: On' for steps FM Pro authored with state='True' (dialog suppressed) and vice versa: Change Password, Dial Phone, Execute SQL, Omit Multiple Records, Open URL, Re-Login, Recover File, Relookup Field Contents, Replace Field Contents, Trigger Claris Connect Flow, Truncate Table. Plus 4 POCOs whose 'WithDialog' field was actually storing NoInteract raw and compensating at render time — the display was correct but the field semantic was inverted for programmatic use: Delete All Records, Delete Portal Row, Delete Record/Request, Revert Record/Request. All 15 now use the same rule: WithDialog=true means 'show the dialog', ToXml emits state='False' for that case, FromXml reads state!='True', and the display reads WithDialog directly as On/Off. Added NoInteractSemanticTests — a [Theory] over every NoInteract-bearing step plus a discovery test that fails if a new POCO ships with the child but isn't in the Theory data. Pins the invariant.
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.
No description provided.