Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/conventions/topic-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog — topic-docs convention

## 2.4.3 — 2026-08-15

Docs-only: the prune recovery pointer no longer pretends squash-merge
preserves branch ancestry. Step 5 now prescribes the Contents API form
`?ref=<pre-prune-commit>` (no `^`), states that unreachable-object
retention is best-effort with no promised lifetime, and makes the
graduation targets (ADR / specs / tracker items) the load-bearing
record. Step 2's "reference the rest" pointer names that pre-prune SHA
plus those targets. (#2699)

## 2.4.2 — 2026-08-12

Docs-only: the contract-slice lifecycle now documents how to retrieve a
Expand Down
46 changes: 30 additions & 16 deletions docs/conventions/topic-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,10 @@ a `<STAGE>-<scope>.md` sidecar.
pasted into the PR description inside `<details>` blocks (bodies cap
near 64 KB — paste the contract, reference the rest). When the
contract exceeds the cap, paste the summary and verification digest in
the body and **name the pruning commit** (or the PR number plus the
path) for everything else — a pointer without a followable ref is not a
preservation.
the body and **name the pre-prune commit SHA** (Contents API form in
step 5) plus where durable outcomes graduated — under squash-merge the
SHA form is best-effort; the graduation targets are the load-bearing
record.
3. Before merge, durable outcomes graduate: architectural decisions and
specs through the **knowledge-vault seam** (default: history-preserving
`git mv` into `docs/adr/` / `docs/specs/`; remote vault backends
Expand All @@ -436,23 +437,36 @@ a `<STAGE>-<scope>.md` sidecar.
4. A final commit prunes the contract slice `<contract_dir>/<slug>/`
(default `docs/topics/`), leaving context pointers (the PR body and
the promoted-doc / tracker locations) in its place.
5. **Retrieving a pruned slice after merge.** The task branch is deleted on
merge and GitHub's three-dot PR diff drops pruned files, so
`docs/topics/<slug>/…` on `main` will not resolve. The pruned content
remains reachable through the merge commit **immediately before** the
pruning commit:
5. **Retrieving a pruned slice after merge (best-effort).** Squash-merge
collapses the task branch into one new commit on `main` and carries
none of its ancestry; the head branch is deleted on merge. GitHub's
three-dot PR diff also drops pruned files, so `docs/topics/<slug>/…`
on `main` will not resolve. Local `git show <pre-prune-sha>:<path>`
fails from a fresh clone until that object is fetched (for example
via `git fetch origin refs/pull/<N>/head` when permitted — typically
the machine that wrote the pointer already has it). The Contents API
form `?ref=<pruning-commit>^` fails for a different reason: it is a
remote lookup, and the squash commit's parent never contained the
slice, so naming the parent of the pruning/squash commit is not a
recovery path regardless of local checkout state.

While GitHub retains the unreachable object, the Contents API can still
resolve a **pre-prune commit SHA** (the last commit that still
contained the slice — name that SHA in the PR body before merge):

```bash
# pruning commit = the merge commit whose message prunes the slice
gh api "repos/{owner}/{repo}/contents/docs/topics/<slug>/PLAN.md?ref=<pruning-commit>^" --jq .size
# pre-prune commit = last commit on the task branch that still held the slice
gh api "repos/{owner}/{repo}/contents/docs/topics/<slug>/PLAN.md?ref=<pre-prune-commit>" --jq .size
```

Given only a merged PR number, list its commits and take the pruning
commit from that list; use `<sha>^` as `ref`. `git fetch origin
refs/pull/<N>/head` may be denied by a consumer permission layer — the
Contents API form above works after the branch is gone. Step 2's
"reference the rest" pointer should name that pruning commit (or the PR
number plus path) so the reference is followable without archaeology.
That retention is an implementation detail with no promised lifetime —
convenience, not a recovery guarantee. The load-bearing record is
where durable outcomes graduated (ADR / specs via the vault seam,
tracker items via the work-item seam); the PR body must name those
locations. Given only a merged PR number, list its commits and take
the pre-prune SHA from that list. `git fetch origin refs/pull/<N>/head`
may be denied by a consumer permission layer — the Contents API form
above is the followable best-effort pointer after the branch is gone.
6. Enforcement: a required check that the net PR diff
(`git diff --name-only base...head`) contains no path under the
resolved `<contract_dir>/**` (default `docs/topics/**`). GitHub's PR
Expand Down
8 changes: 5 additions & 3 deletions docs/conventions/topic-docs/examples/worked-slice.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ about to disappear from the diff.
2. Two follow-ups (rate-limit tuning, mobile client migration) become
tracker items through the work-items seam; the open question
("do we need per-device revocation?") files as a decision item.
3. The prune commit deletes `docs/topics/auth-token-refresh/`, and its
message plus the PR body point at: the PR description (plan +
verification), `docs/adr/0042`, and the tracker items.
3. The prune commit deletes `docs/topics/auth-token-refresh/`. The PR
body names the graduation targets (`docs/adr/0042`, the tracker
items) as the load-bearing record, and may also name the pre-prune
commit SHA for a best-effort Contents API recovery of the disposed
slice.
4. The required check confirms the net PR diff contains no
`docs/topics/**` path. Merge.

Expand Down