From aba68eed2801b2083fa63f6b1e431cca153c9eca Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Wed, 5 Aug 2026 16:51:05 -0600 Subject: [PATCH] docs(conversion): the owner deletes segments, and the source-delete affordance is retired Operator ruling. The open call this plate carried -- legacy segments holding one source's data beside another's, where an owner deleting one source either loses the segment whole or keeps the data they asked to remove -- is CLOSED by removing the question rather than answering it. There is no source-delete affordance. The owner deletes a segment, or a set of segments, and there is no affordance for a partial owner-directed delete of any kind. The legacy-mixed problem existed only as a resolution step, turning "delete my data" into a set of segments; with the owner naming segments directly there is nothing to resolve and no disposition to choose. The surface already exists -- the per-segment delete route, with containment and a 10-second undo window. So the whole-segment verb takes a SET, one receipt covers it, and per-target failures are receipt rows: an owner deleting forty segments must not lose the thirty-nine that succeeded because the fortieth was unreadable. Retired with it: the owner-facing source-delete route, the source-delete implementation and both its branches, the deletable-source-stream allowlist, and the mixed / location-only classification. The reserved-name set divergence loses its last load-bearing consumer -- it fed the mixed classifier, and there is no classifier. Also ruled: the plate keeps TWO units of removal, with the no-partial rule binding owner-directed deletion only. Reading it as binding both makes this plate's own terminal-empty hand-off contradictory, because handing retention a raw file would destroy the marker the audio handler had just written along with the transcript. And a fifth strand is minted: S:*:journal-retention, the removal request, owned by retention as the one-to-many end per rule 1. Four plates request removals and until now that contract had four callers and no name -- the two-places-own-one-thing class the rule exists to make unrepresentable. A request carries its own precondition, so the offload pass's confirmed-snapshot guard travels with the request rather than staying behind in the caller. --- docs/conversion/plates.md | 18 ++++++++++++++---- docs/conversion/strands.md | 11 ++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/docs/conversion/plates.md b/docs/conversion/plates.md index 5eb62127c..8560d5c71 100644 --- a/docs/conversion/plates.md +++ b/docs/conversion/plates.md @@ -263,15 +263,23 @@ The logic that decides what raw media is retained, and what logs are retained fo 2. โ›” **`transcribe` stops unlinking VAD-empty raw audio.** It writes the terminal-empty marker exactly as it does today and hands the raw to retention. One subsystem, one policy, one place to look when owner media went. 3. ๐Ÿ”ด **Retention notifies `P-index` of the paths it actually removed, after removing them.** โ›” Ordering is the contract: the index is told about removals that have happened, never about removals that are intended. An index prune is not a removal โ€” the index is rebuildable by design, so pruning it is a cache invalidation and a rebuild undoes it. **Anything an owner is told was removed must be removed from the chronicle first.** -โš  **Open, and not settled by that ruling: legacy segments holding one source's data beside another's.** An owner asking to delete one source from such a segment either loses the segment whole, including material they did not ask to delete, or keeps the data they asked to remove. Rule 4's unacceptable outcome is older journal data left *unseen*; this is the sibling โ€” older journal data left **undeletable**. +๐Ÿ†• โ›” **CLOSED 2026-08-05 by operator ruling, by removing the question rather than answering it. Do not re-derive it.** This entry used to record an open call: legacy segments holding one source's data beside another's, where an owner deleting one source either loses the segment whole or keeps the data they asked to remove. + +๐Ÿ”ด **There is no source-delete affordance.** The owner deletes **a segment, or a set of segments** โ€” that is the only owner-facing removal there is, and โ›” **there is no affordance for a partial owner-directed delete of any kind.** The legacy-mixed problem existed only as a *resolution* step, turning "delete my โŸจsourceโŸฉ data" into a set of segments; with the owner naming segments directly there is nothing to resolve and no disposition to choose. + +โœ… **The surface already exists** โ€” the per-segment delete route under the transcripts app, with containment via `commonpath` and a **10-second undo window**. โ›” Retention never resolves anything; it receives owner-chosen targets. Selection lives with the surface. + +๐Ÿ”ด **The whole-segment verb therefore takes a SET.** One receipt covers it, and per-target failures are receipt rows. โ›” It is not all-or-nothing: an owner deleting forty segments must not lose the thirty-nine that succeeded because the fortieth was unreadable. + +โ›” **Retired with it:** the owner-facing source-delete route ยท the source-delete implementation and both of its branches ยท the deletable-source-stream allowlist ยท the mixed / location-only classification and its discovery helper. โš  **The reserved-name set divergence loses its last load-bearing consumer** โ€” it fed the mixed classifier, and there is no classifier. ### Two units of removal, and the plate serves both -๐Ÿ”ด **The plate removes owner media under two different units, and reading it as one unit makes ยง 2 above contradictory** โ€” handing retention a VAD-empty raw would destroy the terminal-empty marker `transcribe` had just written, along with the segment's transcript and every derived output. The distinguishing property is **what the owner asked for**, not what is on disk. +๐Ÿ”ด **Ruled 2026-08-05: ยง 1 binds owner-directed deletion ONLY, and the plate keeps two units.** Reading ยง 1 as binding every removal makes ยง 2 above contradictory โ€” handing retention a VAD-empty raw would destroy the terminal-empty marker `transcribe` had just written, along with the segment's transcript and every derived output. The distinguishing property is **what the owner asked for**, not what is on disk. | unit | the owner asked for | what goes | what survives | |---|---|---|---| -| **the segment** | *"delete my โŸจsourceโŸฉ data"* | every file in the segment | `tombstone.json` only | +| **the segment**, or a set of them | *"delete these segments"* | every file in each | `tombstone.json` only | | **the proven originals** | nothing โ€” this is the retention lifecycle | raw media whose processing is proven terminal | every derived output | โ›” **ยง 1 binds the first unit.** What it forbids is a *deletion* that leaves part of its target behind โ€” the failure it was ruled against was a segment keeping derived output on disk, undisclosed, after the owner asked for that data to go. The second unit is the plate's standing scope (`retention.py:12-14`: *"Scope: raw media ONLY. Chronicle JSONL, derived outputs, `talents/` directories โ€ฆ persist indefinitely"*), and derived output surviving is the point of it. @@ -280,7 +288,9 @@ The logic that decides what raw media is retained, and what logs are retained fo ### The removal-request contract -๐Ÿ”ด **Retention is the one-to-many end for removal requests and that relationship has no strand name.** All four strands in [`strands.md`](strands.md) ยง Tier 1 are ones where retention is the *consumer* and the other plate owns the contract. The requesters โ€” the source delete, the terminal-empty hand-off, the offload pass, the configured policy โ€” are many, and rule 1 puts the contract at the one-to-many end. โ›” Naming the strand is not decided in this repo. +๐Ÿ†• โœ… **Minted 2026-08-05 as `S:*:journal-retention`, and retention owns it.** Retention is the one-to-many end: it serves all comers and cannot negotiate per-caller, so rule 1 puts the contract here. The other four strands are ones where retention is the *consumer* and the far plate owns the contract; this is the one where it is the provider. โš  **Four requesters** โ€” the owner's segment delete, the terminal-empty hand-off, the offload pass, and the configured policy โ€” and until this strand existed that contract had four callers and no name. See [`strands.md`](strands.md) ยง Tier 1. + +โ›” **The request names its unit**: whole segments (one, or a set) leaving a `tombstone.json`, or the proven raw originals leaving every derived output. โ›” There is no third unit, and โ›” no partial owner-directed delete. ๐Ÿ”ด **A removal request must carry its own precondition, because consolidating the removers must not weaken the strongest one.** `think/offload.py:257-312` archives to backup, **confirms the snapshot holds every byte at the recorded size**, appends its ledger, and only then unlinks โ€” where retention's own path hashes the bytes and unlinks with no archive. When that removal becomes a request, the confirmed-snapshot precondition travels **with the request**; a request type that can be constructed without it has moved the guard out of the executor and into the caller. diff --git a/docs/conversion/strands.md b/docs/conversion/strands.md index 5f50503d7..a4ec7d178 100644 --- a/docs/conversion/strands.md +++ b/docs/conversion/strands.md @@ -246,18 +246,23 @@ First-run journal establishment. **Creates the identity root** that `S:device-li ## Tier 1 โ€” retention -`P-journal-retention` connects through four strands, each a different contract: +`P-journal-retention` connects through five strands, each a different contract: | Strand | For | Owner | Tier | |---|---|---|---| | `S:journal-retention:journal-config` | the **posture / settings** it reads | `P-journal-config` | fixture | | `S:journal-retention:system` | **when it runs** | `P-system` | schema | | `S:journal-retention:journal` | **tending the files** โ€” changes, and recording status | `P-journal` | fixture | -| ๐Ÿ†• `S:journal-retention:index` | **telling the indexer what was removed**, after the removal | `P-index` | schema | +| `S:journal-retention:index` | **telling the indexer what was removed**, after the removal | `P-index` | schema | +| ๐Ÿ†• `S:*:journal-retention` | **asking retention to remove owner media** โ€” the only way in | `P-journal-retention` | schema | ๐Ÿ”ด **The segment is the unit of deletion, retention executes every removal, and retention tells the indexer afterwards.** That ordering is the design: removal happens first and the index is informed, never the reverse. The fourth strand above exists because of it. -โš  **Retention is the consumer on all four** โ€” every one of these contracts sits at the other end. The relationship in which retention is the *provider* is the **removal request**, and it has no strand name here; rule 1 puts that contract at retention's end because it serves many requesters and cannot negotiate per-caller. See [`plates.md`](plates.md) ยง the removal-request contract. +๐Ÿ†• ๐Ÿ”ด **The fifth strand is the removal request, and retention owns it** โ€” minted 2026-08-05 by operator authorization. Retention is the **consumer** on the other four; every one of those contracts sits at the far end. This is the one relationship where it is the provider, and rule 1 puts the contract at retention's end because it serves all comers and cannot negotiate per-caller. โš  **Four plates request removals** โ€” the owner's segment delete, `P-segment-sense`'s terminal-empty hand-off, the backup offload, and retention's own configured policy โ€” and until this strand existed that contract had four callers and no name, which is the two-places-own-one-thing class rule 1 makes unrepresentable rather than merely detectable. + +๐Ÿ”ด **A removal request carries its own precondition, because consolidating the removers must not weaken the strongest one.** The offload pass archives to backup, **confirms the snapshot holds every byte at the recorded size**, appends its ledger, and only then removes โ€” where retention's own path hashes the bytes and removes with no archive. When that removal becomes a request, the confirmed-snapshot precondition travels **with the request**. โ›” A request type constructible without it has moved the guard out of the executor and into the caller. + +โ›” **Two units, and the request names which.** Whole segments โ€” one, or a set โ€” leaving a `tombstone.json`; or the proven raw originals, leaving every derived output. โ›” There is no third unit and no partial owner-directed delete. See [`plates.md`](plates.md) ยง `P-journal-retention`. โš  **`S:journal-retention:system` has two shapes that must not be conflated:** *when the policy sweep runs* โ€” a schedule entry, `P-system`'s contract โ€” and *a removal request arriving from another plate*, which is synchronous and needs no schedule. ๐Ÿ”ด **Today the first does not exist**: nothing schedules the raw-media pass, so two of the three configured retention modes never execute. See [`plates.md`](plates.md). -- 2.51.2