Skip to content

Wiki Change Log

Append-only log of all ingestion events, query-derived pages, and major maintenance passes. Format: ## [YYYY-MM-DD] <type> | <title> so entries are grep-parseable.


[2026-08-19] fix | Credentials in exported workflow variables redacted at source; wiki published to a team mirror

Trigger: give the Versaa team access to this wiki. Before sharing anything, the repo was audited — and the obvious route (invite people to the working repo) turned out to be unsafe.

Found: live plaintext credentials, tracked and already pushed. The 1st Touch and QL SQL service-account passwords, the SMTP password, and the full connection strings that embed them sat in extracted/workflow_variables/WorkflowVariables/ENV_*.variable and their Versaa_Exports_md/ counterparts, in every commit since 92d32bb. Versaa stores workflow variables with IsEncrypted=false, so exporting that category exports the credentials verbatim. The .gitignore was not at fault.env, ms_export_config.json, .halo_profile/ and scratch/ were all correctly excluded and history was genuinely clean of them. The leak was in exported content, which no ignore rule had ever been aimed at. A tracked versaa-rag/data/freshdesk_session.json (live session cookies) was missed the same way — the ignore rules named its siblings, not it.

Fixed at source. New versaa-rag/redaction.py is the single definition of what a secret looks like, called from two write paths: watch_and_process._extract_package() (so extracted/ is redacted as the ZIP is unpacked — this also covers extract_all_categories.py, which reuses that function) and preprocessing/convert_exports.py (so a stale pre-redaction file can't leak into the markdown). Server and database names are deliberately kept — they are documented in wiki/sql/cheat-sheet.md anyway and are what makes the export useful — and $VAR$ indirections are left intact; only credentials are blanked. versaa-rag/scrub_variable_secrets.py cleaned what had already landed: 18 .variable files redacted, 18 markdown exports regenerated, post-scrub audit 0 matches. freshdesk_session.json untracked and versaa-rag/data/*_session.json added to .gitignore.

⚠ Rotation is still required and is independent of all of the above. History rewriting cannot undo an exposure that has been on a remote since 92d32bb. The three accounts must be treated as compromised; rotating them means updating the ENV_*_Password / ENV_*_ConnectionString workflow variables in Management Studio, per environment — the values live in Versaa, not in a config file.

Sharing route: a wiki-only private GitHub mirror, not access to this repo. wiki/ was verified clean (no passwords, connection strings, API keys or tenant data; 10 internal LHP email addresses across 3 pages; catalog pages describe the ENV_* variables in prose but hold no values). The working repo stays Imran-only — it also carries 28 verbatim Teams transcripts of named people and ~170 MB of licensed Aareon documentation, neither of which is ours to redistribute. New versaa-rag/publish_wiki.py + publish_wiki.ps1 copy the 49 hand-written pages to ..\versaa_wiki_publish (outside this repo so its .git can never be swept into a commit here), demote out-of-tree ../../ links to code spans, generate a README stating internal-only and one-way, secret-scan every page, and exit 1 without committing on any hit — GitHub push protection is not available on free private repos, so this local guard is the only one. Verified by planting a fake connection string in a wiki page: abort, nothing written.

GitHub rather than SharePoint because wiki pages cross-link with relative paths (../platform/x.md) that GitHub renders as working links with no build step; SharePoint would break every one of them, and Browser File Handling blocks serving a built static site there too. wiki/catalog/** is excluded from the mirror — unreviewed LLM-generated prose, and every page links out to Versaa_Exports_md/, which is not published.

Operational detail in CLAUDE.md §16; §13 gained a publish step so a wiki change reaches the team as a matter of course.

[2026-08-18] fix | Ticket 17337 — Laundry blank is device-side, not server-side; datalist dedup mechanism corrected

Kelly (07/08) reported the Laundry location still blank on B47451 / B3701 / B3786 in Test, 17 days after the fix was logged RESOLVED, and asked whether the duplication fix had gone Live. Duplication: yes — all three parts landed 27/07; the outstanding gap is that B47461/D52521 were never re-tested in Live.

The recorded root-cause mechanism was wrong. wiki/asbestos/overview.md and memory/LESSONS.md carried Peter Steele's rule that a duplicate desn means neither row is written to the datalist. Reference-data .list files are gzipped XML — nothing in the pipeline ever gunzipped them, which is why Versaa_Exports_md/reference_lists/*.md all render as *(parse error)* and why nobody had checked. Decompressing Aareon.QL-x.AsbestosLocations.list at three git revisions shows the broken 15/07 list held exactly one row for every duplicate pair, always the lower code_idBINSTORE✅ / COMMONROOM✅ / PASSAGE✅ / FELEV / LAUNDR. Corroborated by AsbestosLocationsSQL being order by desn with no DISTINCT and no tie-break, so ties fall to index order. Laundry was the only pair that broke because it is the only one whose dead code sorts first. Consequence: Bin Store / Common Room / Passage were never at risk — that open item is closed without running any SQL, and ticket_17337_verify_after_peter.sql (V2) is moot for the purpose.

All three environments verified server-side (18/08): Dev 218 / Test 215 / Live 217 items, Laundry → LAUNDRY, zero duplicate Names, BINSTR/PSSAGE/COMNRM absent everywhere. Kelly's own screenshot shows Location Code: LAUNDRY on the task, and every server resolves that code — so the remaining suspect is a stale reference-data cache on her device. Next action is a forced full resync and re-check, not another QL change; if it survives a clean resync, escalate as a RefData notification-delivery fault.

Structural finding — two workflows write the same datalist, and the manual one is not the scheduled one. QL Asbestos Base Data has a bare <Start> (no timer, no message, allowRunFromPortal="false") → manual only. QL Property Survey Base Data writes the same list on a nightly 20:20 schedule with near-identical SQL (extra desn not like 'CAT&GENCODE%', different provider-type variable). Last writer wins, so Peter's 21/07 manual run was overwritten that same evening — the durable fix is always cmpgencd. Schedules are per-environment. Also noted: Asbestos Data is the only asbestos form resolving location client-side; all others (Append Asbestos By Property, Load Asbestos Data Config, EleLoadAsbestosData, Electrical Cert AdHoc) join cmpgencd server-side — which is exactly why this form alone can blank on one device while QL looks perfect.

Tooling: new versaa-rag/fetch_reference_list.py fetches and decodes a single reference list from any environment — necessary because Live's ~6,255 reference-data items make a whole-category export 504 every time (8/8 attempts; 357-byte payload vs 150 KB). Fixed a dead -IUnderstandLive switch: pull.ps1 gated Live locally but never forwarded --i-understand-live, and run_dev_pull.py had no such argument, so every Live pull failed at the Python guard. Trap avoided: the Test reference-data package landed in all_versaa_forms/, where the next watch_and_process.py --once would have extracted Test content over the Dev-derived extracted/ tree — quarantined to scratch/; -SkipProcess protects the current run, not a later one.

No Versaa artefact changed — no form, package or redeploy, so no §12 control-document row.

[2026-08-14] fix | Asbestos Site Audit v78 — PDF tenant-comment de-duplication (Ticket 7930, Kelly 07/08)

Kelly's completed-form PDF showed the tenant-comment label twice: static heading Any other comments from the tenant with regards to the survey? then the Tenant Comments merge field re-printing Any other comments from the tenant: she was very happy. Root cause: the PDF is a Word template (extracted/document_templates/DocumentTemplates/Asbestos+Site+Audit.template, selected via Form_DocumentTemplateName), which holds the static heading and binds a Tenant Comments:1d8ba2f9-… merge field; my v76 relabel had put the label inside that field's value, so it duplicated the heading. Fix (scriptable, no template edit): Tenant Comments calc field → raw answer only; the Any other comments from the tenant: label moved into the XSLT (asbestos_site_audit_xslt, <xsl:if> on non-empty) so the QL work tray full description is unchanged. Transform-tested (lxml): label present with a comment, cleanly omitted when empty — order still Issues Identified → tenant → Additional Comments. Also aligned the two on-form captions customertenant. Packages asbestos_site_audit_v78_140826.ftpackage + asbestos_site_audit_xslt_var_140826.ftpackage (build versaa-rag/build_asbestos_site_audit_v76.py, ASA_VER=78). Pending Dev→Test import + Kelly re-test. Gotcha recorded: duplicated PDF text was static Word-template text, not a form field — check the .template word/document.xml (static runs + descr="Field:GUID" bindings); a bound merge field renders even when the form item's availableForDocumentRendering=False. CPL017 → COMPLIANCE still a separate QL admin task (Kelly asked it be passed on); SQL at scratch/halo_tickets/ticket_7930_cpl017_allocation.sql.

[2026-08-14] refresh | Repo refreshed from a manual 9-package MS export (Dev) — 9 forms flipped record lineage

Imran hand-exported all 9 artefact categories from Management Studio into all_versaa_forms/ (*_140826.ftpackage). Processed with watch_and_process.py --once: 1,890 files extracted → 2,167 Markdown exports → catalog → ChromaDB reindex (3,124 files) → commit 4e1f82f, pushed. Dependency-graph ground-truth checks all pass.

Environment verified before processing (post-drift reflex): ENV_1T_DB_ConnectionString in the workflow-variables package reads Versaa_PrtyDev → Dev. All 9 ZIPs valid.

Dev changed almost nothing over the two-week gap. One genuinely new artefact — workflow Versaa 2FA OTP Generator (v0). Everything else in the diff is .schedule / .workflow.rels / binary reference-data serialisation churn.

The one real finding: nine .form files changed by record id only — eight Electrical Edit *_0 (content byte-identical) and Damp and Mould Inspection_0 (72418e2d…74fc77b1…, a materially different form). All nine moved onto the Dev lineage suffix 000d3ad48fe9, and D&M returned to the id held by the pre-drift 16/04 Dev export, so this reads as Dev content overwriting residue from the 30/06 Live-mislabelled pull. Not fully explained: the 29/07 corrective Dev pull rewrote 37 form files but skipped these nine. Confirm the Active Dev Damp and Mould Inspection record in MS before trusting its catalog page. Written up in sessions/gotchas-and-tips.md.

Two export defects handed back: all_user_properties_140826.ftpackage exported empty (metadata entries only, no UserProperties/ folder); reference data arrived as the full ~34 MB system set because a manual MS export skips the business-only filter pull.ps1 applies.


[2026-07-29] audit | Wiki freshness audit — catalog is 2/3 stale (Live-derived); new Void Post Inspection page

Full freshness pass over the wiki.

Current and verified: log.md covers all three recent workstreams; gotchas-and-tips.md carries the 29/07 additions; index.md has 0 broken links and 0 orphan pages; and _verify_dependency_graph.py passes every ground-truth check.

The gap — the auto-generated catalog. ~1,267 of ~1,925 catalog pages (forms, workflows, reference data) are still derived from the 27/07 Live export, because the corrective Dev pull only partially succeeded. See the correction appended to the env-drift entry below.

Fixed in this pass: - New page forms/void-post-inspection.md — ticket 22387 had no wiki page despite being the largest active workstream (§13 requires one for a form touched for the first time). Indexed. - The form-record recovery procedure promoted from log prose into forms/how-to-edit-a-form.md: unlock latest → export the ACTIVE version from MS → re-import; MS has no delete-a-version; Create Version copies from the latest, not the active. - Catalog pollution removed. Void+Post+Inspection_ls.form and five _stage_*.form build artefacts were sitting in extracted/forms/Forms/, which convert_exports.py globs — so a build artefact had become a catalog page (wiki/catalog/forms/Void_Post_Inspection_ls.md, now deleted along with its Versaa_Exports_md/ counterpart). All six moved to versaa-rag/staging/void_post_insp/ and the three transform/build scripts repointed. They are kept tracked, not ignored: the transform mints ids with uuid.uuid4(), so _ls.form is not reproducible and is the exact artefact that became Dev v9. - Version contradiction settled by a read-only MS query. STATE.md claimed Void Post Inspection was on v7 and "not yet proven to import"; the log claimed v9. Dev is on v9, ACTIVE + LATEST (record bfc2b5bc…) — the log was right, STATE.md was stale and has been corrected, along with its "nightly pull healthy · STABLE" line (the task is disabled) and a cleared 23/06 uncommitted-work backlog.


[2026-07-29] build | Ticket 22387 — Void Post Inspection "Lettable Standard" checklist (Dev v9 active) + .ftpackage must be CLONED, never hand-authored

Ticket 22387 (Jo Blackburn, owner Andy Tyler) asked for a "new" digital void post-inspection checklist. Peter and Mark were right that one already exists: Void Post Inspection, and it already carries the QL order launch + diary appointment, the PostInspection/CompleteOrder QL write-back, the EDRMS DocEx handler (VoidPostInsp), and the Transaction_Task_VoidPostInspection* warehouse tables. So the work is an extension, not a new form — and the fact it is order-driven settles Jo's own open question about launching from Property Search.

Baseline (Phase 0). Three separate records share the name and the version numbers are not comparable: Dev bfc2b5bc… v3 (548 items, modified 30/04/2026 — the newest content), Test cbea7488… v28 (2022), Live 01c8873c… v18 (2022). The on-disk _28 file is the Test lineage. Always confirm the record id per environment.

Delivered to Dev (v9, active): a 6th Lettable Standard branch off the Inspection Hub — 22 × LS_QNN Yes/No/N-A questions with comments gated on answer ≠ Yes, 19 photos, LS_KeysSupplied/LS_FobsSupplied/LS_FobsPhoto/LS_ItemsToFit, plus Meter_ElectricLocation/Meter_GasLocation/Meter_ElectricDebt. 70 new items; existing branches untouched. Scripts: transform_void_post_insp_lettable_standard.py + build_void_post_insp_package.py.

The expensive lesson — packaging. Hand-authored package metadata failed import repeatedly with only An unexpected error occurred, please try again later; the response body carries no detail and the Logging API 504s. Theories were tested one at a time through Imran (BOM, compression, version number, description), costing many round-trips and leaving four corrupt draft versions (v4–v6) on the Dev record. It was resolved in minutes once Imran supplied a package that had genuinely imported (VPI-v3.ftpackage): clone the known-good package and swap only the artefact bytes. Format facts and the full rule are in sessions/gotchas-and-tips.md — including that the metadata files do carry a UTF-8 BOM, that .rels/[Content_Types].xml are STORED, and that version="3" imports fine even when v3–v8 exist.

Recovering a stuck form record (also proven here): unlock the latest version on the Form Versions page, export the active version from MS and re-import it. Note that the Create Version button copies from the latest version, not the active one — clicking it while the latest is corrupt just makes another corrupt version.

Still open: five questions with Jo (planning mailbox for the completion email, photos mandatory or not, keys/fobs as counts, §3 photos, and whether her five outcomes become new QL POSINS_RES codes); the QLpost_ contact block; and the Word template bindings. Drafts: scratch/halo_tickets/ticket_22387_jo_questions.md, ticket_22387_peter_ql_asks.md.


[2026-07-29] fix | Nightly "Dev pull" was exporting Test/Live — --env now authoritative, cookies no longer drag base_url with them

Symptom reported: "the daily export was pulling Live". Confirmed, and it ran for ~4 weeks. Auditing Fetching items from across every versaa-rag/logs/dev_pull_*.log: Dev until 29/06 → Live 30/06, 01/07, 07/07Test for most runs 07/07–27/07Live again on the 27/07 20:30 nightly (the run that produced commit 73ae050).

Root cause: export_from_management_studio.save_cookies() persisted the session with json.dump(cfg) — the whole config. Every MS tool sets cfg["base_url"] = ms.base_url_for_env(args.env) before logging in, so one --env test/--env live run permanently rewrote versaa-rag/config/ms_export_config.json. The export script had no --env and simply followed that field, so the nightly task inherited whichever environment was last worked in. Nothing in the output, the status file or the commit message named the environment.

Fix (4 files): - export_from_management_studio.pysave_cookies() re-reads the on-disk config and writes cookies only, per-env under cookies_by_env; new --env {dev,test,live} (default dev, authoritative), --i-understand-live guard, ENVIRONMENT: banner + non-Dev warning; build_session() falls back to the matching env's cookie jar. - run_dev_pull.py — new --env (default dev), passed explicitly to the export step; logged as ENV and in the RESULT: line. - scheduled_dev_pull.ps1 — nightly runs --env dev --active-only; logs/last_run_status.json now records the environment actually hit.

Verified: --env live refuses without --i-understand-live; --env test --dry-run hit PrtyTest (169 forms) and left on-disk base_url on Dev; --env dev --dry-run returned Dev's 206 forms; cookie jars separated (cookies_by_env: {test, dev}).

Schedule switched off, replaced by an on-demand script (same day, user's call). Versaa Dev Pull (nightly) is disabled. New versaa-rag/pull.ps1 is the single entry point — -Env / -Only / -Force / -AllRefData / -DryRun / -SkipProcess / -Toast / -IUnderstandLive — printing a coloured summary, writing logs/last_run_status.json (now including the env actually hit, read back from the export's own banner, plus failed categories) and suggesting the exact -Only retry command. scheduled_dev_pull.ps1 is reduced to a one-line caller so the scheduled and manual paths cannot drift. A new --only flag on export_from_management_studio.py / run_dev_pull.py makes retrying the 504-prone categories cost one list call each instead of a full 11-endpoint sweep. CLAUDE.md §7 and §15 rewritten around pull.ps1. Also fixed: archive_existing_packages() used a lazy Path.glob while moving files, so one package per run was skipped and later re-processed as new.

Cleanup + open item: the 28/07 manual pull ran --skip-process, so the repo still carried the 27/07 Live export — a corrective full Dev pull was run to overwrite extracted/, Versaa_Exports_md/ and wiki/catalog/.

⚠ CORRECTION (audited 29/07, same day): that corrective pull only partially succeeded and the sentence above overstates it. Forms (206), Workflows (368) and ReferenceData (1079) 504'd on all 4 attempts and the deferred pass — on 28/07 and on both 29/07 runs. Only the six small categories (WorkflowVariables, DocumentTemplates, DiaryConfigurations, Providers, GroupTableMappings, UserProperties) actually re-exported from Dev. So the forms, workflows and reference-data slices of extracted/, Versaa_Exports_md/ and wiki/catalog/ are still derived from the 27/07 LIVE export — roughly 1,267 of ~1,925 catalog pages. Evidence: logs/last_run_status.json records "result": "failed", and dev_pull_20260727_203003.log shows the last successful Forms/Workflows export fetching from …/ManagementStudioPrtyLive. Do not trust a form or workflow version number read from the catalog until .\versaa-rag\pull.ps1 -Only forms,workflows,reference_data completes cleanly.

✅ RESOLVED the same day, 16:52–16:59. pull.ps1 -Only forms,workflows,reference_data completed SUCCESS against Dev in 6.5 minutes — Forms (206), Workflows (368), ReferenceData (1079), no 504s. extracted/, Versaa_Exports_md/ and wiki/catalog/ are now genuinely Dev-derived; Void+Post+Inspection_9.form (Dev v9) landed as expected.

⚠ One piece of evidence in the original audit was WRONG: "43 files under extracted/forms/Forms/ contain PrtyLive" was cited as a sign of Live-derived content. It is not. Those files were rewritten by today's successful Dev export and the match is legitimate form content — <Value type="Constant">PrtyLive</Value>, an environment name held as a constant inside the form. A PrtyLive string in a form proves nothing about which environment exported it. The export log is the only reliable indicator of the source environment — check ENVIRONMENT: / Fetching items from in versaa-rag/logs/dev_pull_*.log. Live DB connection string + password remain in git history (73ae050 and earlier) — flagged for Imran to decide on scrub/rotation. Detail: sessions/gotchas-and-tips.md.


[2026-07-27] deploy | Ticket 17337 — form + guard workflow imported to Live; generic PCA workflow must NOT be promoted (would regress Void Weekly Flushing)

Imported to Live: Asbestos Data form (Test v12, carries sendAllItems=True) and Asbestos Get Property Data workflow (Test v3, the idempotency guard — Live v4 had none, a case of a higher version number holding older content).

Third item STOPPED before import. A full structural diff of the generic Property Check Appoint LoadDat (Test v24 vs Live v8, compare_asbestos_test_live.py) shows promoting Test v24 would have carried three unrelated generic changes, one a straight regression: 1. It drops Void Weekly Flushing from the ignored-forms exclusion list. The lists diverge in both directions — Test v24 adds Asbestos Data/Damp and Mould Inspection/Red Flag Reporting/Component Replacement Request/Family Survey 2 MW, but Live v8 alone carries Void Weekly Flushing. Importing Test would re-expose that form to the generic loader on top of its own Week Void Flush App Data — recreating this very double-load bug on a different form. 2. Adds a third component-load pass (Append/Wait/Unpack Component 3 data, callback AppendPCAComponent3CallBack) — affects every form traversing the generic loader. 3. Removes the terminal End SetUserStatus step (Live: Send Callback Event → End → end node; Test: straight to the end node) — changes end-of-run user status for every PCA form.

Correct action instead — a one-token surgical edit of Live v8's existing XPath: append or contains(//Event[not(ancestor::Event)]/@name, 'Asbestos Data.') (trailing dot — entries match the event prefix, e.g. Asbestos Data.PCALoadData), save as a new Live version and activate.

✅ DONE 27/07: edited Live v8 in place on the Ignored forms gateway connection (Exclusive Fork, Source Step Initial Data, Value true) → Live v9, activated, comment "Ticket 17337: added Asbestos Data to ignored forms". Void Weekly Flushing preserved. All three parts of the fix are now in Live (form + guard workflow + PCA exclusion). Remaining: re-test B47461 / D52521 in Live, then reply to Kelly.

Flagged for Mark/Peter (separate from 17337): four forms are excluded in Test but not Live — Damp and Mould Inspection, Red Flag Reporting, Component Replacement Request, Family Survey 2 MW. Any of those that are live with their own loader may be double-loading right now, unreported. Don't fix blind.

Lesson recorded: never promote a shared workflow wholesale to fix one form — diff its exclusion list as a set, in both directions, and prefer editing the target environment's existing version. Checklist: scratch/halo_tickets/ticket_17337_live_deployment_check.md.


[2026-07-21] diagnose | Ticket 17337 — Asbestos Data duplication VERIFIED fixed; blank "Location" traced to a duplicate-description collision in QL general codes

Duplication (the ticket's subject) is fixed and confirmed. Kelly re-tested in Aareon Mobile Test on 11/06/2026 across six properties — B47461, D52521, B45526, D54386, D34586, B47451 — no duplicates anywhere. Root cause was sendAllItems=False on the Get Asbestos Data request starving the Asbestos Data Loaded idempotency guard, so Asbestos Get Property Data ran twice and appended every component again ("duplicates below the yellow line"); fixed by sendAllItems=True (form v41, Dev now v42). Live deploy held pending sign-off — Live has run the duplicating version since 21/11/2025.

Kelly's leftover — 2 Laundry items with no location description on B47451 and B3701 — is a separate, pre-existing defect and is NOT a Versaa bug. First hypothesis (missing QL general code) was disproved by SQL: LAUNDRY exists in cmpgencd as HSG/LOCATION → Laundry and resolves correctly; the AsbestosLocationsSQL exclusion list is not involved (0 components use an excluded code). Actual root cause: a duplicate-description collision. cmpgencd holds two HSG/LOCATION codes with the identical description LaundryLAUNDR (used by nothing, any component type) and LAUNDRY (93 asbestos components / 33 properties). The datalist behind the grid's Location column (z_QLPrtyCm_location lookupAareon.QL-x.AsbestosLocations.list, built by AsbestosLocationsSQL) collapses duplicate descriptions and keeps the first code; LAUNDR is a prefix of LAUNDRY so it sorts first — the dead code wins and the in-use code renders blank. Why only Laundry: there are 5 duplicate pairs (Bin Store, Common Room, Front Elevation (Legacy), Laundry, Passage) and in every other one the in-use code sorts first (BINSTORE<BINSTR, COMMONROOM<COMNRM, PASSAGE<PSSAGE) — Laundry is the only pair where the unused legacy code wins. Fix = one QL field: rename LAUNDR's description to Laundry (Legacy) in Category & General Code Maintenance (the convention already used by Front Elevation (Legacy), Landing (Legacy), Corridor (Legacy)…). The list rebuilds live from QL → no form change, no package, no redeploy, all 33 properties self-correct. Routed as a separate QL ticket, following the CPL017 precedent from 7930.

Answering Kelly's "check all location descriptions" — yes, there are others, and more than expected. A full sweep of every location code used on asbestos components found 8 codes absent from HSG/LOCATION entirely (so also blank): KIT (29 components / 28 properties), BATH (27/27), plus WETRM, HALLCUP2, LOUNGE, REARGARDEN, LANDCB2, CUPB1 (1 each). KIT/BATH look like legacy variants of KITCHEN/BATHROOM.

Gotchas recorded: (1) "works in QL but blank in Versaa" — QL resolves a code by its own lookup, Versaa resolves it through a datalist built by a workflow-variable SQL with its own filters; always re-run that variable's SQL verbatim before blaming the form. (2) A duplicate description is as breaking as a missing code, and which one survives depends on sort order — so the failure looks arbitrary until you list the pairs. (3) ms_sql.py --out opens the file with mode "w" before connecting, so a failed run (e.g. launched without run_as_sql_admin.ps1, authenticating as the wrong identity) truncates the previous good results.

Diagnostic SQL: scratch/halo_tickets/ticket_17337_location_desc_investigation.sql, ticket_17337_followup.sql. Wiki: wiki/asbestos/overview.md (new section + 3 Common Issues rows).

⚠️ SUPERSEDED 18/08/2026 — the mechanism stated in this paragraph is wrong. A duplicate description writes one row (the lower code_id), not neither; and Bin Store / Common Room / Passage were never at risk. See the 2026-08-18 entry at the top of this log. The LAUNDR deletion and the re-run described below did happen and were correct.

Update (21/07, same day) — Peter Steele confirmed, corrected the mechanism, and fixed Laundry. Kelly escalated to Peter, who independently traced the same chain (z_QLPrtyCm_location lookupAsbestosLocationsSQLcmpgencd) and corrected the mechanism: the datalist needs a unique Name AND Code, so a duplicate description means neither row is written (blank) — not "first-sorting code wins" as I'd inferred. He deleted LAUNDR (after confirming hgmprcom has zero uses) in Dev/Test/Live and re-ran the QL Asbestos Base Data workflow in Test+Dev to rebuild the list → Laundry now shows, ready for user re-test. Key correction captured: the list is regenerated by that workflow, not purely live — re-run it after any cat/gen-code change. Two items still open (flag to Peter, not new tickets per Kelly's ask): (1) by the same rule the other in-use duplicate pairs — Bin Store (BINSTORE/BINSTR), Common Room (COMMONROOM/COMNRM), Passage (PASSAGE/PSSAGE) — should also be blank; unverified → check via ticket_17337_verify_after_peter.sql; (2) the 8 codes missing from HSG/LOCATION entirely (KIT 29/28, BATH 27/27, +6 one-offs). Ticket stays open (Kelly's request) and will be reassigned with the diagnosis rather than closed. Duplication fix still needs the Test→Live promotion.

Version audit (21/07, versaa-rag/audit_asbestos_versions.py, live MS across all 3 envs) — corrects my records. MS Dev active for the form is v40, not the "v42" I'd logged (that came from the on-disk package filename _41/_42; per the standing lesson MS assigns its own version and ignores the label — track MS, not the filename). Audited active/latest: Asbestos Data Dev 40 / Test 12 / Live 6; Asbestos Get Property Data 5 / 3 / 4; Asbestos Survey Load Data 8 / 4 / 4; Property Check Appoint LoadDat 31 / 24 / 8. Numbers are not comparable across envs (each auto-increments per import). Kelly verified Test (form v12). To deploy: promote the Test artefacts → Live — Live is behind on the form (6<12) and the PCA workflow (8<24). Content-check two first: Asbestos Get Property Data Live v4 > Test v3, and Property Check Appoint LoadDat is a generic PCA workflow whose 24→8 gap may carry non-asbestos changes. Control doc + wiki/asbestos/overview.md + STATE corrected to the audited numbers.


[2026-07-16..17] fix | Void Survey 21617 — CRR pull-through, Meter Readings error, clearance photos, debug panel hidden (ticket close)

Closing pass on ticket 21617 (form Dev v48 / Test v57). All device-verified.

  • CRR sub-form pull-through (v45–v46). The ComponentReplacementRequest SubTask mapped the property id into the generic SubTask Property ID header (a copy-paste from the Safeguarding sub-task), but the CRR loader reads GenBP_Key_PropertyId/QLPrtyCm_PropertyId"Request Component Data" queried with no property → empty list. Reference = Repair Works Order (v97) — it maps into the loader keys. Mirrored it: property id → GenBP_Key_PropertyId + QLPrtyCm_PropertyId + QL_Property ID; address → QLPrty_AddressLine1..5/QLPrty_PostCode. Components + address now load. Property Type left blank (no clean source; RWO doesn't map it either).
  • Meter Readings doc error (v47). Void Insp Meter Readings/1T_Export_Document failed every completion on a deleted prepay item used in a template condition operand. Hand-editing the .template is impossible — VSTO-customized docs; re-zipping breaks ThisDocument (ControlNotFoundException). Fixed form-side: re-added the 5 removed prepay items as hidden FormItems, the 2 operand ones carrying value formulas → the current type selectors (Meter_Electric1TypeCredit/Meter_GasTypeCredit). Template conditions now resolve + evaluate correctly. Errored instances gone.
  • Clearance on the PDF (v43–v44 + template). The 5 clearance _btn + their _wg photo/description groups were the only availableForDocumentRendering=False items of 600. Flipped to True; then bound onto the report via the Connector field picker (yes/no fields as items; photos/descriptions via Insert Group Item → Begin/End Repeater, mirroring the existing Room/Misc photo repeaters). Gas cards/keys added the same way.
  • Debug panel hidden (v48). Removed the always-visible QLcrm_ contact display block (24 fields + labels) from the surveyor layout; FormItem defs kept so the contact still fires.

Corrections to our own knowledge, all mine, all logged as lessons: (1) Word-template bindings can't be hand-authored — the Connector issues GUIDs server-side (proven when two clean hand-built files were rejected); the earlier "it's a document-local key" claim was wrong. (2) .template files are VSTO docs — never re-zip them. (3) Client-no switches are a fallback chain, not alternatives (the v38 tenant-flags-to-No error).

Deferred (separate ticket, NOT 21617): CRR completion email not sending — reproduces in Repair Works Order, so it's a shared CRR document-template/recipient issue (ComponentReplacementRequest@lincolnshirehp.com), not form-specific. The Void Survey report email (to the address the surveyor types on the last page) works.

[2026-07-15] fix | Diary Portal — void survey tasks not arriving: the Appointment poller wasn't Running (Peter Steele)

Symptom. Jo's recipe for self-dispatching a test Void Survey (QL → Contact Management → Contact Actions → Add Action VDR234 → Create 1st Touch Appointment → Register and Issue → Diary Portal → drag onto self → Dispatch) stopped producing anything in Diary Portal. It had worked the day before.

Root cause. The Appointment Early / Main / Late Polling scheduled workflows are what look at QL and pull appointments into Versaa. At all times one of them should be Running; if none is, Versaa isn't looking at QL at all (Peter). On PrtyTest none was Running (while PrtyDev's Appointment Main Polling had been Running since 08:15). The workflow's own schedule showed Last run at 14/07 08:15Next run at 16/07 08:15 despite Occurs every 1440 minutesthe schedule had silently skipped the 15th.

Fix. MS → Workflows → View Workflows → Appointment Main PollingStart Instance"came through straight away". Queued appointments flowed without re-issuing, and no service restart was needed.

Two corrections to our own knowledge — both mine: 1. hpm1stoa is not the poller's source. I diagnosed this from hpm1stoa.processed_yn (orders 381183/381184 sat at False) and concluded the polling service had stalled, recommending an AAREON-TCH02 Installer restart. Wrong table — an action creates an order (hpm1stoa), but the diary Task only follows from an appointment (hpm1stua), which is what the Main Appointment Poller reads. The wiki page had said hpm1stoa was "the primary polling source"; corrected. 2. A scheduled workflow that completes normally leaves NO instance history"the only time you see history of it is if it is cancelled or errored". So "I can't see the poller ran yesterday" was never evidence of a fault, and chasing it wasted most of the hunt. Only the Running state is meaningful.

Also fixed en route: ms_workflow_errors.py, ms_workflow_actions.py and ms_user_info.py all had the same --env bug as ms_import.pyif args.env != "dev" meant --env dev silently fell through to the config's base_url (PrtyTest), so "Dev" queries were really hitting Test (they returned identical instance IDs, which is what gave it away). --env is now authoritative in all of them. Note ms_workflow_errors.py's --status any, --workflow and --since filters are still unreliable — prefer explicit statuses.

Updated it/appointment-polling.md (rewritten around the Running-state check, plus Jo's self-dispatch recipe, previously undocumented) + scratch/diary_portal_task_check.sql.

[2026-07-13] change | Void Survey v40 — Jo session-7: contact capture redesigned, back-door mandatory bug, clearance on PDF

Ticket 21617, from Jo Blackburn's 14/07 call. She confirmed the contact still wasn't creating (that's the workflow gate — see the v12 entry below) and asked for a redesign of the bedroom/property capture plus polish before her user-testing session.

  • Contact redesign. The surveyor no longer edits corrected values. The three editable fields (QLPrtyx_con_type/con_style/bed_nos_hgmprty2) are removed from the layout — they duplicated the captions of the read-only QLPrtyxO_* "current value" siblings shown above them (two "Property Type" boxes on screen). New Form_PropertyDetailsComments (multi-line, mandatory, visible only when Form_PropertyDetailsCorrect=="No") is where the inspector describes the discrepancy in their own words. QLcrm_Brief Description = the question ("Are the bedrooms and type of property correct?" — Jo: "what Pete does is put the question in there"); QLcrm_Full Description = the comment. A housing officer reads it and decides. Jo confirmed CreateDefaultAction stays No — she builds the action in QL against the classification. Class Level 1..5 land in QL's "Additional Classes" 1–5.
  • Jo's "mandatory" complaint was one mislabelled field. Misc_FrontDoorComments is actually the BACK door Comments box, and its Visible formula was enabled="false" so it never hid — exactly her "say no back door and it still wants comments". Fixed by gating it like its sibling Misc_BackDoorType. Rule learned: all 117 isMandatory flags on this form are static; relevance gating is via Visible formulas and hidden items are not validated — so "mandatory when irrelevant" is a visible bug. Do not blanket-flip isMandatory (that would have stripped guardrails off risk assessment, meters, rooms…).
  • Inspector Comments now also on the survey complete = Yes path — the box lives in the Operative Comments block which the Yes route never visits. Cloned the element into Inspection Result rather than rerouting, because Operative Comments also holds debug items that would then be exposed to inspectors.
  • Clearance on the PDF. The five VFP_PreWork*_btn items had availableForDocumentRendering=Falsethe only False items out of 600, so they never reached the report and didn't even appear in the Template Designer's field picker. Flipped to True (stays optional).
  • Template bindings: hand-authoring FAILED — the original "cannot be scripted" conclusion was right after all. I overturned it mid-session (the GUID looked like a document-local key: 494/654 shared fields have different GUIDs per template; condition0 is bound 10× in one template with 10 GUIDs) and authored 7 new bindings by cloning table rows — Void Survey (clearance + gas keys).docx, then v2 after fixing duplicate w14:paraId/wp14:anchorId (the source has zero duplicates; w14:textId="77777777" repeats 201× and is meant to). Both were rejected by Save to Server: System.NullReferenceException at DocumentEx.RangeInGroups ← DocumentValidator.CheckDocument(validIdentifiers, customTables, managedShapes). The evidence was ambiguous: differing GUIDs fit "the Connector mints a fresh GUID per insertion, server-side, and remembers which it issued" equally well — and that is what the rejection says is happening. A GUID the server never issued cannot be resolved → null. Lesson: don't overturn a conclusion on evidence that fits both readings. What actually unblocked it: the 5 clearance fields were availableForDocumentRendering=False — the only False items of 600 — so they weren't even in the Connector's field picker. That flag is now True (Dev v42 / Test v51), so the correct route is: load the template from the server → insert each field via the field picker (it mints valid GUIDs and writes both halves) → Save to Server. The generated v2 docx is kept purely as a layout reference (which rows, which labels, where); its bindings do not validate. Editing existing bindings (remove/move/re-layout) still works and still pastes fine — only new ones need the picker.
  • Gotcha found while fixing Inspector Comments: a gated SectionBreakElement hides every element after it. Inspection Result has a break gated on z_ShowDebugData=="Yes", so the tail (debug fields, permissions, Red Flags) is debug-only. Appending GenBP_InspectorComments to the end of the block left it invisible despite visible=True; moved it to directly after Total SMV (v41 / Test v50). Element order decides visibility, not just the element's own flag.

Imported+activated Dev v40 / Test v49 (transform_void_survey_v40_session7.py).

v42 — the client fallback chain (my v38 error, surfaced by removing the gate). With the gate gone the contact step finally RAN, and errored: "Unable to create contact as there is no client no provided. Contact no=-117190, class1='TENANCYMGT', class2='CHANGES', class3='BEDROOM', class4='PROPERTY', class5='QUERY'" (Test instance 7e1b168b-987f-f111-8ebc-6045bdd12ed7). The classification was landing perfectly — only the client resolution failed. Cause: in v38 I set QLcrm_Use Property Tenant Client No and QLcrm_Use Tenancy Seq No Tenant Client No to No, reasoning "a void has no tenant, and the proven QLMV_ block used void-only". Wrong — the three switches are a fallback chain ("if there is no client no specified, use …"): explicit client → property's void client → property's lead tenant's client → lead tenant on the tenancy. Test property D2351 is now tenanted (tenancy 20055026, client 415398 — visible in Jo's screenshot), so there was no void client AND no fallback. QLMV_ only seemed to prove void-only because it never carried the tenant items and the property was genuinely void in June. Restored both to YesDev v42 / Test v51 (transform_void_survey_v42_clientchain.py). Now: real void → void client; tenanted → tenant's client (which is exactly where Jo's screenshot expects it, in Household Contact Info). Meta-lesson: the June gate had been masking this live bug for weeks — suppressing a symptom hid the real fault.

Pending: end-to-end re-test, load Void Survey (clearance + gas keys).docx via the Connector, hide the QLcrm_ debug panel before user testing, then CRR sub-form pull-through. (Also seen in the same error list: Void Insp Meter Readings / 1T_Export_Document still failing on Could not locate Item Meter_ElectricMeterPrePayment — the known stale prepay bindings in Meter+Readings.template.)

[2026-07-13] fix | Void Survey — QL "bedroom/property incorrect" contact consolidated onto Peter's QLcrm_ block (v38)

Ticket 21617. Peter Steele saved a Dev v37 with the _Data Items "raise contact" block copied in under prefix QLcrm_ (Jo's 5-level classification TENANCYMGT/CHANGES/BEDROOM/PROPERTY/QUERY; Classification S, Source I, Type I, Use Property Void Client No=Yes), plus a reversed trigger. It was ~80% done. I finished it and consolidated onto it, discarding my earlier QLMV_ classification change (which only ever reached Test v44, never activated): - QLcrm_Brief Description"Bedrooms and type of property incorrect" (was empty). - QLcrm_Full Description → the proven concat "Property Type: "+…con_type_hgmprty2+" Sub-type: "+…con_style_hgmprty2+" Bedrooms: "+…bed_nos_hgmprty2 — Peter's copy left a disabled formula pointing at leftover FRA_descriptn_original. - QLcrm_Requested → tightened to fire only when Form_PropertyDetailsCorrect=="No" (Peter's copy fired on ≠Yes, so also on a blank answer). - QLcrm_Use Property Tenant Client No / QLcrm_Use Tenancy Seq No Tenant Client NoNo (leftover copy defaults of Yes; a void has no tenant, and the verified QLMV_ block used void-only resolution). - Neutralised the old QLMV_ block: QLMV_Requested hard-set to constant "No". Key gotcha: Form_RaiseContactGroupNameList is empty — contact-block registration is by suffix-match (the workflow's ImportContactCreationRequest processes every *_Requested group), so leaving two live blocks would raise two contacts.

Fix during device test (v39): the contact blocked submissionQLcrm_DefaultAction_Action Outcome showed "A value is required". Cause: QLcrm_CreateDefaultAction (copied from _Data Items) defaulted "Yes" (QL creates an allocated action) and that outcome field is unconditionally mandatory + on the layout. The proven QLMV_ block sets CreateDefaultAction to constant "No" (contact only) and carries no DefaultAction detail items. Fix = mirror it: QLcrm_CreateDefaultAction → "No"; QLcrm_DefaultAction_Action Outcome isMandatory → False (transform_void_survey_qlcrm_noaction.py). Dev v39 / Test v46 active.

Workflow fix (v12) — void contacts were silently skipped. A Test void-survey submit (order 381178) produced no row in hpm1stcr — the contact never reached QL. Root cause: Void Pre Inspection QL Updates gates the contact on string-length(QL_Client No)==0 → skip, and a void always has an empty QL_Client No (void client is resolved inside the contact step, downstream of the gate). Every void skipped the contact; the 23/06 success only worked on the pre-gate workflow v7. Re-gated on QLcrm_Requested=='Yes' (transform_void_qlupdates_v12.py) → Dev v11 / Test v12 active. Separately fixed ms_import.py --env dev silently importing to Test (see the tooling lesson). ⏳ Re-test: a NEW void survey in Test answering "No" should now post one contact.

Built Void+Survey_38.form → imported+activated Dev v38 / Test v45 (scripts transform_void_survey_qlcrm_finish.py, build_void_survey_qlcrm_finish.py). ⏳ Pending re-test in Test (one contact, hpm1stcr.processed_yn=1) → then close the ticket. Open for Peter/QL: CreateDefaultAction=Yes on both blocks (creates an allocated action, not just a contact) and whether the BEDROOM/PROPERTY/QUERY classification + routing is configured in QL.

[2026-07-08] fix | CRR v102 — removed duplicate Last Date grid column (ticket 11469)

Ticket 11469 (Component Replacement Request), 02/06/2026 demo feedback from Lorna Holdcroft. Ask 1 (done): removed the Last_date column from the component-selection grid (QLPrtyCm_Components GroupDataGridElement, line 20003 of the form) — it duplicated the installation date (inst_date). Grid-column-only change; underlying QLPrtyCy_last_date FormItem + row-tap detail field + the Comp Request App Data v12 loader are untouched. Built Component+Replacement+Request_102_remove_lastdate_11469.ftpackage (single-form pkg, verified: last_date column gone, next_date kept, version 102); extracted via watcher (v102 now in extracted/). Pending: MS import Dev→Test→Live (MS auto-increments version). Ask 2 (scoped, not built): line-manager approval step before triage is a QL classification/work-tray routing change — today QLcrm_Class Level 1 = ASSET sends the contact straight to the Asset team; QL team (with Pete) must define the Repairs-Team-Leader tray classification + onward "approve → AssetMan" action, then the Versaa side is a small QLcrm_Class Level * repoint. Also captured the CRR QL-contact routing (QLcrm_* fields) as reusable knowledge on the form page. Updated forms/component-replacement-request.md + §12 control doc.

[2026-07-01] ingest | Void Weekly Flushing — discovery + field map

Meeting with Peter Steele & Jo Blackburn (Teams_Sessions/Call with Jo and Pete regarding Void Weekly Flushing.md + scratch/Peters-void-weekly-flushing-plan.pdf). Imran owns the form (work package 3); Pete owns the QL Task Centre job + reports. Discovery finding: the read and cycle-date write-back pipelines already exist and are already pointed at CPLEGVOIDFLUSH cycle 2Week Void Flush App Data (v4, AppendPCAData, SQL 111–130) reads dates/duration/location by block; Weekly Void Flushing Prty Comp (v7, Update QL Comp Cycle, SQL 166–188) writes the flush date via hgm1stuy. Missing = the descriptive/decision columns (*Make/SYSTEMTYPE, Model_no/COMMUNAL, Description, Cond_code, HRAVOID.desn, Lot_id) on read, and component-master writes (COND_CODE='On_hold', notes) — both QL-side extensions for Pete. Form v50 is bloated; plan is strip-back in place (deferred until Pete's test data lands). New page wiki/forms/void-weekly-flushing.md; full field map + QL asks in scratch/Void_Weekly_Flushing_discovery.md.

[2026-06-29] fix | Ticket 20472 root cause — username case-sensitivity creates duplicate accounts (Aareon defect)

FRAs completed then "returned blank" (statutory-compliance impact, escalated by David Fillingham; also Greg Smith). Root-caused against 1stTouch_PrtyLive using the new ms_sql.py/ms_user_info.py: - Aareon Mobile keys identity on the exact case-preserved username; SQL collation is case-insensitive → case-variants are separate accounts to the app but one to the DB. Split is mostly the domain prefix and tracks the client platform: Windows sends LINCOLNSHIREHP\, Android/tablet sends lincolnshirehp\. Same person → 2 accounts by device. - Proven via ClientManagement.UserConnectionRecord: Walker's Windows = LINCOLNSHIREHP\WalkeA, Samsung tablet = lincolnshirehp\Walkea; FRA tasks owned by …\WalkeA but completed on tablet (…\Walkea) → not reconciled → lost. - Scale: 472 case-sensitive usernames for 354 users = 118 dup accounts across 107 users (scratch/dup_accounts.csv). WalkeB = a different person (excluded). Profile.Users empty → AD-backed (Profile.ExternalUser). - Gotcha: plain GROUP BY UserName HIDES it (collation folds case) — must COLLATE Latin1_General_CS_AS. - Deliverables: Aareon bug report + CSV (scratch/halo_tickets/ticket_20472/), internal response draft, new page it/username-case-sensitivity.md. Workaround: standardise login casing across devices + delete dup credential records; lost FRAs re-done after.

[2026-06-29] tooling | Four diagnostic tools added (ticket 20472 prep)

Built to speed Aareon ticket diagnosis (see platform/management-studio-api.md §6–8): - ms_sql.py + run_as_sql_admin.ps1 — read-only SQL runner (SELECT/WITH guard, named cheat-sheet queries, --env, table/json/csv). Windows auth: launched via runas /netonly /user:LINCOLNSHIREHP\adminis (ODBC Driver 18; Encrypt=yes;TrustServerCertificate=yes). Verified: driver/guard/connect-path work (current user SaleeI gets 18456 as expected — needs the runas account). pyodbc added to requirements. - ms_user_info.py — Users API diagnostic (find/show/props). Discovery: api/Users/GetUsers returns {DisplayName,UserName,Properties,Capabilities} (ignores search params; client-side filter); no literal "Work Team" property — visibility is Appointment Pools/DiaryResource(Group)/Operative Trades. Dev user Properties are empty → real data is in Live. Verified find/show/props in Dev. - extract_ticket.py — now reads a ticket_<N>/ folder + .eml (decodes base64/QP, text/plain pref, attachments) + standalone images. Verified on ticket 20472's email (David Fillingham→Anthony Walker thread decoded). - halo_ticket.py — Playwright fetch of a Halo ticket via the authenticated session (LHP has no Halo API); persistent .halo_profile (git-ignored). HALO_BASE_URL=https://lhp-servicehub.haloitsm.com, HALO_TICKET_PATH=tickets?id={n}. VERIFIED end-to-end on ticket 20472. Key fix: Halo renders the conversation in an iframe, so the tool reads inner_text from all frames (main frame is only queue chrome). Also saves full-page PDF/PNG. Also fixed: ENV_BASE_URLS["live"] host (testmobilemobile.lincolnshirehp.com); ms_user_info per-user values via GetUser?id=<UserName>.

[2026-06-25] verify | Void Survey (Ticket 21617) — workflow rework VERIFIED COMPLETE end-to-end in Test (v11)

Void Pre Inspection QL Updates reworked and verified Complete on a rooms void survey (order 381165, Test): - Full materials bypass — removed Merge Room Materials + Change Materials Provider (the old partial fix kept Merge Room Materials, which crashed AppendItemGroupsFromSubGroups.GroupCursor with NullReference whenever the survey had rooms). Re-pointed Change Schedules Provider → Map GenPB. No more crash. - SORs pull through to the QL orderSend Inspection Result delivers them; 4 SOR lines (£703.36) confirmed on order 381165 via Single Order Entry. This is Jo Blackburn's "SORs not pulling through to QL" — fixed (it was blocked because the workflow died before Send Inspection Result ran). - Contact gated — a clientless void was erroring fatally at Raise Property Change Contact ("no client no provided") and blocking the order tail. Added an XPathConditionalTarget on QL Completion Reference: QL_Client No empty → skip to Process Order in QL; else raise the contact. Clientless void now Completes; a void with a resolvable client still raises the contact (works when QLMV_Use Property Void Client No=Yes resolves a void client, e.g. 3000384). - Process Order in QL completes — auto-retries through QL's interface-processing delay (TransitoryError self-clears; Complete at runs=4, ~11 min). - Clean diagram — gating gateway given an <Editor><Layout> so the MS designer renders it tidily (it was a mess without one).

MS-tooling extensions this session: --env dev|test|live added to ms_workflow_errors.py, ms_import.py, ms_workflow_actions.py (live-guarded on the mutating two), enabling Test diagnosis/import/retry directly. Versioning rule pinned down: MS reads a workflow's version from the .workflow filename suffix — a suffix that collides with an existing historical version 500s; next-free or current-latest is fine. New gotchas + this learning captured in sessions/gotchas-and-tips.md. Remaining: import the gated+clean package to Dev (blocked on Dev QL scheduler); §12 control doc.

[2026-06-24] feature | ms_workflow_info.py — workflow briefing (structure + runtime) so I can learn a workflow before changing it

New read-only CLI versaa-rag/ms_workflow_info.py: given a workflow name it prints the structure (trigger message → ordered steps with type + key params → each step's transitions incl. XPath branch conditions) and a runtime summary (status breakdown + recent instances, --sample errored|latest to drill in). Fills the gap left by ms_workflow_errors.py (runtime only) — the design-time side. Structure read live from api/WorkflowEditor/GetLatestWorkflowDefinition?workflowId= / GetWorkflowDefinition?workflowId=&version=&lockForEdit=false (resolved via api/RefData/GetWorkflowsWithVersions), with an automatic fallback to the extracted .workflow XML (flagged [fallback: local … may be stale]). Finding: the live definition JSON mirrors the on-disk .workflow XML 1:1 (Steps/Starts/Ends; Transition.Targets carry Expression/Value/Label), so one normaliser renders either source. Reuses the proven unwrap/get/fetch_instances/fetch_instance_info/render_detail from ms_workflow_errors.py and the export tool's session/auth. Read-only by design (no Save/SetActiveVersion/Start). Verified on Create Jobs (v3, 18 steps — shows the Demo_TypeOfJobToCreate branch to Void Survey/Repair WO/etc.) and Void Pre Inspection QL Updates (v7, trigger Void Survey.UpdateHMSData); disambiguation, --local, --version latest, and --json all confirmed. Docs: platform/management-studio-api.md §5 + a "learn the workflow first" note in workflows/how-to-edit-a-workflow.md.

[2026-06-23] verify | Void Survey (Ticket 21617 item 2) — QL contact VERIFIED end-to-end in Test; + Create Jobs v4→Test fix

Item 2 contact proven in Test (PrtyTest QL = t_qlfdat on lhp-sql02). Completing a Void Survey with "are bedrooms/property correct? = No" raised the QL contact: t_qlfdat.dbo.hpm1stcr row class_level1/2/3 = TENANCYMGT/CHANGES/CHECK, brief "Property information is incorrect", full "Property Type: 16 Sub-type: 01 Bedrooms: 12", processed_yn=1, error_flag/error_code NULL, client_no=3000384 (void client resolved by QLMV_Use Property Void Client No=Yes). The Void Pre Inspection QL Updates instance ran CompleteRaise Property Change Contact + Send Inspection Result + Process Order in QL all Complete; the materials-merge NullReferenceException is gone (older Test runs 08–22/06 errored on it). Active versions: form Dev v34 / Test v42, workflow Dev v6 / Test v7 (same content; MS auto-increments per environment). ⚠ hpm1stcr has no prty_id column (it's on hgmcntct) — drop it from staging queries. Control doc + wiki/forms/void-survey.md updated.

Create Jobs (shared form, unrelated to 21617) — Test was stuck on old v2. In Test, clicking an order opened the new Order Information detail screen but never selected it, so GenBP_Key_OrderNumber (= GetSelectedItemValue("OrderSearchResults","order_no")) stayed empty and the "Add to task list" button never appeared. Root cause: active-version mismatch — Dev = working v4, Test = old v2 (not a regression; nothing was imported). Fixed by packaging Dev's working v4 content → import to Test → Test v3 active → button works. Lesson: a form can behave differently per environment purely via the active version — check Dev-vs-Test active versions before assuming a code change.

[2026-07-20] verify | Asbestos Site Audit VERIFIED in Test (Ticket 7930) — all Kelly's points closed bar CPL017

Form Test v31 (= Dev v77) + reordered asbestos_site_audit_xslt imported to Test; end-to-end verified on property D6621, contact 1560551: Issues Identified: Q8: … | Any other comments from the tenant: … | Additional Comments: …ordering swap confirmed, label confirmed, no duplication inside Issues Identified, COMPLIANCE > POSTINSPEC > ASBESTOS classification. Contact action confirmed open via QLHGM_ContactActions: CompletedYN=False, CompletedDate/OutcomeCode/Outcome all empty — proving the chain Action Outcome now optional → left blank → action created open, which was previously only reasoned. CPL017 still AllocatedUser = Jonathan Coggin → confirms that item is genuinely QL action-type config, not form/workflow. Two false alarms worth recording: (1) the persisting validation error after the v76 fix was Test still on form v30 — always version-audit both envs before believing a fix failed; (2) an apparent "1 audit → 3 contacts" duplicate-worktray bug was an artefact of querying WHERE contact_no > N without a prty_id filter on a shared env — the two blanks were BALDEM's EDI form completions. One audit creates exactly one contact; Kelly's old "2 worktrays" issue does not reproduce. Versioning quirk: MS auto-incremented Test 30→31 but accepted the manifest number in Dev (→76/77), so the two envs now disagree numerically on identical content. QL tables used: hgmcntct (contact + full_desc) and QLHGM_ContactActions (action status/allocation) on lhp-sql02 / t_qlfdat.

[2026-06-19] fix | Asbestos Site Audit v77 — desktop column layout restored (Ticket 7930, Kelly's "used to be side by side")

Kelly's layout complaint ("questions 1, 3 and 5 are to the right and 2, 4, 6, 8 are below, they used to be side by side") was missed from the v76 round and is now fixed. How the layout actually works: a SectionBreakElement carries the width of the section it opens. The Audit block opens three consecutive 50%-wide sections on a large device — Front Photo, then Q1/Q3/Q5/Q7, then Q2/Q4/Q6/Q8. The odd/even question order is therefore deliberate: two 50% sections side by side make each row read Q1|Q2, Q3|Q4, Q5|Q6, Q7|Q8. Root cause: three 50% sections can't fit two columns — the photo took column 1, pushing the odd questions to column 2 and wrapping the even ones below. Fix (one value): Front Photo section break largeDeviceControlWidth 50100 (Asbestos+Site+Audit_77.form, package asbestos_site_audit_v77_190626.ftpackage). Only broke on desktop because that section was already regularDeviceControlWidth=100 — phones were always right, and Kelly tested the Windows desktop app. Key trap recorded: the XML question order Q1 Q3 Q5 Q7 Q2 Q4 Q6 Q8 is identical in every version back to v10 — not a regression, and must not be "corrected" to 1–8 or the pairing breaks. Also established: MS honoured the manifest version this time (Dev jumped 30 → 76, not 31), and a version audit found Dev=76 / Test=30 — Kelly's failing re-test ran against Test (still the old v30 form), which is why the validation error appeared to persist after the v76 fix.

[2026-06-19] fix | Asbestos Site Audit v76 — submit-blocking validation fixed + QL contact relabel/reorder (Ticket 7930, Kelly 11/06/2026)

Kelly re-tested Site Audit v75 in Test (property D54451, 08–11/06/2026) and couldn't submit: VALIDATION ERRORS: QLcrm_DefaultAction_Action Outcome: A value is required. Root cause: that Data List field (Aareon.QL-x.TenancyContactOutcomes.list) had isMandatory=True despite sitting under the red "Optional, rarely used" label — a mandatory field hidden in a cosmetically-"optional" block. Since QLcrm_CreateDefaultAction defaults Yes (action always created), forcing an Outcome is also why the contact action came through "already completed" (she set it to completed to proceed). Fix (form v76): isMandatory False → default action now created open in the Compliance worktray; both symptoms resolved by one change. Q2/4/6/8 "A value is required" in her screenshots are legitimately mandatory questions (not a bug). Also per Kelly: relabel the Tenant Comments calc field " Any Other Comments: "" Any other comments from the tenant: "; and reorder the asbestos_site_audit_xslt FullDescription so Any Other Comments (tenant) precedes Additional Comments (matching the on-form flow — "Any other comments" sits before the Additional Information grid). Built asbestos_site_audit_v76_190626.ftpackage + asbestos_site_audit_xslt_var_190626.ftpackage (build script versaa-rag/build_asbestos_site_audit_v76.py; var manifest round-trips to correct order). CPL017 → COMPLIANCE (allocated user COGGIJ; carried over from 2025 item 7) is not in the form/workflow — separate QL admin SQL (UPDATE [QL action-type table] SET alloc_id='COMPLIANCE' WHERE actn_code='CPL017', table TBC). Pending: import to Dev → app test → export to Test for Kelly. Control doc + wiki/asbestos/overview.md updated.

[2026-06-19] ingest | _Data Items library form — copy-from pattern catalogue + the _Use Property Void Client No client-resolution switches

Peter pointed out (re ticket 21617) that the voids "no client no" fix already existed as a standard data item, and that the _Data Items form holds a lot of reusable solutions worth copying going forward. Investigated _Data+Items_3.form: it is a non-operational library form (leading underscore sorts it to top) holding pre-configured, known-working data-item blocks by namespace prefix — QLcrm_* (CRM contact), QLRR1_*/QLRRC1_* (repair request + paired contact), QLPrty_*/QLTen_*/QLCl_*/QL_* (property/tenant/client/keys), GenBPInsight+z_* (Insights + debug), FRA_*, Form_*. The provider-level twins are QL Request Generic AdHoc Contact 1/2/3 (QLAH1/2/3) and …Repair Request 1/2. Key reusable mechanic captured: the contact/repair blocks expose option-list switches — _Use Property Void Client No, _Use Property Tenant Client No, _Use Property Tenancy Seq No, _Use Tenancy Seq No Property, _Use Tenancy Seq No Tenant Client No — that make QL resolve the client/tenancy from the property (needs propertyId), so a contact can be raised without an explicit client_no. This is the generalised form of the void survey fix (a single form data item, no workflow change). New page wiki/forms/data-items-library.md; indexed; back-linked from void-survey.md. Memory updated (versaa-data-items-library, versaa-copy-what-works, and versaa-ql-contacts-need-client flipped to RESOLVED).

[2026-06-19] fix | Void Pre Inspection QL Updates wrongly on v5 (broken) — reverted to v4; v6 (=v4) package to supersede

The active workflow had drifted to v5 (my reverted blank-clientNo/tenancySequenceNo/propertyId experiment) — re-activated in MS after the 18/06 revert. v5 breaks the void-client contact fix: the contact step needs propertyId (the Void Survey form has no QLMV_Property ID, so propertyId comes only from the workflow step param, which v5 blanks → QL can't resolve the void client). Working combo = form v34 + workflow v4 (v4 passes propertyId). Reverted active → v4 via POST api/Workflows/SetActiveVersion?workflowId=960d8a06-…&version=4. Built void_pre_insp_ql_updates_v6_190626.ftpackage (v6 = byte-identical v4 content) for upload+activate so the latest version is correct and supersedes broken v5 (on-disk Void+Pre+Inspection+QL+Updates_6.workflow). Control doc updated (changelog row + workflow cell warning "v5 DO NOT activate"). Lesson: the fix is form-only; the workflow must stay on v4 content — never activate v5.

[2026-06-18] ingest | Workflow instance actions tool (ms_workflow_actions.py) + Void Survey fix confirmed via real device

Real-device acceptance: Imran submitted a Void Survey (form v34) from Aareon Mobile (order 375584, property 10804). Instance 3b228c66-… ran to Complete: QLMV_Use Property Void Client No=Yes was emitted by the form, Form_PropertyDetailsCorrect=NoQLMV_Requested=Yes, Raise Property Change Contact Succeeded, and Process Order in QL (which briefly went PendingRetry/TransitoryError on an order-interface backlog in hpm1stud) self-cleared on retry within ~2 min. So the fix is validated end-to-end in the wild and the "stuck order" was transient, not stuck. Built versaa-rag/ms_workflow_actions.py (write companion to the read-only viewer): retry (GET RetryCurrentStep), abort (POST AbortWorkflowInstance), cancel (POST CancelWorkflowInstance) — all ?workflowInstanceId=; previews without --yes, fires with it. Verified: aborted two leftover errored test instances (51a4e432 IS Test Survey, ee8e5c38 Void v5) → both Aborted (abort is async: True returned, status transitions a moment later). Note SetActiveVersion (version activate/rollback) is separate from instance actions. Docs: wiki/platform/management-studio-api.md mutation section updated (no longer "not wired").

[2026-06-18] fix | Void Survey QL contact for voids — RESOLVED via QLMV_Use Property Void Client No=Yes (form v34, verified in QL)

Pete's answer: the contact provider has a Use Property Void Client No option (per the standard <prefix>_Use Property Void Client No data item, e.g. Asbestos QLcrm_Use Property Void Client No, Option List default Yes). For Void Survey the contact prefix is QLMV_, and that item was missing. Added QLMV_Use Property Void Client No = Yes to the form (cloned QLMV_UpdateQL), built void_survey_v34_180626.ftpackage, imported + activated via ms_import.pyDev form v34 active. No workflow change — workflow v4 already passes propertyId (the key the void-client lookup needs); blanking it in the abandoned v5 was the wrong direction. Validated first by injecting the flag into a replayed submission (ms_submit_form.py): contact step went Complete/Succeeded, and the live QL contact landed — a_qlfdat.dbo.hgmcntct.contact_no=1701884, prty_id=10804, client_no=3000384 (the property's void client, not 0), TENANCYMGT/CHANGES/CHECK, brief "Property information is incorrect", taken_by=SALEEI (correct — routes to the operative who filled the form). Import gotcha hit + fixed: first v34 import failed SQL 8152 because the manifest comment was ~155 chars (>~100 limit — the recurring Asbestos/Void gotcha); rebuilt with a 78-char comment (form XML unchanged) → clean. Corrected wiki/sql/cheat-sheet.md (void contact uses a real void client_no, not 0; hpm1stcr has no prty_id column). Pending: real device submission of form v34. Docs: wiki/forms/void-survey.md Known-Issues → RESOLVED + v34 row.

[2026-06-18] fix-attempt | Void Survey QL contact for voids — param-blanking fix tried, FAILED, reverted (needs Pete/QL)

Re-examined Jo's Teams transcript (Teams_Sessions/Call with Jo Blackburn re ticket 21617 - 3.vtt): she wants the QL contact created on "property details correct? = No" ("it'll go to the housing officer", "just going to come in as a contact", not a data-field change) — so the earlier "suppress for voids" recommendation was wrong and is corrected. Tried the investigated fix: a Void Survey is always a void (no client), and Family Survey task complete RL calls the same ImportContactCreationRequest step with clientNo/tenancySequenceNo/propertyId all blank and works — so built workflow v5 blanking those 3 params on Raise Property Change Contact, imported+activated via ms_import.py (first proven workflow import via our tooling, not just forms), and replayed ec16c051 via ms_submit_form.py. Still errored identically ("no client no provided"). Conclusion: the requirement comes from the contact classification TENANCYMGT (tenancy-management = client-level), not the step params — not fixable in the workflow step. Reverted to v4 via POST /api/Workflows/SetActiveVersion?workflowId=…&version=4 (v5 left inactive). Real fix is QL-side (a property-level contact classification for a void) — open question for Pete. Used SetActiveVersion (new lever) + confirmed workflow import/activate/revert all work via our tools. Docs: wiki/forms/void-survey.md Known-Issues rewritten (OPEN, needs Pete).

[2026-06-18] fix | Diagnosed Void Survey "no client no provided" workflow error (Ticket 21617 item-2 feature, ungated for voids)

Using the new ms_workflow_errors.py, diagnosed the Void Pre Inspection QL Updates runtime error on Dev instance ec16c051-…: the Raise Property Change Contact step (ImportContactCreationRequestQLContactUpdateTask) fails "Unable to create contact as there is no client no provided" on voids where the operative answered "property details correct? = No". Root cause: that answer sets QLMV_Requested=Yes (the Ticket 21617 item-2 QL-contact feature, added in form v32 + workflow v2/v3, 10/06/2026); the workflow is linear (no gateways) so it always runs the contact step, whose clientNo XPath binds to QL_Client Noempty on a void (no client/tenancy). Intermittent because it only fires on void + details-incorrect. The property-change notification is not lost: the workflow's first step spawns Void Pre Insp Prop Email. Recommended fix: gate the contact — add an Exclusive Gateway before Raise Property Change Contact (run only when QL_Client No non-empty), or stop the form requesting QLMV_Requested=Yes for voids. Documented in wiki/forms/void-survey.md (Known Issues). Related: earlier 11/06 runs errored "Object reference not set" (separate, undiagnosed void-data gap). Fix not yet implemented — pending decision on intended void behaviour (suppress contact vs property-level contact).

[2026-06-18] ingest | Front-end form testing via workflow trigger — ms_submit_form.py (StartWorkflow)

Built versaa-rag/ms_submit_form.py to test the form-data → workflow path without completing a form on the Aareon Mobile device. Discovery (3 Explore agents) found the mobile app uses WCF SOAP (DiscoveryServiceIdentityService/UserLoginITaskQueueService/SendTaskEventData at testmobile.lincolnshirehp.com:9006/PrtyDev, creds DPAPI-encrypted locally) — but also a simpler authenticated REST path on the API we already drive: POST /api/Workflows/StartWorkflow {WorkflowName, ExternalId, InitialData} (returns the new instance Id). The tool replays a captured instance's XML (--from-instance) or a hand-built Task XML (--xml+--workflow), tags a unique ExternalId, then reads the outcome back via ms_workflow_errors. --dry-run is the approval gate (prints payload, posts nothing). Verified against Dev: (1) --dry-run + an invalid workflow name → clean 500 "Workflow '…' does not exist" (endpoint+auth proven, zero side-effects); (2) user-approved real trigger of the safest workflow IS Test Survey Completion → created instance 51a4e432-f76a-f111-8eb8-6045bdd12ed7, read back live as PendingRetry with the DW-step error (minimal crafted data missing a field) — submit→run→read loop proven. Chose StartWorkflow over the faithful SOAP route (deferred, documented) per user. Caveat: StartWorkflow starts a NAMED workflow directly — bypasses the device's event-routing + Task.Task persistence. Also fixed a tool-B bug: GetAllWorkflowInstances requires workflowInstanceStatuses (--status any now sends the full enum, not nothing). Extended wiki/platform/management-studio-api.md §4. Aareon Mobile = MSIX 1stTouchLtd.AareonMobile v7.0.2301.0 (UWP/.NET-Native+WebView2, AUMID …njahj7x7srnjw!App); GUI automation rejected as fragile.

[2026-06-18] ingest | Management Studio REST API mapped — package import + workflow-instance viewer tools

Reverse-engineered the Management Studio SPA's import and workflow-instance APIs from main.js (pulled via the export tool's authenticated session; saved under versaa-rag/tools/_ms_js/) and built two tools. versaa-rag/ms_import.py — imports an .ftpackage via the 3-call flow: tus 1.0.0 resumable upload to <BASE>/files (read Location header → fileId = last segment; chunks sent as POST + X-HTTP-Method-Override: PATCH), then POST api/RefData/UploadImportPackage/?id= (inspect → manifest), then POST api/RefData/ImportCurrentPackage/?id=&activate=True|False → array of per-item {Successful, ErrorMessage, ItemName, ItemType}. Defaults to activate=False (staged/inactive); --inspect-only/--clear/--activate. versaa-rag/ms_workflow_errors.py (read-only) — list (GetAllWorkflowInstances{Items, TotalCount}) + show <id> (GetWorkflowInstanceInfo?...&includeTasks=trueWorkflowInstanceInfo + WorkflowHistory steps with LastError + InitialData Task XML); surfaces runtime workflow errors (the class invisible to import). Verified against Dev: import upload→inspect→import succeeded on the Unvented Hot Water Storage form package; the viewer reproduced the exact ec16c051-… "Void Pre Inspection QL Updates v4 — no client no provided" errored instance + full stack. Key gotchas: forms auto-version on import (re-import OK) but re-importing an existing workflow version returns a generic HTTP 500; a wrong endpoint path returns SPA index HTML at HTTP 200; errored instances have no CompletedAt (sort on StartedAt). Mutation actions (Retry/Abort/Cancel/Update-to-Active) found but deliberately not wired (read-only first). New page wiki/platform/management-studio-api.md; cross-linked from index + workflows/fix-errors.md. (Aareon Mobile located as MSIX app 1stTouchLtd.AareonMobile v7.0.2301.0 — front-end testing scoped as a later separate plan.)

[2026-06-18] fix | Nightly Dev pull hardened against transient gateway 504s (backoff + deferred retry pass)

The nightly Dev pull intermittently dropped categories to HTTP 504 from Microsoft-Azure-Application-Gateway on DownloadExportPackage (e.g. 11/06 lost reference_data + workflows + document_templates). Diagnosis: the 504 is the gateway timing out the server-side package assembly, not our client (our GET already waits 600s; the gateway kills it first). It's transient/load-dependent — the failing categories vary night-to-night and succeed on other runs — so a longer per-request timeout is useless; the cure is waiting longer between attempts so a load spike can clear. The old retry (3 attempts, flat sleep(5)) burned all 3 inside ~30s. Fix (in versaa-rag/export_from_management_studio.py, tuning constants at top): exponential backoff (RETRY_BASE_DELAY=30s ×2^(n-1), cap 180s, 4 attempts); a INTER_CATEGORY_PAUSE=5s between categories so the backend isn't hit back-to-back; and a deferred second pass that re-tries only the failed categories after a DEFERRED_PASS_COOLDOWN=60s cooldown — when the server has finished assembling every other package and load is lowest. Re-prepare-before-each-download retained (a 504 discards the prepared package, so a bare download retry 404s). Exit-code contract unchanged (0/2/1); run_dev_pull.py still processes+commits on partial (2). Waits only fire on failure → clean runs unchanged in duration. Verified: --dry-run + py_compile clean. See wiki/sessions/gotchas-and-tips.md.

[2026-06-11] fix | Void Survey report email attachment missing .pdf extension (folded into v32)

The void-survey report email attachment ("Void Survey Report for Order No … on 20260611") had no .pdf extension, so Windows wouldn't auto-open it (had to "open with Adobe"). Root cause: the report email is sent by Generic Generate DocumentSend Email with Attachment (SendSecureEmailWithAttachedDocument), attatchmentName ← form item Form_DocumentEmailAttachmentName. That item's value formula defines Calc1 = Form_DocumentName + '.pdf' but its final Summary output the raw Form_DocumentName (no extension) — the .pdf calc was defined but never used. Pre-existing since ~v10. Fix: repoint the Summary to Calc1 (<Value type="FormItem">Form_DocumentName</Value><Value type="Calculation">Calc1</Value>). Added as step 10 in transform_void_survey_v32.py and folded into the rebuilt void_survey_v32_100626.ftpackage (validated; manifest comment 76 chars). Also captured the broader doc-template work this session: Void Survey Word-Connector template rebuilt + beautified (tools/void_survey/build_void_survey_template.py + beautify_void_survey_template.py) — dead fields removed, meter section added before Property Details, label renamed, room table fixed, section banners + label shading. WC rejects re-serialised templates as "foreign"; workaround: copy-all → blank doc → load template from Aareon server → delete all → paste → save (fields become native). See wiki/forms/word-connector-templates.md.

[2026-06-10] feature | Void Survey v32 + workflow v2 — ticket 21617 item 2 (QL classification contact)

Built the QL-contact-on-"No" change (item 2). Mechanism verified from artefacts: ImportContactCreationRequest matches contact items by suffix (prefix-agnostic — Routine Tenancy Visit uses QLAH1_/QLArrear_/… in one form), so Void Survey's dormant QLMV_ stub works as-is (root-level, processRoot=true). Void Survey does not run Generic Task Completion (which does far more — data-warehouse/docs/email/SharePoint); the clean precedent is custom "QL Updates" workflows that raise contacts directly (e.g. Gas Servicing QL Updates). Two parts: (a) form Void+Survey_31→_32 (transform_void_survey_v32.py) wires QLMV_* — Class Level 1/2/3 = TENANCYMGT/CHANGES/CHECK, Classification S, Source A, Brief="Property information is incorrect", Full=corrected type/sub-type/bedrooms (153-char expr, <178), gate QLMV_Requested="Yes"Form_PropertyDetailsCorrect=="No", CreateDefaultAction=No; (b) workflow Void Pre Inspection QL Updates_1→_2 (transform_void_qlupdates_v2.py) inserts an ImportContactCreationRequest step (modelled on Gas Servicing) between "QL Completion Reference" and "Process Order in QL", self-gated by QLMV_Requested. Packaged: void_survey_v32_100626.ftpackage + void_pre_insp_ql_updates_v2_100626.ftpackage (both validated). Pending: import both to Dev, complete a Void Survey with "No", verify the contact lands in QL (classification TENANCYMGT/CHANGES/CHECK). v1 open items: classification code S/source A and the housing-officer routing (QL default-action allocation) to confirm by test; for a void (no tenant) the contact is property-level (clientNo/tenancySeq may be empty).

[2026-06-09] feature | Void Survey v31 built — ticket 21617 Jo Blackburn feedback (form-only changes)

Jo's 09/06 call (Teams_Sessions/Call with Jo Blackburn re ticket 21617 - 3.vtt) + feedback doc (scratch/halo_tickets/ticket_21617_attachments/Feedback Void Survey with amendments.docx, 9 screenshots) = the final form changes before the Word-template review and ticket close. Built v31 via versaa-rag/transform_void_survey_v31.py (SRC Void+Survey_30.form, now on disk via the 09/06 nightly export — clean baseline that keeps the v28–v30 MS button fixes) → build_void_survey_v31.pyvoid_survey_v31_090626.ftpackage (zip-validated, form + manifest + rels parse). Changes: (1a) hide Travelling "Appointment start" label; (1b) Travelling button On routeI'm on my way; (4) drop "central" from the heating heading, 2 questions, and the main-screen branch button caption (branch name + targetBranchName kept); (7) remove the inlined VFP_LockChange + VFP_KeySafe questions (UI + defs, SectionBreak kept); (5/6) add labelled optional photo groups to the Front Door page (Misc_FrontDoorPhotos) and Miscellaneous main page (Misc_GeneralPhotos), reusing the shared :Photo group. Gotcha: the existing Misc_Photos group sits on a separate sub-page (post-PageBreak, blank label) — present even in v19 which Jo tested — which is why she didn't see a photo option; Misc_GeneralPhotos is a new labelled group on the main page. Pending: Dev import (MS, manual) → device test → Test for Jo sign-off. Item 2 (QL classification contact, TENANCYMGT>CHANGES>CHECK) ships separately as v32; item 3 (location codes) parked with Pete. Details in wiki/forms/void-survey.md.

Import fix (10/06): first v31 import failed with SQL 8152: String or binary data would be truncated — the manifest comment was 180 chars (form-version comment column is ~100; a recurrence of the Asbestos Data v39/v40 gotcha). Confirmed the form XML was clean (its long formula expressions + option-list items are byte-identical to v27, which imported fine; no new long values). Rebuilt build_void_survey_v31.py with an 85-char comment (form unchanged) and added a len(comment) < 100 assert. Import the freshly-built all_versaa_forms/void_survey_v31_090626.ftpackagenot the stale archive/ copy the nightly job kept.

[2026-06-08] feature | Storage management for the nightly Dev pull (archive retention + 442 MB legacy cleanup)

The nightly pull archived the previous run's packages into all_versaa_forms/archive/ with no cap (~27 MB/night, unbounded). Added export_from_management_studio.prune_archive(keep=3) (runs each export) — keeps only the newest 3 of the job's own packages per category, matching only the strict <category>_YYYYMMDD_HHMMSS.ftpackage pattern so hand-built/legacy packages are never deleted (--archive-keep N to tune). run_dev_pull.py now prunes dev_pull_*.log to the newest 60. Built versaa-rag/cleanup_legacy_packages.py (dry-run default, --confirm, writes a manifest to logs/) and ran it once: removed 125 legacy packages / 442 MB across all all_* folders (incl. 237 MB old reference-data, 87 MB document-templates, 130 MB hand-built forms archive). Git-ignored + untracked the now-vestigial all_* package folders (the per-category export routes everything into all_versaa_forms/). Verified: prune unit-tested (keeps 3 newest, leaves legacy + sub-keep categories untouched); 0 legacy remaining; footprint ~490 MB → ~52 MB. Caveat: previously-committed binaries remain in git history (reclaiming remote size needs a history rewrite — out of scope). See wiki/sessions/gotchas-and-tips.md + CLAUDE.md §15.

[2026-06-08] fix+feature | Scheduled evening Dev pull built + MS export fixed to cover all 9 artefact types

Discovery: the automated MS export (versaa-rag/export_from_management_studio.py) was silently Forms-only. 8 of its 10 GetAll* endpoint names were wrong; an unmatched Management Studio API route returns the SPA index HTML with HTTP 200 (not 404), and the script counted any non-list response as "0 items" — so workflows, reference data, variables, providers, diary, templates, mappings, user-properties were all exported empty. Every prior "full export" was Forms-only.

Fix (reverse-engineered from the app's main.js): corrected all 10 endpoints (incl. GetDiaryConfigurations/All, GetReportConfigurations/All, and UserProperties via /api/Users/GetConfigureProperties); send {Info, Version} for versioned types (the raw {Info,Versions,ChosenVersion} shape makes DownloadExportPackage 500); export one package per category because a combined ~60 MB package 504s during server assembly (all 9 land in all_versaa_forms/ so watch_and_process.py extracts them in one pass — extract_all_categories.py not needed); filter Reference Data to business List/DataTree (~1,073 of 1,794; --all-refdata for full); validate the download is a real ZIP before saving (server can return HTML on error); archive existing packages before each export.

Hardening: scoped watch_and_process._git_commit to generated paths only (was git add -A — dangerous for an unattended push); moved MS credentials from the git-tracked config to .env (MS_USERNAME/MS_PASSWORD); stopped git-tracking ms_export_config.json and the timestamped .ftpackage binaries (repo-bloat).

Automation: scheduled_dev_pull.ps1 (runs run_dev_pull.py --active-only → writes logs/last_run_status.json → desktop toast) + install_scheduled_task.ps1 (task "Versaa Dev Pull (nightly)", daily 20:30, wake/run-if-missed/on-battery, interactive). Evening over overnight (laptop asleep-but-charged at 20:30 vs flat by morning). VERIFIED end-to-end: scheduled task ran in ~135 s and committed+pushed the first true all-types refresh. Details in wiki/sessions/gotchas-and-tips.md (Automation / Script Gotchas) and CLAUDE.md §15.

[2026-06-08] fix+feature | Knowledge Base auto-update fixed (catalog crash) + per-form dependency cross-linking

Root cause the KB never refreshed: versaa-rag/generate_catalog.py (the dev-pull step that writes wiki/catalog/* pages) crashed with UnicodeEncodeError on the Windows cp1252 console — its progress print() uses box-drawing chars (/=), and when run piped as a subprocess by watch_and_process.py the default cp1252 stdout raised on the first separator print, killing the whole step before any page was written. The crash is non-fatal to the pipeline (it continues), so it failed silently — the catalog had been frozen since 2026-06-01 even though exports/ChromaDB updated. That's why localhost:8000 showed Unvented Hot Water Storage v4 while Dev was on v9. Fix: force UTF-8 stdout/stderr at startup of generate_catalog.py (sys.stdout.reconfigure(encoding="utf-8", errors="replace")). Re-ran the catalog: generated=206, errors=0; UvHWS page now shows v9 + dependencies. Watch out for: any pipeline Python that prints non-ASCII and is run piped on Windows — reconfigure stdout or set PYTHONIOENCODING=utf-8; a silently-crashing non-fatal step can freeze downstream output indefinitely.

New feature — dependency cross-linking (Phase 1: forms). versaa-rag/dependency_index.py builds ONE relationship graph from extracted/data/dependency_graph.json (used-as-sub-form-in, spawned-as-sub-task-by, workflows-that-reference-it). generate_catalog.py renders a clickable ## Dependencies & Related section on each form page from that graph (regenerates when an item's deps change, via a deps_hash). Ground-truth asserted by versaa-rag/_verify_dependency_graph.py (UvHWS/NIBE/Damp ← RWO Comp; Client ← Client Visit). Phases 2–3 (workflows, then variables/templates/ref-data) to follow.

Live auto-reflect. versaa-rag/server/web_server.py now re-fingerprints the wiki/doc source trees (throttled) and clears its sidebar + search caches automatically, and invalidate_doc_index() also clears the wiki search index — so the running site reflects regenerated pages with no restart (restart only to load web_server.py code changes; page content was always read fresh per request).

In-document links fixed (frontend). Clicking ANY link inside a rendered doc (the new dependency cross-links, plus existing ../ links) did a full browser navigation to e.g. /wiki/catalog/forms/X.md — which FastAPI has no route for → {"detail":"Not Found"}. The SPA only wired up sidebar clicks, never in-doc <a> clicks. Added a delegated handler on #markdownBody (versaa-rag/frontend/app.js: bindInDocLinks / resolveDocPath / openDocByPath) that intercepts internal .md links (absolute /wiki/..., relative ../..., and raw-export links), resolves them to a workspace-relative path, and loads via /api/doc instead of navigating. Requires a browser hard-refresh to pick up the new app.js (served from /static, no server restart needed).

Process. Added CLAUDE.md §15 "Knowledge Base Update Protocol" — refresh the KB after touching any artefact (run run_dev_pull.py for MS changes / watch_and_process.py --once for local packages); the catalog step rebuilds the dependency graph; overnight scheduled run_dev_pull.py noted as a later follow-up to auto-catch direct-MS edits.

[2026-06-05] build+fix | Insights rollout — importable self-service packages; recipe VERIFIED in Dev (Unvented Hot Water Storage v9 Active)

Built the first importable Insights .ftpackage and proved the recipe end-to-end in the Aareon Mobile Dev app (Imran self-tested). Unvented Hot Water Storage v9 is now Active in Dev with the insights panel populating. Findings/solutions captured across the wiki:

  • RWO sub-tasks INHERIT insights — display-only. UvHWS/NIBE/Damp & Mould run inside the Repair Works Order task, which already loaded insights (Repair+Works+Order+QL+Data_16). They need display + switch only, NOT a loader. This corrects the earlier "Category B data-push" prediction.
  • Duplication rule: the Append Property Insights handler appends (does not replace). Two load sources => duplicate rows. Adding a self-loader to an inherited sub-task duplicated the panel; autoSendOnceOnly=True and hiding the trigger button did NOT fix it (copies come from two sources). Fix = display-only for inherited forms; self-loader only where nothing else loads (e.g. Void Survey via Create+Jobs_3).
  • "Missing switch" bug fixed. The original injector added the z_Insights_Switch data item but no display control, so injected panels had no on-screen toggle and showed unconditionally. Added switch_control.frag (an OptionsListRadioButtonElement); inject.py now always inserts it.
  • Grid settings: showAdd=False (no Add button — users can't add insight rows) + showEdit=True (tap a row to view detail). Per Mark in the catch-up ("we don't need the show ad… they want to edit because it allows them to go into it and have a look inside").
  • Self-loader pattern (for non-inheriting forms): RequestPropertyInsights request (messageName=AppendPropertyInsights, items=GenBP_Key_PropertyId;, from Family Survey 2 MW) + an autoSend RequestButtonElement (Asbestos Data fire-once/self-hide pattern). Proven to auto-load on open; still to be finalised for Void Survey.
  • MS versioning: Management Studio assigns the next sequential version on import — the manifest version in the package is cosmetic (five "v5" pilot imports became Dev versions 5→9).
  • Toolkit changes (versaa-rag/insights_injector/): new fragments switch_control.frag, request_item.frag, request_button.frag; inject.py gains --with-trigger + always-adds-switch + showAdd=False; build_package.py unchanged. Built against a fresh Dev pull (run_dev_pull.py).

Updated wiki/forms/insights-route-loader-map.md (status → VERIFIED; worklist corrected), insights-piece.md (anatomy: switch control + grid settings; three population patterns + duplication rule), insights-rollout-plan.md (recipes corrected). Not yet: NIBE / Damp & Mould / Void Survey packages; control documents; Dev→Test→Live migration.

[2026-06-05] analysis | Insights rollout — route→loader map + corrected population model (verify-first)

Grilling + deep workflow read with Imran refined the rollout model and produced wiki/forms/insights-route-loader-map.md. Core correction: population is decided by the launch route's LOADER workflow, and has NEVER been confirmed in-app — "DONE" in the audit only means the grid exists, not that it fills. XML-confirmed loaders that run the insight query ($QL_Property_Insights$GenBPInsight): Property+Search_57, FRA+Property+Search_20/23, Repair+Works+Order+QL+Data_16, ClientVisitBasicLoad_13/17, Estate+Search_40, Order+Appointment_9, and the reusable handler Append+Property+Insights_15 (message-start *.AppendPropertyInsights, needs only GenBP_Key_PropertyId). Launchers with NO insight load: Create+Jobs_3, Property+Check+Appoint+LoadDat_30 (PCA itself does not push insights), Repair+Works+Order+Comp_7 / Gas+Servicing+Completion_4 (sub-task spawns — GenBPInsight not in any formItemNameList), QLWorktrayRequest*Cmp (Complaint). Two population patterns: loader-route (route-dependent; Mark's DONE forms use this — Pre-Leaving empty via RWO) vs self-trigger (Family Survey 2 MW v73 fires AppendPropertyInsights with items=GenBP_Key_PropertyId; — route-agnostic). Decisions: rollout is method-first (verify 3 experiments → one template per pattern → roll the rest); self-trigger is the default for rollout forms if experiment 3 shows no duplicate rows. Handover corrections: Void Survey is B not A (created by Create+Jobs_3, no insight load; not in Property Search); Tap Flushing is A via Estate Search not "PCA"; EAI may not be a clean skip (Estate Search loads insights). Succession/Relinquishment/Mobility Scooter launch as separate tasks from Client Visit (only Client is an inline sub-form). No MS changes; nothing deployed; predictions pending Imran's in-app tests.

[2026-06-05] plan | Insights rollout — completion plan + launch-route analysis (Category A/B/C)

Worked out how to actually finish the Insights rollout (handed to Imran). Key insight: a form's launch route decides the method. (A) Forms launched via Property Search/PCA get insights pushed automatically by Property+Search_57.workflow (gated by $Insights_OnSwitch$) — copy-paste the display group only. (B) Forms spawned as sub-tasks by a master's ExtractSubTaskCreateWorkflow (RWO: Damp & Mould, Unvented HW, NIBE; Client Visit: Succession) get no auto-push — they need the master→sub-task GenBPInsight mapping (Peter's Void First Person / Pre-Leaving pattern). (C) No-property-context forms (EAI ×5, Relinquishment) → skip, use the standalone Insights form. Confirmed the PCA loader (Property+Check+Appoint+LoadDat_30) ignore list (incl. SCS, Damp & Mould). Also: even SCS v48 (Mark's "done") is PCA-ignored so may not populate — flag to Mark. Wrote wiki/forms/insights-rollout-plan.md (worklist + Recipe A/B + sequence + coordination). Extended classify_forms.py to flag Category-B sub-task forms (is_subtask/subtask_masters via formItemNameList in ExtractSubTask steps) — Succession/Relinquishment use a z_-prefixed item name so are best-effort-missed (curated in the plan). No MS changes; nothing deployed.

[2026-06-04] fix | Insights rollout stood down — stale-snapshot + manual-rollout misread; re-pulled Dev, retired all injector packages

The whole programmatic insights_injector rollout rested on two mistaken premises, both now corrected. (1) Peter's "list" is a May usage/completion report for prioritisation — not a "forms that need insights" list. (2) The agreed rollout is Mark adding the piece MANUALLY in Management Studio Dev, form-by-form by usage — there was never a programmatic-package step (confirmed in the Insights catch-up transcript). The classifier had also run against a stale snapshot: extracted/ held Stock Condition Survey v43 (no insights) while Dev was already on v48 (full insights) — the same export-lag noted for Void Survey (pkg v27 vs Dev v30) in CLAUDE.md §10. A fresh Dev export + extract_all_categories.py refreshed the snapshot (SCS→v48, Client→v71, Void Survey→v30); re-running the audit moved SCS to DONE (27 forms now have the grid). Importing any injector package would have reverted Mark's newer Dev versions (SCS v44 over v48, Client v69 over v71, Void Survey v28 over v30). Resolution: all 15 injector packages moved to out/packages/_retired/; rollout.py is now audit-only (build gated behind --build, marked SUPERSEDED); classify_forms.py audit retained. Added a "snapshot lags Dev" gotcha to wiki/forms/insights-piece.md and rewrote the rollout section (Mark owns it; Peter's list = usage; data-load + master→subform passing required). Nothing was imported to Dev/Test/Live by this work.

[2026-06-04] fix | Insights rollout — Client v69 broke task sync (sub-form duplicate-group collision); package retired

Client_69_insights was imported to Dev and broke Aareon Mobile task sync: tasks created via Client Search would not download while v69 was active; reverting to v68 released them instantly. Root cause: Client is a sub-form loaded by Client Visit (Client Subform Load) and reached via CRM/EDI — and Client Visit v100 already carries the Insights piece. Injecting GenBPInsight + z_Insight_* into the Client sub-form created a duplicate group/items on the device's runtime merge → form fails to provision on the device (MS import still succeeds; XML well-formed, GUID preserved, ids unique). Resolution: retire Client_69_insights.ftpackage, keep Client on v68. Rule added: the injector must exclude sub-forms (detect via name="<X> Subform Load"); a clean MS import does not prove device provisioning — verify task sync after activating a version. Client was the only sub-form among the 15 rollout targets. Documented in wiki/forms/insights-piece.md ("CRITICAL gotcha — never inject the piece into a sub-form").

[2026-06-04] rollout | Insights piece — audit + programmatic rollout to regularly-used forms

Following the Insights catch-up with Mark & Peter, audited which forms have the Insights piece and built a programmatic injector to add it to the regularly-used gaps (prioritised by Peter's May completion report).

  • Audit: 25 of 206 forms already have the insights grid. Correct test = a GroupDataGridElement bound to GenBPInsight (column spelling drifts by form age, so match the binding not one column name). Client Visit and Building Safety Check ARE done (an early narrow grep missed them).
  • Anatomy & data-load gate documented in new page wiki/forms/insights-piece.md. Key gotcha: the grid only populates if the form's launch workflow fires *.AppendPropertyInsights — there is no insights-load wiring in the .form itself, so population must be verified in-app per form.
  • Toolkit: versaa-rag/insights_injector/ (classify → merge_usage → extract_reference → inject → build_package → rollout). Edits raw .form XML by string-splice (lxml round-trip is too lossy), regenerates all formula/element ids, places the grid after the header, gates the legacy "Property Alerts" section to hide when insights show. Validation tolerates pre-existing duplicate ids (Client, Complaint) and fails only on injection-introduced ones.
  • Built 15 packages (one per gap form) in out/packages/, each version-bumped: Stock Condition Survey, Client, Void Survey, Tap Flushing Check, Damp & Mould Inspection, Unvented Hot Water Storage, 5× EAI forms, Succession Inspection, NIBE, Relinquishment Inspection, Complaint. Held: Van Check (confirm property context). Skip: Gas Servicing. Done by Mark: FRA Review v68. Needs mapping (no standalone form file): Safeguarding Report, Estate Inspection, Pets Questionnaire.
  • Live status: versaa-rag/insights_injector/insights_rollout_status.{md,csv}. Packages not yet imported — pending TcyDev verification.

[2026-06-04] ingest | Full re-export of all Dev packages (040626) — 9 category packages, first full multi-category extraction

Full export from PrtyDev Management Studio (04\06\2026), delivered as 9 separate category packages (one per all_* folder) rather than a single combined package. Previous packages moved to each folder's archive/. All 9 extracted via a new helper versaa-rag/extract_all_categories.py (the standard watcher only scans all_versaa_forms/), then convert_exports.py regenerated Versaa_Exports_md/ and generate_catalog.py refreshed wiki/catalog/.

What actually changed (manifest version-diff vs 270526)

  • Forms (206↔206): only Void Survey v10 → v27. Nothing else bumped.
  • Workflows (368↔368): no version bumps.
  • Variables / Providers / User Properties / Diary / Group Mappings / Doc Templates: no per-item versioning in manifest; content reconciled (see below).

Key finding — stale multi-category extracted content reconciled

The routine pipeline (watch_and_process.py) only watches all_versaa_forms/, so only the forms package was ever auto-extracted. Workflows, reference data, providers, variables and document-template content in extracted/ had drifted stale since before the 270526 ingest. This was the first run to freshly extract all categories, so the diff captured accumulated drift: - Asbestos Get Property Data workflow markdown now generated at v5 (workflow was already v5 in Dev at 270526 — only the markdown was catching up). - 40 workflow files changed — almost all .schedule / .workflow.rels scheduler/relationship metadata (export noise); a handful of v0 .workflow re-serialisations, no logic change. - 18 extracted/reference_data files changed (QL lookup data re-serialisation); grouped reference_lists/ markdown unchanged. - asbestos site audit xslt variable markdown changed by entity-encoding only (&apos;&#39;, same character) — not a logic change.

Discrepancy flagged

This export captured Void Survey v27, but Dev is on v30 (v28–v30 were in-place Management Studio edits on 2026-06-03 with no exported package). The on-disk/package state therefore lags Dev by v28–v30; wiki/forms/void-survey.md and the control document remain ahead (documented through v30).

Packages: all_forms_04062026, all_workflows_04062026, all_workflow_variables_04062026, all_providers_04062026, user_properties_04062026, all_ref_data_040626, all_doc_templates_04062026, all_diary_config_04062026, group_table_mapping_04062026 (.ftpackage).


[2026-06-03] change | Void Survey v27 → v30 — Travel screen button alignment/width (Management Studio in-place edits)

Three in-place version bumps (v27→v28→v29→v30) done directly in Management Studio on TcyDev to fix Travel screen button alignment and width. No transform script and no on-disk .form/package — these edits exist only in Dev, which is now the source of truth for the Void Survey. Dev state: v30 (TcyDev). Test still v19, Live not deployed.

v31 abandoned: Clean-rebuild scripts versaa-rag/transform_void_survey_v22.py (SRC v21 → DST Void+Survey_31.form) and versaa-rag/build_void_survey_v22.py (→ void_survey_v31_030626.ftpackage) were prepared to fold all v22–v27 changes into one pass from v21, but were never run (no v31 artefacts exist). They omit the v28–v30 MS fixes, so rebuilding would regress Dev. The v31 path is dead; deploy the v30 MS state directly. Treat those scripts as reference only.


[2026-06-02] change | Void Survey v21 → v27 — pricebook filter, VFP questions + photo grids inline, CRR sub-form

All 4 SchedulePicker elements locked to "Void Rates (2)" pricebook. 7 VFP questions inlined into Miscellaneous section after Misc_PropertyOkToLet: SectionBreak, lock change, key safe, pre-work label, and 5 Y/N pre-work buttons (Clearance, Fumigation, Cleaning, Needle Sweep, Garden Clearance) each followed by a photo grid visible when answered Yes. 5 photo Group defs injected. "Is CRR Required?" Y/N + ComponentReplacementRequest SubTask added before Form_IsInspectionComplete. Package: all_versaa_forms/void_survey_v27_020626.ftpackage


[2026-06-02] change | Void Survey v20 → v21 — meter restructure, back door inline, materials assessment removed

Gas/Electric meter routing restructure: removed Gas Main, Gas Prepay, Electric Main, Electric Prepay blocks; Meter_GasTypeCredit and Meter_Electric1TypeCredit items updated to Pre-Payment/Quarterly/None; upstream meter formula (6108586f) conditions simplified. Back door type (Misc_BackDoorType) inlined onto Front Door screen with conditional visibility; Back Door block removed. Materials Assessment section removed from room blocks. Currency symbol (£) added to Debt/Credit Amount captions. Package: all_versaa_forms/void_survey_v21_020626.ftpackage


[2026-06-02] build | Void Survey v19 → v20 — Jo second meeting changes (Ticket 21617)

14 changes applied: Travelling block button alignment Left; remove room HeatingType/HeatingTypeOther/DecorativeOrder/Cleanliness (UI + FormItem defs); rename front door "Replacement Comments" → "Comments"; remove Misc BackDoor "Replace During VOID?" UI element; remove all Materials (External + Misc StockPickers, labels, PageBreaks, FormItem defs, Materials Group template); add External_ConditionPhotos group to External Conditions screen. Package: all_versaa_forms/void_survey_v20_020626.ftpackage


[2026-05-29] deploy | Void Survey v19 deployed to Dev and Test

v19 imported to TcyDev and PrtyTest. Control doc updated with Test export date. Meeting with Jo Blackburn 01/06/2026 to confirm on-hold specs (screens 25, 27, 29).


[2026-05-29] fix | Void Survey v18 → v19 — Restore room sub-block fields (Heating Type, Decorative Order, Cleanliness, Comments) removed in error in v11

Root cause: v11 changes 13–17 removed 5 fields from the shared generic room sub-block. Jo Blackburn never asked for this. Because the sub-block is shared, all room types (Kitchen, Bedroom, Living Room, etc.) lost those fields. Fix: Transform script reads field XML from v10 (untouched baseline) and inserts it back into v18 at the correct position within the shared block. Package: all_versaa_forms/void_survey_v19_290526.ftpackage


[2026-05-28] form | Void Survey v17 → v18 — Ticket 21617 Electric Credit screen additions

Changes: Electric Credit sub-screen: added Electric Reading decimal field (Meter_ElectricCreditReading), info label "The reading is the kwph read (kilo watt per hour)", Debt Amount (Meter_ElectricDebt), Credit Amount (Meter_ElectricCreditAmount), optional Photo (Meter_ElectricPhoto).

Artefacts: extracted/forms/Forms/Void+Survey_18.form, all_versaa_forms/void_survey_v18_280526.ftpackage Scripts: versaa-rag/transform_void_survey_v18.py, versaa-rag/build_void_survey_v18.py Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (v18 row added)


[2026-05-28] form | Void Survey v16 → v17 — Ticket 21617 Gas Credit screen overhaul

Changes: Gas Credit sub-screen redesigned: heading renamed "Gas Reading"; Meter Type dropdown (Standard/Smart) added; reading caption renamed; info label "The reading is the m3 (meter cubed)" added; Debt Amount and Credit Amount decimal fields added; optional Photo (ImageFormElement, Meter_GasPhoto) added. New FormItem definitions: Meter_GasTypeCredit, Meter_GasDebt, Meter_GasCreditAmount.

Artefacts: extracted/forms/Forms/Void+Survey_17.form, all_versaa_forms/void_survey_v17_280526.ftpackage Scripts: versaa-rag/transform_void_survey_v17.py, versaa-rag/build_void_survey_v17.py Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (v17 row added)


[2026-05-28] form | Void Survey v15 → v16 — Ticket 21617 Electric Supplier removed from meter sub-screens

Changes: Meter_ElectricSupplierSel (OptionComboBoxElement) and Meter_ElectricSupplierOther (TextBoxElement) removed from both Electric Prepay and Electric Credit sub-blocks. Gas Supplier was already removed in v11.

Artefacts: extracted/forms/Forms/Void+Survey_16.form, all_versaa_forms/void_survey_v16_280526.ftpackage Scripts: versaa-rag/transform_void_survey_v16.py, versaa-rag/build_void_survey_v16.py Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (v16 row added)


[2026-05-28] form | Void Survey v14 → v15 — Ticket 21617 None on meter type routes to End

Changes: Gas Meter and Electric Meter branch Main block NextBlockName formulas updated: selecting None now routes to [End] (skips all meter questions) instead of falling through to the Credit sub-block.

Artefacts: extracted/forms/Forms/Void+Survey_15.form, all_versaa_forms/void_survey_v15_280526.ftpackage Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (v15 row added)


[2026-05-28] form | Void Survey v12 → v14 — Ticket 21617 meter type push-buttons inside sub-screens

Note: v13 was an intermediate local form created in edit mode during testing; v14 is the imported/activated version.

Changes: Gas Meter and Electric Meter branch Main blocks (previously empty routing blocks) now display a "Meter Type" push-button selector (Pre-Payment / Quarterly / grey None). Tapping a button routes to the existing Gas Prepay/Gas Credit and Electric Prepay/Electric Credit sub-blocks as before. No new FormItems required — reuses Meter_GasMeterPrePayment and Meter_ElectricMeterPrePayment.

Artefacts: extracted/forms/Forms/Void+Survey_13.form, all_versaa_forms/void_survey_v13_280526.ftpackage Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (v14 row added)


[2026-05-28] form | Void Survey v11 → v12 — Ticket 21617 Meter Types screen changes (Jo Blackburn call)

Changes: Meter Types navigation screen simplified to 2 buttons only. Removed "Type of Electric Meter?" and "Type of Gas Meter?" pre-selection radio buttons. Removed Water Meter navigation button. Made Electric Meter and Gas Meter buttons always visible (removed conditional visibility formulas). Confirmed with Jo Blackburn in call 28/05/2026.

Artefacts: extracted/forms/Forms/Void+Survey_12.form, all_versaa_forms/void_survey_v12_280526.ftpackage Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (v12 row added)


[2026-05-27] form | Void Survey v10 → v11 — Ticket 21617 field removals

Changes: 29 items removed/updated per Jo Blackburn review (12/05/2026). Key changes: alarmed property field, electric Rate 1 + duplicate serial no., all gas supplier/connected/service fields from both gas blocks, room heating type/cleanliness/decorative order/comments, Bathroom block, Kitchen block, Materials block, warden present/alarm, garage condition/decorative order, Front Door Replace field. Formula 20 updated: Kitchen/Bathroom routing removed, Schedules → Photos navigation fixed.

Artefacts: extracted/forms/Forms/Void+Survey_11.form, all_versaa_forms/void_survey_v11_270526.ftpackage Control doc: word_templates/Form_Creation_Control_Document_Void_Survey.docx (new, v11)


[2026-05-27] fix | Ticket 14128 — Rent Statement Request (RT0016) not firing for self-serve tenants

Root cause: QL HGM CRM Classification row RENTS / BALANCE / STATEMENT / SELFSERVE existed in all three QL environments but had no action code assigned. Contacts from the MyLHP portal were being created correctly in HGMCNTCT (confirmed via SQL on QLFDAT) but RT0016 never fired. No error was thrown — completely silent.

Fix (QL admin change — Peter Steele, 27/05/2026): Added action code RT0016 to the existing SELFSERVE row in QL CRM Classifications in Acceptance, Test, and Live.

Key findings from call with Peter (27/05/2026): - RENTS / BALANCE / STATEMENT / OFFICE row (for QL arrears automation) was already linked to RT0016 and untouched - RENTS / BALANCE / STATEMENT / SELFSERVE row existed but had no action code — this was the gap - Live already had RT0016 against SELFSERVE; Test/Acceptance were missing it - RT0016 = "Rent statement request" workflow task → routed to Income Officer for that property - Self-serve contacts: contact_source = W, class_level4 = SELFSERVE; arrears contacts: contact_source = L, class_level4 = OFFICE

New wiki knowledge captured: - QL environment naming: no "Dev" in QL; Versaa Dev → QL Acceptance (a_qlfdat), Test → t_qlfdat, Live → qlfdat - QL CRM Classification silent failure gotcha (row with no action code) - QL field name lookup: Ctrl+Shift+F1 - QL asterisk label convention - QL responsible persons on properties (housing officer, income officer, etc.) - Citrix access path for QL non-live environments

Wiki pages updated: wiki/sessions/gotchas-and-tips.md (new QL Integration Gotchas section), wiki/platform/versaa-overview.md (QL environment mapping + RT0016 detail)

Testing: Submit a self-serve rent statement request in SS Test → run HGMCNTCT query on t_qlfdat → confirm SELFSERVE contact appears → check income officer's worktray in QL Test for RT0016 action item.


[2026-05-27] ingest | Full re-export of all Dev packages (270526) — 206 forms, versions updated

Full export from PrtyDev Management Studio (27/05/2026). All packages replaced (270526 suffix). 206 forms extracted and committed. Key changes vs previous export (060526):

New forms (v0 — entirely new)

  • Electrical certification suite — 18 new forms: Electrical Cert AdHoc, Electrical Cert Unscheduled, Electrical Cert Search, Electrical danger notification, Electrical fire inspect report, Electrical fire install, Electrical lighting periodic, Electrical Edit DataList, and 10 more Edit/maintenance forms. See wiki/forms/electrical-forms.md.
  • New workflow: Electrical render documents v2

Major version bumps

Form From To Change
Component Replacement Request v82 v100 Document rendering expansion — ActualLifecycle, ComponentToUpdate, Component_In_Place etc. now availableForDocumentRendering=True; Form_FormName added to nameFields; Red Flags in KeyFields
Insights v11 v59 New Debugger group added — z_DebugUsers, z_Env_Dec, z_InsightsConfig, z_Insights_Switch, z_Insights_Visible for Dev environment testing
Client Visit v92 v100 Incremental fixes — structure stable at current scan depth
Repair Works Order v63 v73 Incremental refinements — appointment/completion logic
Fire Door Check v22 v40 Extensive iteration
Asbestos Data v36 v42 Bug fix series (sendAllItems=True applied at v41)
Asbestos Site Audit v70 v75 Site audit refinements
Asbestos Reinspection v66 v70 Incremental
Asbestos Survey v11 v12 Incremental
HHSRS Survey v20 v25 Incremental
Estate Search v51 v54 Incremental
Red Flag Reporting v217 v220 Incremental
FRA Review v66 Updated
Property Search v102 v103 Incremental
Building Safety Check v362 v363 Incremental
Mutual Exchange Inspection v16 v17 Incremental
Passenger Lift Audit v35 v36 Incremental

Workflow updates

Workflow From To
Asbestos Get Property Data v1 v4
Asbestos Survey Load Data v6 v8
Property Check Appoint LoadDat v30 v31
Order Appointment v9
Comp Request App Data v12
FRA Property Search v23
ClientVisitBasicLoad v17
ClientVisitCompletion v17
Repair Works Order Comp v7

Wiki pages updated

  • wiki/forms/electrical-forms.mdnew page for electrical suite
  • wiki/forms/component-replacement-request.md — v100 entry added
  • wiki/asbestos/overview.md — version numbers updated (Data v42, Survey v12, Site Audit v75, workflows)
  • wiki/index.md — electrical forms page added to catalog

[2026-05-20] fix | Asbestos Site Audit v75 — Any Other Comments deduplication + Class Level 1 always COMPLIANCE (Halo 7930 items 5 & 6)

  • Item 5: QLcrm_Full Description formula had +$formItem.Tenant_Comments — caused "Any Other Comments" to appear inside "Issues Identified:" section of QL full description. Removed from formula. Added Tenant Comments DataItem output to XSLT variable (asbestos_site_audit_xslt) after the </xsl:for-each> line, before </FullDescription>.
  • Item 6: QLcrm_Class Level 1 final fallback Summary returned NoValue — contacts with no issues identified received no QL classification. Changed to Constant = COMPLIANCE so all site audit completions generate COMPLIANCE > POSTINSPEC > ASBESTOS > DEFECT.
  • XSLT variable gotcha: Value is double-entity-encoded in the raw XML (&amp;lt; for <). Edit raw with .find("&amp;gt;") patterns.
  • Form XML gotcha: Self-closing tags in XML source appear as <Tag></Tag> not <Tag/> — string replacements must match.
  • Packages: all_versaa_forms/asbestos_site_audit_v75_200526.ftpackage, all_workflow_variables/asbestos_site_audit_xslt_var_200526.ftpackage
  • Control doc: word_templates/Form_Creation_Control_Document_Asbestos_Site_Audit.docx (created)
  • Item 7 (CPL017 action code allocated user): QL admin change required — not in form/workflow. SQL: UPDATE [QL table for action types] SET alloc_id = 'COMPLIANCE' WHERE actn_code = 'CPL017' — exact table TBC by QL admin.

[2026-05-19] fix | On-demand Dev pull runner added (terminal/chat trigger); export script Unicode output crash fixed

  • Added versaa-rag/run_dev_pull.py as the single on-demand command entrypoint for VS Code. Flow: export from Dev Management Studio -> watch_and_process.py --once.
  • Added run logging to versaa-rag/logs/dev_pull_YYYYMMDD_HHMMSS.log for each invocation.
  • Updated docs/instructions with the new preferred command:
    • & "versaa-rag\.venv\Scripts\python.exe" "versaa-rag\run_dev_pull.py"
    • --force variant for reprocessing.
  • Root cause fixed: manual run crashed in Windows terminal with UnicodeEncodeError due to Unicode arrow characters in export_from_management_studio.py print output.
  • Fix: replaced non-ASCII arrows in console output with ASCII (<-, ->).
  • Watch out: terminal code page differences can make scripts intermittently fail if console output includes non-ASCII symbols.
  • Wiki updated: wiki/sessions/gotchas-and-tips.md (new automation gotcha note).

[2026-05-19] fix | Asbestos Data form — sendAllItems=True fixes GetAsbestosPropertyData guard; manifest comment length gotcha

  • Root cause confirmed: Get Asbestos Data request FormItem had sendAllItems=False and items=GenBP_Key_PropertyId;Asbestos Data Loaded was never included in the GetAsbestosPropertyData event, so the guard in Asbestos Get Property Data v4 always saw empty.
  • Fix: sendAllItems=True → form now sends all DataItems (incl. Asbestos Data Loaded) with the event.
  • New gotcha: Form manifest comment attribute has a ~100-char SQL column limit. Both v39 and v40 failed with SQL 8152 due to long comments. v41 (short comment) imported successfully.
  • Wiki updated: wiki/asbestos/overview.md (active dev section + common issues table), wiki/sessions/gotchas-and-tips.md (new gotcha entry).

Added Section 13 to .github/copilot-instructions.md: Post-Task Wiki Update Protocol. This is a mandatory standing rule that fires automatically at the end of any task involving a bug fixed, new form/workflow touched, new SQL written, new pattern discovered, or new infrastructure finding. Defines triggers, what to do, scope, and examples. Replaces the passive ingest-only wiki maintenance model with an active, continuous-documentation model.

Also updated Section 10 (Completed Work Log) to reflect the three most recent entries (was still showing 07/05/2026).


[2026-05-18] fix | Self Serve repairs — backfilled into wiki from user memory

Created wiki/self-serve/repairs-workflow.md and wiki/self-serve/myLHP-app.md. These contain the May 2026 repair bug fixes that were previously only stored in agent user memory (not in the wiki).

Repairs workflow fixes covered: 1. RepairCardTitle not recalculated after ApptStartDate fetch — fixed with 4th UPDATE 2. "View Progress" missing next appointment label — fixed with deduplicating hpmaptmt JOIN 3. Repair History duplicate rows — fixed deduplication on both hpmaptmt and hpmordnt 4. ApptStartDate exposed as output DataItem for Aareon portal consumption

MyLHP app testing guide covers: - Person ID swap procedure, session cache clearing - No test app — use SS Test web portal - QL database name mapping (t_qlfdat, a_qlfdat, QLFDAT) - Void clients causing HTTP 500, overnight deployment job

Updated wiki/index.md — added Self Serve section. Updated wiki/sql/cheat-sheet.md — added SS testing queries and QL database name table.


[2026-05-18] ingest | Asbestos Survey ecosystem — wiki/asbestos/ created

Created wiki/asbestos/overview.md. This is a new section covering the full Asbestos Survey form ecosystem at LHP.

Sources: extracted/workflows/Workflows/Asbestos+Survey+Load+Data_4.workflow, Asbestos+Get+Property+Data_1.workflow, Asbestos+Survey+Completion_4.workflow, workflow variables, Teams Sessions 5/6.

Key knowledge captured: - Why Asbestos is explicitly excluded from the generic Property Check Appoint LoadDat workflow (XPath exclusion list at first step) - The two-workflow pattern: Asbestos Get Property Data orchestrates; Asbestos Survey Load Data does the QL fetch - Idempotency check: Asbestos Data Loaded = Yes prevents re-loading - Component filtering: appendAsbestos=true, $Asbestos_SurveyCycle$ = 5 - Status normalisation: ExistingActive, others → Historical; ordered by QLPrtyCm_location - Tag FRA reused for Asbestos Survey (intentional) - The Asbestos Data form as a test/debug tool (Peter: "bypasses the property screen") - Active development (18/05/2026): get_property_data_v2/v3/v4, survey_load_data_v5/v6 - Critical bug + fix: SQL 8152 string truncation on items column → replaced with sendAllItems=True on GetAsbestosPropertyData request

Updated wiki/index.md — added Asbestos section.


Ingested knowledge from Mark Wright covering: - Forms as a presentation layer — XML backing, group item auto-population, workflow stripping - Form access via Active Directory — 1TPG_PRTY[ENV]_[FORM_NAME] group pattern, best practice (assign user to role), AD Administrative Center procedure - Text field behaviour — addition as concatenation, SUM incompatible with strings, no reliable group concatenation - Numeric field (INT) behaviour — aggregation/subtraction, JS dual treatment, precision loss risk - Workflow variables — what they are, $VariableName$ syntax, when to use them, migration warning - Workflow BPMN structure — multiple MessageStart triggers, no notes/comments in designer

Pages updated: - wiki/forms/how-to-build-a-form.md — added "Forms as a Presentation Layer" and "Form Access via Active Directory" sections - wiki/forms/calculated-fields.md — added "Field Types: Text vs Numeric" section - wiki/workflows/how-to-create-a-workflow.md — expanded Key Concepts, renamed Triggers section, added Multiple Message Event Triggers and Workflow Variables sections - wiki/it/environments.md — expanded AD groups with form-specific group naming and AD Administrative Center procedure


[2026-05-13] update | Form Creation Control Document — Component Replacement Request (v61→v97)

Updated word_templates/Form Creation Control Document_Component_Replacement_Request.docx: - Added Changes table (9 rows) under the "Form Components Changes" section covering the full CRR development history: v61 (Other mode, PercentthroughLife fix, z_ComponentToUpdate_Manual), v80 (QLPrtyCy_cost cost field), Word template V3 (5-section PDF, 17 GUIDs), v85 (z__Fmt date strings), v95 (availableForDocumentRendering fix + JS Date fix), v97 (Get Component Request Data, Safeguarding, Insights, debug framework). - Updated Forms table: CRR version 6 → 97; RWO version 71 → 73. - Updated Workflows table: Property Check Appoint LoadDat 26 → 30; Comp Request App Data 9 → 12; Repair Works Order Comp (blank) → 7. - Fixed Email* field: No → Yes.


[2026-05-06] init | Wiki structure created

Created wiki/ folder, wiki/index.md, and wiki/log.md. Defined standard page format with YAML frontmatter.

[2026-05-06] ingest | Versaa_IT_DMC_md/ and Versaa_One_Note_md/ (50+ files)

Ingested IT documentation and OneNote notes. Created 5 new wiki pages: - wiki/it/environments.md — all environment URLs, ports, DB names, AD groups, mobile app config - wiki/it/daily-operations.md — daily check procedure, SSRS reports, common errors table - wiki/it/appointment-polling.md — polling mechanics, hpm1stoa, troubleshooting - wiki/platform/data-warehouse.md — DW workflow step, z_ prefix, key tables, error handling - wiki/platform/sql-lite.md — SQLite .db3 files, builder+parser pattern - wiki/platform/formula-reference.md — date calcs, null coalescing, regex, arrow functions


[2026-05-06] ingest | Teams_Sessions/ (8 session files)

Ingested all Teams training session files from Teams_Sessions/ (Peter Steele × 3, Mark Wright × 4, overview notes × 1). Created 2 new wiki pages: - wiki/sessions/gotchas-and-tips.md — critical gotchas and non-obvious tips - wiki/sessions/naming-conventions.md — form item naming conventions and prefix patterns


[2026-05-06] ingest | Freshdesk_KB_md/ (57 articles, URL index only)

Attempted to ingest Freshdesk KB articles from Freshdesk_KB_md/general/. The scraper captured only article titles and URLs — body content requires SSO authentication at aareonuk.freshdesk.com and was not captured. Created 1 reference page: - wiki/freshdesk-kb/index.md — index of 57 article URLs organized by 14 topic categories

To re-capture body content: re-run versaa-rag/preprocessing/scrape_freshdesk_kb.py with fresh session cookies (FRESHDESK_EMAIL + FRESHDESK_PASSWORD in .env).


[2026-05-06] ingest | Guides/ (all 13 guides)

Ingested all 13 how-to guides from Guides/ into wiki pages: - wiki/platform/versaa-overview.md - wiki/forms/how-to-build-a-form.md - wiki/forms/how-to-edit-a-form.md - wiki/forms/form-xml-conventions.md - wiki/forms/calculated-fields.md - wiki/forms/visibility-formulas.md - wiki/forms/testing-configuration.md - wiki/forms/email-documents.md - wiki/forms/component-replacement-request.md - wiki/workflows/how-to-create-a-workflow.md - wiki/workflows/how-to-edit-a-workflow.md - wiki/workflows/fix-errors.md - wiki/workflows/multi-form-workflows.md - wiki/sql/cheat-sheet.md - wiki/deployment/dev-to-live.md - wiki/it/restart-services.md

Also extracted cross-cutting concepts from .github/copilot-instructions.md into: - wiki/forms/form-xml-conventions.md - wiki/forms/component-replacement-request.md - wiki/platform/versaa-overview.md

[2026-05-06] schema | copilot-instructions.md rewritten as schema

Removed inline knowledge from copilot-instructions.md (form XML detail, ftpackage build pattern, CRR field table, Word Connector GUID map). These now live in wiki pages. Added wiki navigation and maintenance instructions.


[2026-05-13] pipeline | Management Studio full pull-down — all 9 item types (_130526)

Re-exported all 9 Management Studio item types from PrtyDev and ran the pipeline with --force. Results:

  • Forms: 345 form files extracted (up from 205 on 06/05/2026 — all historical versions included)
  • Workflows: 376 workflow files extracted
  • All other item types: reference data, diary config, document templates, group table mappings, providers, user properties, workflow variables — all refreshed
  • Total: 2955 files updated

Key version changes confirmed: - Component+Replacement+Request_97.form — CRR is now at v97 (was v85 on 06/05/2026; version increments reflect imports through Dev→Test→Live) - Client+Visit_100.form — Client Visit now at v100 (was ~v79; multiple changes including GenBP_key fix, Rent Statement Request button) - Repair+Works+Order_73.form — RWO now at v73 (was v63; CRR sub-task added in v64) - ClientVisitBasicLoad_17.workflow — updated with GenBP_key Append Data Item fix from session 10 - Comp+Request+App+Data_12.workflow — "Get Component Request Data" workflow at v12 - Repair+Works+Order+Comp_7.workflow — RWO completion workflow (CRR processing step added, WIP)

Packages processed (all staged in all_versaa_forms/ before pipeline run): - all_versaa_forms_130526.ftpackage - versaa_all_reference_data_130526.ftpackage - versaa_diary_config_130526.ftpackage - versaa_document_templates_130526.ftpackage - versaa_group_table_mappings_130526.ftpackage - versaa_providers_130526.ftpackage - versaa_user_properties_130526.ftpackage - versaa_workflows_130526.ftpackage - versaa_workflow_variables_130526.ftpackage

Updated wiki pages: - wiki/forms/component-replacement-request.md — added v97 to version table; updated current file reference - wiki/forms/repair-works-order.md — NEW PAGE: full RWO form reference (version history, sub-tasks, key fields, CRR integration, completion workflow) - wiki/index.md — added RWO page to Forms table; updated CRR summary


[2026-05-13] ingest | Teams_Sessions/Catchup - Imran & Peter - 10.vtt

Ingested session 10 (Imran Saleem + Peter Steele + Mark Wright, ~70 min). Key topics:

  1. Client Visit Basic Load bugGenBP_key missing from sub-workflow initial context caused XPath errors on GenBP_Insight_kind / GenBP_Insight_red_flags. Mark Wright diagnosed and fixed live by adding Append Data Item steps to 5 workflows. Fix only needed in Dev (not Live).
  2. CRR "Get Component Request Data" button — new button on CRR form sends message getComponentRequestData; wildcard workflow trigger any *.getComponentRequestData fetches component SQL data; z_component_count calculated field used as checksum to hide button once data loaded.
  3. CRR ↔ Repair Works Order: Insights data passing — fixed by using "Defined Format and Mappings" on the "Starting Work" button to map the full Insights group across (possible because all items share the same names).
  4. Component SQL group table updated — Peter added windows/doors and other types from Lorna's list to the SQL group table in dev.
  5. Client Visit — Rent Statement Request tested — "Request a rent statement" button on Client Visit form successfully created a CRM contact in QL in dev. Ready to migrate to Test.
  6. CRR Word template — photo aspect ratio — photos displaying but stretched. Fix: adjust image placeholder dimensions in the Word template.
  7. Deployment / ticket closure process — confirmed: deploy to Live → resolve Freshdesk ticket. Incomplete workflow steps that are no-ops are safe to deploy.

Updated pages: - wiki/forms/component-replacement-request.md — added "Get Component Request Data" button section, z_component_count checksum, RWO→CRR insights passing, Repair Works Order Completion CRR step (WIP), photo aspect ratio note - wiki/workflows/fix-errors.md — added "Client Visit Basic Load" known error pattern with diagnosis and fix steps - wiki/workflows/multi-form-workflows.md — added "Sub-Workflow Data Passing" section: Append Data Item pattern, retry-from-step warning, real example - wiki/sessions/gotchas-and-tips.md — added: "First thing: check workflow instances"; "Retrying only retries from that step"; "One change can break unrelated things"; SQL Server multi-cursor tip - wiki/deployment/dev-to-live.md — added Freshdesk ticket closure process; "test yourself before handing to users" tip; packaging strategy for partial-ready deployments

  • Teams_Sessions/ ingestion → wiki/sessions/
  • Versaa_IT_DMC_md/ + Versaa_One_Note_md/ ingestion → wiki/it/ + wiki/platform/
  • Freshdesk_KB_md/ selective ingestion
  • RAG retirement — disable MCP in .vscode/mcp.json

[2026-05-07] ingest | CRR date format fix — Word Connector lessons

Created 1 new wiki page and updated 4 existing pages with lessons from the CRR date format fix (form v85, deployed as v94/v95):

New page: - wiki/forms/word-connector-templates.md — NEW: how field bindings work (server-side GUID registry), correct procedure to re-bind fields, safe .docx patching via PowerShell ZipFile, availableForDocumentRendering requirement, archivable requirement for formula values, decorative image warning, CRR V3 GUID map, troubleshooting table

Updated pages: - wiki/forms/calculated-fields.md — added "Formatting a Date Field as a String" section: JS Date object caveat, correct getDate()/getMonth()/getFullYear() pattern, z_ helper field pattern for document templates, 178 char formula length limit + SQL 8152 error - wiki/forms/email-documents.md — added troubleshooting section "Emails Stopped After Saving a New Template" covering availableForDocumentRendering=False as root cause, detection via Word Connector invalid items count, step-by-step fix procedure - wiki/forms/component-replacement-request.md — added v85 row to version history; added z_*_Fmt date field reference table; added list of 10 fields set to availableForDocumentRendering=True; updated current file path - wiki/sessions/gotchas-and-tips.md — added "Word Connector / Document Template Gotchas" section with 5 gotchas: Date objects from JS, silent email failure, binding re-do requirement, Python .docx incompatibility, 178 char formula limit


[2026-05-07] ingest | Confluence_KB_md/ — Aareon Atlassian wiki space QH

Built versaa-rag/preprocessing/scrape_confluence_kb.py (Confluence REST API v1, HTTPBasicAuth with API token). Scraped 25 pages and 106 images from space QH (UK Home) into Confluence_KB_md/. Created 3 new wiki pages:

  • wiki/contractor-portal/user-guide.md — NEW SECTION: full user guide for the Contractor Portal synthesised from 5 Confluence sub-pages; covers logging in, icon reference, managing works orders (viewing, filtering, variations), managing appointments (creating, completing, outcome reasons), invoicing (single + multi-order, credit notes), bulk order completions via CSV, and CSV export
  • wiki/forms/section-breaks.md — Section Breaks in Form Designer: what they are, visibility control pattern, how to add them, access permissions
  • wiki/platform/aareon-official-docs.md — Catalogue of all official Aareon PDF/DOCX docs available on the Atlassian wiki: Versaa user/technical guides, Forms Designer guide, Management Studio guide, Workflow Designer guide, Repairs Works Order (configuration, data dictionary, technical docs, testing, update, installation, release notes), Tenancy Sign Ups configuration, iAppoint module note, Contractor Portal config guide links

Updated pages: - wiki/index.md — added Contractor Portal section; added section-breaks.md to Forms table; added aareon-official-docs.md to Platform table


[2026-05-07] ingest | Confluence_KB_md/ — full 825-page scrape + wiki ingest pass #2

Fixed pagination bug in scrape_confluence_kb.py (was using totalSize=25 as loop terminator → changed to _links.next absence detection). Re-ran full scrape: 825 pages, 1838 images, 379 image failures (auth-gated attachments). Manifest written to Confluence_KB_md/import_manifest.json.

Created 4 new wiki pages synthesised from the new Confluence content:

  • wiki/platform/versaa-release-process.md — Versaa 1.1 Release Packaging Changes (Update 44+ flat snapshots, <100 MB, plugin versioning independent of product); Identity Token Service (OAuth2/SSO, deployment requirements for ITS); Environment Audit Tool (forms + workflow auditing, PDF reports)
  • wiki/forms/versaa-letters.md — Full Versaa ↔ QL letter writing integration: Arrears, CRM (Contact Management), and Case Management letter routes; PDA_LETTERDEF table schema; PDA_LETTTERDATA staging table; all HSG parameters (PDA_LETTERS, PDA_LETTERACTION, PDA_TABLE_LETTERACTS, PDA_LETTER_WORDPATH, etc.); letter/action cross-reference (Option 1 parameter vs Option 2 HGMPDAAC table); QL Document Production Types 2 & 3; device update via Data Lists workflow; scheduler setup constraints (Word dependency, /IT flag)
  • wiki/it/appointments-ql-parameters.md — Complete QL appointments parameter reference: HPM Main Parameters (screens 7/8/9), Screen Layouts (Appointments Used flag), all HSG parameters for 1st Touch integration (1ST_TOUCH_APP, ACTUAL_OPERAT_APPTS, BULK_APPT_*, DEL_CANCELLED_APPS, MORE_APPT_INF_IN_ACC, IGNORE_NWD_CL_OP_POP, etc.); definitions (Trade Codes, DLO Operatives, HPM Filters, Calendar Locations); HGM Sequence Maintenance (APPT_NO)
  • wiki/platform/property-components-ql.md — QL property components data model (Main Info tab fields: Comp Ref, Location, Condition, Failure Effect, Installation Date, Quantity, Sub Component, DHS Code); cycle management; history/photos buttons; sub-component limitation (not updated via SOR completion); CRR form relevance (QLPrtyCm_Components data group)

Updated pages: - wiki/index.md — added versaa-release-process.md and property-components-ql.md to Platform table; added versaa-letters.md to Forms table; added appointments-ql-parameters.md to IT table

[2026-07-21] fix | CRR completion email doesn't fire as a sub-form — root-caused + RWO fixed (Dev v8)

Symptom (reported by Imran; previously parked as a separate ticket off 21617): the Component Replacement Request completion email to ComponentReplacementRequest@lincolnshirehp.com fires when CRR is completed standalone, but never arrives when CRR is completed as a sub-task of Repair Works Order or Void Survey.

The previously-logged diagnosis was wrong. wiki/forms/component-replacement-request.md recorded it as "a shared CRR document-template / recipient issue, not specific to any parent form". The form and the mailbox are fine — the defect is in the parent completion workflows, and it differs per parent.

Root cause. CRR has no completion workflow of its own; its email is sent by the shared Generic Task Completion, whose every XPath is anchored to //Task[not(ancestor::Task)] — outermost Task only, nested Tasks explicitly excluded. A sub-task stays a nested <Task> inside the parent's TaskXml until the parent's completion workflow extracts it. Standalone CRR works because it is the root task.

  • Repair Works Order — the Process Component Replacement Request step used Toolkit.Workflow.CreateWorkflowInstanceForSubtasks, which starts a workflow instance but does not extract. All 6 working sub-tasks in the same workflow use Toolkit.ExtractSubTaskCreateWorkflow; CRR was the sole outlier (repo-wide: 128 correct vs 2 wrong). CRR stayed nested → Form_EmailRecipient invisible → email gate false → skipped silently, no error. This also resolves Peter's session-10 note "the step runs and completes but does nothing."
  • Void Surveyno extraction step at all; all 29 Void*.workflow files have zero sub-task steps. The CRR sub-task is orphaned inside the parent TaskXml.

Fix shipped (RWO). Swapped the step class + parameters to match the Damp & Mould step verbatim; step id, name, transition and editor layout untouched (diff is 1 step, nothing else). Scripts versaa-rag/transform_rwo_comp_crr_extract.py + build_rwo_comp_crr_package.py. Imported to Dev as v8, INACTIVE — v7 remains active; activate deliberately before device testing. Verified server-side: step 25 now ExtractSubTaskCreateWorkflow / formItemNameList=ComponentReplacementRequest / workflowName=Generic Task Completion, trigger and 25-step count unchanged.

Two gotchas logged (wiki/sessions/gotchas-and-tips.md): 1. A sub-task only gets its own workflow if the parent extracts it — plus the near-miss CreateWorkflowInstanceForSubtasks step class that completes successfully while doing nothing. 2. extracted/ workflow filenames collide across environments. Three files named Repair+Works+Order+Comp_* carry three different workflow ids and only _4/_7 are in Dev — _8 and _15 belong to other environments. The highest _N is not the latest. Basing the fix on _15 (as first attempted) built on a foreign lineage; caught by querying the live record. The transform script now asserts the expected Dev workflow id. Related: MS matches by name on import, ignores the XML id, and assigns its own sequential version — a package labelled version="16" landed as v8.

Not yet done: the Void Survey side. Needs an equivalent ExtractSubTaskCreateWorkflow step in Void Pre Inspection QL Updates (has an Unpack Data step + Void Survey.UpdateHMSData MessageStart) — confirm that message is raised on completion before building. Also noted for testing: Form_EmailSubject, Form_EmailBody and Form_DocumentEmailAttachmentName are blank with no defaults, so the mail is subject-less and the CRR PDF is not attached — separate additive form change if the Asset team wants those.

[2026-07-22] fix | CRR completion email — RWO VERIFIED in Dev; Void Survey side built (Dev v12 inactive)

Follow-on to the 21/07 root-cause fix.

  • RWO — VERIFIED end-to-end in Dev. RWO Comp v8 activated; completing a CRR from inside a Repair Works Order produced the email at ComponentReplacementRequest@lincolnshirehp.com. Confirmed the whole RWO must be completed (not just the CRR sub-form filled) for the completion workflow to fire — a red herring during diagnosis was that filling only the sub-form fires nothing.
  • Diagnostic gotcha logged: Dev's Search Instances view (GetAllWorkflowInstances) is effectively a PendingEvent graveyard — it retained 126 parked instances, newest Aug 2025, and surfaced no recent completed/errored runs. So "no instances found" is NOT evidence a workflow didn't fire in Dev. To check a specific run, query by --external-id <order> and show <id>.
  • Void Survey side built. Unlike RWO (wrong step class), the Void completion workflow had no extraction step at all. Added Toolkit.ExtractSubTaskCreateWorkflow (formItemNameList=ComponentReplacementRequestGeneric Task Completion) to Void Pre Inspection QL Updates, inserted on the common path just before the terminal End step. Base = live Dev v11 (verified equal to on-disk _11 by step-list comparison; the _12 file is the Test-lineage record id f10cfe5c — filename collision). Imported to Dev as v12, INACTIVE; verified server-side (29 steps, extraction step present, correct params). Scripts transform_void_qlupdates_crr_extract.py + build_void_qlupdates_crr_package.py.
  • Design note: targeting Generic Task Completion via ExtractSubTaskCreateWorkflow is a proven precedent — the Process FirstPersonVisit step in RWO Comp uses exactly that. So this is not a novel construction.
  • Next: activate Void v12 in Dev + device-test (complete a whole void survey with a CRR sub-form → email fires); then promote RWO Comp v8 + Void v12 Dev→Test→Live together.

[2026-07-22] verify | CRR email fix — RWO VERIFIED in Test (Comp v16 active)

RWO Comp promoted to Test (record 1deb4b13, active v16, server-verified: ExtractSubTaskCreateWorkflow → Generic Task Completion). User completed a whole Repair Works Order with a CRR sub-form in Test → email arrived at ComponentReplacementRequest@lincolnshirehp.com. RWO side now proven in Dev + Test; next = business sign-off → Live. Void Survey side (Void Pre Inspection QL Updates v12) still inactive in Dev awaiting its own device-test.

[2026-07-22] verify | CRR email fix — Void Survey side VERIFIED in Test (Void QL Updates v13)

Completing the CRR sub-form email fix. The Void extraction step (added to Void Pre Inspection QL Updates) was imported + activated in Test as v13 (record f10cfe5c, server-verified: 29 steps, ExtractSubTaskCreateWorkflow → Generic Task Completion) and a CRR completed inside a Void Survey in Test produced the email at ComponentReplacementRequest@lincolnshirehp.com. Test was the first real verification because Dev QL was down — Dev's v12 (record 960d8a06) stays inactive/unverified until QL recovers; Dev v11 == Test v12 base content was confirmed identical before porting, so the cross-env port was safe. Both sides of the fix now verified in Test (RWO Comp v16 + Void QL Updates v13) → next = business sign-off → Live. Watch the version-number collision when reading records: Test Void v12 = the earlier contact-gate fix (no CRR step); Test v13 / Dev v12 = the CRR-extraction fix.