The Insights Piece — anatomy, data-load gate, and rollout¶
The Insights piece (built by Mark Wright) is a reusable form section that shows a coloured grid of property insights (icon, kind, type, text, clients-affected) loaded from the property in question. It is intended to replace the old ad-hoc property alerts / warnings and be added to all regularly-used forms.
A standalone Insights form also exists, so insights for a property can always be viewed even on forms that don't yet embed the piece.
Anatomy — three XML parts (canonical: Insights_59.form, also Routine Tenancy Visit_13)¶
GenBPInsightgroup — two-part:- a top-level
<FormItem type="Group" name="GenBPInsight">(the binding handle), and - a
<Group name="GenBPInsight">block inside<Groups>carrying the member itemsz_Insight_Emojicon(+ColouredInternal),z_Insight_Kind(+Coloured),z_Insight_Type(+Colour),z_Insight_Text(+Coloured),z_Insight_ClientsAffected(+Colour),z_Insight_Colour,z_Insight_Detail,z_Insight_Type_Negation. The*Coloured/*Colouritems arebase.fontcolor(z_Insight_Colour)calcs. - Toggle logic (top-level items):
z_Insights— readsGetConfigurationValue("Insights")(the global on switch)z_Insights_Switch— option listShow Insights;Hide Insights;(user toggle)z_Insights_Visible— computed: Show if switch=Show, or if config=True and switch is null; else Hide.- Display run in the Main page
FormElements(placed after the form header): - the Show/Hide switch control — an
OptionsListRadioButtonElementbound toz_Insights_Switch(push-buttons "Show Insights" / "Hide Insights"). This is a separate display element from thez_Insights_Switchdata item — the data item alone gives you no on-screen toggle (see the "missing switch" bug below). - a gate
SectionBreakElementwhoseVisible=z_Insights_Visible == "Show Insights", - the
GroupDataGridElementbound toGenBPInsight(caption "Insights", 5 columns). SetshowAdd=False(no Add button — users must not add insight rows) and keepshowEdit=True(users tap a row to view its detail). Per Mark in the catch-up: "we don't need the show ad… not going to be adding anything" / "they want to edit because it allows them to go into it and have a look inside." - a trailing spacer
SectionBreakElement.
"Missing switch" bug (fixed 2026-06-05): the original injector added the
z_Insights_Switchdata item but not the switch display control, so injected forms rendered the panel with no toggle and (config defaulting on) showed it unconditionally.inject.pynow always addsswitch_control.frag.
Legacy property-alerts inverse-hide: the old "Property Alerts" section is gated to
hide when insights are shown — its Visible becomes z_Insights_Visible != "Show Insights"
(kept in place, not deleted, so toggling insights off restores it). Many forms also carry
other warning sections (e.g. "Property Warnings", "Client Warning", "Alerts and
Warnings") that still need manual tidy-up per Peter.
Data-load gate (critical gotcha)¶
The grid only shows rows if the task the form runs in has its GenBPInsight group
filled at runtime ([Versaa].[usp_all_insights], keyed on GenBP_Key_PropertyId). There
are three ways that happens (see the verified
route→loader map):
- Loader-route — the launch/load workflow runs the insight query (Property Search, FRA Search, RWO QL Data, Client Visit Load, Estate Search, Order Appointment).
- Inherited — the form runs inside a parent task that was already loaded (e.g. RWO sub-tasks UvHWS / NIBE / Damp & Mould inherit the RWO's insights). Display-only.
- Self-trigger — the form fires
*.AppendPropertyInsightsitself: aRequestPropertyInsightsrequest item (messageName=AppendPropertyInsights,items=GenBP_Key_PropertyId;, copied fromFamily Survey 2 MW) + anautoSendRequestButtonElement(the fire-once, self-hiding pattern from Asbestos Data). Use this only for forms whose task is not already loaded (e.g. Void Survey via Create Jobs).
Consequences:
- Adding the display grid to a form does not guarantee data appears — confirm in the app, per form.
- ⚠️ Duplication rule: the
Append Property Insightshandler appends, it does not replace. If a task gets insights from two sources you get duplicate rows. Putting a self-trigger on an inherited sub-task (e.g. UvHWS inside RWO) duplicates —autoSendOnceOnlyand hiding the button do not fix it because the copies come from different sources. So: inherited sub-task → display-only; self-trigger → only where nothing else loads. - Forms with no property context (e.g. Van Check) show an empty grid — skip.
Verified 2026-06-05: Unvented Hot Water Storage v9 is Active in Dev with display-only
(inherits from the Repair Works Order) — panel populates, single copy, Show/Hide works, Add
disabled, click-in to view works, tasks still sync. Note: Management Studio assigns the
next sequential version on import — the manifest version in the .ftpackage is cosmetic.
The workflow-side load is gated by the Insights_OnSwitch workflow variable
(workflow_variables/…/Insights_OnSwitch.variable, "1 = On, 0 = Off", currently
true) — a single global variable, not a per-form or Management-Studio toggle.
Don't confuse it with the form items z_Insights_Switch (the user's Show/Hide
toggle) or z_Insights (GetConfigurationValue("Insights")). See
insights-rollout-plan.md "the three switches".
Audit (2026-06-04, against a freshly re-pulled snapshot) — which forms have insights¶
Of 206 forms (latest version each), 27 have the insights grid (count rose from 25
once the stale snapshot was refreshed — e.g. Stock Condition Survey appeared at v48
with the full piece). Re-run this audit only against a fresh Dev pull (see the
snapshot-lag gotcha). The authoritative test is a GroupDataGridElement bound to
GenBPInsight with
Column formItemName="z_Insight_..." (column spelling varies by form age —
z_Insight_EmojiconColoured vs ...ColouredInternal, ClientsAffectedColour vs
...Coloured — so match the binding, not one column name).
Already done include: Repair Works Order, Client Visit, Building Safety Check,
Awaabs Triage, all Asbestos forms, Pre-Leaving Visit, Routine Tenancy Visit, Fire Risk
Assessment, Red Flag Reporting, Balcony Inspection, and others. FRA Review was completed
by Mark as v68 (all_versaa_forms/FRA_Review_04062026.ftpackage).
Rollout — who does it, and how (corrected 2026-06-04)¶
The rollout is owned by Mark and done MANUALLY in Management Studio Dev, form-by-form, prioritised by usage. This is what the catch-up actually agreed (Mark copies the group between forms in the MS UI; "copy that entire section and paste it across"). There is no programmatic-package step in the agreed process.
Peter's "list" is a May usage/completion report — NOT a "needs insights" list. It ranks how many of each task type were completed in May, so Mark can do the most-used forms first. Peter: "it's not 100% accurate… but it gives you a good idea of what is used the most." Treating it as a to-do list of forms-missing-insights was a misread.
Retired experiment: the programmatic injector¶
An
insights_injectorwas built (2026-06-04) to inject the piece and emit one.ftpackageper "gap" form from the usage list. It has been stood down — do not import its packages. Three reasons it was wrong: 1. Built on a stale snapshot (see gotcha below) → its packages would revert Mark's newer Dev versions on import (it built SCS v44 while Dev was already on v48; Void Survey v28 vs Dev v30; Client v69 vs Dev v71). 2. Sub-form breakage — Client v69 (a sub-form) broke task sync (see next section). 3. The grid is inert without workflow work (data-load gate) — injecting form XML only does half the job; the launch-workflow data-push is a manual per-form task.The packages now live in
versaa-rag/insights_injector/out/packages/_retired/.rollout.pyis now audit-only by default (build is opt-in behind--buildand still must not be imported without reconciling versions with Mark). The useful part that remains is the audit (classify_forms.py→insights_audit.csv) for tracking coverage: -versaa-rag/insights_injector/insights_rollout_status.md/.csv— current gap list -versaa-rag/insights_injector/insights_audit.csv— raw tier per form
Snapshot-lags-Dev gotcha (critical for any audit)¶
Exported .ftpackages — and therefore everything in extracted/ — trail the live Dev
state. The MS export captures the latest exported version of each item, but Mark's
in-progress edits live in Management Studio and aren't in a package until re-exported.
Concretely: today's first export captured Stock Condition Survey v43 (no insights)
while Dev was actually on v48 (full insights); the CLAUDE.md §10 log records the same
for Void Survey ("package captures v27; Dev is on v30 — MS-only v28–v30").
Always re-export from Dev and re-extract (run_dev_pull.py, or a fresh export +
extract_all_categories.py) immediately before auditing or doing anything version-
sensitive. Auditing a stale snapshot is exactly what produced the false "these forms
lack insights" list.
Held / excluded (still valid): Van Check (confirm property context), Gas Servicing (skip — offshoot of Works Order), FRA Review (done by Mark). Sub-form sections (no standalone launchable form, inherit from parent): Safeguarding Report, Estate Inspection, Pets Questionnaire, Client (sub-form of Client Visit).
CRITICAL gotcha — never inject the piece into a sub-form (2026-06-04)¶
Client_69_insights broke task sync in Dev. Root cause: Client is a sub-form, not a
standalone form, and its parent already has Insights.
The Client form (category Tenancy) is loaded as a sub-form by Client Visit
(via the Client Subform Load item) and is reached through the CRM / EDI flows. Client
Visit v100 already carries the full Insights piece (GenBPInsight group + 25
z_Insight_* items), so Insights already renders in the Client area at runtime —
inherited from the parent.
Injecting the piece into the Client sub-form (v69) added a second GenBPInsight
group + duplicate z_Insight_* items. When the device merges the sub-form into the
parent it sees the same group/items declared twice → the form fails to provision
on the device (Management Studio import still succeeds — the file is well-formed,
GUID preserved, all formula ids unique/valid, no parse error). Symptom: tasks created
via Client Search did not sync to Aareon Mobile while v69 was active; reverting to v68
released them immediately.
Rules:
- A form's eligibility for injection is not just "lacks the grid" — it must be a
standalone launchable form. Sub-forms must be excluded, because they inherit
the piece from their parent and a duplicate group breaks the runtime merge.
- Detect sub-forms by searching all forms for a name="<FormName> Subform Load" item
(the load-control convention) — if any parent loads the target, do not inject.
- A clean MS import does not mean the form works: device provisioning is a separate
compile step. Confirm new tasks still sync after activating a new version.
- Client was the only sub-form among the injector targets. All injector packages are now
retired (out/packages/_retired/); none should be imported. In current Dev, Client
is on v71 and inherits Insights from its parent Client Visit.
classify_forms.py now carries the sub-form exclusion (it scans for <X> Subform Load
items and flags any target loaded by a parent as is_subform), and rollout.py excludes
those automatically.
The injection toolkit¶
versaa-rag/insights_injector/ (run with versaa-rag\.venv\Scripts\python.exe):
| Script | Purpose |
|---|---|
classify_forms.py |
Tier every latest-version form (DONE / NONE) → insights_audit.csv |
merge_usage.py |
Join Peter's May counts to the audit → usage-prioritised gap list |
extract_reference.py |
String-slice the canonical blocks from Routine Tenancy Visit_13 → templates/ |
inject.py |
Idempotent string-splice of the missing components into a form, regenerate all formula/element ids, place the grid after the header, gate Property Alerts, validate |
build_package.py |
Wrap a modified .form into a single-form .ftpackage (CLAUDE.md §8, UTF-8 no BOM) — used only behind rollout.py --build |
rollout.py |
Audit-only by default — writes the gap-list tracking tables. The inject+package path is SUPERSEDED and gated behind --build; do not import its output. |
components.py / diff_conversion.py |
Inspection helpers |
Supported use today = the audit. classify_forms.py (coverage tiers + sub-form
detection) and the rollout.py tables are the useful, maintained outputs. The
inject/build path is kept for reference only — the rollout itself is Mark's manual job.
Why string-splice, not lxml: an lxml round-trip rewrites the whole file's
formatting (<Value></Value> → <Value/> etc., ~20k diff lines). The toolkit edits the
raw text so each form stays byte-identical except at the insertion points (verified:
purely additive, zero content deletions). Every injected formula id is regenerated to a
fresh 32-lowercase-hex value and every element GUID re-minted, so there are no id
collisions (CLAUDE.md §8). Validation tolerates pre-existing duplicate ids (Client
and Complaint ship with them and import fine) — it only fails on duplicates the
injection itself introduces.
To refresh the audit: first re-pull Dev (run_dev_pull.py, or fresh export +
extract_all_categories.py), then python versaa-rag\insights_injector\classify_forms.py
and python versaa-rag\insights_injector\rollout.py (audit-only). Never audit a stale
snapshot — see the snapshot-lag gotcha.
Deployment & control docs¶
The injector packages are retired and must not be deployed. Mark adds the piece
directly in Dev and migrates it Dev → Test → Live per CLAUDE.md §9 (confirm grid
populates + Show/Hide toggle + legacy alerts hide at each stage; the grid needs the
launch-workflow data-push to show data). The rollout.py audit tables are the coverage
tracker, not a deploy queue. Control documents (CLAUDE.md §12) are updated per form as
Mark confirms each in Dev.
Related¶
- forms/insights-rollout-plan.md — how to finish the rollout: launch-route categories (A copy-paste / B data-push / C skip), per-form worklist, recipes, sequence
- forms/insights-route-loader-map.md — route→loader map: which loader workflow fills each form's grid, corrected per-form A/B/C, self-trigger default, 3 verification experiments
- forms/repair-works-order.md — a DONE form; RWO links to Pre-Leaving Visit / Void First Person (data-push targets)
- forms/void-survey.md — on Dev v30 (switch scaffolded, grid pending Mark)
- forms/visibility-formulas.md — the show/hide + inverse-hide pattern
- asbestos/overview.md —
sendAllItems/ group data-load behaviour - deployment/dev-to-live.md — version comments, control document