# Crash-safe ownership journal for installer-managed settings entries

How an installer adding a fixed set of entries, such as exact permission allow rules, to a developer-edited settings file can install, repair and uninstall them without deleting developer entries, overriding denies or erasing unrelated edits, how recovery decides a write landed, and how a partial uninstall stays recoverable.

Exact reference: {"kind":"skill_version","skill_id":"skl_kpzAm8lR5wTfU3kKBrZGgA","version_id":"skv_IUv64-VFxq1FfuTpy_-z4g"}

Applicability: [{"constraint":"Entries are identifiable by exact value or a canonical form, and occurrences can be counted","technology":"JSON settings files edited by both developers and tools","version_scheme":"unknown"},{"constraint":"Atomic rename within one directory, file identity readable before and after the rename, and awareness that device plus inode may be reused after unlink","technology":"POSIX filesystems","version_scheme":"unknown"},{"constraint":"Recovery runs only at the next invocation, which may be much later, and the host or an editor may rewrite the file meanwhile","technology":"Installers with no background process","version_scheme":"unknown"},{"constraint":"The managed set may be split across more than one file or container, so one command performs more than one atomic replacement","technology":"Settings that span several files or containers","version_scheme":"unknown"}]

# Crash-safe ownership journal for installer-managed settings entries

## When to use
Use this when a command-line installer, plugin or setup script adds a fixed managed set of entries, such as exact-string tool permission allow rules, to a settings file the developer also edits by hand, and must later repair or uninstall them. It applies when the host application may reformat the same file, when recovery can happen only at the next invocation and possibly much later, when the managed set may be split across more than one file or container, and when deleting an entry the developer authored would be unacceptable.

## Failures it prevents
- Uninstall deletes an entry the developer authored, because ownership was inferred from file content.
- Uninstall deletes or reverts an owned entry the developer hand-edited after install, instead of treating the edit as a transfer of ownership.
- Recovery decides a write landed from file content, and so claims an entry the developer or the host added inside the crash window. This is the same defect as inferring ownership from content, one step removed.
- A landed verdict computed over an empty set of effects is vacuously true, declaring a write landed with no evidence at all.
- A set-based content hash cannot see the removal of one of two identical copies, so a crash before the rename is misread as landed and the installer's own copy is orphaned.
- A reused inode after an interrupted uninstall makes a host rewrite carry the installer's own post-rename identity, so recovery declares landed, deletes the journal and leaks every grant.
- A mistaken not-landed verdict rolls ownership back, then re-classifies the installer's own still-present entries as developer-authored, leaking every grant permanently.
- One hand-edited rule breaks unanimous corroboration for the others, so every clearly landed addition is stranded as unverified.
- A value absent from its recorded container but present elsewhere is treated as absent, goes terminal, and becomes a permanent developer entry.
- An uninstall spanning several containers is interrupted between them, and recovery either replays a plan built before the developer edited a later container or abandons the remaining containers and leaks their grants.
- A single mis-parse, or a host migration that moves the container, makes every owned entry look absent at once, and one unconfirmed observation durably lapses all of them.
- Repair treats a changed file identity as proof the host rewrote the file, but an editor that saves by temp-and-rename produces the same signal, so a grant the developer deliberately deleted is silently restored.
- The command the developer actually runs to fix things has no defined answer for a lapsed entry, so it becomes either a permanent no-op or a silent restoration of a revoked grant.
- One acknowledgement covers a batch of re-grants, so the entry the developer deliberately revoked is restored alongside the ones that were lost by accident.
- Uninstall restores a pre-install snapshot and erases unrelated later edits.
- The pre-rename check hashes only the managed container, so an in-place edit elsewhere in the file, such as a deny added while a per-entry prompt is open, is overwritten by the rename.
- The settings temp file is never flushed, so a power loss after the rename leaves a truncated target carrying the post-rename identity, and recovery declares landed, finalises and deletes the journal over a destroyed file.
- A crash between the commit mark and the journal write leaves grants with no owner, and on a first install leaves no journal at all, so a later uninstall removes nothing.
- Repair after an uninstall that left the journal behind because one entry was conflicted silently re-adds the entries the uninstall deliberately removed.
- Retiring an entry of an older generation only in the journal leaves its value in the file with no owner.
- A textual splice removes a last-position value without its separator, so the reparse fails and the fallback aborts on exactly the commented files it exists for.
- A re-install demotes owned entries to developer entries, so their grants can never be removed.
- An older installer build retires entries a newer build owns, and the two flap on every alternation.
- A duplicate copy of a managed entry permanently withdraws the authority to remove any copy.
- An ambiguous verdict becomes absorbing, so the product can never be cleanly uninstalled.
- Two concurrent runs of the same installer overwrite each other's write-ahead record.
- A probe concludes that the host preserves unknown keys because it was read back before the host had rewritten the file, and that cached conclusion later converts an undecided verdict into a verdict that authorizes writing.
- A post-write check confirms that untouched keys are unchanged, but skips the one key the write touched, which is where damage happens.
- An unbounded conflict report enumerates developer values from a large file, flooding output and leaking developer content into any shared summary.

## Steps

1. **Two artifacts, two lifetimes.** Keep a durable ownership journal holding classification, and a separate transient pending record describing one in-flight change. The journal is deleted only once an uninstall has completed and every entry in every container is terminal. The pending record is resolved at the end of every command, including ambiguous ones. Deleting the journal at the end of a normal install leaks every grant.

2. **Ownership is historical, never observational.** File content may revoke ownership; it may never establish it. With no journal, matching entries are left in place and reported, never adopted. Deciding whether a write landed is an ownership judgement, so it obeys the same rule: it may not rest on content alone, and for the verdict that authorizes deleting the journal it may not rest on identity alone either.

3. **Record per effect** the container key path addressed by key rather than array position, the exact value and a canonical form, the occurrence count observed at plan time, the count expected after the write, a classification, and the generation of the managed set that introduced it.

4. **Classify only entries the journal does not already know.** Already owned stays owned, so a re-install cannot demote its own entries. A value already present becomes preexisting and is never removed. A value an explicit deny covers is blocked and never added. A value now present only in a narrower or altered form is conflicted, not added, and reported. States that record a reason rather than a history, namely blocked, lapsed and withdrawn, are re-evaluated against the current file on every run under steps 11 and 13; only owned and preexisting are exempt from re-evaluation.

5. **Take an exclusive lock for the whole command.** Two runs of the same installer are cooperating writers, so a lock genuinely protects against them even though it cannot bind the developer's editor or the host. If locking is unavailable, name pending records per invocation and have recovery iterate over all of them, rather than keeping one slot that a second run silently destroys.

6. **Write-ahead ordering, with a witness, once per container.** Build the settings temp file first and read its file identity, which the rename preserves. Flush the temp file's data before the rename with the same primitive used for the pending record; flushing only the directory does not make the content durable, and a truncated target carrying the post-rename identity would otherwise read as landed. Persist a pending record holding the planned effects with both counts, the conflicts deliberately kept and the reason for each, the base content hash, the base file identity, the identity the temp file will carry after the rename, and the resolved target. Make the record durable with the same flush primitive used for settings. Then re-check the base hash and identity, rename, flush the directory, and only then mark the record committed. If any path between the record becoming durable and the rename exits without renaming, durably mark the record aborted; that knowledge is definitive and must not be thrown away.

    The base check must cover the whole file, not only the managed container. Record a whole-file content hash together with size and modification time alongside the container hash, and re-check all of them immediately before the rename. An in-place edit outside the container changes neither the container hash nor the file identity, so without the whole-file check the rename silently overwrites it; a deny added in that window is the most dangerous example, because the rename both erases the deny and lands the allow it was meant to block. Build the temp file only after every per-entry prompt of step 13 has been answered, never before, because a prompt makes the window unbounded, and re-parse the file after the prompts rather than reusing the document read before them.

    A record marked committed remains a pending record until its effects have been transcribed into the journal. Recovery replays the bookkeeping of a committed record before any other work. Without this, a crash between the commit mark and the journal write leaves grants with no owner, and on a first install leaves no journal at all, so a later uninstall removes nothing.

    The content hash is computed over the sorted multiset of canonical values in the container, never over a set and never in file order. A set hash cannot see the removal of one of two identical copies, so the pre-write and post-write hashes coincide and a crash before the rename is read as landed, orphaning the installer's copy. An order-preserving hash turns every host reorder into an undecided verdict. When the planned effects would leave the multiset unchanged there is nothing to witness: skip the write and record no pending entry.

7. **Patch, never restore.** Change only owned effects in the live document. Confirm first that parsing and re-serializing the unmodified bytes reproduces them; otherwise edit the text surgically. When splicing a value out of an array textually, remove its adjacent separator as well, taking the preceding comma when the value is the last element; otherwise the reparse fails on exactly the commented files that needed the textual path. Before the rename, re-parse the bytes produced and assert both that every untouched key is unchanged and that each touched collection equals its prior contents minus exactly the planned occurrences of exactly the planned values. Preserve file mode, and for a linked path write beside the resolved target on the same filesystem, since a cross-device fallback would void atomicity. This is what preserves unrelated developer edits: nothing ever writes from a snapshot or from a stale plan.

8. **One removal procedure, shared by every command including recovery.** Compare on parsed values, never on bytes. First search the whole document for the recorded value and for near matches of it, then decide. Present in its recorded container at its recorded count means remove exactly that many. Present at a count above the recorded one means remove exactly the recorded number of occurrences: identical values are interchangeable, so counting alone guarantees no developer copy is lost. Present at a count below the recorded one, present only as a near match such as an altered pattern, a broadened or narrowed form, or the same value in a different container, means remove nothing and record a conflict; the developer's edit has transferred ownership to them. Only when neither the exact value nor a near match exists anywhere is the entry absent, which means do nothing, and it may be recorded as lapsed only under the clean-read precondition in step 11. Near matching runs before the absent test precisely so a moved or edited value cannot go terminal as absent. Near matching may only report and block additions; it must never reach the removal path. Denies are never added, edited, reordered or removed. An owned entry withdrawn because a deny now covers it is withdrawn through this same procedure, and coverage is computed by exact value only. If the deny language admits patterns, a pattern that appears to cover an owned value is an inference, so it may report and block an addition but may never authorise a removal.

9. **Deciding whether the write landed.** This is one bit per rename, and it must rest on a witness other writers cannot forge. Compare the file identity now at the target against the two identities recorded before the rename. The base identity means not landed, and that verdict is safe to act on alone, because a false not-landed only re-derives a plan from current state. The post-rename identity means landed only if identity carries a component that is not reused after unlink, such as file birth time together with device and inode. Where identity is device and inode alone, a matching post-rename identity is necessary but not sufficient for landed, because the orphan temp file of an interrupted uninstall can be removed and its inode reused by a later host rewrite; in that case content must also corroborate. Content corroboration requires at least two effects that are not no-ops. For removals it requires all of them absent at their recorded location. For additions it requires none of them exactly absent, where a near match counts as present, because the base count was zero and an edited value is evidence the addition arrived before it was edited. One effect cannot certify itself, since its presence and its absence are each consistent with two opposite histories. An empty set of such effects certifies nothing; treat it as a precondition failure rather than a satisfied universal. In every landed branch, additionally require that the target parses cleanly and the recorded container resolves. A target that carries the post-rename identity but does not parse, or has lost the container, is a durability failure rather than a landed write; treat it as undecided, report it prominently and do not finalise. Everything else is undecided.

10. **Recovery actions.** Landed means finish bookkeeping only, using the effects the pending record already holds, and an entry planned for removal that is still present was re-created by the developer, so report it and leave it. Not landed, on positive evidence, means roll the journal back; any value the rolled-back plan intended to add that is nonetheless present becomes unverified, never silently preexisting, because reclassifying it as developer-authored leaks the grant forever. Then re-derive the comparison against the file as it exists now under step 8; never replay the stale plan. Undecided means change nothing in settings, but still resolve the pending record by transcribing its effects into the journal as unverified and clearing the slot: ambiguity is durable state, a write-ahead slot is not. Unverified entries need an explicit adopt before they can ever be removed, and uninstall may finish while leaving them in place, marking them terminal and exiting with a status distinct from success. Read-only commands write nothing and only report that recovery is pending, and recovery never completes an uninstall during an unrelated command.

11. **Terminal is not always permanent, and absence is not always evidence.** Distinguish terminal by completion, where an uninstall finished, from withdrawn, where an entry was removed for a reason that may later stop holding. An entry withdrawn because a deny covered it, or left behind by an abandoned uninstall, becomes eligible again for ordinary classification once that reason is gone; otherwise the installer is silently and permanently incomplete. A conflicted entry is retained while the variant that caused the conflict is present, and is re-reported at every later invocation. Only an explicit per-entry override naming the exact value may remove a conflicted variant; a blanket force flag is not acceptable, since one acknowledgement over a batch removes the edit the developer meant to keep alongside the ones they did not.

    An absent owned value is evidence of lapse only from a clean read: the file parsed without falling back, the recorded container resolved, and at least one other managed key was found where expected. A container that does not resolve is never evidence of lapse. A host migration to a new key path, a reinitialisation to defaults, and a dialect the host accepts but the parser does not all present as a missing container, and each lapses every owned entry in one pass. Record the first qualifying observation as a provisional lapse stamped with the file identity it was seen at, and promote it to settled only on a second qualifying observation at a different identity. A provisional lapse is resurrectable in the same way as a withdrawal; a settled one still yields to an explicit adopt.

12. **Generation comparison is ordered.** Retire an owned entry only when its recorded generation is strictly older than the running installer's and the entry is absent from the current managed set. A recorded generation that is newer or unrecognised means leave the entry alone and report that a newer build manages it. Keying retirement on absence from the current set alone lets an older build delete a newer build's grants. Retirement is a removal: it routes through the procedure of step 8 under its own pending record, and only becomes terminal when that removal is decided. A journal-only retirement leaves the value in the file with no owner, so it can never be removed.

13. **Repair and install do not guess at intent, and they answer per entry.** An absent owned value means the same thing in every command: lapsed. Deliberate deletion is the most common way a developer revokes a grant and it leaves no residue, so absence of evidence of intent is not evidence of accident. Record with each lapse the cause actually known, and only two causes are knowable: removed by this installer's own uninstall, or unexplained. A changed file identity is not evidence that the host rewrote the file, because an editor that saves by temp-and-rename changes identity in exactly the same way, so a lapse first seen across an identity change is still unexplained. Install and repair both re-add a lapse of the first kind and both require an explicit request for an unexplained one, and neither re-adds a value a deny now covers. An entry terminal by completion is never re-added by install or repair, even when the journal survives because a sibling entry is conflicted or unverified; only an entry withdrawn by an abandoned uninstall, one that did not reach completion before the developer ran install or repair instead of retrying, qualifies as the first kind of lapse. Leaving install undefined here is not neutral: refusing every lapse makes install a permanent no-op after one accidental deletion, while accepting every lapse makes the request pointless, because install is the command developers run. Make the request per entry and name the exact value being restored, since one acknowledgement covering a batch restores the grant the developer revoked alongside the ones that were lost by accident.

14. **One command, several containers: each container is its own commit unit.** When the managed set spans more than one file or container, the command performs more than one atomic replacement, so partial completion is real. Give each container its own pending record and its own pair of recorded identities under step 6, and resolve each independently under steps 9 and 10. Because a single atomic replacement either lands whole or not at all, this confines partial state to the boundary between containers, where a per-container witness can decide it, and removes it from inside any one file.

    Three rules follow. Only the verdict survives recovery, never the plan: a not-landed container discards its recorded plan and re-derives the comparison under step 8 against the file as it exists now, because the recovery window is unbounded and the developer may have edited that container since. Resolving a pending record is not executing it: if the next command is an install rather than a retried uninstall, recovery reconciles the journal for each container and stops, so a stale removal cannot fire in the middle of an install. And terminality is per container but deletion of the journal is global, so an uninstall that finished three containers and left one undecided is not complete and must exit with the status distinct from success.

    Because every effect is decided from current state under one shared removal procedure, a re-run after any interruption never compounds damage. It does not always converge on its own: an interruption followed by an editor save that changes the identity reaches undecided, every planned effect becomes unverified, and the retry removes nothing until the developer adopts each entry. That stall is the intended safe direction, and the report must say so plainly. Do not shortcut it by reading a removal-only plan whose values are all still present as not landed: a restored backup or a settings file copied from another machine reproduces every planned removal after a landed rename, and re-deriving would then remove them without a report.

15. **Bound the conflict report.** Key every reported conflict by its kind, by the container path with array indices replaced by a wildcard, and by the managed entry identity or a single bucket for unrecorded values, and carry a count per key. The number of distinct keys is then kinds times containers times managed identities plus one, independent of how many entries the developer's file holds. Unrecorded developer values are counted, never listed, which also keeps developer content out of any summary that leaves the machine.

## Limits
- A lock binds only other runs of the same installer. The race with the developer's editor and with the host is narrowed by re-checking the whole-file hash, size, modification time and identity immediately before the rename, but a residual window is inherent to rename and cannot be closed.
- File identity built from device and inode alone is reused after unlink on common filesystems. Adding birth time makes reuse improbable but not impossible, which is why step 9 requires content corroboration for the landed verdict wherever identity may be reused. A per-write random value held in the durable journal and written into the file is the portable fallback, but a host that strips unknown keys defeats it. Detection of stripping is not a one-time probe: a host strips only when it next rewrites on its own schedule, so a probe read back promptly always reports that the key survived. Treat the probe as unproven until the file has demonstrably been rewritten by the host at least once, revalidate it when the host version changes, and while it is unproven prefer undecided over the verdict that authorises writing.
- An in-file monotonic counter is not a valid witness: it restarts after a strip and is reproduced by a restored backup or a copied settings file.
- A content hash, even as a sorted multiset, is forgeable by a developer who reproduces the managed set by hand after a failed run. That is why it corroborates and never decides alone.
- Counting a near match as present for addition corroboration admits the case where the developer typed a similar value inside the crash window. The consequence is bounded: the entry is recorded as owned, and a later uninstall finds only a near match and keeps it as a conflict, so nothing of the developer's is removed.
- Treating every non-self lapse as unexplained means an accidental loss caused by the host requires the same per-entry request as a deliberate deletion. That is the safe direction: the request names the value and costs one confirmation, whereas the alternative silently restores revoked grants.
- Making each container a commit unit bounds partial completion but does not make the command atomic across containers. A developer who interrupts an uninstall can observe a state in which some grants are gone and some remain, and must be told that in the report rather than left to infer it.
- The undecided branch leaves grants in place, which is safe for developer data and unsafe for authority. Its report must be prominent and repeated, and the opposite trade needs an explicit override.
- Lapse confirmation costs a second invocation, so a genuine deletion is reported later than it happened. That delay is the price of not lapsing everything on one bad read.
- A developer who deletes an owned entry and retypes the identical value before any command runs leaves no trace, so a later uninstall removes their copy. This is inherent to identifying entries by exact value, not a defect the journal can repair.
- A host that canonicalises entries can turn an owned value into an apparent developer edit and orphan the grant. Recording a canonical form alongside the exact value reduces this but does not remove it.
- Restricting deny coverage to exact values means a pattern deny that genuinely shadows an owned value will not withdraw it. The entry stays owned and granted-in-name-only until the exact test matches, which is the safe direction but is not the same as correct.
- Trusting a bare base-identity match for the not-landed verdict on filesystems where identity is device and inode alone admits a false not-landed after inode reuse. The consequence is that the installer's own additions become unverified until adopted, which stalls but loses nothing.
- Ownership across moved or version-controlled settings files needs separate design.

## Evidence status
Reasoned design only, refined across eight independent reviews that were themselves carried out by reasoning. The eighth review handed the full design to a separate reviewing agent instructed to reason only, read nothing and run nothing; it returned nine candidate findings, of which five were confirmed by re-tracing and adopted in this version, three were rejected as already documented limits, and one suggested shortcut was declined because a restored backup defeats it. No implementation exists, and no tests or crash-injection runs have been executed. The witness mechanism in step 9 is the least settled part and should be treated as unverified; the per-container commit units in step 14 inherit that uncertainty, since they multiply the same witness rather than replacing it. The durability claims about flushing and the in-place write behaviour of specific editors and hosts need executed tests on each target platform.

## Suggested tests
- Crash injection at each step of install, repair and uninstall, with plans of zero, one, two and the full number of effects that are not no-ops.
- A deny added to the file in place while a repair prompt is open, checking that the pre-rename check refuses the write and the deny survives.
- Simulated power loss after the rename with an unflushed temp file, checking that a truncated target is read as undecided and the journal is kept.
- A crash between the commit mark and the journal write on a first install, checking that the next command replays the bookkeeping and the journal owns all six rules.
- A duplicate copy present, an uninstall planning to remove one copy, and a crash before the rename, checking that recovery reads not landed rather than landed.
- Removal of the last element of a commented array through the textual path, checking that the result reparses and the tree comparison passes.
- A developer hand-edits one owned rule and an unrelated key after install, then uninstalls, checking that the edited rule is kept and reported, the unrelated key is untouched, and the remaining owned rules are removed.
- An uninstall that finishes with one conflicted entry and a surviving journal, followed by repair, checking that no terminal entry is re-added.
- A developer deletes one owned rule with an editor that saves by temp-and-rename, saves an unrelated edit later, then runs repair, checking that nothing is re-added without an explicit per-entry request.
- An uninstall interrupted between the pending record and the rename, followed by removal of the orphan temp file and a host rewrite that reuses its inode, checking that recovery reaches undecided rather than landed and does not delete the journal.
- An install whose rename landed but was never committed, followed by a hand edit to one owned rule and an uninstall, checking that the unedited rules are corroborated as landed and removed rather than stranded as unverified.
- An owned value moved by hand to a different container, checking that it is conflicted rather than lapsed.
- Crash injection between containers during an uninstall that spans several, with a developer edit applied to a later container inside the window, checking that the re-derived plan removes nothing the developer authored and that the run converges on retry.
- An install whose write did not land, followed by the developer or the host adding one managed value by hand before the next invocation.
- An uninstall whose write landed but was never committed, followed by the developer re-creating the values by hand or restoring a backup, checking that recovery does not remove them.
- An interrupted uninstall followed by an install rather than a retried uninstall, checking that the journal is reconciled and no removal fires.
- A forced mistaken not-landed verdict, checking that still-present intended additions become unverified rather than preexisting.
- A host migration that moves the permissions container, and separately a dialect the parser rejects, each observed by a read-only command, checking that no lapse is recorded and that the next clean read restores normal operation.
- An accidental loss of every managed entry followed by install and then repair, checking that each entry is named and requested separately.
- A deny added after install and later removed, checked at repair and uninstall, including a pattern deny that covers an owned value without matching it exactly.
- A newer installer build that drops one rule, run against a journal from an older build, checking that the retired value is actually removed from the file and not only from the journal.
- An older installer build run against a journal written by a newer one.
- Two installer runs started concurrently against the same file.
- A duplicate copy added after install, then uninstall.
- A settings file with thousands of developer entries, checking that the conflict report stays within the key bound and lists no developer values.
- A host-driven reformat, a host that strips unknown keys only on its own later rewrite, and a settings file copied from another machine.

## Supporting basis and limitations

Reasoned analysis only. No implementation exists, and no tests, crash injection or configuration inspection were executed. The design in the base version was restated in the cited conversation and handed to an independent reviewing agent instructed to reason only, read no files and run no commands. The reviewer returned nine candidate findings; each was then re-traced by the author before adoption. Five were confirmed as concrete errors in the base version. First, the pre-rename check compares the container multiset hash and the file identity, and an in-place edit outside the container changes neither, so with a repair prompt open the developer or host can add a deny that the rename then overwrites. Second, the base version flushes the pending record and the directory but never states that the settings temp file itself is flushed, and its birth-time landed branch has no parse requirement, so a truncated post-rename target reads as landed. Third, recovery is defined only for uncommitted pending records, leaving no path for a record committed before the journal write. Fourth, the rule that repair re-adds a lapse caused by the installer's own uninstall did not exclude entries terminal by completion in a journal that survived because a sibling was conflicted. Fifth, generation retirement was not stated to route through the removal procedure. Three findings were rejected as already documented limits or already handled: exact-value identity cannot detect a developer who deletes and retypes an identical value, linked paths are handled by writing beside the resolved target, and the asymmetric trust of a bare base-identity match for not landed is a deliberate safe-direction choice. One reviewer suggestion was declined: resolving an undecided removal-only plan as not landed whenever every planned removal is still present, because a restored backup or a settings file copied from another machine reproduces all planned removals after a landed rename, and re-deriving would then remove them without a report. The convergence claim was instead qualified rather than strengthened.

## Change and rationale

Five corrections from an eighth independent reasoning review. The pre-rename base check must cover the whole file, not only the managed container, and the temp file must be built after any per-entry prompt, or an in-place edit such as a new deny is overwritten. The settings temp file must be flushed before the rename, and every landed verdict must also require a clean parse with the container present. A record marked committed stays pending until transcribed into the journal. Entries terminal by completion are never re-added by repair; retirement of an older generation routes through the removal procedure. The convergence claim is qualified: a retry never compounds damage but can stall in undecided until a per-entry adopt.

Each gap turns an ordinary sequence into one of the failures the skill exists to prevent. A container-only base check lets the rename erase a deny added while a repair prompt is open, which is a deny override and an unrelated-edit loss in one step. An unflushed temp file plus an identity-only landed branch lets a power loss finalise and delete the journal over a truncated file. A crash between the commit mark and the journal write on a first install leaves six grants and no journal, so uninstall removes nothing. Repair after a partial uninstall re-adds deliberately removed grants unless terminal by completion is excluded. A journal-only retirement leaves an orphaned value. The convergence wording promised more than the undecided branch delivers.
