116 lines
6.3 KiB
Markdown
116 lines
6.3 KiB
Markdown
---
|
|
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."
|
|
---
|
|
|
|
# One fact, one owner: dedupe the vault without losing a fact
|
|
|
|
Use when a repo has accumulated one page per attempt/era (runbooks, bench logs, swap
|
|
narratives) and the law is **one page owns one thing**. Applies to
|
|
`serverAppConfigs-docs/` (Obsidian vault, `AGENTS.md` → *Documentation Policy*), but the
|
|
mechanics are general.
|
|
|
|
## 1. Decide ownership before touching files
|
|
|
|
- Endpoint/service page owns: current shape, **Eras** table (window | payload | runtime |
|
|
shape | replaced because | rollback), **Laws**, wire contract, files.
|
|
- Technique page owns the mechanism and the arithmetic — **no per-endpoint throughput
|
|
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.
|
|
|
|
## 2. Single-writer rule (the mistake that costs the most)
|
|
|
|
Facts get **ported into the owner page before they are cut** anywhere else. So while any
|
|
subagent may port facts, only ONE writer may touch the owner pages. Spawn children with an
|
|
explicit `DO NOT EDIT <owner pages>` and require a `needsPort` list in their report instead
|
|
of letting them append. Send the narrowing message early — a child already editing them has
|
|
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.
|
|
|
|
```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)
|
|
```
|
|
|
|
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.
|
|
|
|
## 4. Deletion mechanics
|
|
|
|
- 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`.
|
|
|
|
## 5. Rewire + 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
|
|
|
|
`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.
|
|
|
|
## 7. 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
|
|
line untouched and PUT only the indented continuation lines, or convert the note to a nested
|
|
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)
|
|
|
|
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`.
|
|
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".
|
|
|
|
## 9. Verify, open, land
|
|
|
|
- `./tests/run.sh` (full suite) + `bats tests/docs_completeness.bats` +
|
|
`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.
|
|
- 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.
|
|
- Docs-only change ⇒ **no deploy**. Fast-forward the deploy checkout only
|
|
(`git -C ~/Projects/serverAppConfig merge --ff-only origin/main`).
|