@@ -1,6 +1,6 @@
---
name : vault-one-fact-one-owner-dedupe
description : "Collapse per-payload/per-attempt vault pages into one owner page per endpoint without losing a fact: port-before-cut orphan census, index regeneration, the one-page seam , and the merge-resolution order."
description : "\" Collapse per-payload/per-attempt vault pages into one owner page per endpoint without losing a fact: port-before-cut orphan census, doubled-page repair, trash-aware tools, delegated-write verification, index regeneration , and the merge-resolution order.\" "
---
# One fact, one owner: dedupe the vault without losing a fact
@@ -18,6 +18,9 @@ mechanics are general.
table or boot ledger**.
- `log.md` owns incidents and laws only. A swap is an Eras row, never a log entry.
- `AGENTS.md` carries operator-critical numbers + cross-border laws; everything else links.
- A **retired ** endpoint still gets one locator page: era windows quoted verbatim from each
era page's own banner, no numbers restated, and any date conflict recorded as an open
question instead of silently resolved. Never pick a winner between two primary sources.
## 2. Single-writer rule (the mistake that costs the most)
@@ -29,56 +32,71 @@ to be reconciled by hand.
## 3. Port-before-cut census (proves nothing was lost)
For every deleted line, extract distinctive keys (integers ≥ 4 digits with commas stripped,
decimals with ≥ 2 fraction digits) and check them against the whole vault minus the file
being rewritten. Any line with **no ** surviving key is an orphan → port, or accept
deliberately and say so .
For every deleted line, extract distinctive keys and check them against the vault minus the
file being rewritten. Any line with **no ** surviving key is an orphan → port, or accept
deliberately and say so. Use `scripts/port-census.py <new-dir> <git-HEAD:path|file>` (numbers
with ≥ 4 int digits, decimals, flags, paths, backticked spans, `--` , ALL-CAPS, or ≥ 2 hyphens) .
``` python
num = re . compile ( r ' \ d[ \ d,]*(?: \ . \ d+)? ' )
def keys ( line ) :
out = set ( )
for n in num . findall ( line ) :
i = n . split ( ' . ' ) [ 0 ] . replace ( ' , ' , ' ' )
if len ( i ) > = 4 : out . add ( i )
if ' . ' in n and len ( n . split ( ' . ' ) [ 1 ] ) > = 2 : out . add ( n . replace ( ' , ' , ' ' ) [ : 6 ] )
return out
# orphan = deleted line whose keys are absent from corpus(vault minus target file, minus .AGENTTRASH)
```
Two calibration traps:
- **Prose words are not facts.** A hyphen or trailing period inside a word (`long-standing` ,
`deployed:` ) was counted as a tracked token while real identifiers hid behind it; that noise
buried 300 real lines under 979 fake ones. Strip punctuation, require fact shape.
- **The corpus decides the question.** Corpus = the new file only → "what did this rewrite
drop?" Corpus = whole vault → "is the dropped fact orphaned repo-wide?" Ask one at a time.
Expect ~30 orphans per 1,000 deleted lines, of which ~40 % are process trivia recoverable
from git (PR/commit hashes, per-boot log lines). Port the measurement/law ones to the owner
page's Laws/Eras, and list the accepted losses in the commit message and PR body. Also grep
`AGENTS.md` separately — a fact it carries is not an orphan.
Also grep `AGENTS.md` separately — a fact it carries is not an orphan.
## 4. Deletion mechan ics
## 4. When a page is the same page tw ice
- Trash is **gitignored ** (`.gitignore` has `.AGENTTRASH/` ). Move with plain `mv` into the
repo-root dated dir, e.g. `.AGENTTRASH/<YYYYMMDD>-topic/LLM/` . `git mv` a tracked page into
it stages a rename * into an ignored path * (index entry the next agent cannot see); undo with
`git restore --staged <src> <dst>` and let the deletion be the change.
- Never `rm` , never `git clean -f` .
Signature: a page whose `wc -l` is ~2× its `HEAD` size, with a **second frontmatter block in
the body** whose closing `---` is glued onto the end of a body/timestamp line. Cause chain: a
frontmatter rewrite that re-reads the file it just wrote (so its own output becomes the body),
then a fence-repair pass keyed on `line == "---"` that cannot see a glued fence and inserts a
fence mid-page, so the next pass treats the tail as a new document .
## 5. Rewire + indexes mechanically
Un-double rule that worked: find the first frontmatter's close (accepting a glued fence), then
the first `type:` line after it that has more keys (`title|description|tags|timestamp` ) behind
it — that is the second copy, and a stray `type:` in a code block has no key run. Keep the copy
whose body is a **superset ** (compare after stripping the dropped copy's own frontmatter),
un-glue the fence, and prove `0` census losses before writing. Refuse to guess: if the halves
differ materially, stop.
## 5. `.AGENTTRASH` is not vault content
Every tool that walks the vault must exclude it — **both the scan set and the resolution
index**. The link checker resolving links * into * the trash turns "live page points at a
deleted page" into a false PASS, which is worse than a false alarm. The `docs_completeness`
seam's `find` must carry `-not -path '*/.AGENTTRASH/*'` in both walks, or a trashed page
demands an index row and a trashed page can satisfy the docs requirement for a live service.
Trash is gitignored; move with plain `mv` into the dated dir. `git mv` into an ignored path
stages a rename the next agent cannot see (`git restore --staged <src> <dst>` ). Never `rm` ,
never `git clean -f` .
## 6. Rewire + regenerate indexes mechanically
``` bash
grep -rnoE "\[\[(page-a|page-b|...)\]\]" --include= "*.md" serverAppConfigs-docs | grep -v AGENTTRASH
grep -rnoE "\((page-a|page-b|...)\.md\)" --include= "*.md" serverAppConfigs-docs | grep -v AGENTTRASH
```
Both wikilinks and markdown path refs must be repointed (Research `See Also` lists and
`ubatch` -style bench pages are where path refs hide). Then **regenerate ** index pages with a
script that walks the vault (`os.walk` , skip `.obsidian` /`.AGENTTRASH` , one row = `[[basename]]`
+ first-sentence description stripped of markdown links) rather than hand-editing rows — the
`docs index links every docs page` seam fails on any row left behind.
## 6. Enforce the law with a seam
Both wikilinks and markdown path refs must be repointed (Research `See Also` lists and bench
pages are where path refs hide). Then **regenerate ** index rows with a script, never by hand.
The generator bug to check for: presence tested as a **bare stem substring ** — a page whose
name prefixes another's (`frank-medium` inside `frank-medium-rocmfpx` ) then silently never
gets a row. Test the wikilink form `[[<stem>]]` .
## 7. Enforce the law with a seam
`tests/docs_completeness.bats` : derive the live set instead of hardcoding it — an endpoint is
live when `scripts/` holds **both ** `<name>.env` and `<name>.service` ; fail if any
`<name>-*.md` page exists in the vault , and guard against a vacuous derivation
( `derived -ge 3` ). A re-created era page then cannot pass tests.
`<name>-*.md` page exists, and guard against a vacuous derivation ( `derived -ge 3` ). A
re-created era page then cannot pass tests. For the length hint, prefer **derived ** exemptions
(live endpoint, or a `retired-endpoint` tag: a campaign ledger that exists nowhere else is
owned by the page that measured it) plus one documented per-page opt-out; never let a size
ceiling force a ledger to be split across two owners.
## 7 . Edit-tool hazard (costs the most time)
## 8 . Edit-tool hazard (costs the most time)
Body rows beginning with `-` or `+` are op markers. The documented `+-` escape **writes a
literal `+` ** — do not use it. Safe rewrites inside an existing bullet: keep the `- ` opener
@@ -86,30 +104,49 @@ line untouched and PUT only the indented continuation lines, or convert the note
bullet whose body lines start with two/four spaces. If a wrapped `[[Two Word Page]]` shows up
as an unresolved link, the target was hard-wrapped across lines — unwrap it.
## 8 . Merge order (a long-lived branch under a moving main)
## 9 . Delegated writes: verify bytes, never narrative
Measured in one campaign, all four failure modes live:
- A subagent killed by the runtime limit yielded a 495-line owner page, a verified era table
and a byte census — **the file did not exist ** and its table was self-referential.
- Another wrote its output to disk and died without reporting, leaving a rewritten `log.md` ;
the census showed it had dropped laws held nowhere else.
- Verdict tables claimed "0 sole records / no contradictions" while missing the contradiction
sitting in their own evidence row, and invented bullet IDs — one renamed a bullet to the
**opposite ** claim.
- A scout called accurate docs "WRONG" because the docs said `wip: "down"` .
Rules: after ANY delegated write run `wc` /`ls` + the orphan census yourself; re-grep every
claimed holder before cutting a line; treat `cancelled` /timed-out yields as unverified no
matter how concrete they read; and re-read the source bullet before trusting a table's ID.
## 10. Merge order (a long-lived branch under a moving main)
1. Commit your work in atomic groups **before ** merging (a dirty tree cannot merge cleanly).
2. Inspect the incoming commits first : `git log --oneline --stat HEAD..origin/main` . If they
modify a page you deleted, **port their additions into the owner page ** , then merge and
resolve the modify/delete as `git rm` .
2. Inspect the incoming commits: `git log --oneline --stat HEAD..origin/main` . If they modify
a page you deleted, **port their additions into the owner page ** , then merge and resolve
the modify/delete as `git rm` .
3. Resolve a content block with `write conflict://<id>` (`@ours` / `@theirs` / explicit text),
then * * `git add` the file** — resolving content does not stage it, and `git commit` refuses
with "you have unmerged files".
4. `git * --no-verify` is forbidden here; commit hooks are fine.
5. Sweep for dead paths left in `AGENTS.md` : `grep -oE "[A-Za-z0-9._-]+\.md" AGENTS.md | sort -u`
then `find` each. Expect false positives from names hard-wrapped across two lines — confirm
each with `ls` before "fixing".
then * * `git add` the file** — resolving content does not stage it.
4. **Re-probe live state after the merge, before you write any number. ** A campaign that
reported fleet shapes at the start was made stale by a mid-campaign reslice on main: probe
`/slots` and `tr '\0' '\n' < /proc/$(pidof <engine>)/cmdline` again at the end, and let the
live answer beat both your earlier probe and the docs.
5. `git * --no-verify` is forbidden here. Sweep dead paths in `AGENTS.md`
(`grep -oE "[A-Za-z0-9._-]+\.md" AGENTS.md | sort -u` , then `find` each; hard-wrapped names
are false positives — confirm with `ls` ).
## 9 . Verify, open, land
## 11 . Verify, open, land
- `./tests/run.sh` (full suite) + `bats tests/docs_completeness.bat s` +
`python3 scripts/check-vault-links.py serverAppConfigs-docs` → must print `0 unresolved links` .
- Env trims: prove value-neutrality mechanically — `git show HEAD:scripts/<svc>.env | grep -E
'^[A-Z_]+=' | sort` vs the working copy, diff must be empty, then run that service's seam.
**Warn that ` scripts/<svc>.env` is a deploy-planner path**: a comment-only trim makes the
next service-scoped deploy restart a healthy endpoint for identical values.
- `./tests/run.sh` (full suite) + `python3 scripts/check-vault-links.py serverAppConfigs-doc s`
→ must print `0 unresolved links` , and the OKF linter `0 violation s` .
- Env trims: prove value-neutrality mechanically ( `git show HEAD:scripts/<svc>.env | grep -E
'^[A-Z_]+=' | sort` vs working copy, diff empty). **` scripts/<svc>.env ` is a deploy-planner
path**: a comment-only trim makes the next service-scoped deploy restart a healthy endpoint.
- Open the PR through the API with a 600-perm curl config (managed skill
` gitea-api-pr-operations`), end the body with the ` cwd:` / ` session:` footer, then
` xdg-open <PR-URL>` the instant it exists.
` xdg-open <PR-URL>` the instant it exists. Never merge without Frank's current explicit OK.
- Docs-only change ⇒ **no deploy**. Fast-forward the deploy checkout only
(` git -C ~/Projects/serverAppConfig merge --ff-only origin/main`).
- Say what was NOT done, in the PR body: soft lint tallies left (missing ` ## Citations`),
conflicts left standing, repo config that was never installed on a host.