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
2 changes: 1 addition & 1 deletion docs/ATTRIBUTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ predates the k8s-semconv split into `k8s.pod.name` +
`k8s.pod.namespace` documented above. Both names co-emit through
the v0.4–v0.5 deprecation window; the legacy name is removed in
v0.6 per the two-minor removal-without-rename contract in
[Soft-lock policy](#soft-lock-policy). Migrate dashboards and
[Soft-lock policy](#lock-policy). Migrate dashboards and
LogQL queries off `evicted_pod` onto `k8s.pod.{name,namespace}`
before v0.6.

Expand Down
6 changes: 3 additions & 3 deletions docs/DEPRECATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ binding document for v1.0-rc1 cut criterion 4
and is enforced by `scripts/deprecation-check.sh`
(wired into `make deprecation-check` and CI).

Read [`docs/ATTRIBUTES.md` §Soft-lock policy](ATTRIBUTES.md#soft-lock-policy)
Read [`docs/ATTRIBUTES.md` §Soft-lock policy](ATTRIBUTES.md#lock-policy)
first for the attribute-namespace-specific rename mechanics that
this policy generalises.

Expand Down Expand Up @@ -87,7 +87,7 @@ before it can move to `removed`:
value): half the window — 1 minor for v1.x. The old name co-emits as
`deprecated` for one minor, then is `removed` in the next minor. The
attribute-namespace soft-lock policy
([`docs/ATTRIBUTES.md`](ATTRIBUTES.md#soft-lock-policy)) already
([`docs/ATTRIBUTES.md`](ATTRIBUTES.md#lock-policy)) already
documents this for `pattern.*` / `tracecore.*` / `gen_ai.*` keys;
that policy is the binding one for attribute renames specifically.

Expand Down Expand Up @@ -266,7 +266,7 @@ symbol (and add the migration guide entry).
— the binding rubric this doc satisfies.
- [`docs/RELEASE-CHECKLIST.md`](RELEASE-CHECKLIST.md) RC gates —
"Deprecation policy doc in place at `docs/DEPRECATION.md`".
- [`docs/ATTRIBUTES.md` §Soft-lock policy](ATTRIBUTES.md#soft-lock-policy)
- [`docs/ATTRIBUTES.md` §Soft-lock policy](ATTRIBUTES.md#lock-policy)
— attribute-namespace-specific rename mechanics; this policy
generalises it to non-attribute surfaces.
- [`docs/STYLE-docs.md`](STYLE-docs.md) — doc-prose conventions
Expand Down
139 changes: 139 additions & 0 deletions docs/audits/2026-06-cross-ref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Cross-reference audit — 2026-06

## Scope

Deep audit of internal cross-references across `docs/**/*.md` after the
v1-rc1 wave (partner-outreach, launch/, NetworkPolicy README,
threat-model §6.G, security-audit-rfp). Five reference kinds inspected:

1. Markdown links to other `.md` files (relative + repo-root-absolute paths).
2. Markdown links with `#fragment` anchors (heading slugs).
3. PR / issue numbers (`#NNN`).
4. RFC numbers (`RFC-0NNN`).
5. Memory-style slugs (`[[name]]`).

## Method

`scripts/md-anchor-check.py` (introduced by this audit, see §"Enforcement"
below) holds the GitHub-flavored markdown heading-slug algorithm and
walks every git-tracked `.md` file, validating relative links and
their `#anchor` fragments against the resolved target's heading
inventory. RFC numbers were validated by spot-check against
`docs/rfcs/`. PR/issue numbers were not validated (no offline source of
truth; they resolve to github.com).

Historical-record exemptions (consistent with the rest of
`scripts/doc-check.sh`):

- `docs/rfcs/**` — RFCs intentionally reference deleted / pre-merge
artifacts as part of the design record.
- `docs/research/**` — raw extracts from external sources.

## Before / after

| Reference kind | Total | Broken (pre-audit) | Broken (post-audit) |
| ------------------------------------ | ----- | ------------------ | ------------------- |
| `.md` file targets | 1099 | 0 | 0 |
| Non-`.md` intra-repo targets | 235 | 0 | 0 |
| `#anchor` fragments (this audit) | 217 | 15 | 0 |
| YAML cross-links into `docs/` | 48 | 0 | 0 |
| RFC-style references | 677 | 0 | 0 |
| `[[memory-slug]]` references | 6 | 0 | 0 |

The file-existence and YAML-link gates were already green before this
audit — `make doc-check` was exit-0 from PR #459 onward. The drift
this audit caught lived one layer down: the fragment portion of the
`.md` link, which the existing gate intentionally stripped.

## Fixes applied (15 broken anchors → 0)

All fifteen breaks trace to two heading renames where the consuming
docs were not updated:

### 1. `ATTRIBUTES.md` heading rename: `Soft-lock policy → Lock policy`

The page's anchor became `#lock-policy` but five call sites still
referenced `#soft-lock-policy`. Anchor updated; the link-text "Soft-lock
policy" was preserved because it remains semantically accurate (the
section documents both soft-lock add/rename/remove mechanics and the
v1.0 hard-lock CI gate that consolidated under it).

| File | Sites |
| ----------------------------------- | ----- |
| `docs/ATTRIBUTES.md` | 1 |
| `docs/DEPRECATION.md` | 3 |
| `docs/standards-roadmap.md` | 1 |
| `docs/migration/v0.x-to-v1.0.md` | 1 |

### 2. `RFC-0013` heading slug: `migration-rollout → migration--rollout`

GitHub's slug algorithm preserves the double dash that surrounds the
`/` in `Migration / rollout`. The original consuming docs all used the
single-dash form `#migration-rollout` (a manual guess that didn't
match how the slugifier actually behaves around stripped punctuation).

| File | Sites |
| ------------------------------------------ | ----- |
| `docs/integrations/k8sobjects-events.md` | 1 |
| `docs/integrations/filelog-container.md` | 1 |
| `docs/integrations/journald-kernel.md` | 1 |
| `docs/notes/ci.md` | 1 |
| `docs/reproducibility.md` | 1 |
| `docs/migration/v0.1-to-v0.2.md` | 2 |
| `docs/migration/v0.2-to-v0.3.md` | 1 |

### 3. `RFC-0013 §1 (adoption posture)` — wrong section number

`docs/reference-architectures/README.md` linked
`#1-adoption-posture` but RFC-0013 §1 is "Binary assembled by OCB" and
§2 is "Adoption matrix" — the latter is what the prose calls out
(upstream OTel core/contrib vs tracecore in-tree). Retargeted to
`#2-adoption-matrix` and link text updated to match.

## Intentional / known-stale references retained

- **`#L<num>` blob-line anchors** (e.g. the
`reproducibility.md#L36` citations in
`docs/v1-rc1-operational-gaps.md`). These resolve on github.com's
blob view, not the rendered markdown view, and are an intentional
citation style across operational-gap memos. The anchor gate
introduced in this audit skips `#L<digits>` so this style does not
false-positive.
- **`` `[text](path)` `` inside double backticks** in
`docs/notes/pr-workflow.md` line 73 — that is meta-documentation
explaining the link-rot gate's own scope, not a real link.

## De-duplication candidates

The audit surfaced two heavily-reused cross-refs but neither warrants
single-sourcing today:

- `NORTHSTARS.md#o2-convenience--quality` (12 sites) and
`NORTHSTARS.md#o5-distribution--community` (11 sites) — these are
the project's stable goal anchors; high call-site count is the
feature, not the bug. Inlining or includes would obscure the
one-hop traceability that the doc set leans on.
- `v1-rc1-cut-criteria.md` per-criterion anchors (multiple) — same
reasoning. The cut criteria are deliberately the single source of
truth and every consuming doc one-hops to a specific criterion.

## Enforcement

This audit introduces `scripts/md-anchor-check.py`, wired into
`scripts/doc-check.sh` as a sibling to the existing
`.md-file-exists` and YAML-link gates. Mutation test (break a link,
expect gate fail) verified during introduction.

The gate's scope:

- Walks every git-tracked `*.md` file outside `docs/rfcs/**` and
`docs/research/**`.
- For every relative markdown link with an `#anchor` fragment whose
target is a `.md` file, computes the target file's
heading-slug set with the GitHub-flavored algorithm and asserts the
anchor is present.
- Skips `#L<num>` blob-line anchors (intentional doc-style).
- Skips anchors inside fenced or inline-code spans (meta-doc
references to link syntax).

The gate ran clean on 217 anchor references at audit close.
2 changes: 1 addition & 1 deletion docs/integrations/filelog-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ unit-normalized) + `cuda_oom.gpu_index` (Int) attributes that
[pattern #10's detector](../patterns/10-cuda-oom-deceptive.md) consumes.
Replaces the in-tree `containerstdout` receiver scheduled for deletion
at v0.2.0 per
[RFC-0013 §migration PR-K](../rfcs/0013-distro-first-pivot.md#migration-rollout)
[RFC-0013 §migration PR-K](../rfcs/0013-distro-first-pivot.md#migration--rollout)
and §7 (Deletion list).

## Config
Expand Down
2 changes: 1 addition & 1 deletion docs/integrations/journald-kernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ journald and `/dev/kmsg` and normalizing the records through an OTTL
[RFC-0013 §3](../rfcs/0013-distro-first-pivot.md#3-customer-stable-telemetry-contracts)
so existing operator alerts survive the swap. Replaces the in-tree
`kernelevents` receiver scheduled for deletion at v0.2.0 per
[RFC-0013 §migration PR-K](../rfcs/0013-distro-first-pivot.md#migration-rollout)
[RFC-0013 §migration PR-K](../rfcs/0013-distro-first-pivot.md#migration--rollout)
and §7 (Deletion list).

## Config
Expand Down
2 changes: 1 addition & 1 deletion docs/integrations/k8sobjects-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Tracecore watches the Kubernetes Events API via the upstream
`node_pressure`, `image_pull_failure`) via an OTTL `transform`
processor. Replaces the in-tree `k8sevents` receiver scheduled for
deletion at v0.2.0 per
[RFC-0013 §migration PR-K](../rfcs/0013-distro-first-pivot.md#migration-rollout)
[RFC-0013 §migration PR-K](../rfcs/0013-distro-first-pivot.md#migration--rollout)
and §7 (Deletion list).

> **Validation note.** The upstream `k8sobjectsreceiver` calls
Expand Down
4 changes: 2 additions & 2 deletions docs/migration/v0.1-to-v0.2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Migration: v0.1.x → v0.2.0

This guide tells operators how to move from a `v0.1.x` deployment to `v0.2.0`. Every operator-visible break gets a row; everything not listed below is unchanged. Sections below mirror [RFC-0013 §migration](../rfcs/0013-distro-first-pivot.md#migration-rollout) (PR-A through PR-L).
This guide tells operators how to move from a `v0.1.x` deployment to `v0.2.0`. Every operator-visible break gets a row; everything not listed below is unchanged. Sections below mirror [RFC-0013 §migration](../rfcs/0013-distro-first-pivot.md#migration--rollout) (PR-A through PR-L).

## TL;DR

Expand Down Expand Up @@ -79,7 +79,7 @@ To verify what's actually registered in the binary you're running:
./_build/tracecore components
```

The chart does not yet ship a per-receiver `recipe:` switch — that mechanism arrives in [RFC-0013 PR-J](../rfcs/0013-distro-first-pivot.md#migration-rollout) along with the upstream-recipe templates. Until PR-J lands, the migration path for the in-tree receivers other than `clockreceiver`/`stdoutexporter` is: pin v0.1.x → wait for PR-J → cut over to the upstream-recipe values shape in one minor.
The chart does not yet ship a per-receiver `recipe:` switch — that mechanism arrives in [RFC-0013 PR-J](../rfcs/0013-distro-first-pivot.md#migration--rollout) along with the upstream-recipe templates. Until PR-J lands, the migration path for the in-tree receivers other than `clockreceiver`/`stdoutexporter` is: pin v0.1.x → wait for PR-J → cut over to the upstream-recipe values shape in one minor.

## Self-telemetry metric vocabulary

Expand Down
2 changes: 1 addition & 1 deletion docs/migration/v0.2-to-v0.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ helm upgrade tracecore install/kubernetes/tracecore \

- [#222: PR-M deferral memo](https://github.com/TraceCoreAI/tracecore/issues/222) — current PR-M status + re-evaluation triggers (OTel Profiles → Beta, parca-agent OTLP export)
- [RFC-0013 §Adoption matrix](../rfcs/0013-distro-first-pivot.md#2-adoption-matrix) — why pyspy is on the eventual deletion path in favour of parca-agent (note: timing in the RFC predates the #222 deferral)
- [RFC-0013 §Migration / rollout](../rfcs/0013-distro-first-pivot.md#migration-rollout) — original PR-M and PR-N sequencing (supersede with #222 for current timeline)
- [RFC-0013 §Migration / rollout](../rfcs/0013-distro-first-pivot.md#migration--rollout) — original PR-M and PR-N sequencing (supersede with #222 for current timeline)
- [RFC-0009 §Safety properties](../rfcs/0009-pyspy-receiver-scope.md#proposal) — design record of the cooperative receiver's zero-capability posture (still in force at v0.3.0)
- [`components/receivers/pyspy/README.md`](../../components/receivers/pyspy/README.md) — cooperative receiver's user-facing docs (the receiver ships in v0.3.0)
- [`components/receivers/pyspy/RUNBOOK.md`](../../components/receivers/pyspy/RUNBOOK.md) — per-kind operator triage for the cooperative receiver
Expand Down
2 changes: 1 addition & 1 deletion docs/migration/v0.x-to-v1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ rejected.

**What changed.** v0.x did not publish a binding deprecation
policy; the per-attribute soft-lock note in
[`docs/ATTRIBUTES.md` §Soft-lock policy](../ATTRIBUTES.md#soft-lock-policy)
[`docs/ATTRIBUTES.md` §Soft-lock policy](../ATTRIBUTES.md#lock-policy)
named windows in prose but the lint enforcing it was advisory.
v1.0.0-rc1 promotes both the policy document and the CI gate to
binding status.
Expand Down
2 changes: 1 addition & 1 deletion docs/notes/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Anchor: issue #327; `PRINCIPLES.md` §10; `Makefile` targets

The release + integration workflow set changes across the v0.1.0 →
v0.3.0 migration window per
[RFC-0013 §Migration](../rfcs/0013-distro-first-pivot.md#migration-rollout)
[RFC-0013 §Migration](../rfcs/0013-distro-first-pivot.md#migration--rollout)
and §7 (Deletion list). Concrete schedule:

- **v0.1.0** - `.github/workflows/release.yml` is rewritten on top
Expand Down
2 changes: 1 addition & 1 deletion docs/reference-architectures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ lives where:
2. **Required components.** Receivers + exporters + extensions that
must be enabled. Every named component is upstream OTel collector
core or contrib unless explicitly flagged as tracecore in-tree
(per [RFC-0013 §1](../rfcs/0013-distro-first-pivot.md#1-adoption-posture)).
(per [RFC-0013 §2 adoption matrix](../rfcs/0013-distro-first-pivot.md#2-adoption-matrix)).
3. **`values.yaml` snippet.** Drop-in overlay against the in-repo
chart. Snippets are bounded — anything bigger goes into
[`docs/integrations/`](../integrations/) and is cross-linked.
Expand Down
2 changes: 1 addition & 1 deletion docs/reproducibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Verify a published `tracecore` release end-to-end from source. The
release pipeline lives in
[`.github/workflows/release.yml`](../.github/workflows/release.yml)
(`package` job per [RFC-0013 §Migration PR-C](rfcs/0013-distro-first-pivot.md#migration-rollout)):
(`package` job per [RFC-0013 §Migration PR-C](rfcs/0013-distro-first-pivot.md#migration--rollout)):
inline shell builds each `linux/{amd64,arm64}` OCB binary via
`make build`, archives it with `tar --sort=name
--owner=0 --group=0 --numeric-owner --mtime=@$SOURCE_DATE_EPOCH | gzip -n`
Expand Down
2 changes: 1 addition & 1 deletion docs/standards-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ post-training), and predates this filing.
owning the namespace.
3. **Neither lands; OTel ships a *third* shape.** We adopt the third
shape. Tracecore's renames are governed by [ATTRIBUTES.md
§Soft-lock policy](ATTRIBUTES.md#soft-lock-policy) (one-minor
§Soft-lock policy](ATTRIBUTES.md#lock-policy) (one-minor
deprecation window at v0.4, two-minor at v1.0).

### 4.2 Scope-creep risk
Expand Down
19 changes: 19 additions & 0 deletions scripts/doc-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,25 @@ fi

echo "doc-check: $link_count markdown link(s) resolve to on-disk files"

# --- Markdown anchor-fragment integrity -------------------------------------
#
# Drift pattern this gate closes: a heading rename leaves
# [text](path.md#old-slug) links pointing at a fragment the file no
# longer contains. The .md-file-exists gate above (#frag stripped)
# doesn't see this — the file resolves; the anchor doesn't.
#
# Caught during 2026-06 cross-ref audit (docs/audits/2026-06-cross-ref.md):
# 15 such breaks across 12 files traced to two renames + one section-ref fix
# (ATTRIBUTES.md `Soft-lock policy → Lock policy`, RFC-0013
# `Migration / rollout` heading slug doubled the dash, never updated
# in the consuming docs).
#
# Sibling to PR #459's YAML link-rot gate but for markdown anchors.
# Python script holds the GitHub-flavored slug algorithm; this block
# is just the wrapper that exits non-zero on dead anchors.

scripts/md-anchor-check.py

# --- Markdown link integrity: non-.md intra-repo targets --------------------
#
# Drift pattern this gate closes: post-wave-5 sweep deleted ~30 source
Expand Down
Loading