Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
db0a9fd
feat(scripting): canonicalize script-step XML against the FileMaker r…
fuzzzerd Jun 30, 2026
3cb1bdd
feat(scripting): migrate 64 script steps to the shape engine
fuzzzerd Jun 30, 2026
5b0a618
feat(scripting): migrate reorder/value-type steps; shrink divergences…
fuzzzerd Jun 30, 2026
d93fb49
feat(scripting): implement Passthrough parsing; canonicalize Insert F…
fuzzzerd Jun 30, 2026
fcecb2c
feat(scripting): canonicalize Perform JavaScript in Web Viewer
fuzzzerd Jun 30, 2026
3038f38
feat(scripting): canonicalize callback/web-viewer/excel/replace steps
fuzzzerd Jun 30, 2026
37aed65
feat(scripting): canonicalize import/export records and Save Records …
fuzzzerd Jun 30, 2026
884faa3
feat(scripting): canonicalize Show Custom Dialog; 99.2% of fixtures r…
fuzzzerd Jun 30, 2026
6655a75
feat(scripting): remodel Save a Copy as XML; 100% of fixtures round-trip
fuzzzerd Jun 30, 2026
b59b6ce
test(scripting): validate shape-driven steps against their declared s…
fuzzzerd Jun 30, 2026
265995d
feat(scripting): add typed POCOs for the FM26 PDF steps
fuzzzerd Jun 30, 2026
24cc293
feat(scripting): complete the shape engine for the full step catalog
fuzzzerd Jul 7, 2026
658b069
refactor(scripting): migrate the remaining 123 steps to the shape engine
fuzzzerd Jul 7, 2026
4b6c546
refactor(scripting): replace ParamMetadata with shape-derived display…
fuzzzerd Jul 7, 2026
098451e
chore: ignore msys stackdump files
fuzzzerd Jul 7, 2026
a9feb3a
test(scripting): add the display-mutation round-trip contract
fuzzzerd Jul 7, 2026
924dc64
feat(scripting): display affordances for the shipped grammar
fuzzzerd Jul 7, 2026
af4b245
fix(scripting): register trimmed display-factory alias for trailing-s…
fuzzzerd Jul 7, 2026
303ad2f
fix(scripting): make display-text mutation faithful for every documen…
fuzzzerd Jul 7, 2026
beb3f52
feat(scripting): shape-driven display parser
fuzzzerd Jul 7, 2026
4cf9921
refactor(scripting): cut display rendering and parsing over to the sh…
fuzzzerd Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,4 @@ ASALocalRun/

# MFractors (Xamarin productivity tool) working folder
.mfractor/
*.stackdump
23 changes: 12 additions & 11 deletions docs/advanced-filemaker-scripting-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ the same flag:

- `Exit after last: On` on found-set iterators.
- `With dialog: Off` on steps that can suppress their confirmation UI.
- `Restore: On|Off` — **reserved**. Not currently emitted by any POCO;
see "What to drop vs. surface" below for the rationale.
- `Restore: On|Off` — emitted by steps whose XML carries a meaningful
stored-state flag (e.g. Enter Find Mode); see "What to drop vs.
surface" below for the fixed-value cases that stay XML-only.

### Form 3 — trailing `; Kind: [...]` blocks

Expand Down Expand Up @@ -125,16 +126,16 @@ Pro never alters it, never emits it in clipboard output, and no user
workflow produces a different value. Round-tripping such state adds
visual noise for zero information.

### Canonical drop: `<Restore state="False"/>` on `If`
### Canonical hidden state: `<Restore state="False"/>` on `If`

Upstream `agentic-fm` snippets include the element; FM Pro's own
clipboard output never does; no FM Pro user interaction produces
`state="True"`. `IfStep` drops it on both read and write. The audit
entry documents the drop:

> &lt;Restore state="False"/&gt; — intentionally dropped. FM Pro never
> changes the value and never emits the element in clipboard output; it
> carries no information worth round-tripping.
The vendored canonical reference (`docs/filemaker-xml-canonical/`,
round-trip verified against FM Pro 2025/2026) shows FM Pro *does* write
the element, so `IfStep` emits it in XML — but the value is fixed (no FM
Pro user interaction produces `state="True"`), so it stays hidden from
the display line: the shape declares it `Display = DisplayMode.Hidden`
and it round-trips in XML only. The same treatment applies to Loop's
`<Restore>`/`<FlushType>` pair. See the divergence review log in
`docs/filemaker-xml-canonical/divergence-review.md`.

### Canonical surface: field `id` via `(#id)` suffix

Expand Down
40 changes: 40 additions & 0 deletions docs/filemaker-xml-canonical/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# License — Vendored canonical FileMaker XML references

The reference material in this directory is **not** original to SharpFM. It is
vendored, unmodified except where noted, from two open-source projects by
**Andrew Kear** of **Clockwork Creative Technology** (https://www.clockworkct.co.uk),
and is redistributed here under its original licence.

## License: CC BY 4.0

Both sources are licensed under the
[Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/).

You are free to share and adapt the material for any purpose, provided you give
appropriate credit. SharpFM gives that credit here and in the `README.md`
alongside this file.

## Attribution

- **FileMaker XML Snippet Claude Skill** — reverse-engineered specification of
FileMaker's `fmxmlsnippet` clipboard format.
Source: https://github.com/andykear/FileMaker-XMLsnippet-Claude-Skill
Vendored version: **v1.12** (FileMaker Pro 2026 / FM 26 support).
Vendored under `skill/`.

- **Clockwork Inspector** — open-source FileMaker DDR / database-design XML
analyzer. SharpFM vendors only two extracts: the `FM_STEP_IDS` step catalog
and the `extractStepText` display renderer, used as a cross-check catalog and
a reference for SharpFM's own step-display convention.
Source: https://github.com/andykear/FileMaker-XML-inspector-open-source
Vendored version: **v2.0**.
Vendored under `inspector/`.

## What SharpFM uses this for

SharpFM is an XML-faithful FileMaker clipboard editor. These references are the
authoritative description of the exact native XML FileMaker's Script Workspace
paste handler accepts cleanly, including the documented silent-failure modes.
SharpFM's step model is validated against them. No SharpFM source code is
derived from Clockwork's code; the vendored files are reference data and are
parsed at test time only.
45 changes: 45 additions & 0 deletions docs/filemaker-xml-canonical/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Canonical FileMaker XML references (vendored)

This directory vendors the authoritative, reverse-engineered description of the
native XML FileMaker Pro's Script Workspace paste handler accepts. SharpFM's
script-step model (`src/SharpFM.Model/Scripting/`) is validated against it so
that XML written back to the clipboard round-trips through FileMaker with no
silently-dropped elements.

See [`LICENSE.md`](LICENSE.md) for attribution. Both sources are CC BY 4.0,
authored by Andrew Kear / Clockwork Creative Technology.

## Layout

| Path | Source | Used for |
|------|--------|----------|
| `skill/SKILL.md`, `skill/references/*.md` | FileMaker XML Snippet Skill **v1.12** | Canonical clip-XML skeletons per step. The refresh step extracts these fenced blocks into checked-in test fixtures (under `tests/SharpFM.Tests/CanonicalSkill/fixtures/`) that the round-trip suite reads — the markdown is not parsed live at test time. **Sole source of truth for clip-XML emission.** |
| `skill/README.upstream.md` | same | The upstream project's own README, kept for provenance. |
| `inspector/FM_STEP_IDS.js` | Clockwork Inspector **v2.0** | Verbatim 217-entry `stepId → {name, category}` catalog. Used as the **working list** to align SharpFM's POCO `StepMetadata` (Id/Name/Category) and find the steps we don't yet model. Once the POCOs match it, **SharpFM's `StepMetadata` is the authoritative catalog** — this file stays only as the upstream reference for spotting drift on future FM releases. |
| `inspector/extractStepText.js` | Clockwork Inspector **v2.0** | Verbatim reference renderer for human-readable step text. Seeds SharpFM's `StepDisplayRenderer` convention. The Inspector's DDR parser is **not** reused — it targets a different XML dialect (database-design XML, not clip XML). |

## Silent-failure modes (from `skill/references/core.md`)

The paste handler is lenient as an XML parser but strict as a paste handler.
Structurally valid XML can paste with data silently dropped. The verified modes
SharpFM's renderer must respect:

- **Set Variable** — the variable name must be in a `<Name>` child, present and
last. A bare `<n>` or misplaced name pastes with no variable name.
- **Perform JavaScript in Web Viewer** — parameters use `<Parameters Count="N"><P>…</P></Parameters>`; a `<Parameter>` element is silently dropped.
- **Install OnTimer Script** — the interval must be wrapped in `<Interval><Calculation>…</Calculation></Interval>`; a bare top-level `<Calculation>` binds to the wrong slot and the timer never fires.

## Refreshing

Run [`refresh-canonical.ps1`](refresh-canonical.ps1) to re-sync both sources
from upstream. Review the diff before committing — upstream version bumps (new
FM releases) may add steps or change canonical shapes, which is exactly the
signal the audit tests are meant to surface. After refreshing, regenerate the
test fixtures from the updated skill markdown:

```
dotnet run --file docs/filemaker-xml-canonical/extract-fixtures.cs
```

This rewrites `tests/SharpFM.Tests/CanonicalSkill/fixtures/` and reports any
catalog ids left without a fixture.
158 changes: 158 additions & 0 deletions docs/filemaker-xml-canonical/divergence-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Divergence review log

Every place SharpFM's emitted XML **changed** to match the vendored skill's
canonical form during the canonicalization audit. The policy is *trust the
skill*: where our output differed, we adopt the skill's form and record the
change here for a second review.

Each entry: the step, what we emitted **before**, what we emit **now**, the
skill section that mandates it, and a review box. Tick the box once a human has
confirmed the change is correct.

> Status legend: `[ ]` awaiting review · `[x]` reviewed & accepted · `[!]` flagged for discussion

## Phase 5 — worst offenders

### Set Variable (141) — no behavioral change
- [ ] Already canonical (skill §3). Refactored to the shape-driven renderer;
emitted XML is byte-for-byte unchanged. Listed for completeness only.

### If (68), Else If (125), Else (69) — added `<Restore>`
- **Before:** `<Restore state="False"/>` was dropped on read and never emitted.
- **Now:** emit `<Restore state="False"/>` as the first child (before
`<Calculation>` where present).
- **Skill:** §8.1. Round-trip tested against FM Pro 2025/2026 — FileMaker *does*
write it. Supersedes the earlier `docs/advanced-filemaker-scripting-syntax.md`
note that claimed FM never emits it.
- [ ] Reviewed

### Loop (71) — added `<Restore>` and `<FlushType>`
- **Before:** emitted no children.
- **Now:** emit `<Restore state="False"/>` then `<FlushType value="Always"/>`.
- **Skill:** §8.1.
- [ ] Reviewed

> Both values are fixed (FileMaker never varies them), so they are hidden from
> the display line and round-tripped in XML only.

### Install OnTimer Script (148) — element order Interval → Script
- **Before:** emitted `<Script>` then `<Interval>`.
- **Now:** emit `<Interval>` then `<Script>` (interval wrapped in `<Interval>`,
which was already correct).
- **Skill:** §8.1 / §7.3. Out-of-order children bind the interval to the wrong
internal slot on paste (a verified silent-failure mode).
- [ ] Reviewed

### New Window (122) — `NewWndStyles` value type + optional slots
- **Before:** emitted a text-bodied `<NewWndStyles>{raw string}</NewWndStyles>`
and always-present empty `<Height>`/`<Width>`/`<DistanceFromTop>`/
`<DistanceFromLeft>` elements; used generic `Calculation`…`Calculation5` fields.
- **Now:** `<NewWndStyles .../>` is the attribute-bearing element produced by the
`NewWindowStyles` value type; `Name`, the four dimensions and `Layout` are
optional and omitted when unset.
- **Skill:** windows reference.
- [ ] Reviewed

### Configure RAG Account (227) — inner wrapper + reorder
- **Before:** emitted `RAGAccountName`, `Endpoint`, `AccessAPIKey`,
`VerifySSLCertificates` as flat `<Step>` children.
- **Now:** `<VerifySSLCertificates state="False"/>` first, then a
`<ConfigureRAGAccount>` wrapper holding the (optional) account fields; empty
wrapper when unconfigured.
- **Skill:** accounts/AI reference.
- [ ] Reviewed

### Go to Related Record (74) — dropped spurious `<Animation>`
- **Before:** appended `<Animation value="None"/>` after `<Layout>`, and always
emitted `<Layout>` even when empty.
- **Now:** no `<Animation>` element; `<Layout>` is optional (omitted when unset);
`<Table>` remains always-present.
- **Skill:** navigation reference (documents no Animation for this step).
- [ ] Reviewed

## Phase 6 — batch migration

### Go to Next Field (4) / Go to Previous Field (5) — id swap fixed
- **Before:** our POCOs emitted id **5** for "Go to Next Field" and **4** for
"Go to Previous Field".
- **Now:** Next = 4, Previous = 5.
- **Sources:** both the skill and the inspector `FM_STEP_IDS` catalog agree
(4 = Next, 5 = Previous). Confirmed before flipping.
- [ ] Reviewed

### Trigger Claris Connect Flow (211) — step id 0 → 211
- **Before:** `XmlId` was left 0 ("canonical id unconfirmed"); the step emitted
`<Step id="0" …>`.
- **Now:** id 211 per the skill's control reference. The rest of the step's XML
was already canonical.
- [ ] Reviewed

### Batch migration — 64 steps to the shape engine
The bulk of the remaining steps were migrated to the shape-driven renderer in
one batch. The dominant change is **no longer emitting optional/empty child
elements** (`<Field/>`, `<Calculation/>`, `<UniversalPathList/>`, dimension
calcs, etc.) that FileMaker omits from an unconfigured step's canonical form;
several steps also had element **reorders**, wrapper **nesting** (Add Account →
`<AddAccount>`, Configure AI Account → `<SetLLMAccount>`, Insert Embedding →
`<LLMEmbedding>`/`<LLMBulkEmbedding>`), a typo fix (`AccoutName` → `AccountName`
on Configure AI Account), and the field-id and Trigger-Claris-Connect-Flow id
fixes above. Every migrated step now round-trips to its skill canonical form
(the `CanonicalSkillRoundTripTests` suite is the proof); the per-step
`*Tests.cs` constants were updated to the canonical XML where they encoded the
old form.
- [ ] Reviewed (spot-check the per-step diffs in this commit)

### Value types — stop emitting empty optional attributes
- **Speak / `SpeechOptions`**: emit `WaitForCompletion` always but only the
populated `VoiceName`/`VoiceId`/`VoiceCreator` attributes.
- **Send Event / `SendEventTarget`**: emit the three behaviour booleans always
but only the populated `TargetType`/`TargetName`/`id`/`class` attributes; the
step no longer emits an empty `<Text/>` element.
- **Skill:** the canonical `<SpeechOptions>` / `<Event>` forms omit empty
optional attributes.
- [ ] Reviewed

### Second reorder/wrapper batch (16 steps)
Move/Resize Window, Set Window Title, Convert File, Open Transaction (now emits
`<Restore>`), Configure Prompt Template (now wraps in `<ConfigurePromptTemplate>`)
and the data-file / window / object steps were reordered or had optional
children dropped to match canonical, with their per-step test constants updated.
- [ ] Reviewed

### Final structural batch — to 99.2% round-tripping
The remaining hard cases were resolved with targeted fixes (and the engine
gained `Passthrough` parsing plus `FlagChild` / value-attr-bool / variable-text
primitives): Insert From Device, Comment (self-closing divider), Perform
JavaScript, Perform Script on Server with Callback, Set Web Viewer, Save Records
as Excel, Replace Field Contents (re-emits `<Restore>`; `<SerialNumbers>` gains
`increment`/`InitialValue`), Import/Export Records, Save Records as PDF (the
`PDFOptions` value type gained `<PDFSaveType>`, page-range pages and security
passwords), Speak / Send Event (value types stop emitting empty attributes), and
Show Custom Dialog (optional title/message/geometry).

**Result: 251 of 253 canonical fixtures round-trip (99.2%)** as of this batch;
the Save a Copy as XML remodel below closed the gap to 100%.
- [ ] Reviewed

### Save a Copy as XML (003) — remodelled to the canonical element set
- **Before:** a generic path/window-name model that could not express the
canonical children; tracked as the last `KnownDivergence`.
- **Now:** the `<Option>` flag, the optional FM 26 `<OutputEntireBinaryData>` /
`<SpecifyJSONOptions>` flags, and the optional
`<SaXML><JSONOptions><Calculation>` export-options block.
- **Skill:** files reference.
- **Result: all 253 canonical fixtures round-trip (100%); `KnownDivergences` is
empty.**
- [ ] Reviewed

## Phase 7 — missing typed coverage

### FM 26 PDF steps (243–247) — new typed POCOs
- **Before:** Create PDF (243), Append PDF (244), Close PDF (245), Open PDF
(246) and Cancel PDF (247) fell through to `RawStep`.
- **Now:** dedicated typed steps; Create PDF reuses the `PdfDocument` /
`PdfSecurity` / `PdfView` value types. Print PDF (242) and the MBS plugin step
(186) intentionally remain `RawStep` — their `PlatformData` blobs and
file-specific plugin `index` are preserved verbatim, never synthesized.
- **Skill:** PDF reference.
- [ ] Reviewed
Loading
Loading