No phase advanced. Matthew opened with "let me know when you're ready" and then two feed complaints from
/infrastructure/reference-data/: the CPE concession data "imported but not published", and SNOMED CT UK Clinical
Edition showing overdue. Both were reproduced and measured; neither was a data failure.
1. The 02:00–04:00 rotation window passed unexecuted, and C4 has nothing to record. The pack at
/root/rotation-20261018 is untouched — no backup-* stamp, no finalize.sh run, old-value still the 2026-10-13
rehearsal placeholder — and the read-only verifier run today returns 87 literal hits against the 85 baseline
(exit 1), with the retired literal still authenticating over TCP as postgres, fh_app_user and wikijs. So the
post-window DSPT evidence note cannot be written as a completed window; the honest entry is window not executed,
rescheduling, which is Matthew's call. The pack's own README still lists two pre-window items open (rehearse the apply,
reply to Hermes), which is consistent with it never being ready to run.
2. SNOMED "overdue" is the rule, not the release. Measured through the deployed code
(health.health_rows(date(2026,10,18))): live release 20260923, publication 2026-09-23, age 25 days,
next_expected 2026-10-01, overdue True — and REFERENCE_DATA_ALERT_EMAILS is matthew@veripath.co.uk, so
the 06:00 job has emailed that falsehood daily since 06/10. next_expected_publication()'s monthly branch returns the
first day of the following month (right for the Drug Tariff and the NHSBSA notices, which apply to a calendar month;
wrong for a TRUD monthly edition, which applies from its own publication date). Separately, the source has no scheduled
pull at all — no beat entry, and zero ReferenceFetchAttempt rows ever, so its key state reads "Not yet checked"
and no edition can arrive on its own; wiring it must follow the editions-union dedupe already in handoff note (18).
3. CPE "imported not published" is a legacy record, and disposing of it alone would have made the page lie
differently. Release 4 (2026-09, imported, 2 updates / 398 price rows) is from the pre-phase-6 mailbox parse;
since phase 6 the mailbox is is_signal_only and the client-facing prices come from the NHSBSA notices release —
verified that nothing reads it (derive.py and pack_link.py both call effective_rows(notices_release), and the live
October set is 58 applied lines on notices release 19 with 4,440 DerivedPackPrice rows). A rolled-back
transaction then showed that discarding it today flips the card to "Not running — no release yet", i.e. one wrong
message for another — so the discard was deliberately held to travel with the code change. The same probe exposed a
dated fault: from 20/10 the CPE source reports Release overdue and emails "no concession update has arrived for
October" regardless of release 4, because is_overdue() asks whether that source produced a release rather than
whether its signal did.
4. Filed to OpenCode as note (19) — four items: the monthly TRUD expectation rule; the missing snomed_uk_clinical
beat entry (after (18)); the signal-only presentation and the legacy parse path no longer creating publishable releases;
and the dated 20/10 false-alert fix. The discard of release 4 is written as a data step after the code, with the
acceptance check stated (source reads signal only, nothing offered for publication, summary line "All configured feeds
are current"). Note verified on the served page after the restart.
4a. OpenCode's fix came back, was verified, and is DEPLOYED (2026-10-18). Suite reproduced here (271 OK,
skipped=1), diffs read, and the new tests checked for the dated assertions rather than a proxy. Commit 5d64c4f,
rollback /root/rollback-gp_booking_app-20261018.tgz, scripts/deploy.sh (no migrations, static collected, app + worker
uk_sct2cl_43.0.0_20260923000001Z.zip), not the release name. Thepull_reference_release('snomed_uk_clinical') → already_held, no download, and theReferenceFetchAttempt now reads Key accepted instead of "Not yet checked". Release 4 discarded withDISCARDED event; publish_release() refuses the source outright. Live result: summary "AllCurrent (live 20260923, next 23/10, overdue from 28/10);5. The sidebar fault was one missing row, and it is FIXED (2026-10-18, authorised by Matthew). The wiki had warned
"Site Navigation is missing or corrupted" continuously since 15 Oct — 4,490 times in 24 h, every ~30 s — and pages
carried no sidebar. Cause: navigation.getTree() looks up the row key = 'site', and this database had one row keyed
default with config = []; the missing row sent the resolver into its warn-and-return-[] branch. There is no
API route that creates the row (navigation.updateTree only patches key='site'; navigation.updateConfig writes
settings.nav), so the repair had to be a single direct row write — taken as an explicit one-off exception, since the
standing rule forbids direct DB writes for pages. The row was renamed default → site and given the stock shape from
server/setup.js (one locale, a Home link), with settings.nav left at MIXED so the site tree is appended below it.
Verified: warnings 0 in the 60 s after restart (GraphQL navigation.tree returns the item; a headless-Chrome DOM
dump shows the sidebar with Home and Browse, and pages answer 200). Backups: wiki-nav-backup-20261018.sql (the old row,
--data-only) and wiki-nav-mode-backup-20261018.txt; the applied SQL is wiki-nav-repair-20261018.sql, all three in
/root/work/.
6. Capacity: the intake is bounded, and the pass answered the question that was actually asked. VPS1 was at 60%;
Matthew's framing was that the amount being taken on is rising fast because of the feeds. Measured: the whole reference
store is 2.98 GB (~2% of the disk), every feed's retention terminates (dm+d live + previous; the SNOMED loader
drops *_old before renaming, so the previous corpus is replaced not accumulated — bounded 1.07 GB; MHRA rows purge
weekly; the CPE mailbox retains nothing; release archives live on VPS2), and the UK extension cost ~240 MB once —
visible as the gp_booking_pg dump going 219 → 303 MB on 17/10 and staying flat. The gp_booking_pg dump size is the
honest meter for intake, not df. The 60% was container images (84 GB across the containerd + docker stores) and
17 GB of backups. Reclaimed, regenerable only: 6.1 GB SNOMED extraction staging (archive + merged build kept),
3.0 GB trivy cache, 1.4 GB npm, ~0.3 GB builder/apt/tmp → 139 G → 129 G (56%). Then the retired Ollama
installation (7.3 GB) on Matthew's ruling — his question ("was it chunking, part of an app process, probably dental?")
was right: it is dental's nomic-embed-text chunk-and-embed step for agent memory (integrations/memory_service.py),
enabled and running on VPS2, with the VPS1 copy stopped since 11/09; checked that nothing here used the image and
that the 11434 listener was an SSH reverse forward from Matthew's workstation, not the container. Container and image
removed, 262 MB model volume kept. 122 G used, 111 G free, 53%; 26 containers running, wiki/site/GP 200, dental and
its ollama healthy on VPS2. Round 2 recorded on
Disk Capacity Review with the per-directory numbers. Open for Matthew:
~6.7 GB of container-less images (old postgres:15/15-alpine/16, pgvector:pg15, forgejo:14.0 are pure win;
netdata/wiki/keycloak:latest are staged newer pulls, so pruning means a re-pull) and whether the defectdojo backup
retention drops 7 → 4 days (~3.5 GB). Never touch the ZAP image (monthly DAST cron) or the MinIO rollback image.
Still open. C4 waits on a re-scheduled rotation window. DSPT Standard 1 remains the next review step (the
platform work is finished; Standard 6 is closed, SIRO appointed). Note (19) is HANDLED — deployed and verified,
including a dated fix that would otherwise have fired at 06:00 on 20/10. Next session resumes DSPT Standard 1
(the work Matthew named); the capacity round's two decisions are listed above and wait on him.
The four incident decisions, the specimen agreement's ten [DECISION] items, and the snomed_*_old retention question
still wait on Matthew.
No phase advanced. Matthew opened with "let me know when you're ready", then: "there are some outstanding issues that
require your intervention so it can complete the work" — i.e. clear the blockers in OpenCode's inbox so it can finish.
That is what this session did.
1. The previous pointer was wrong on its own first item, and measured first. It told the next session that
snomed_descendants was still the un-rebuilt 257,605 rows and that the acceptance re-measure could not honestly be
done before a rebuild. Measured: the table holds 261,899, and OpenCode's production run of
rebuild_snomed_descendants.py --allow-removals measured stored 261,899 / rebuilt 261,899 / added 0 / lost 0 — a
confirmed no-op, because the (L) loader rebuilds the closure during the corpus import. (J) is closed, the
"36% short" justification is withdrawn (it walked the graph without the active filter; the active-only closure is
within 1.9% of a fresh walk), and amoxicillin/diabetes resolve identically either way. The pointer has been
corrected in place rather than left to mislead a third session.
2. Four rulings written to from-hermes as note (17), each measured before it was made.
/opt/scripts/refresh-image.sh copies (VPS1 and the app engine)--kind image_refresh in report mode; the repo version differs in exactly${KIND:-image_audit} default and the --apply selector. refresh-image.timer is report-only on both and nextadmin_users = stats_users = veripath_pooler and log_connections = log_disconnections = 0, so both halves[databases] entries, so two credential changes in one/tmp/pooler-dry --no-reload and a diffuserlist.txt line — anything else stopsclient-dental-vps shared. Ruling: one physical server is one maintenance3. Inbox hygiene. Note (17) inserted above the newest note, the (A) and (F) status lines rewritten to match
it, and the three stray ==== ruler lines that a past upsert had left above the H1 stripped — the page was
43,443 → 48,615 chars and verified three ways (stored body byte-identical to the intended, one of each ### anchor
present, rendered 200 with the note's own phrases on the public URL).
4. OpenCode's completion was verified here, not taken on trust. Measured after its session: the live pooler reads
admin_users = stats_users = veripath_pooler_admin, log_connections = log_disconnections = 1, with admin.env 0600
and a .bak-20261017 pair beside it; /opt/scripts/refresh-image.sh on the app engine now carries
${KIND:-image_audit} (VPS1 synced by its own report); GP, dental and pharmacy containers are healthy; and the
shared-box change is real in the central database — tenant 7 now hangs off box 2, box 4's client cadence is
gone, and the two surviving client cadences read 2026-11-01 (04:07 / 02:23). The MinIO protection is recorded on
the replacement scope page (PROTECTED + the keep tag).
5. The acceptance re-measure — done, and it passes. Eighteen queries × five runs through the app's own
snomed_search() inside the deployed container, against the live corpus (383,853 active concepts / 1,408,706 active
descriptions, max(effective_time) 20260801): worst single search 51.1 ms, medians 8–22 ms, against the 300 ms
budget. heart attack under the clinical hierarchy leads with Myocardial infarction (disorder) [22298006] (3
results); the drug term leads Product containing amoxicillin (medicinal product). One honest failure, and it is
content not speed: waterworks returns only Water treatment plant operator (waterworks) (occupation) unrestricted and
nothing under the clinical hierarchy — the corpus has no lay synonym for urinary symptoms, so it is a UK-Extension or
local-term-map question, not a query one.
6. The SNOMED page was corrected, because it asserted a figure that measurement had already disproved. The whole
Closure Table Roots section was replaced: the six real roots with measured counts (129,171 / 60,724 / 45,366 / 26,401 /
127 / 110 = 261,899), the 2026-10-16 measurement marked WITHDRAWN with the reason (a walk without the active
filter), the rebuild recorded as a confirmed no-op with its script named, the acceptance table published, 63449007
stated as not a root, and §6's "4 roots + safety rules" line fixed. The page went 22,461 → 24,252 chars, stored body
byte-identical, rendered 200.
7. The dental item was a misread — measured before acting, and nothing needed binding. OpenCode's report that no live
dental client user is bound to test-client-dental was taken from the central users_customuser table (pmx,
dental_admin, dental_manager, all org_id NULL). But a dental client host reads users from the tenant's own
database, and test_client_dental.users_customuser carries admin.test-client-dental,
manager.test-client-dental and the rest all bound to org_id = 'test-client-dental'. Rendered through the app's
own request path on test-client-dental.dental.veripath.co.uk: the manager and admin get 200 on /infrastructure/
(card present) and /infrastructure/my-maintenance/ (5 real rows, "Your server / test-client-dental", "Patched
monthly on the 1st; security updates are applied continuously"), both CSVs export, and a DENTAL_CLINICIAN is refused
403. Acceptance criterion 6 is met on the dental side; the correction went back to OpenCode in note (18). A probe
of my own first returned 403 for every user — it was fetching the user outside the client-tier context, so users
routed to the wrong database; the measurement only became valid once the user was read inside the tenant context.
8. The UK Clinical Extension is imported and live — and the loader could not load it as-is. Matthew's scope call.
The union of the two packages fails: 683 relationship ids appear in both files — UK revisions, 642 flipping
active 1→0 and 41 already 0, module 999000011000000103, later effective_time. A union therefore duplicates the
primary key, and any naive merge would leave 642 UK-retired relationships active. So the load set was merged before
loading (build-merged.sh: concepts and descriptions concatenated — measured overlap 0 — relationships =
international minus the 683, then the extension; one file per role, so the loader's per-file count(*) == lines − 1
guard still holds; merged files have 0 duplicate ids). Imported and published: concepts 638,855, descriptions
1,956,244, relationships 3,881,039, max(effective_time) 20260923, release 34 published/is_live,
closure rebuilt to 281,363 rows over the same six roots, 13 canonical indexes intact, snomed_*_old kept
(≈1.07 GB). Release 33 is the failed un-merged attempt, kept as the audit trail. Two deployment gaps surfaced:
seed_reference_sources had never been run against dsp_clinic (the snomed_uk_clinical source row did not exist),
and the loader's editions union has no dedupe step — both filed to OpenCode as note (18), with the fix
(dedupe on id, greatest effective_time wins). Acceptance re-measured after the swap: identical orderings, no
regressions, worst search 51.6 ms; waterworks is unchanged — the extension adds UK clinical content, not the
urinary lay synonym, so that remains a genuine content gap.
9. SIAAS became the foundation for the legal documents — and the specification had to be rewritten twice to be honest.
Matthew's direction: settle the technical makeup first, then draft the agreement. V2.0 was a blueprint; V3.0 is measured.
Writing it exposed that three of its own pillars do not hold on the estate: the estate root key logs in on both
client-vps instances, port 22 is reachable from the internet on both (password auth refused, key accepted), the GP
client-vps binds PostgreSQL to all interfaces (public access dropped by its firewall — probed), and two transports
coexist (WireGuard live on the dental client-vps, SSH reverse tunnels on both, plus orphaned firewall rules on the GP
one). The credential inventory's "key revoked 2026-08-11" row was stale and is corrected. Ruling recorded (Matthew):
posture is per client-vps, by role — dev/canary (ours, key present, development and the patching canary), example
model (installed, key then removed), production client (no login from handover) — and the development estate keeps its
login until example-model instances exist. Vocabulary fixed: client-vps, never "box".
10. The specimen service agreement is drafted (DRAFT 0.1: 18 clauses, 6 schedules) on those defined terms and on the
procurement/handover model Matthew chose: we procure the client-vps on the client's behalf, the client is account holder
from the outset, we install, hand over credentials, the client changes them, we remove our access and verify it by a
failed login, and the handover is recorded as a maintenance record so the client holds dated proof. Ten [DECISION]
items are flagged for him: term, fees, who pays the host, support hours, incident threshold, backups, transition rate,
liability cap, reference rights, governing law.
11. DSPT reorganisation executed. 59 artefacts across 7 locations mapped on
the workspace page; 14 work products moved into
compliance/governance-compliance/{evidence,guides,registers} and 5 duplicates archived (the wiki policies/ tree was a
stale copy of the CMS set; the tasks/ checklist was a Category-4/v8 generated tracker with dead /policies/ links; the
older standards set). Policy text stays in the CMS; the tree holds
the policy register. The register exposed 8 policies
with no named owner and 3 with no dates at all (Business Continuity, Unsupported Systems, Accountable Suppliers) — the
three that Standards 7, 8 and 10 lean on.
12. Standard 6 closed and deployed. One policy survives (responding-to-incidents), amended with the SIRO named, a
breach risk-assessment and notification section (item 6.1.3) and the processor duty tied to clause 8.3 of the
agreement, and a stale Watchtower row removed. The draft Data Security Incident Response Policy — which literally began
with pasted model reasoning, not policy text — and the redundant 6b draft are unpublished with superseded banners;
its incident-log template moved into the new
Incident Log & Breach Assessment Register, which records
five real events (14 May wiki loss, 1 Aug outage, 1 Aug login-landed-on-staff-dashboard near miss, 11 Aug miner, 14 Aug
root cause) with the two judgements each — were personal data involved and on what basis, what was the risk and what did
we decide. Site deployed and the build verified: the survivor carries the new section, both superseded pages are absent
from the output. SIRO appointed: Matthew, 2026-10-17 — written record, duties, decision log and four open points (no
deputy, no DPO decision, one-person staff items, SIRO is also the sole administrator).
13. Two operational findings worth keeping. (a) A wiki-manage write does not surface on the served page until the
wikijs process restarts. The database is correct and the tool reports "Render: OK", but the site keeps serving the old
page object — it cost three wrong "it's done" reports before the served HTML was measured. Always fetch the served page,
then restart the wiki. (b) The wiki has logged "Site Navigation is missing or corrupted" continuously since at least
15 Oct (6,327 times in 48 h) and pages render with no sidebar — a separate repair, not caused by today's edits.
Still open. C4 — the post-window DSPT evidence note — falls due after tonight's rotation window (Sunday 2026-10-18
02:00–04:00), which also carries the client pharmacy database password rotation, after which the pooler's [databases]
entries must be regenerated. The DSPT review pass continues at Standard 1, then 2–3, then the three undated policies;
six items need new material (premises controls, induction, training records, an IAR, supplier assurance, and the SIRO's
own review of administrator work). Four incident decisions await the SIRO's sign-off. The specimen agreement's ten
decisions await Matthew. Then: example-model client-vps instances (the no-login posture), the GP client-vps loopback
rebind and orphan firewall rules, the loader dedupe fix (OpenCode, note 18), the nav corruption, the credential
cleanup list, and the snomed_*_old tables (1.07 GB) kept pending his word. Phase 4 still waits on him naming it.
No phase advanced. Matthew opened with "let me know when you're ready", then handed over a Gemini exchange about
automating dataset updates and observed that we already pull from TRUD. The next-session pointer was the SNOMED corpus
update, and it is done.
1. The email route was never needed — and the subscription was already held. TRUD's own guide is explicit that the
credential is real ("Each account has a unique API key, derived from the account's email address and password",
"Keep your API key private", "If either of these changes, a new key is generated and the old one is disabled") — so
the parked plan's first step ("confirm what the account can see") was done by measurement: the platform's existing key,
read-only and never printed, returns HTTP 200 for item 101, SNOMED CT UK Clinical Edition, RF2: Full, Snapshot &
Delta — latest release SNOMEDCT2_43.0.0_20260923000001 (2026-10-06, 990,865,460 bytes, published SHA-256) —
while items 105 (UK Drug Extension) and 1799 (UK Monolith Edition) return 404. No new subscription, no
licence acceptance and no mailbox tooling are required; the corpus is not open data (dm+d is OGL, the UK Edition is
licence-gated). Note (L).
2. The corpus update went in, staged and swapped. Built into staging tables while the live tables served traffic,
asserted against the source files' own line counts, then swapped in one transaction (the corpus was never empty):
concepts 529,392 → 535,233, descriptions 1,567,909 → 1,716,441, relationships 3,548,588 → 3,580,370,
max(effective_time) 20260201 → 20260801; all 13 index names preserved, idx_desc_term_trgm included. Measured
through the app's own snomed_search(): heart attack under the clinical hierarchy now leads with Myocardial
infarction (disorder) [22298006] — the new release gives MI the active synonym "Heart attack" — and the acceptance set
runs 9–219 ms. The dental app serves the same corpus over HTTP (200, 0.15–0.48 s). The replaced tables are kept as
snomed_concepts_old / snomed_descriptions_old / snomed_relationships_old (49 / 428 / 571 MB) for rollback, being
the only copy of the 2026-02 corpus; provenance, hashes and the rollback list are on the
SNOMED page and in /root/work/snomed-20261016/PROVENANCE.md.
3. A load defect from the previous import, found and repaid. 15 snomed_descriptions rows carried embedded CR/LF —
one alone held a 3.7 MB blob — and between them 127,627 embedded newlines, i.e. ~127,627 description records
(~8% of the table) had never been searchable. Cause reproduced on a 200,000-row sample of the new release: PostgreSQL's
default CSV quoting plus a stray " in a term (335 lines of the file) swallows every following line —
34,698 of 199,999 sample rows vanished; loading with QUOTE E'\b' loads all 199,999. The requirement now
travels with note (L), so the coming sixth source cannot repeat it.
4. One hazard raised, two things deliberately left open. TRUD's error bodies echo the API key, and
reference_data.pull.fetch_latest_release stores TRUD's message verbatim into ReferenceFetchAttempt.message — the
field the health surface renders. Nothing has leaked (only the subscribed item is polled; 0 hits across 30 days of
engine logs) and no model persists a URL; raised as note (K). Left open: (a) snomed_descendants is still the
un-rebuilt 257,605 rows and now filters a newer corpus, so note (J) is the next step — the prescription-picker
shortfall is a closure symptom and will not move until then; (b) the corpus is International content only, as it
always was, and the same package carries the UK Clinical Extension (+103,622 concepts / +239,803 descriptions /
+301,352 relationships) — a scope call for Matthew, not an update.
5. Hygiene. from-hermes gained notes (K) and (L) plus an update line on (J) (7 → 9 notes, rebuilt from
the raw body and verified); the SNOMED page was corrected where it was wrong (the corpus was Snapshot, not the Full
files it named; the row counts; the new "one copy, in dsp_clinic, aliases default + sector_gp" measurement) and
carries the import record; round 61 → the log archive (the live page keeps the last two rounds). Working files,
including the COPY-option micro-test that proves the defect, are under /root/work/snomed-20261016/.
6. Space — the rollback tables were dropped the same day. On Matthew's instruction the three snomed_*_old
tables (1.05 GB) and the extracted staging files (942 MB) were removed, leaving the corpus at 1,070 MB
of tables and the database host ~2 GB lighter; the release archive is kept, so the load stays reproducible.
Recorded on the SNOMED page so nobody looks for rollback copies that no longer exist.
Still open. (A1) the pharmacy client-maintenance port; (A3); (A5); all of (F); notes (K),
(H), (I) with OpenCode plus (L)'s wiring and (J); C4 — the post-window DSPT evidence note after
Sunday 2026-10-18 02:00–04:00; the client pharmacy database password rotation. The parked SNOMED corpus
release is no longer parked — imported and live. Phase 4 still waits on Matthew naming it.
No phase advanced. Matthew opened with "let me know when you're ready" and worked the three follow-ups the
previous session had queued, in order, one at a time.
1. The installer re-run (follow-up 1) — and a third defect, same class as the two before it. Both installed boxes
were re-slotted from the 483c399 tree (dry run first; each box's own /etc/veripath/client-patch.env supplied the
credentials, values byte-identical before and after, nothing printed): canary 02:23 / 02:53 / 03:23,
test-client 04:07 / 04:37 / 05:07. The resume note's premise that the GP box's hand-fix already read
04:07/04:37/05:07 was wrong — measured, all three were 04:07, so it needed the re-slot as well. Verified per box:
one OnCalendar with the reset line, four timers with one next run each, env 0600 and round-trip clean, all other
units byte-identical to the pre-run snapshot, and the re-slot's inventories landing as records 44/45. New
finding — note (H): client-health.service is static (no [Install], no symlink, no other unit pulls it in),
so the post-boot self-heal never runs; the journal on both boxes shows only the installer's own start. Left for
the code (the fix is WantedBy=multi-user.target plus an idempotent installer re-run) rather than hand-patched.
2. The figures (follow-up 2) — seven re-shot from the deployed app and published on both surfaces. Rendered in
the deployed container against the real test-client tenant, with the search dropdown populated by the app's own JS
over payloads captured from the real endpoints, then cropped and published: the wiki assets 281–288 (byte-verified
over the public URL) and the site's seven image bytes (a targeted copy, after proving the live content equalled
the current build). Sizes sit within ~20% of the published ones and both surfaces needed no embed edit. The
re-shoot is what the fixes made possible: the symptoms search now leads with Nausea (finding), the 215-character
note wraps, and the debug console is absent because it is gated on DEBUG — nothing removed by hand.
Verified by OCR against the published figures (same screens, same entities). Finding — note (I): the note form's
coded heading never gains the side or severity (rebuildTemplate() refuses the rewrite once the line starts with
the chosen term; measured, the attributes are stored and the heading omits them).
3. snomed_descendants (follow-up 3) — measured, then decided. The closure is 36% short of its own
hierarchy: 93,909 missing pairs, worst in the product root (49% missing); the IS-A walk is a strict
superset and returns in 1.5 s, the corpus is held once in sector_gp, and the rebuild is ~40 MB against
today's 29 MB. Measured cost to users: the prescription picker offers 55 of 121 amoxicillin, 124 of 316
insulin, 85 of 200 morphine; the diagnosis search is short the same way (diabetes 631 of 837); and
heart attack gains a leading cardiac concept under the clinical-note hierarchy with no regressions on the other
terms measured. Decision (Hermes, on Matthew's delegation): the closure rebuild goes ahead — independent of any
release import, lossless, and the script is a step a later import re-runs (note (J); the corpus release is
parked behind SNOMED CT UK Edition becoming the sixth source of the central reference routine, which already
pulls dm+d weekly from TRUD with a release register, per-release licences and staleness alerts). Baseline
recorded: the corpus is the 2026-02 release, loaded by hand, with no release identity recorded anywhere.
Written up in three places: the SNOMED page (the measurement and
the dated decision), the reference-data plan §5.4
(the sixth source) and note (J).
4. Records and hygiene. The figure pipeline (/root/work/figshoot/: harness builder, crop measurement, the
captured SNOMED payloads) and the three record-writer scripts (session86-*.py) are on disk; the seven replaced site
images are backed up at /root/work/figs-backup-20261016/, and the pre-re-slot agent state on each box at
/root/agent-snapshot-20261016/. No inbox archive pass was needed — the inbox is 30 KB / 7 notes and gained only
the three notes this session; session 83 → the resume archive and round 60 → the log archive in this pass
(the resume keeps three blocks, the log two rounds).
5. Still open. (A1) the pharmacy client-maintenance port — the last step of Matthew's 2026-10-15 sequence;
(A3); (A5); all of (F); notes (H), (I), (J) with OpenCode. Owed: C4 — the
post-window DSPT evidence note after Sunday 2026-10-18 02:00–04:00; the client pharmacy database password
rotation; OpenCode's the pooler's client credential and the dental canary's agent credentials. The parked SNOMED
corpus update is next after the follow-ups (Matthew's line: "deal with this once we have finished with all of the
follow-ups … it should be quite straight forward as we have all of the other datasets being updated regularly
now"). Phase 4 still waits on Matthew naming it.
| Phase | Scope | Status |
|---|---|---|
| 1. Archives | demo-run reports, ephemera | DONE 2026-08-11 |
| 2. Agents | agents/ tree |
DONE 2026-08-11 |
| 3. Root strays + en/ | root-level pages, en/ prefix |
DONE 2026-08-11 |
| 4. policies + compliance + security | DSPT consolidation, incident trees, home/ strays |
IN PROGRESS — section A (incident trees) DONE 2026-09-21; section B re-scoped 2026-09-27 (Round 18): a content question first (three policies/ pages carry pasted model reasoning; the authoritative text is the website's content/policies/*.md), the archived-copy marks DONE |
| 5. tasks/ | incl. the IXG pages mixed in | pending |
| 6. guides/ | clinician dup tree, AES Guide, dental_app_guides vs work_processes, SNOMED | pending |
| 7. development/ | deep-dive — multiple sessions, content-first review | IN PROGRESS — the pharmacy_app section (sub-directorisation) done 2026-09-26; the rest of development/ untouched |
| 8. infrastructure/ | deep-dive — SIAAS comparison pages, case/underscore cleanup | pending |
| 9. Metadata drive | descriptions (~134 missing), tags, naming normalisation | pending |
Maintenance passes (not phases): 2026-08-25 token split of this page; 2026-09-21 prune pass — log split, 15 demo-run reports archived, 4 stubs deleted (see Round 7); 2026-09-21 handoff inbox archive pass (see Round 8, 8b); 2026-09-27 handoff inbox archive pass (24 notes, see Round 16) and the wrapper-artefact repair on the four pages the section-B audit named (see Round 16, item 2); 2026-10-01 handoff inbox archive pass — 24 notes (13 off from-hermes, 11 off from-opencode) and the first pass into 2026-10 month pages (archive/2026-10/*), so the September pair (now ~364K / ~260K) stops growing; see Round 26; 2026-10-01 (session 48) — one inbox note closed in place (nothing archived, no month-page growth) and one page amended where its spec contradicted the build; see Round 27; 2026-10-01 (session 49) — one new build spec with its mockup approved the same day, the first of the four banner-owing research pages marked, and the build brief left in OpenCode's inbox; see Round 28;
2026-10-02 (session 50) — A8, the bookkeeper handover pack, written and verified live; the guides index, the
phase-6 record and the app overview updated with it; one inbox note opened; the resume note pruned (198 KB → 39 KB);
the research transcripts archived with every live link repaired; and the satellite given its own sub-directory; see
Round 29; 2026-10-02 (session 51) — OpenCode's three handbacks verified live and accepted (the accounting nav
entry, the dental Redis URLs, the ERPNext desk fix); the A8 pack and the Phase 6 record amended so neither carries a
caveat that is no longer true; the order-file header drift closed across five pages; C6's stale figure re-shot and
the handover pack illustrated; and the dental Notify-key brief issued; see Round 30;
2026-10-02 (session 52) — the parked C3 item recovered and corrected, the same drift closed on C2, C4 and
C5, B4/B5 finished (their pending corrections had already landed and been deployed), and the adjustment-refusal
defect authorised by Matthew and briefed to OpenCode; seven figures re-shot against the live tenant; see Round 31; 2026-10-06 handoff inbox archive pass — 25 notes (15 off from-hermes, all 10 off from-opencode) and the resume note trimmed 43 KB → 17 KB (sessions 52–56 archived); see Round 32.; 2026-10-13 (session 76) — the whole-wiki wrapper sweep (628 pages enumerated, 126 artefact lines removed from 33 pages, and the tool defect that had eaten two sections fixed), the from-hermes inbox archive pass (6 notes into the new archive/2026-10/from-hermes) and the resume trim (sessions 71→67; 26 KB → 6.5 KB); see Round 51. 2026-10-14 (session 78) — the from-hermes inbox archive pass (13 closed notes into archive/2026-10/from-hermes; the live inbox 101 → 57 KB, 27 → 14 notes) and the resume trim (sessions 73–72 archived; 18 → 14 KB); see Round 53. 2026-10-14 (session 79) — the rig's front door opened and made realm-native only (the realm brokers partners, so a partner identity would have been accepted; a host-level nativeOnly guard now refuses brokered logins before a session exists), the gate process put under veripath-auth-server.service (it was an unsupervised nohup orphan), and the rig's operating guide published; see Round 54. 2026-10-14 (session 80) — the client-VPS patching rulings recorded (12 ruled, the commercial item left open) and three plain-English companion pages published under infrastructure/SIAAS/client_vps/; see Round 55. (session 81) — no pass; the session's own subject was the dental Client VPS access question and the Sunday rotation window's stale premise, and both its inbox trigger and the log's were measured and left (see Round 56). 2026-10-15 (session 82) — the from-hermes inbox pass (1 HANDLED note archived; the page's 2026-10-06 reading-order index retired and replaced by a preamble at the top, because the newest-on-top prepends had pushed the index mid-page — 100,308 → 93,451 chars, 23 → 22 notes) the resume trim (session 78 archived; 25,384 → 19,187 chars) and the log's own prune (round 55 moved to the archive log, so the live page keeps 57/56); see Round 57. 2026-10-16 (session 85) — the from-hermes inbox rebuilt as one verified queue: 30 notes audited against the deployed estate, 12 closed with evidence, 18 carried into six queue notes; the live inbox 137 KB → 24 KB (30 → 6 notes) and the archive 89.5 KB → 239 KB (30 notes verbatim); see Round 60. 2026-10-16 (session 86) — the three follow-ups discharged (round 62): the installer re-run on both client boxes, seven figures re-shot from the deployed app and published on the wiki and the site, and the SNOMED closure measured (36% short) and decided (rebuild now; the corpus update parked behind the TRUD route). No inbox archive pass was needed (30 KB / 7 notes); session 83 and round 60 moved to their archives.
Rounds 6–49 moved 2026-10-13 (session 76); rounds 50–54 on 2026-10-14 (sessions 78–80); rounds 55–59 on 2026-10-15/16 (sessions 82–85), rounds 60–61 on 2026-10-16 (session 87) — the log regrew the
same way, so the trigger is the same: prune when the current round stops being the one you see first, or when
the page passes ~35 KB. Round 54 went across on the same day for the size reason alone: Round 55 is a whole
day and carries every live item forward. Newest first: — [housekeeping-log-archive]
(/en/agents/hermes/housekeeping-log-archive). Archive-not-delete; the live page keeps the last two rounds,
the phase tracker, the open items and the conventions.
Earlier rounds (1-5) and the dated session handovers 2026-09-02 → 2026-09-15 (dental charting/perio/radiographs/finance, pharmacy phases 1-6) were pruned off this page on 2026-09-21 and prepended to the archive — same content, archived not deleted, so this page stays small (it is re-read at the start of every housekeeping session).
→ agents/hermes/housekeeping-log-archive (newest first; pre-2026-08-25 entries follow the moved block).
CURRENT (2026-10-16, round 63 — after session 87): the SNOMED corpus is UPDATED AND LIVE (2026-02 → International RF2 Snapshot 20260801; 535,233 / 1,716,441 / 3,580,370; heart attack under the
clinical hierarchy now leads with Myocardial infarction (disorder) [22298006]), with ~127,627 descriptions
the previous import had swallowed restored, and the replaced tables kept as snomed_*_old for rollback.
Open: snomed_descendants (note (J)) is the next step; notes (K) (the TRUD key echo), (L) (the
sixth source and its COPY-quoting requirement), (H)/(I) with OpenCode; C4 after Sunday 2026-10-18; the
client pharmacy database password rotation; (A1)/(A3)/(A5) and all of (F). Awaiting Matthew:
whether the corpus should also take the UK Clinical Extension (+103,622 concepts). The *_old
rollback tables were dropped the same day on his instruction (space; see round 63 item 6).
SUPERSEDED 2026-10-16 (round 63) — round 62 record: the three follow-ups were DONE. The installer re-run on both
installed client boxes (the code's own drop-ins, patch/reboot/inventory on three distinct minutes; new defect,
note (H): client-health.service is static and unwired, so the post-boot self-heal never runs); the seven
figures re-shot from the deployed app and published (wiki assets 281–288 byte-verified; the site's bytes by a
targeted copy, repo files updated and uncommitted); and snomed_descendants measured 36% short (93,909 pairs;
product root 49%) and DECIDED — the closure rebuild goes ahead as note (J), the corpus release is parked
behind SNOMED CT UK Edition becoming the sixth source of the central reference routine. Still open: (A1) the
pharmacy client-maintenance port, (A3), (A5), all of (F), and notes (I)/(H)/(J) with OpenCode; the
C4 DSPT evidence note (owed after Sunday 2026-10-18 02:00–04:00) and the client pharmacy database password
rotation are mine.
The handoff queue — REBUILT 2026-10-16 (round 60), DISCHARGED to one decision (round 61): 30 notes audited; 12 closed; 18 carried into six queue notes; OpenCode answered C, A2/A4, B, D and E; Hermes verified each on the working trees (13 / 22 / 693 tests reproduced; installer slots reproduced) and closed B/C/D/E, narrowing (A) to A1/A3/A5; (F) untouched. SUPERSEDED — deployed and live-verified 2026-10-16, and the three follow-ups it left are done (round 62). Historical entry below. 30 notes audited against the deployed estate, 12 closed with evidence, 18 carried into SIX queue notes (A–F) on from-hermes.** The inbox is now the live queue and nothing else waits anywhere: (A) the client-VPS patching surface — test-client-pharmacy is the pharmacy port (A1), the last step of Matthew's 2026-10-15 sequence; (B) the agent/installer; (C) the rotation window, time-boxed to before Sunday 2026-10-18 02:00–04:00; (D) pharmacy counter/notifications; (E) GP clinical/record/integration; (F) scaling, front door, pooler — plus two decisions that are Matthew's.
Client patching surface — FIXED, DEPLOYED AND VERIFIED (2026-10-15); three residuals OPEN (carried as queue A2/A3). The gate now admits
the client's own manager/admin and keeps VeriPath staff out of the client surface; verified on the deployed apps in both
sectors, every isolation check passing (round 58; note 20 on from-hermes). Residuals: a
client tenant whose own admin is a superuser still gets the GP fleet dashboard; the dental dashboard has no gate;
the dental MaintenanceRecord mirror is missing the real table's source. Still open: no client box has cadence
rows, so no box's "next run" exists in the app.
Matthew's sequence (2026-10-15): backend /infrastructure/maintenance/ → test-client →
test-client-dental → test-client-pharmacy, one at a time; then documentation + DSPT policies.
Backend gaps measured (not yet written): client boxes absent from the host filter and from
_maintenance_status_strip; box runs do appear in the list (today's canary inventory is its newest row).
Rotation window — Sunday 2026-10-18 02:00–04:00: OpenCode's pack is rehearsed, the 85 → 0 baseline
reproduced; queue note (C) must land its scan-scope fix before the window (the pooler's config directory is
invisible to the pack's scan roots); the dated DSPT evidence record is owed after it, and two further
credentials from 2026-10-15 disclosures are owed rotation (the client pharmacy database password and the dental
canary's agent credentials). The pooler's own client credential rotation
stays its own item (my 2026-10-14 disclosure).
Swap residuals: the dental box's MinIO decommission (its own backup-cycle gate), the surplus local MinIO
image (awaiting Matthew's confirmation), and the orphan patient-images bucket (undecided). WO-D's docs half
and WO-M7 are done (2026-10-15).
Dental web-content drafts → pointer pages — DECIDED 2026-10-06, do it in ONE pass once the whole dental set is published.
Once a marketing page is published, the site copy is canonical (the client edits it in the CMS) and the wiki draft is
surplus — so the end state for each published draft is a short pointer (title, live URL, published date) that keeps
the page, its path and its Wiki.js comment thread but drops the duplicated text; archive the tree once the whole set has
shipped. Every published draft already carries a dated > **LIVE on the website** marker with its live URL — that marker
is the worklist for this pass. The durable wiki artefacts are the plan page, the style decisions and the client's review
comments, never a second copy of the copy. Method: veripath/marketing skill.
agents/handoff/hermes-resume prune — RESOLVED (2026-10-13 session 76; re-done 2026-10-14 sessions 78/80
and 2026-10-15 session 82 — rounds 51, 53, 56 and 57). The rule now lives on the resume page itself, where it
belongs: keep the three newest session blocks plus the cold-start order, and the session that writes a
block is never the one to cut it. Trimmed blocks move verbatim (never edited in transit) to
agents/handoff/hermes-resume-archive.
Dental marketing holds (2026-09-02) — wiki marketing/web-content/dental_surgeries/
holding notes on all undrafted pages (PWA, dentistry x3, financial x2); each records
verified facts + reopen conditions. PWA: patient portal mixes GP+dental, separation
needed (handoff OPEN to OpenCode 2026-09-02). Dentistry (charting/perio/radiographs):
charting ROUND 1 IMPLEMENTED + deployed 2026-09-02 (feature/dental-charting,
commit d9ceceba) per plan development/projects/dental_app/charting_plan;
user tested, Q1.1/Q1.2 RESOLVED (appointment dates = anchor; Appointment
field MANDATORY on chart create). R2 + R3 IMPLEMENTED + deployed 2026-09-02
(feature/dental-charting-r2 12112715 incl. migration 0020 NOT NULL;
feature/dental-charting-r3 43f1008f: dental/rbac.py + note-detail view +
openable patient-record rows, 6 tests). Charting doc pass DONE 2026-09-03 (3 manuals published wiki + manuals incl. NEW
clinical-notes guide; screenshots regenerated faithfully). STILL QUEUED:
role-permissions-matrix + walkthrough_test_users RBAC sections (R3 handoff).
Perio + radiographs still awaiting Matthew's testing. Financial (fp17/accounting): awaiting ERPNext
integration + per-treatment charging. Drafted so far: triage + emergency-slot-management
(patient_management).
Stale SSR cache after wiki upsert — RECURRING (2026-08-19, 2026-09-02, 2026-09-02 x2):
development/projects/dental_app served an 08-01 payload after a fresh
upsert, and marketing/web-content/dental_surgeries/patient_management/triage
served its old "Your content here" body after a 2026-09-02 upsert, even
though both mutations returned OK and the DB rows were correct. Fix both
times: docker restart wikijs. Convention (updated): after any upsert,
curl the live /en/<path> URL and diff the RENDERED BODY (not just the
wrapper updated-at attribute — that updates even when the body cache is
stale); if the body is stale, docker restart wikijs and re-verify. Flagged
to OpenCode 2026-08-19 as a tool gap (cache invalidation after render
mutation) — still open; recurring twice in two weeks makes it worth a real
fix (e.g. invalidate the page's SSR cache entry on upsert).
--prune feature for empty folder nodes — nameless (ARCHIVED) root
node, Client Admin Guide/, empty Incidents/ folder. Noted to OpenCode 2026-08-11.
home/ section is real (Wiki Overview + partner-user-management plans +
wireguard-vpn-setup) — needs its own tidy. home/partner-user-management-plan
has an EMPTY title and looks like a duplicate of
development/projects/gp_booking_app/partner_client/partner-user-management-plan.
System-prompt mirror divergence — agents/opencode/system_prompt_opencode
vs archived root system-prompt-opencode; OpenCode to refresh from /root/AGENTS.md.
Secrets sweep needed — plaintext DB password in
development/local-agent-instructions; API-token mentions in
development/vps-response-to-local-agent. ROTATE, don't just redact.
(2026-08-18: also redacted a client-DB postgres superuser password from
development/projects/dental_app/dental_app_erpnext_plan A1 — rotation
recommended, flagged to OpenCode.)
2026-09-26 — a third instance, and the reason this should be a sweep of
every page rather than page-by-page: infrastructure/backups/quarterly-restore-drill
carried the central PostgreSQL password as a literal in six shell commands.
Redacted (the page now reads it from the host-local 0600 config the backup
script already uses) and verified against the rendered page; the container name
on that page was also stale. Rotation is still owed — the value was readable
to anyone with wiki access and survives in page history, so it is disclosed.
Runbook: infrastructure/central-db-password-rotation. Recorded on
Disk Capacity Review — VPS1.
Two of the three known instances sat on live operational pages, not drafts.
compliance/ root vs infrastructure/compliance/ duplicates — Phase 4.
SIAAS 6-way comparison pages (siaas_intro, siaas-vs-cloud, cloud-v-siaas,
cloud-v-siaas-2, price_comparison, cost-savings) — Phase 8.
~134 pages missing descriptions — Phase 9.
VPS2-reference docs sweep — ON HOLD (2026-08-16): Matthew chose
Option A — restore the 3-VPS split (apps back on VPS2); Hermes review
given 2026-08-16 (agrees; see agents/handoff/from-opencode). If Option A
proceeds, update infrastructure/SIAAS/client_vps, dental_onboarding,
AGENTS.md + bring-up.sh wording to the restored 3-VPS topology (plan
step 5) — NOT single-primary (avoids double work). Only if Option A is
dropped, do the single-primary sweep (one page at a time, per user rule).
archive/ remains searchable.--archive original.--move/--archive have a folder-drag hazard — ALWAYS --list source ANDhttps://wiki.veripath.co.uk/archive/... URLs.