NEXT SESSION STARTS HERE (2026-10-19, after session 90). Standard 1 is half done: the IAR exists and one policy is rewritten and deployed. Pick up at the policy queue in item 3.
1. Session 90 did five things, all verified. (a) DSPT 6.2.1 anti-malware closed — ClamAV 1.5.4 with a weekly
staggered scan on all three hosts (39e54ef); first runs CLEAN (Primary 50,859 files / 1.74 GiB / 6m49s, 1.0 GB peak
inside the 1.5 GB cap).clamav-freshclamships DISABLED — enabled on all three, or signatures stop updating at the
next power-up. (b)gitlab-api-proxyretired (bae8a0b) — it was broken, carried a hardcoded dead Forgejo PAT in a
world-readable file, ignored the caller's token, and nothing used it; the/api/v4/and/login/oauth/access_token
nginx locations are gone. (c) Host-log rotation added (b9a7afd) — none of our own logs were under logrotate; the
deploy log had reached 214 MB. Explicit file list, never/var/log/*.log. (d) The IAR was written (see item 2).
(e)dsp_clinicrenamedveripath_platform(4b0abaa) with every live consumer updated.2. Standard 1's true position, measured — and this is the part that matters. 1.1.5 (SIRO) present. 1.1.2 answered by
the IAR, which had to be built because the
page the workspace map counted as the ROPA is a pasted AI conversation with an unfilled template. The IAR's headline:
the estate holds workforce personal data, not patient data —veripath_platformis 14.2 M reference rows (SNOMED +
dm+d) against 82 K app rows with no patient data; patient data is on client VPS instances only. Both renamed databases
carry a catalogue comment stating that rule. 1.3.13 premises controls still needs facts only Matthew has.3. Tomorrow's queue, in this order. (a)
business-continuity-plan-bcp— same AI-paste problem, undated, blocks
Standard 7; rewrite likeunsupported-systemsand it unblocks a whole standard. (b)accountable-suppliers—
undated, blocks Standard 10. (c) The two pages still carrying[Your Organisation Name]:
data-security-staff-acknowledgement-record,staff-confidentiality-agreement-acknowledgment-procedure— mechanical
fills, no judgement needed. (d)data-processing-activitiesneeds a DECISION, not an edit: replace with a real ROPA
or unpublish. (e) Then premises controls (1.3.13), then Standards 2–3 (induction 2.1.1, training records 3.2.1 — both
"not found", the people gaps).4. Deployed today, awaiting Matthew's read:
8. Unsupported Systemsrewritten as a real policy and live (site
deployed 18:20,b902625) — verified on disk at/var/www/veripath/internal/policies/unsupported-systems/. It names the
genuine exceptions (MailHog unmaintained, Wiki.js 2.x accepted risk, PostgreSQL 15 Nov 2027, Python 3.11 Oct 2027) instead
of claiming everything is supported. He is reading the five remaining policy pages in his own time — do not pre-empt
his edits.5. SIRO decisions taken 18 Oct — recorded on the IAR (D1/D2/D8), do not reopen. Password rotation declined: the
shared superuser password sits in 12 live config files (27 with backups) and — worse — the same value is the Redis
password in three stacks; recorded as an accepted risk, with a staged per-service-role plan (no rotation needed, one app
at a time,keycloakandwikijsalready show the pattern) available whenever he wants it.dsp_clinic_devleft as is
(in use as the GP stack'sCLIENT_DB_NAME, demo rows).clinic_vaultidentified — encrypted append-only Borg repo of
our own dumps, not client data; needs only retention + a restore test, and it is the estate's one encrypted-at-rest store.6. Handoff left for OpenCode (
agents/handoff/from-hermes, 2026-10-18, OPEN): the DB rename lands in their lap —
any local config hardcodingDB_NAME=dsp_clinicwill fail;dsp_clinic.settingsis the settings package, not the DB.7. Two of my mistakes, so they are not repeated: I printed the DB passwords, and part of a Borg repo key, into the
session while "masking" config output. Never display a config file however masked — parse it and print only the keys
asked for. Also: I quoted "27 files" before checking, when 15 of them were backups.8. Still open from earlier sessions, unchanged: C4 (the post-window DSPT note) cannot be written — the 02:00–04:00
rotation window on 18 Oct did not run, so it needs rescheduling; the four incident decisions await SIRO sign-off; the
snomed_*_oldretention question is unanswered;/root/forensics(880 MB, 14 Aug incident material) has no disposal rule.ADDENDUM (2026-10-18, session 90) — DSPT 9 / 6.2.1 anti-malware is now CLOSED on all three hosts. The block
below is still the next work (DSPT Standard 1); nothing in it was invalidated.1. What the power cut actually interrupted. Not the install. A session was live 13:04–13:58 and died at
13:58:38 on signal 1 (the SSH dropped; the VPS never lost power — uptime is 17 days). It had already deployed
ClamAV to all three hosts and installed the Primary last, at 13:50:24;dpkg --auditwas empty and the real scan it
launched at 13:52 completed at 13:59, after the cut, and wrote its state file — only the agent never saw the
result. Lesson: check the artifacts before assuming a dead session left breakage. What actually needed doing was
the evidence page and the ops commit, not a repair.2. The control.
clamav-weekly-scan.timeron all three hosts, scope = only paths where content we did not write
lands (wiki uploads, Orthanc DICOM, ERPNext sites,/tmp, backups), staggered Sat 05:00 / 05:40 / 06:20 UTC —
deliberately clear of the Sunday security window (Trivy 03:00, nmap 03:30, Lynis 06:30; the original comment said
"Sat 03:00" and was wrong).nice 15+MemoryMax=1536M; measured peak 1.0 GB, 6 m 49 s, 50,859 files, CLEAN;
app engine and auxiliary CLEAN.clamav-freshclamships DISABLED — enabled on all three, or signatures stop
updating at the next power-up. Reports 120 days in/var/log/clamav, state in/var/lib/clamav-scan-last.json.3. Done: Standard 9 summary re-issued (§A
rewritten, verdict table corrected, old "no AV engine" text gone — verified in the served HTML after a wikijs
restart); ops committed as39e54ef(unpushed; the branch was already 2 ahead) withclamav/README.mdcarrying
the per-host install map. Open for Matthew: the SIRO decision log should record the implement-not-accept choice,
or 6.2.1 is evidenced but not governed.4. Two things found on the way.
logrotatehad been failing every night since 26 Sep — one stray
/etc/logrotate.d/rsyslog.bak-20260926with duplicate entries aborted the whole run, so nothing rotated for three
weeks; moved to/root/work/logrotate-stray-rsyslog.bak-20260926, service now succeeds. And/var/logstill holds
unmanaged logs with no rotation rule at all —veripath-deploy.log(214 MB) andauto_no_show.log(28 MB, +5 min
cron). Both are now under rotation — commitb9a7afd(item 7).5. Housekeeping: stale SSH session from the cut held
127.0.0.1:11434(an Ollama forward) and every reconnect
failed to bind — the port is Ollama's, retired; the client still forwards it. Kill the orphan session, and drop
RemoteForward 11434from the client config.
6. HANDLED 2026-10-18 — the systemd "nobody" warning led to a retired service carrying a credential. Chasing
gitlab-api-proxy.service(Node, GitLab-v4 → Forgejo-v1,0.0.0.0:3050) turned up four things, and the answer was
remove, not harden: (i) broken —FORGEJO_HOSThardcoded to the pre-move bridge address, so every call failed
EHOSTUNREACHand/api/v4/*returned 500 with the internal address echoed inerror_description; (ii) a
Forgejo PAT hardcoded in a world-readable/optscript, withfetchForgejo()taking the caller's token as
_tokenIgnoredand always authenticating with that PAT — so the front-door auth gate meant nothing; (iii) unused —
all retained access logs hold 5 requests to/api/v4/*(3 scanner probes, my 2 curls) and 1 to
/login/oauth/access_token, against 542 to/api/decap-proxy, and the CMS backend here isproxy, not gitlab;
(iv) the PAT is dead — proven with a control: 401 where an anonymous request gets 200, identical to a garbage token.Done: unit disabled and removed,
/api/v4/and/login/oauth/access_tokenlocations dropped from the
git.veripath.co.ukvhost (those paths now fall through to Forgejo:/api/v4/*→ clean 404), script archived
0600at/root/work/removed-gitlab-api-proxy-20261018/and deliberately not committed. Ops commitbae8a0b
(retired/gitlab-api-proxy/README.mdcarries the whole story). Verified: git root 200,/api/v1/version200,
/api/v4/version404 with no address in the body,/api/decap-proxy302 unchanged, no listener on 3050, and zero
"Special user nobody" warnings after adaemon-reload+ nginx restart. No action left — if a GitLab-compatible
API is ever wanted again, the README says what to do differently (env file, forwarded caller token, loopback, dedicated
user).
7. HANDLED 2026-10-18 — our own logs had no rotation at all; token estate inventoried.
logrotatecovered only
the distro and nginx files: none of the logs our crons and deploy scripts write were under it.veripath-deploy.log
stood at 214 MB andauto_no_show.logat 29 MB (written every 5 min). Added/etc/logrotate.d/veripath-local
— explicit file list, never/var/log/*.log, because a glob overlaps the distro/nginx rules and logrotate aborts on
duplicate log entry, which is precisely the failure that had been silent since 26 Sep.copytruncatethroughout (every
writer appends with>>, none can be signalled to reopen). Verified: service exit 0, no duplicate errors, deploy log
→.1.gzat 1.4 MB (from 214 MB),auto_no_show.logpicked up 663 new bytes minutes later, so the cron writer
survived;/var/log1.4G → 1.1G.The journal was already capped — correct the instinct.
99-veripath-cap.conf(26 Sep) had setSystemMaxUse=1G,
which is exactly why it sat at 1.0G in 128 MB archives. I briefly added a second drop-in setting the same limit and
removed it, merging into the existing file instead:SystemMaxFileSize=64M+RuntimeMaxUse=128Mwere the only
real additions. One file, one limit — two sources of truth for one value is how it gets misread later. Journal now
924.8M; both configs tracked in ops (b9a7afd) because neither was version-controlled before.Token estate (13 tokens, no orphan). Nothing corresponds to the retired proxy — its PAT is dead and absent from the
table. The live CMS path isveripath-cms-proxy.service(→/var/log/decap-proxy.log, now rotated) using
cms-proxy-2026-10-06, scopewrite:repository— correctly repo-scoped, in a0600EnvironmentFile
(/etc/veripath/cms-forgejo.env, not readable bynobody). The retired service was the outlier, not the norm.
Candidates Matthew could revoke in the Forgejo UI:nuxt-studio-proxy-v2(last used 11 Sep, superseded by the CMS
proxy on 6 Oct) andverify2(test token, never used). Several others showupdated == created(never used since
creation) — plausible, since agent and deploy pushes all go over SSH keys, not tokens — but I did not delete
credentials on a heuristic.New finding, not fixed:
veripath-cms-proxy.serviceruns asrootand binds0.0.0.0:3052. It is at least behind
nginxauth_request, and its token is properly scoped — but a root Node service holding a repo-write token is the same
shape as what item 6 retired. Not a tweak: a dedicated user cannot traverse/root(0700), so the code has to move out
of/root/work/veripath-site/server/first. That is its own job.
8. 2026-10-18 (later in session 90) — DSPT Standard 1 opened, the database renamed, and three SIRO decisions recorded.
Standard 1's real position: 1.1.5 (SIRO) is present; 1.1.2 was not "partial" as the workspace map claimed — the page it
counted as the ROPA (data-processing-activities) is a pasted AI conversation containing an unfilled template
([Your Organisation Name]), published since May with an effective date. Same artifact in three published policies
(that one,business-continuity-plan-bcp,unsupported-systems— the latter two also the two undated ones). Two more
published pages carry[Your Organisation Name]:data-security-staff-acknowledgement-record,
staff-confidentiality-agreement-acknowledgment-procedure. That cleanup has NOT been done — it was proposed and not yet
approved. 1.3.13 (premises) still needs facts only Matthew has.The IAR exists now at information-asset-register,
measured not copied. Its headline is the correction that matters: the estate holds workforce personal data, not patient
data — patient data is on client VPS instances only. Evidence:dsp_clinicwas 38 reference tables / 14.2 M rows
(SNOMED + dm+d) against 121 app tables / 82 K rows, with the only patient-shaped table holding 2 rows.dsp_clinic_dev
(theCLIENT_DB_NAMEthe GP stack uses in three places — in use, not obsolete) holds 90 demo rows.Renamed
dsp_clinic→veripath_platform(commit4b0abaa), because the name is what made it misread. Every live
consumer updated: three app.env(DB_NAME,GP_DB_NAME), celery start scripts, env template, pgbouncer[databases]
regenerated from the tenant register, and the security check-in config + fallback. Do not "fix" references to
dsp_clinic.settings— that is the Django settings package, not the database. Verified: three apps healthy,
/admin/login/200 on 8000/8010/8020, six celery workers, zero connections to the old name, check-in job clean.
Bothveripath_platformandveripath_dentalnow carry a catalogue comment stating the no-patient-data rule.SIRO decisions taken 18 Oct, recorded on the IAR (D1/D2/D8) — do not reopen: (i) password rotation declined;
the shared postgres superuser password sits in 27 config files across several stacks, rotation is a coordinated
change not a swap, and the exposure was contained (transcripts live in/root, 0700) — recorded as an accepted risk,
with per-service DB users named as the durable fix; (ii)dsp_clinic_devleft as it is; (iii)clinic_vault
identified — an encrypted append-only Borg repo of our own nightly dumps (DefectDojo dominates at 1.15 GB/day),
needing only a retention rule and a restore test. It is the only encrypted-at-rest store on the estate: cite it under 9.5.2.Also found, unresolved:
/root/forensicsholds 880 MB of 14 Aug incident material (attacker account list, 253 repo
copies, a DB dump) with no disposal rule (IAR D5); retention periods for the workforce stores are still proposals (D6);
a daily guard against patient-shaped tables gaining rows was proposed but not built (D7).Two of my own mistakes, recorded so the next session does not repeat them: I printed the DB passwords, and part of a
Borg repository key, into the session while "masking" config output. Never display a config file, however masked — parse
it and print only the keys asked for. Rotation was declined, so those values remain as they are.SUPERSEDED by the block above — PREVIOUS POINTER (2026-10-18, after session 89) — DSPT Standard 1 is the work; the Reference Data defects are fixed and deployed, the wiki sidebar is repaired, and VPS1 is at 53%.
1. Do not write C4 yet, and do not assume the window happened. Measured this session: the pack at
/root/rotation-20261018is untouched (nobackup-*, nofinalize.sh,old-valuestill the rehearsal placeholder)
and the read-only verifier returns 87 literal hits against the 85 baseline, with the retired literal still
authenticating over TCP aspostgres,fh_app_userandwikijs. C4 needs a re-scheduled window — Matthew's
call. That window must still regenerate the pooler's[databases]entries afterwards.2. The feed defects are closed — deployed and verified,
5d64c4f. Note (19) is HANDLED: the TRUD monthly rule
now dates an edition from its own publication (SNOMED reads Current, next 23/10, overdue from 28/10); the CPE
source reads Signal only — prices arrive with the NHSBSA notices and its overdue state comes from the notices
release, so the false 20/10 alert cannot fire; release 4 is discarded with its reason recorded; summary is "All
configured feeds are current" with zero action alerts. Two things worth not re-learning: the new 03:30 SNOMED pull
decides "already held" on the publisher's release id, and item 101's id is the archive filename
(uk_sct2cl_43.0.0_20260923000001Z.zip) — not the release name; and the hand-imported release carried no
provenance, so without the backfill the first scheduled run would have pulled 1 GB and re-imported the corpus
unscheduled. The routine is proven:pull_reference_release('snomed_uk_clinical')→already_held, no download.3. Then DSPT Standard 1 is the next substantive work (Standard 6 closed, SIRO appointed, the estate organised
undercompliance/governance-compliance/). Six items still need new material; the four incident decisions, the
specimen agreement's ten [DECISION] items and thesnomed_*_oldretention question still wait on Matthew.4. The sidebar fault is FIXED (2026-10-18) — one missing row, and it is worth knowing why.
navigation.getTree()
reads the rowkey = 'site'; the database had one row keyeddefaultwithconfig = [], so the resolver warned
(4,490 times in 24 h, pages with no sidebar, since 15 Oct) and returned[]. No API route creates the row
(navigation.updateTreeonly patcheskey='site';updateConfigwritessettings.nav), so it was repaired as a
single direct row write under an explicit one-off exception from Matthew: renamed tositewith the stock shape from
server/setup.js,settings.navleft atMIXED. Verified: 0 warnings in the 60 s after restart, GraphQL
navigation.treereturns the item, a headless-Chrome DOM dump shows the sidebar (Home + Browse), pages 200. Backups
and the applied SQL are in/root/work/(wiki-nav-backup-20261018.sql,wiki-nav-mode-backup-20261018.txt,
wiki-nav-repair-20261018.sql). Future nav edits: use the GraphQL mutation on the existing row — never re-key it.5. Capacity: measured, reclaimed, and the intake is not the problem. VPS1 went 60% → 53% today (122 G used,
111 G free). The whole central reference store is 2.98 GB ≈ 2% of the disk, and every feed's retention
terminates — dm+d keeps live + previous, the SNOMED loader drops*_oldbefore renaming (bounded 1.07 GB),
MHRA rows purge weekly, the CPE mailbox retains nothing, archives live on VPS2. The UK extension cost ~240 MB
once (thegp_booking_pgdump 219 → 303 MB on 17/10, flat since) — read that dump size quarterly, it is the
honest meter for intake, notdf. Reclaimed: 6.1 GB SNOMED extraction staging (archive 945 MB + merged build
kept), 3.0 GB trivy, 1.4 GB npm, then the retired Ollama installation (7.3 GB) on Matthew's ruling — dental's
nomic-embed-textchunk/embedding service, which is live and enabled on VPS2, not here; its 262 MB volume was
kept. Open for Matthew: ~6.7 GB of images with no container (oldpostgres:15/15-alpine/16,pgvector,
forgejo:14.0are pure win;netdata/wiki/keycloak:latestare staged newer pulls, so pruning them means a
re-pull), and whether the defectdojo backup retention drops from 7 days to 4 (~3.5 GB). Never touch the ZAP
image (the monthly DAST cron needs it) or the MinIO rollback image. Round 2 is on
Disk Capacity Review.6. Two habits that hold. A
wiki-managewrite only surfaces on the served page afterdocker restart wikijs—
verify the served HTML, not the tool's "Render: OK", then restart. And wiki reads/writes stay on
/opt/wiki-manage.py; the GraphQL endpoint (127.0.0.1:3002/graphql, key read fromapiKeys) is for the things the
tool cannot do — this session used it forsettings.navand restoredMIXEDunchanged.
PREVIOUS POINTER (written after session 88, 2026-10-17) — SUPERSEDED BY THE BLOCK ABOVE; its window did not run. Read
this first: a maintenance window runs 02:00–04:00 today and C4 is owed after it.1. Tonight's window came first in the calendar and comes first here. Sunday 2026-10-18 02:00–04:00 carries the
client pharmacy database password rotation. After it: regenerate the pooler's[databases]entries (a plain
generate_pooler_configrun now reloads with the admin credential — no bootstrap), then write C4, the post-window
DSPT evidence note. Do not report C4 before measuring the window's outcome.2. Then continue the DSPT review pass — this is where the work is. The estate is organised: everything DSPT lives
undercompliance/governance-compliance/(policies/,evidence/,guides/,registers/, the standards pages and
the workspace map); policy text stays in the website CMS, the
tree holds the policy register. Standard 6 is closed
(one policy, one log, SIRO appointed). Next:
Standard 1, then 2–3, then the three undated policies (Business Continuity, Unsupported Systems, Accountable
Suppliers) which unblock 7, 8 and 10. Six items need new material, not amendments: premises controls, induction,
training records, an IAR, supplier assurance, and the SIRO reviewing administrator work.3. What waits on Matthew. Four incident decisions to sign as SIRO (all proposed "not reportable"); the specimen
agreement's ten [DECISION] items (term, fees, who pays the host, support, incident threshold, backups, transition,
liability, reference rights, law); and whether to drop thesnomed_*_oldtables (1.07 GB, the only copy of the 20260801
corpus).4. Corrections — do not re-derive. The SNOMED closure rebuild was a no-op and the "36% short" figure is
withdrawn; the UK Clinical Extension is imported (638,855 / 1,956,244 / 3,881,039,max(effective_time)20260923,
closure 281,363) and its loader gap (683 revised relationship ids) is with OpenCode as note (18). A session is not
valid across tenant hosts. The frozen MinIO image is protected. The dental client surface was never blocked — a 403
there means the user was read outside the client-tier context. SIAAS posture is per client-vps by role (dev/canary ·
example model · production), and no client-vps is in the no-login posture yet — that is the example-model build.5. Two habits that saved and cost time today. A
wiki-managewrite only surfaces afterdocker compose restart wikijs— verify the served HTML, not the tool's "Render: OK", then restart. And the wiki has been logging "Site
Navigation is missing or corrupted" since at least 15 Oct (6,327 times in 48 h): pages render with no sidebar. That
repair is outstanding and separate.
SESSION 87 (2026-10-16) — the SNOMED corpus is updated, and the question that was opened as "email or something
else?" was answered by measurement: there is nothing to build.Matthew opened with a Gemini exchange (published on the wiki at
development/snomed_updates) asking whether TRUD's email alerts should trigger an
agent, or whether there was a better way — Gemini's answer: don't trigger on email, poll the TRUD API. He added the
observation that we already receive TRUD updates, so the setup may already exist. It does. The central reference
routine already pulls dm+d weekly from TRUD on a configured key; the only open question had been whether the
account could see the SNOMED item.1. Measured, read-only, key never printed: item 101 is already subscribed.
SNOMED CT UK Clinical Edition, RF2: Full, Snapshot & Deltareturns HTTP 200 with latest releaseSNOMEDCT2_43.0.0_20260923000001(2026-10-06,
990,865,460 bytes, published SHA-2564D6F…51DF); 105 (UK Drug Extension) and 1799 (UK Monolith Edition)
return 404. So no new subscription, no licence acceptance, no mailbox tooling, no change of route — the parked
gate in note (J) was cleared. TRUD's guide is the authority on the credential: "Each account has a unique API key,
derived from the account's email address and password", "shown on the Your account page", "Keep your API key
private", and changing either disables the old key. It travels in the request path and in every
archiveFileUrl— which is why TRUD's own 404 body echoed it back, and why note (K) exists (our client stores
TRUD's message verbatim intoReferenceFetchAttempt.message; nothing has leaked — only the subscribed item is polled,
0 hits across 30 days of logs, and no model persists a URL).2. The corpus update landed. Downloaded on the app engine (key stays in the container's environment), hash-verified,
transferred, hash re-verified, then staged alongside the live tables and 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; every count asserted against the source file's own line
count, all 13 index names preserved (idx_desc_term_trgmincluded). Measured through the app's own
snomed_search():heart attackunder the clinical hierarchy now leads with Myocardial infarction (disorder)
[22298006] — the new release gives MI an active "Heart attack" synonym — 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_*_old(49 / 428 / 571 MB) because they are 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. And a defect from the previous import was found and repaid. 15
snomed_descriptionsrows carried embedded
CR/LF — one held a 3.7 MB blob — and between them 127,627 embedded newlines: ~127,627 description records
(~8% of the table) had never been searchable. Reproduced on a 200,000-row sample of the new release: PostgreSQL's
default CSV quoting, plus a stray"in a term (335 lines), swallows every following line — 34,698 of 199,999
sample rows vanished; withQUOTE E'\b'the same sample loads all 199,999. The requirement travels with note
(L) so the coming sixth source cannot repeat it.4. Two things deliberately left open, one scope call for Matthew. The load is International content only — the
recipe the corpus has always used — while the same package also carries the UK Clinical Extension (+103,622 /
+239,803 / +301,352). That is a scope decision, not an update, and it sits with note (L)'s wiring. And
snomed_descendantsis untouched at 257,605 rows: note (J) is the next step.5. Records. The SNOMED page was corrected where it was wrong (Snapshot, not the Full files it named; the row
counts; the "one copy, indsp_clinic, aliasesdefault+sector_gp" measurement) and carries the import record.
from-hermesgained (K) and (L) plus an update line on (J) (7 → 9 notes). Round 61 → the log archive
(the live log keeps rounds 63 and 62). Working files, including the COPY-option micro-test that proves the defect, are
under/root/work/snomed-20261016/.
SESSION 86 (2026-10-16) — the three follow-ups are DONE, and the last of them turned into a decision rather than a task.
Matthew took follow-ups 1–3 in order, one at a time, each closing before the next opened.
1. The installer re-run — both installed boxes re-slotted by the code, not by hand. Dry run first, then the real
run, on both boxes from the483c399tree staged on the box; each box supplied its own
/etc/veripath/client-patch.env, so nothing was re-minted, transmitted or printed, and both secrets are
byte-identical before and after. Canarytest-client-dental(194.164.18.33): patch 02:23 / reboot 02:53 /
inventory 03:23;test-client(77.68.97.101): 04:07 / 04:37 / 05:07 — the resume note's claim that the GP box
already read 04:07/04:37/05:07 was wrong (measured: all three were 04:07), so it needed the re-slot too, and the
pharmacy row shares the dental box, so those two are the whole installed estate. Verified: oneOnCalendarper
timer with the code's reset line, four client timers with one next run each, env 0600 round-trip clean, every other
unit byte-identical to the pre-run snapshot, the re-slot's inventories landing as records 44 and 45, snapshots
at/root/agent-snapshot-20261016/. New finding, note (H):client-health.servicehas no[Install]section,
so it isstaticand nothing pulls it in — the post-boot self-heal never runs (measured: the journal's only start
on both boxes is the installer's own). Not urgent — the nightly reboot unit is a verified no-op until a patch run
schedules a reboot; left for the code rather than hand-patched.2. The figures — seven re-shot from the deployed app and LIVE on both surfaces. Rendered inside the container
against the realtest-clienttenant (read-only view renders; the search dropdown driven by the app's own JS
over captured endpoint payloads), then cropped and published: the wiki assets 281–288 byte-verified over the
public URL, and the site's copies updated by a targeted copy into/var/www/veripath(checked first that the
live content equalled the current build, so nothing else could change; a full deploy would have been
content-identical but touched more). OCR-compared with the published figures: same screens and entities with exactly
the intended changes — the symptoms search now leads with Nausea (finding), the 215-character note wraps
instead of clipping, and the debug console is absent because it is gated onDEBUG. Finding, note (I): the note
form's coded heading never gains the side or severity —rebuildTemplate()refuses the rewrite once the first
line starts with the chosen term, and the first write happens at selection time when only the site is known
(measured:snomed_attributesholds Right + Moderate while the heading omits them). Working files
/root/work/figshoot/(the shoot pipeline + captured payloads), backups/root/work/figs-backup-20261016/.3.
snomed_descendants— measured, then decided. The closure is 36% short of its own hierarchy: 93,909
missing pairs (Clinical finding 128,201 of 175,702, Procedure 59,893 of 76,021, Body structure 43,562 of
48,481, and the product root 25,716 of 50,817 — 49%), the walk is a strict superset (0 stored pairs lost)
and returns in 1.5 s, and the corpus lives once insector_gp(the tenant DBs carry empty tables). What it
costs users, measured with the same query: the prescription picker offers 55 of 121amoxicillin, 124 of
316insulin, 85 of 200morphine;heart attackin the clinical form gains Coronary artery
thrombosis (the concept carrying the MI synonym) with no regressions on eight other terms. Decision (mine, on
Matthew's delegation): rebuild now — arithmetic on data already held, and the script is a step a later import
re-runs anyway (note (J), OpenCode's work item: a committed rebuild script + a latency re-measure, because a
query-time walk costs 600–900 ms against a 300 ms budget). The corpus release is parked behind a named gate:
SNOMED CT UK Edition as the sixth source of the central reference routine, which is already live and pulls
dm+d weekly from TRUD (item 24, key configured) with a release register, per-release licences and staleness
alerts — measured baseline: the corpus we hold is the 2026-02 release, loaded by hand, no release identity
recorded anywhere. Recorded on the SNOMED page (dated
decision), the reference-data plan §5.4 and note
(J). Matthew's line on the parked item: "it should be quite straight forward as we have all of the other datasets
being updated regularly now" — deal with it once the follow-ups are closed, which they now are.Where this leaves things: nothing is in flight; every write is verified live (3 wiki pages, 7 wiki assets, 7
site image bytes). Owed: C4 — the post-window DSPT evidence note after Sunday 2026-10-18 02:00–04:00
(mine); the client pharmacy database password rotation (mine); OpenCode's: the pooler's own client credential
and the dental canary's agent credentials; the notes (H), (I) and (J); (A1), (A3), (A5) and
all of (F). Phase 4 still waits on Matthew naming it.Read first next time: this block, then from-hermes — (J) the SNOMED
decision, (I) the coded heading, (H) the unwired self-heal unit, then (G), (A), (F) — then
housekeeping-log (round 62), then the
SNOMED corpus page and the
reference-data plan §5.4 if the corpus item comes up.
SESSION 85 (2026-10-16) — the handoff inbox was CLEARED AND REBUILT as one verified queue: 30 notes audited, 12 closed with evidence, 18 carried into six queue notes (A–F).
Matthew opened by asking me to get ready, then — "I think we need to get all of these open notes cleared" — told me to
work them in date order, archive what is no longer needed, and get the rest "done before we continue with any
further work". He then went to OpenCode to start the queue.The audit, and what it found — this is the part worth remembering. All 30 notes on
from-hermeswere
re-verified against the deployed estate and the repositories, never against their own status lines, and the
headline is that most of the older band was genuinely still live: the pharmacy app has no@media print
rule and still dates its Dispensed column byupdated_at; the SNOMED search is still FSN-only; the
clinical-note form'sdlog()still switches its debug console on at load; the appointment record still prints the
note in a bare<pre>; the escaped-quote copy buttons are still inlab_documents/; the GP ERPNext client
still has nosubmit_sales_invoice(); the receptionist patient search still filters encrypted columns with
icontains; the GP dashboard still setsis_fleet_view = user.is_superuserbefore the client branch; the dental
dashboard still has no gate; the dentalMaintenanceRecordmirror still lacks the table'ssource; and
veripath-opsis still atcf2462f— the installer'sOnCalendar=still appends (every installed box
carries two schedules) and the env writer still uses an unquoted heredoc. The twelve that were done were
confirmed in the deployed templates rather than in a note (the insurance panel'spanel_appointment_pk, the
appointments period filter,RadioSelectexcluded, the NHS Notify comment block), plus the repaired
check-backup-freshness.sh(2026-09-27), the merged scaling auth branch (mainat11e3911) and the frozen
MinIO image still present. So "clearing" the rest means building them — that is OpenCode's half, and it is now
one list instead of eighteen overlapping notes.The result, and it is all verified. 12 closed with a dated line saying what was measured, then moved
verbatim to archive/2026-10: the 2026-09-26 backup-gzip pair, the
CV9 counter-views brief (its status line was stale), the insurance-panel 500, the two 2026-10-07 UI changes,
the unbootstrapped ERPNext site, the WO-A…WO-D rulings and the WO-D park, the MinIO→SeaweedFS instruction, the
WO-D un-park, the two correction notes and note (19). 18 carried into six queue notes: (A) the
client-VPS patching surface — the pharmacy port is A1, the last step of Matthew's sequence — (B) the
agent/installer, (C) the rotation window, time-boxed to land before Sunday 2026-10-18 02:00–04:00,
(D) pharmacy counter/notifications, (E) GP clinical/record/integration, (F) scaling, front door and
pooler. Inbox 137,116 → 23,647 chars (30 → 6 notes); archive 89,536 → 238,965 with all 30 verbatim,
newest on top. Both pages were rebuilt from raw extracted bodies (no wrapper fed back), re-fetched and diffed
immediately before the upsert, and verified three ways (Page ID + Render OK, stored body byte-identical, rendered
200 with the new anchors present and the old notes gone). New convention: a note may leave the inbox as
CARRIED — items restated with state re-measured, its text archived verbatim and linked — so the page stays the
current list and the archive stays the trail.Housekeeping in the same pass. Round 60 written (round 58 → the log archive; the live log keeps rounds
60/59 + tracker + open items, 33 KB → 31.5 KB), the open items updated to point at the queue, and session 82
moved to the resume archive (keep the newest three blocks + the cold-start rules).Second pass, same session (2026-10-16 late) — OpenCode's handback VERIFIED and the queue discharged.
Their session answered C, A2/A4, B, D and E, all on the working trees — nothing committed or deployed,
which is Matthew's call. I verified each independently: every diff read, and reproduced myself GP targeted
13 OK, dentalinfrastructure22 OK, pharmacy full suite 693 OK (284 s), the installer's own dry-run
(patch 04:07 / reboot 04:37 / inventory 05:07 fortest-client, matching the drop-in I applied by hand) and the
rotation pack's recreate-before-Django ordering. The pass: my inbox 7 → 3 — an (awaiting-deploy) note
carrying the one decision and its four follow-ups, (A) narrowed to A1/A3/A5, (F) unchanged — with
B/C/D/E and the stale pointer closed and archived with evidence; OpenCode's inbox 20 → 8, their five handbacks
carrying my verification line and 11 closed notes archived. Round 61.DEPLOYED 2026-10-16, at Matthew's word — commit and deploy both done the same session. Commits GP
165dc9e, dental9990fa1, pharmacy6aed942,veripath-ops483c399, all pushed; a targeted file copy
(48 files, hash-verified on the engine, backups at/root/work/deploy-backups-20261016-124628-wo-queue-fixes/)
and container restarts — all three apps healthy, health 200 publicly, client surfaces still gating. Follow-up 1 — the live item-by-item verification — was done the same day (results in the closing block
below and in housekeeping-log round 61).Read first next time: this block, then from-hermes — now four
entries: (G) the one live-verification finding (the dental mirror's missingnotescolumn), the
(awaiting-deploy) record, (A) the client-VPS surface, (F) scaling/front door/pooler — then
housekeeping-log (round 61), then from-opencode
if their inbox has moved, then the pointer pages (the pharmacy spec, the patching design, the rotation runbook).WHERE THE SESSION CLOSED (2026-10-16, late) — closed deliberately, at Matthew's word, so the remaining work
starts in a fresh context. Everything shipped is deployed, healthy and verified; nothing is in flight. Three
follow-ups remain, and they are the first work of the next session, in this order:
- The canary installer re-run — dry-run first, then re-slot the dental box from
veripath-ops483c399so
the code, not my hand-fix, writes the timer drop-ins, and confirm the three timers no longer share a minute
(the installer's dry-run already prints patch 04:07 / reboot 04:37 / inventory 05:07 fortest-client).- The figure re-shoots —
health_clinic/recording-symptoms-search.png(E1 changed the ranking: the plain
Chest pain (finding) is now first) plus the medical-notes and blood-tests figures the 2026-10-08 note asked
to re-shoot now that E2/E3/E4 are live; the embeds need no edit, only the bytes.- The
snomed_descendantsdecision — heart attack cannot surface the MI concepts until that closure is
rebuilt (128,201 rows under Clinical finding of 257,605), and rebuilding the corpus is somebody's call.Also open, not part of those three: A1 the pharmacy client-maintenance port (the last step of Matthew's
sequence), A3, A5, all of F, and note (G) for OpenCode (the dental mirror'snotescolumn —
their fix, small, same class as the one they closed today). Owed: C4 — the post-window DSPT evidence note
after Sunday 2026-10-18 02:00–04:00 (queue note (C) was satisfied: the pack's scan already covers the
pooler's directory); the client pharmacy database password rotation (mine); and OpenCode holds the pooler's
own client credential and the dental canary's agent credentials (split 2026-10-16). Working files:
/root/work/session85-live-*.py(the live-verification scripts, run withsession85-deploy/run-live.sh) and
/root/work/session85-deploy/(baseline.py,deploy.sh,verify-copy.py— the deploy toolkit, reusable for
the next release). The session was closed on context length, not on a blocker.Owed and dated: the post-window DSPT evidence record; the client pharmacy database password (my 2026-10-15
disclosure — mine, after today's split); OpenCode's: the pooler's own client credential and the dental canary's
agent credentials; VPS1's pending reboot; the client-tier migration cadence gap; weekly image scanning covering the
primary only; Matthew's two pending decisions (the frozen MinIO image, the live-mail-domain account in the
gptenant); and Phase 4, which still waits on him naming it.