Status: RESEARCH NOTE (pre-plan). Not a sprint, no code. Per docs/PROCESS.md §1.1, exploratory analysis lives in docs/research/ until committed to a sprint. Date: 2026-07-23 Origin: a computer-repair-shop intake agreement, described by the operator. It states the pattern more plainly than any spec draft, so it is recorded here verbatim in substance.
---
0. The origin artifact
Every customer signed, in ink, before any work began. The terms:
- We did not break your computer. It is broken — that is why you are here.
- We will do our best to fix it.
- If we cannot, we will help you recover your data and help you buy a replacement.
- We are never responsible for your data. Sorry. Period.
- We do not know what data you have, and we will not know — we use automated programs to manage
our customers' privacy and our own mental health.
Alongside the signature: photographs of everything they brought in, and of their ID — so that in the event of incidental discovery of something criminal, there is a verified identity to report.
Three separate mechanisms are doing work here, and only one of them is "a contract."
---
1. The pattern — one concept, three faces
| Face | Question it answers | Repair-shop form | |---|---|---| | Baseline attestation | What state did I receive it in? | "It is already broken" + intake photos | | Informed consent | Did they understand and accept the terms? | Signed, deliberately blunt agreement | | Custody record | Who handed it over, and can this record be altered later? | ID photo; signed paper |
1.1 Baseline attestation is the same epistemics as a positive control
Without a recorded prior state you cannot distinguish "it was already failing" from "you broke it" — and the burden falls on whoever cannot prove it. This is identical to the security-testing discipline in HARVEST-PLAYBOOK.md §4 / PROCESS.md §14: a "blocked" result means nothing unless you first proved the attack worked against the unguarded target.
The same move, three times, hand-rolled:
| Context | Baseline captured | Mechanism | |---|---|---| | Computer repair | Device condition at intake | Ink + camera | | WordPress remediation (HostMonster job) | PHP/WP/plugin versions, host config | wp-fix-internal telemetry probes (20 probe modules — reported by the 2026-07-23 ecosystem survey agent; not independently opened) | | Audit-log convergence (Sprint 3.0) | UPDATE 1 on unguarded DBs before the fix | ad-hoc psql, 2026-07-23 |
None of these is a library crate. This is the gap (§3).
---
2. What already exists (verified this session)
| Component | Path | State | |---|---|---| | domain-agreements | crates/domain/agreements/src/lib.rs | Real, 1009 lines. Agreement, AgreementType, AgreementVersion (versioned content + effective dates + VersionStatus), Consent, ConsentMethod | | Assessments | crates/operations/assessments | Real, 2333 lines | | Curriculum | crates/domain/curriculum | Real, 2233 lines | | application-assessments | crates/application/assessments | Thin — 32 lines | | Gated media delivery | .claude/CLAUDE.md rule 10 | asset://<id> → tokenized, RBAC-gated /media/...; /static is build assets only | | WORM + crypto-shred | crates/foundation/audit-log/migrations/002_audit_hardening.sql:93-128 | subject_audit_keys, RAISEing immutability trigger | | Signed-agreement immutability | buceo-feliz/src/diveops/operations/migrations/0065_signed_agreement_immutable_trigger.py | Django only. prevent_signed_agreement_status_change() — RAISE EXCEPTION when OLD.status='signed' and status changes; ERRCODE = 'check_violation' |
What does NOT exist
- No TTS / speech / audio library crate. `find crates -type d \( -name "tts" -o -name "speech"
-o -name "voice" -o -name "audio" \)` → empty.
- No reading evidence anywhere. No
scroll_depth,dwell, orreading_timein any crate
(the grep hits are browser-automation/humanizer.rs and content/cms — unrelated).
- `Consent` records the click, never the exposure. Fields are
user_id,
agreement_version_id, consented_at, consent_method, ip_address, user_agent, withdrawn_at. ConsentMethod is {Checkbox, Click, Signature, Implicit, Api} — no variant means attested reading.
- No baseline-attestation library crate at all.
- The Django immutability trigger has no Rust counterpart. This is the second case found on
2026-07-23 where buceo-feliz enforces DB-level immutability the Rust side lacks — the first being audit_logs (see sprint-3.0-audit-log-fleet-convergence.md). The pattern is worth a dedicated sweep of the Django ancestor for enforcement the Rust rewrite dropped.
---
3. The gap none solved
Baseline attestation is unowned, despite three independent hand-rolls. A library crate would own: capture of a declared prior state, both-party acknowledgement, immutability, and later comparison (did the state change, and in whose favor?).
Secondary gap: the consent model proves consent, not comprehension.
---
4. Proof of informed consent — strongest to weakest
The operator's framing matters: the goal is "we're going to try our best to make you safer," not a liability shield. That ordering follows from the goal, not from evidentiary convenience.
1. Comprehension check — operations/assessments (2333 lines, exists). Two questions ("maximum safe ascent rate?", "what if you cannot equalize?") are worth more than any dwell telemetry, legally and practically. This is the only mechanism that tests the actual objective. 2. Read-aloud (TTS) completion — audio cannot be skimmed, so completion is stronger evidence than scroll depth; it paces content without a punitive gate; and it is a genuine accessibility accommodation rather than an obstacle. No library crate exists. 3. Curriculum engagement — domain/curriculum (2233 lines, exists). The "how and why and what could happen" material. 4. Reading attestation — scroll depth, per-section dwell. Record it; never make it the sole gate.
Explicit caution on enforced reading speed
A hard "you may not proceed faster than N words/minute" gate is the weakest of the four and carries three costs: it is trivially gamed (open the tab, walk away); it is an accessibility hazard (screen readers, dyslexia, non-native speakers, and repeat readers all legitimately differ); and it reads as manufactured if ever examined — evidence built for a courtroom rather than for the person signing. Prefer TTS pacing + a comprehension check, which are stronger on every axis including the one that actually matters.
---
5. Custody records and the retention conflict
Intake evidence (equipment photos + ID) is itself high-sensitivity data — often a richer PII set than what it was collected to protect. The control for one risk creates a new asset requiring:
- delivery through the tokenized RBAC-gated media route, never
/static(CLAUDE.md rule 10); - an
audit_logsentry for every view — who opened the ID image, when; - WORM storage, because evidence that can be silently edited is not evidence.
The conflict: custody evidence must be retained and unalterable, while ID images should not be hoarded indefinitely and may be subject to erasure requests. These point in opposite directions.
Already solved in `002`. The WORM trigger message states the resolution:
*PII erasure is done via crypto-shred (`subject_audit_keys`), not row mutation.*
Destroy the wrapped per-subject key: the identity becomes unrecoverable while the record — that an intake occurred, its hash chain, its ordering — remains intact and provably unaltered. The evidence survives; the person's identity need not. This is the intended pattern for custody records.
5.1 The working copy — custody's highest-value asset
In the repair shop, custody was not only photos and ID. A copy of the customer's data lived on shop working drives at the bench, because you image before you work — the alternative is discovering mid-repair that the source drive is failing.
That working copy is the physical twin of the baseline attestation: a pre-work snapshot taken so you can both prove the prior state and restore it. It is also, by a wide margin, the most sensitive asset in the shop — a single bench drive concentrates many customers' complete data, and its risk is aggregate rather than per-ticket.
So a baseline attestation library crate must model not just a condition record but an optional retained data copy, with its own lifecycle distinct from the ticket's:
- Existence — is there a copy, where, on which medium?
- Encryption at rest — the aggregate makes this materially different from single-customer risk.
- Retention — bounded by policy, not by "until the drive fills."
- Disposal — verified destruction, recorded. This is the step most likely to be deferred
indefinitely, because nothing forces it and the drive keeps working.
- Linkage — copy ↔ encounter ↔ consent, so disposal can be driven from ticket closure.
Open question: does the customer's signed agreement cover retention of a copy, and for how long? The repair terms disclaim responsibility for data; that is a different question from how long a copy is kept and when it is destroyed.
#### The disposal trigger — confirmed handoff, not a timer
The shop's practice answers it, and answers it better than a retention timer would:
1. Data recovered and returned. 2. Customer helped to organize it, programs reinstalled, machine set up as it was or better — remote and over the phone. 3. Customer confirms satisfaction. 4. Only then the working copy is destroyed — with dd, a full overwrite, not rm.
A set timer does not work here — confirmed in practice, not theorized. It destroys the safety net while the customer may still need it: their restore fails a week later and the copy that would have saved them is already gone. Customer return times are irreducibly unpredictable — days for one, weeks for another — so any fixed retention period is either too short (destroys data someone still needs) or so long it stops meaning anything.
The correct gate is confirmed successful handoff.
And the backstop must not be a clock either — that would reintroduce the same defect one level down. The real backstop is an exhausted-contact state: follow-up attempted, re-attempted, customer unreachable. That is driven by attempts made and recorded, not by days elapsed, and it stays defensible: the record shows what was tried before disposal, rather than "the timer fired." So the rule is "dispose on confirmed handoff, or on recorded exhausted contact" — never on a schedule.
Note the disposal is also gated on a service step, not just a data step: the customer is not "confirmed" until the machine works the way they expect. That is a different completion signal from "the files copied successfully," and it is the one that matters.
#### The trigger that actually fires: capacity pressure
Reported from practice, and it complicates the tidy version above. In reality disposal often happened because the shop needed the space. Tickets accumulated unclosed, working drives filled, and eventually someone plugged the drive in and zeroed it — dd if=/dev/zero of=/dev/sdX — because the bench needed capacity that day.
This is the honest third trigger, and it is the worst of the three: it is driven by the shop's operational need rather than by the customer's state, so it can destroy a working copy attached to a ticket that is still open. It is also inevitable — finite media guarantees it eventually.
The design conclusion is not "prevent this." Capacity pressure cannot be legislated away. It is that if disposal has no working trigger and no visible queue, capacity pressure becomes the trigger by default — and then disposal is uncontrolled, falling on whatever happens to be on the drive someone grabbed.
So the system's job is to guarantee that when space pressure fires, it fires on eligible copies:
- Continuously track disposal eligibility (confirmed handoff / exhausted contact), so there is
always a standing supply of safely-disposable data.
- Surface it as a worklist — "N copies eligible, X GB reclaimable" — so reclaiming space is the
easy path rather than the improvised one.
- Make disposal of an ineligible copy require an explicit, recorded override, not a silent
dd. - Treat a growing count of unclosed tickets as the leading indicator: unclosed tickets are what
convert routine housekeeping into an emergency wipe.
This is the same lesson as §6.5's latency point: a control that is inconvenient gets routed around. If the disposal path is not the easiest way to free space, it will not be the way space gets freed.
Media caveat for a modern implementation: dd zero-fill is sound for magnetic disks. On SSD/NVMe it is not reliable — wear-levelling and over-provisioning can retain data in blocks the overwrite never addresses. The correct modern equivalents are ATA Secure Erase / NVMe Format, or crypto-erase: encrypt the working copy at rest and destroy the key. Crypto-erase is the same mechanism foundation-audit-log already uses for PII (subject_audit_keys, 002:93-100) — so the shop's physical disposal practice and the framework's existing erasure design are the same idea on different media, and a working-copy library crate should reuse it rather than invent a wipe routine.
5. Note the disposal in the ticket. 6. Close the ticket.
The record is what turns disposal from a claim into evidence, and it belongs in the same immutable trail as the intake — the two ends of one custody chain.
#### The lifecycle, expressed in library crates that already exist
| Step | Library crate | |---|---| | Ticket | domain-encounters — Encounter (Ticket/Appointment/Visit) | | Signed terms | domain-agreements — Agreement + AgreementVersion + Consent | | Intake photos, ID | content-assets, RBAC-gated /media (CLAUDE.md rule 10) | | Baseline attestation | unowned — the gap (§3) | | Never looking at the data | application-dlp, masked-only paths | | Every note appended, never edited | foundation-audit-log WORM (002/003/004) | | Working-copy erasure | crypto-erase via subject_audit_keys | | Disposal note → close | operations-workflow — WorkflowStore::transition_if (CAS, harvested 2026-07) |
The closure guard is the interesting one. "Note the disposal, then close" is a guarded state transition: a ticket must not reach closed unless a disposal record exists. That is exactly the atomic compare-and-swap harvested into operations-workflow this month — UPDATE … WHERE id = $1 AND state = $expected, with the disposal note as a precondition. Without the CAS, two actors can both pass the in-memory check and one transition is lost; with it, a ticket cannot be closed out from under a pending disposal.
So the shop's paper-and-bench workflow decomposes into library crates that exist today, with exactly one gap: baseline attestation. Everything else is assembly.
5.1b The ID requirement was a DETERRENT control, and that is not measurable by detections
Observed in practice: customers with something to hide would not produce ID, and went elsewhere.
The intake ID capture is usually described as detective — "so there is a verified identity to report." In operation its dominant effect was deterrent: it changed who walked in the door. The material that would have triggered a report largely never arrived.
This defeats the obvious metric. Asking "how many times did the ID photo enable a report?" could plausibly return zero, from which one would wrongly conclude the friction was not worth it. The value lived in the cases that never presented. A deterrent cannot be measured by its detections — the counterfactual is invisible, and its success looks identical to irrelevance.
This is the same epistemic trap as a vacuous safety test (PROCESS §14), inverted: there, a passing test looks like a working mechanism; here, an empty finding log looks like an unnecessary control. In both cases absence of a signal is not evidence of absence of effect, and in both cases the fix is to reason about the counterfactual explicitly rather than read the metric at face value.
The controls also compounded. The ID requirement filtered the customer base; a good customer base made the unhurried disposal policy (§5.1) affordable — you can wait indefinitely for someone to come back when the people coming back are reasonable. A shop that skipped the intake control would face worse material and pressure toward a fixed retention timer, because it could not afford patience. Upstream controls buy downstream latitude.
Design implication: the control inventory should record a control's intent — preventive, detective, deterrent, corrective — because the evaluation method differs. Grading a deterrent by detection count will always recommend removing it.
5.2 Access tiers are different relationships
Bench repair and ongoing desktop support are not the same authorization. In the shop they were performed by different people, under different circumstances, initiated differently — bench work follows an intake; desktop support is an ongoing invitation.
An observed and useful risk property: the customers who wanted ongoing desktop support were generally not the ones with secrets. Self-selection means the highest-privacy customers carry the least ongoing exposure — the inverse of what results when support is assigned rather than chosen. Any consent model should preserve that property rather than flatten it into one blanket authorization: separate AgreementTypes, separate scopes, separate revocation.
5.3 Symmetric evidence — the record must protect the weaker party too
The shop was camera-covered end to end, except the bathroom. The operator's framing: "that way it protected everyone."
Both halves matter.
Symmetry. The footage cut in every direction: the customer's machine demonstrably was not swapped or opened; the technician was protected against an accusation of theft or mishandling; the owner had an answer for a dispute or an insurer. A record that only serves the party who controls it is monitoring. A record that can exonerate is protection. The design goal is the second.
The exclusion is what legitimizes the rest. Coverage extended exactly as far as its dispute-resolution value and stopped where it became surveillance for its own sake — no dispute is ever resolved by bathroom footage, and only harm results from collecting it. Deliberately defining the excluded zone is the difference between a protective evidence system and a panopticon, and it is the same move as "we use automated programs so we never see your data."
#### Why this is the real argument for WORM
This reframes the audit-log hardening work (Sprint 2.6 / 3.0) as something other than compliance ceremony.
Tamper-evidence protects the weaker party disproportionately, because the stronger party is precisely the one with the ability to alter the record. An employee accused of an unauthorized access can point to the audit trail showing they did not do it — but only if whoever is accusing them could not have edited it. Without immutability, the record defends only the person holding the database credentials.
So RAISE EXCEPTION over DO INSTEAD NOTHING, the row_hash chain, monotonic seq, and the event-trigger guard that stops even a table owner from disabling the trigger (#163) are not audit theater. They are what makes the record usable as a defense rather than only as an accusation. An audit log that management can quietly rewrite is worth less than no audit log, because it carries the authority of evidence without the properties of it.
(Operational note for a modern build: video and audio are governed differently — in many US jurisdictions video in non-private business areas is broadly permissible with notice, while audio recording is subject to one- or two-party consent statutes. A system capturing both should treat them as separate consent scopes, not one "recording" toggle.)
#### 5.3b The cameras earned their keep daily — which is why they worked when needed
They were not an incident-only system. They were a working tool:
- Counter staff watched the floor to see customers out and catch anyone waiting.
- The owner, travelling, drove the PTZ to assist techs remotely — zoom in on a board and say
"it's that screw, it's there." Remote expert assist, in daily use.
This is why the evidence function actually worked. A system used only during incidents rots silently: lenses dirty, angles drift, a disk fills, a feed drops — and nobody discovers it until the day it is needed. A system someone uses hourly is continuously verified by use. The operational value is the health check.
This is the third instance in this note of one principle:
| Control | Daily operational value? | Outcome | |---|---|---| | Fast local evidence capture (§6.5) | Yes — faster than the alternative | Used at every step; record complete | | Disposal workflow (§5.1) | No — pure overhead | Deferred until capacity forced an emergency wipe | | Cameras (§5.3) | Yes — counter awareness, remote assist | Stayed aimed, powered, and working; usable as evidence |
The principle: a control with daily operational value stays functional; a control that is pure overhead decays until an emergency triggers it. This is the operational form of Gate 3.4 ("no capability ships dark") — a capability with a real consumer on the runtime path stays alive; one with no consumer rots regardless of how correct it was at build time.
Design implication for the fleet attestation work (Sprint 3.0 / red-lab): attestation that runs only during an audit will rot exactly like an incident-only camera. It needs a daily consumer — surfaced status somebody actually looks at, wired into an existing routine — or it will be green-because-unrun rather than green-because-correct. Build the consumer, not just the check.
(Relatedly: the PTZ remote-assist workflow is a live use case for the in-flight NVR work — `forge-nvr`, `frigate-box`, `operations-camera-discovery`. "Remote expert zooms in and directs a technician" is a real product requirement with a known user, not a hypothetical.)
Deliberate non-knowledge as architecture
"We do not know what data you have, and we will not know." Most shops implement this as a policy telling staff not to look — which is not a control. Implemented architecturally, it is one, and it protects the technician as much as the customer: incidental exposure to criminal material is a real occupational hazard, and minimizing it is a legitimate engineering objective.
application-dlp already embodies this — sprint-2.8 T9 (test(dlp-sensor): T9 leak-safety — masked-only screen path emits no raw PAN) is precisely "the machine sees it so the person does not," expressed as a §14 invariant test. Incidental discovery cannot be driven to zero, which is why the identity binding exists: prepare for the residual case rather than pretend it away.
---
6. Cross-domain reuse — one library crate, many faces
The same object with different AgreementType:
| Domain | Agreement | Baseline attestation | Custody | |---|---|---|---| | Computer repair | Repair terms + data disclaimer | Device condition at intake | Equipment + ID photos | | Diving (scuba-fill-station, happydiving.mx, buceo-feliz) | Liability waiver, medical declaration | Certification level, medical fitness at date | Cert card image | | Security engagement (red-lab, infrastructure-security-scan) | Written authorization to test / RoE | Pre-test system state | Scope + signatory authority | | Framework remediation (Sprint 3.0) | — | Measured deployed baseline | Evidence record per database |
The security case already has a partial guardrail: ScanTarget / ActiveScanGrant in infrastructure-security-scan is an RoE expressed as a type. That is the shape the others want.
---
6.5 Deployment constraint — in-house, always
The repair shop's ticket system ran on a computer in the shop. Not a SaaS helpdesk.
The primary reason was operational, not philosophical: it had to be in-house to work. A shop's internet goes down like anyone else's. If the ticket system is hosted elsewhere, then during an outage you cannot intake a customer, cannot look up whose machine is on the bench, and cannot print a work order — with a person standing at the counter. The same applies when the vendor has an outage, which you neither control nor can explain to the customer waiting. Point-of-service software that requires a network to function is unavailable exactly when a small business cannot afford it to be.
Privacy is the secondary benefit, and it is real: a cloud ticket system means the customer's name, device, symptom description, and any attached intake photos live in a vendor's database, under a vendor's breach exposure, subject to a vendor's retention policy — none of which the shop controls or can honestly describe to the customer. But availability is what forces the decision.
And speed is what made the process actually get followed. In-house was fast enough that photos were uploaded and the system used at every single step — not just at intake. Images to a machine in the same room are effectively instant; the same workflow against a hosted helpdesk over a small business's uplink is slow enough that staff begin skipping steps under counter pressure. The steps skipped are never random: they are the ones with no immediate payoff to the person doing the work — which is precisely the evidence capture (the intake photo, the condition note) that only matters later, in dispute.
This makes latency a compliance mechanism, not a nicety. Evidence completeness came from speed, not from policy. A slow control is a control that gets routed around, and this is the same failure mode as the always-red CI advisory gate found on 2026-07-23 (28 findings against ignore = [], one with no available fix): a control that is inconvenient becomes a control that is not applied, and its output stops being read at all.
Fourth: physical backups you control. In-house means the backup is the actual database, not a vendor's export format — so it can be copied to media you physically hold, taken offsite, and restored and verified. That last part is the point: an untested backup is a claim, not a proof, and a full-fidelity local dump can be restored to another box and checked, where a SaaS export frequently cannot be meaningfully validated for completeness. It also survives the failure modes an export does not: vendor bankruptcy, account suspension, billing dispute, price change, API deprecation. For a shop whose business is data recovery, holding your own recoverable copy is not a preference.
The design rules that follow:
- Anything on the point-of-service path must function with no network. Treat a network
dependency at intake, consent, or lookup as a defect, not a tradeoff.
- Evidence capture must be fast enough to use every time, under counter pressure. If it is not,
the record will be incomplete in a way nobody notices until it is needed. Budget it as a hard requirement, and measure it — do not assume it.
- Backups must be full-fidelity, locally held, and restore-tested. Not an export; the data.
A restore that has never been performed is unverified — mark it so, and test it.
- Custody media and consent records must be included in that backup set. They are the legal
artifacts; losing them loses the evidence the whole design exists to produce.
This is the operating principle running through the whole portfolio, and it is already the default here: self-hosted Postgres in a local jail, no Docker, privacy-explorer bound to 127.0.0.1, the local-first desktop-actuation spec, the self-hosted-CI roadmap. Consistency, not coincidence.
Design consequences for anything built from this note:
- Custody media (equipment + ID photos) must be storable and servable entirely on-premises —
local filesystem or on-prem S3-compatible, never a third-party CDN. The RBAC-gated /media route already supports this.
- TTS must have an offline path. This upgrades open question 3 from a preference to a
constraint: a dive shop on a boat and a repair counter during an outage both need the read-aloud to work with no connectivity. A cloud-TTS-only design fails the primary use case.
- Comprehension assessments must run locally — no external quiz service.
- The consent record is the legal artifact. It must survive vendor loss entirely, because there
is no vendor.
- Note the ticket system itself is already a library crate here:
domain-encountersowns
Encounter (Ticket / Appointment / Visit), and support is a working multi-tenant ticket system built as pure library-crate composition. A repair-shop intake is an Encounter with an attached agreement, a baseline attestation, and custody media.
---
7. Open questions (for Gate 1.5, if this becomes a sprint)
1. Is baseline attestation its own crate, or an extension of domain-agreements? 2. Does Consent gain a linked ReadingAttestation record, or new ConsentMethod variants, or both? (A linked record preserves the existing enum and carries richer evidence.) 3. TTS: a new library crate, or an adapter over an existing provider? Offline capability matters — a dive shop on a boat may have no connectivity. 4. What is the retention default for custody media, and what triggers crypto-shred? 5. Should the assessment result be required for high-risk agreements (diving) and optional for low-risk (terms of service)? Per-AgreementType policy? 6. Sweep buceo-feliz for other DB-enforced invariants the Rust rewrite dropped — two found so far (audit_logs, signed agreements), which suggests a class rather than two incidents.
---
8. Control-inventory entries this implies
agreements.signed_immutable— a signed agreement's status cannot change; revocation requires a
new signed document. Misuse case: UPDATE the status of a signed agreement. Probe: attempt it, prove it fails, prove it succeeded before the trigger existed. Currently enforced in Django only.
custody.media_gated— ID/equipment images are never reachable outside the RBAC-gated route.custody.view_audited— every view of custody media produces an audit row.consent.version_pinned— consent references an immutableAgreementVersion; terms cannot be
altered after signature.