create-battle-plan 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +434 -0
- package/package.json +31 -0
- package/template/.cascaderc.example +6 -0
- package/template/.claude/commands/distill.md +186 -0
- package/template/.claude/commands/good-morning.md +74 -0
- package/template/.claude/commands/wrap-up.md +61 -0
- package/template/.claude/settings.json +3 -0
- package/template/.githooks/pre-commit +41 -0
- package/template/CLAUDE.md +154 -0
- package/template/docs/README.md +62 -0
- package/template/tools/check-metrics.sh +91 -0
- package/template/tools/init-project.sh +245 -0
- package/template/tools/setup-hooks.sh +14 -0
- package/template/tools/sync-metrics.sh +82 -0
- package/template/tools/touch-date.sh +31 -0
- package/template/tools/verify-cascade.sh +154 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Morning standup — status briefing, metrics snapshot, and today's priorities. Run at the start of each work day.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Morning Standup
|
|
6
|
+
|
|
7
|
+
Run these steps in order. Be concise — the user wants a briefing, not an essay.
|
|
8
|
+
|
|
9
|
+
## Step 0: First-Run Welcome (one time only)
|
|
10
|
+
|
|
11
|
+
Check if `.battle-plan-onboarding.json` exists. If it does, this is the user's FIRST session after running `npx create-battle-plan`. Do the following:
|
|
12
|
+
|
|
13
|
+
1. Read `.battle-plan-onboarding.json` to get their project context
|
|
14
|
+
2. **Welcome them by reflecting what they told the installer** — project name, horizon, metrics, domains, people. Show them you already have full context. Make them feel like they're picking up mid-conversation, not starting from scratch.
|
|
15
|
+
3. **Explain what Battle Plan is and how it works**, briefly:
|
|
16
|
+
- "Battle Plan keeps your project context in markdown files that stay in sync. When you tell me something new — a call, a reply, a metric change — I update a chain of docs in a fixed order so nothing gets dropped."
|
|
17
|
+
- Mention the cascade: metrics.yml → battle plan → source docs → verification
|
|
18
|
+
- Mention: "Just dump context into the chat. Call transcripts, research, meeting notes, replies — I'll cascade it into the right docs."
|
|
19
|
+
4. **Explain the commands:**
|
|
20
|
+
- `/good-morning` — start each session with a status briefing (what you're running now)
|
|
21
|
+
- `/wrap-up` — end each session cleanly: status review, cascade, metrics report, commit
|
|
22
|
+
- `/distill` — compress docs when they get too long for me to read efficiently
|
|
23
|
+
5. **Explain metrics:** "Your metrics live in `metrics.yml`. Every number in every doc traces back to that file. Scripts verify they stay in sync. You never have to worry about stale numbers."
|
|
24
|
+
6. **Explain what to do next:** "Set targets for your metrics in the battle plan, then start working. Tell me about any calls, research, or updates and I'll keep everything current."
|
|
25
|
+
7. Rename `.battle-plan-onboarding.json` to `.battle-plan-onboarding-done.json` so this welcome doesn't repeat.
|
|
26
|
+
|
|
27
|
+
Then proceed to the normal standup flow below (Steps 1-4).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Step 1: Gather State (parallel reads)
|
|
32
|
+
|
|
33
|
+
Read all of these in parallel:
|
|
34
|
+
- `metrics.yml` — current numbers
|
|
35
|
+
- `docs/battle-plan.md` — TL;DR + latest day log (find today or the most recent day entry)
|
|
36
|
+
- Run `git log --oneline -15` — what changed since last session
|
|
37
|
+
|
|
38
|
+
## Step 2: Present the Briefing
|
|
39
|
+
|
|
40
|
+
Print a compact morning report with these sections:
|
|
41
|
+
|
|
42
|
+
### Sprint Position
|
|
43
|
+
- Where you are in the timeline (calculate from battle plan dates if available)
|
|
44
|
+
- One-line status from TL;DR
|
|
45
|
+
|
|
46
|
+
### Key Metrics (from metrics.yml)
|
|
47
|
+
Show as a compact table:
|
|
48
|
+
| Metric | Value | Target | Gap |
|
|
49
|
+
Pull all defined metrics from `metrics.yml`. If targets are defined in the battle plan, include them.
|
|
50
|
+
|
|
51
|
+
### Yesterday's Unfinished Business
|
|
52
|
+
- Scan the most recent day log for unchecked `[ ]` items — list them
|
|
53
|
+
- Flag any that have been carried forward 2+ days
|
|
54
|
+
|
|
55
|
+
### Today's Agenda
|
|
56
|
+
- If there's already a day entry for today in the battle plan, show its tasks
|
|
57
|
+
- If not, suggest one based on yesterday's carryovers + sprint priorities
|
|
58
|
+
|
|
59
|
+
## Step 3: Ask Directed Questions
|
|
60
|
+
|
|
61
|
+
End with 2-3 short questions:
|
|
62
|
+
- "Anything happen since we last talked? Replies, updates, new info?"
|
|
63
|
+
- If there are stale items (no progress for 2+ days), ask about them specifically
|
|
64
|
+
- If a key deliverable is outstanding, ask about it
|
|
65
|
+
|
|
66
|
+
## Step 4: Prep the Day
|
|
67
|
+
|
|
68
|
+
After the user answers:
|
|
69
|
+
- If they report any updates → run the full cascade (Steps 0-4 from CLAUDE.md)
|
|
70
|
+
- Update the battle plan day log with today's plan
|
|
71
|
+
|
|
72
|
+
## Tone
|
|
73
|
+
|
|
74
|
+
Direct, no fluff. Think military briefing, not newsletter.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: End-of-day wrap-up — status check, final cascade, metrics report, and commit. Run at the end of each work day.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# End-of-Day Wrap-Up
|
|
6
|
+
|
|
7
|
+
Run these steps in order. Be concise.
|
|
8
|
+
|
|
9
|
+
## Step 1: Scan
|
|
10
|
+
|
|
11
|
+
Read `docs/battle-plan.md` and `metrics.yml`. Find today's day section. Categorize all tasks:
|
|
12
|
+
- Done
|
|
13
|
+
- Partially done
|
|
14
|
+
- Not started
|
|
15
|
+
- New (added during the day but not in the morning plan)
|
|
16
|
+
|
|
17
|
+
## Step 2: Present
|
|
18
|
+
|
|
19
|
+
Show the user:
|
|
20
|
+
```
|
|
21
|
+
Today's status:
|
|
22
|
+
[x] [done tasks]
|
|
23
|
+
[~] [partial tasks]
|
|
24
|
+
[ ] [not started]
|
|
25
|
+
[+] [new things that happened]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Ask: "Does this look right?"
|
|
29
|
+
|
|
30
|
+
## Step 3: Prompt
|
|
31
|
+
|
|
32
|
+
Ask: "Anything else happen today? Even small things — a reply, an update, a thought, a link. Everything counts."
|
|
33
|
+
|
|
34
|
+
Wait for the user's answer before proceeding.
|
|
35
|
+
|
|
36
|
+
## Step 4: Cascade
|
|
37
|
+
|
|
38
|
+
With all info gathered, run the full cascade from CLAUDE.md:
|
|
39
|
+
1. Update `metrics.yml` if any metric changed
|
|
40
|
+
2. Update battle plan TL;DR + today's day log
|
|
41
|
+
3. Update source docs (only what's relevant to today's changes)
|
|
42
|
+
4. Run `tools/touch-date.sh` on every modified file
|
|
43
|
+
5. Run `tools/verify-cascade.sh` — fix any errors
|
|
44
|
+
|
|
45
|
+
## Step 5: Report
|
|
46
|
+
|
|
47
|
+
Print:
|
|
48
|
+
- **Metrics changed today** (before -> after, with deltas)
|
|
49
|
+
- **Docs updated** (list of files touched)
|
|
50
|
+
- **Verification warnings** (if any)
|
|
51
|
+
- **Tomorrow's top priorities** (carry-forwards + known agenda items)
|
|
52
|
+
|
|
53
|
+
## Step 6: Commit
|
|
54
|
+
|
|
55
|
+
Ask: "Want me to commit today's updates?"
|
|
56
|
+
|
|
57
|
+
If yes, commit with message: `eod YYYY-MM-DD: [one-line summary]`
|
|
58
|
+
|
|
59
|
+
## Tone
|
|
60
|
+
|
|
61
|
+
Direct. No fluff. Close out fast.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Pre-commit hook for Battle Plan.
|
|
3
|
+
# Runs verify-cascade.sh when docs/ or metrics.yml are staged.
|
|
4
|
+
# Default: warn only. Set CASCADE_STRICT=1 to block commits.
|
|
5
|
+
|
|
6
|
+
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
|
7
|
+
|
|
8
|
+
# Load config if it exists
|
|
9
|
+
if [ -f "$REPO_ROOT/.cascaderc" ]; then
|
|
10
|
+
source "$REPO_ROOT/.cascaderc"
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
CASCADE_STRICT="${CASCADE_STRICT:-0}"
|
|
14
|
+
|
|
15
|
+
# Check if any docs or metrics.yml are staged
|
|
16
|
+
STAGED_DOCS=$(git diff --cached --name-only -- 'docs/*.md' 'metrics.yml' 2>/dev/null || true)
|
|
17
|
+
|
|
18
|
+
if [ -z "$STAGED_DOCS" ]; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
echo "=== Battle Plan: Pre-commit check ==="
|
|
23
|
+
echo "Staged files:"
|
|
24
|
+
echo "$STAGED_DOCS" | sed 's/^/ /'
|
|
25
|
+
echo ""
|
|
26
|
+
|
|
27
|
+
# Run verification
|
|
28
|
+
if ! "$REPO_ROOT/tools/verify-cascade.sh"; then
|
|
29
|
+
if [ "$CASCADE_STRICT" = "1" ]; then
|
|
30
|
+
echo ""
|
|
31
|
+
echo "BLOCKED: Verification failed and CASCADE_STRICT=1."
|
|
32
|
+
echo "Fix the issues above or set CASCADE_STRICT=0 in .cascaderc to warn only."
|
|
33
|
+
exit 1
|
|
34
|
+
else
|
|
35
|
+
echo ""
|
|
36
|
+
echo "WARNING: Verification found issues (see above). Committing anyway."
|
|
37
|
+
echo "Set CASCADE_STRICT=1 in .cascaderc to block commits with issues."
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
exit 0
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Battle Plan — System Prompt
|
|
2
|
+
|
|
3
|
+
You are helping manage an interconnected documentation system. Every document stays in sync through a cascade protocol. Follow these rules exactly.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Cascade Protocol
|
|
8
|
+
|
|
9
|
+
**Trigger:** Any incoming information that relates to the project — calls, messages, research, signals, status changes, decisions.
|
|
10
|
+
|
|
11
|
+
When triggered, update in this exact order:
|
|
12
|
+
|
|
13
|
+
### Step 0: Update `metrics.yml`
|
|
14
|
+
If any key metric changed, update `metrics.yml` first. This is the numeric source of truth.
|
|
15
|
+
|
|
16
|
+
### Step 1: Update Battle Plan (`docs/battle-plan.md`)
|
|
17
|
+
- Update the **TL;DR** with current status
|
|
18
|
+
- Update the **Key Metrics** table (numbers reference metrics.yml)
|
|
19
|
+
- Update **Today's Priorities** if relevant
|
|
20
|
+
- Append to **Daily Log** for today
|
|
21
|
+
|
|
22
|
+
### Step 2: Update Cascade Docs
|
|
23
|
+
Update only the docs relevant to the new information. Route new info to the appropriate domain doc under `docs/`. Common patterns:
|
|
24
|
+
|
|
25
|
+
| Info type | Route to... |
|
|
26
|
+
|-----------|------------|
|
|
27
|
+
| Conversation, call, or meeting | `docs/external-insights.md` — append as new dated session |
|
|
28
|
+
| Evidence for/against a hypothesis | The relevant domain doc — amend the claim with an `[UPDATE]` block |
|
|
29
|
+
| Outreach sent/received | The relevant market or sales doc — update tracking tables |
|
|
30
|
+
| Competitor intel | The relevant strategy or market doc |
|
|
31
|
+
| New foundational knowledge | The relevant research or domain doc |
|
|
32
|
+
|
|
33
|
+
If no doc exists for the info, append it to the closest domain overview doc. Only create a new file if the info doesn't fit anywhere.
|
|
34
|
+
|
|
35
|
+
### Step 3: Update Dates
|
|
36
|
+
Run `tools/touch-date.sh` on every file you modified in this session:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
tools/touch-date.sh docs/battle-plan.md docs/validation/hypotheses.md [etc.]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 4: Verify
|
|
43
|
+
Run `tools/verify-cascade.sh` and fix any issues it reports:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
tools/verify-cascade.sh
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Source Reference Rules
|
|
52
|
+
|
|
53
|
+
### Registry Metrics (Tier 1 — deterministic)
|
|
54
|
+
Numbers defined in `metrics.yml`. Reference as: `[**N**](metrics.yml#field_name)`
|
|
55
|
+
|
|
56
|
+
This renders as a bold clickable number. Example: `[**42**](metrics.yml#outreach_sent)`
|
|
57
|
+
|
|
58
|
+
These are verified by exact numeric comparison via `tools/check-metrics.sh`.
|
|
59
|
+
|
|
60
|
+
### Inline Metrics (Tier 2 — LLM-verified)
|
|
61
|
+
Less common numbers from another doc. Reference as: `[**N**](source-doc.md#section-slug)`
|
|
62
|
+
|
|
63
|
+
Example: `60% of time on evidence [**60**](external-insights.md#session-2-key-insights)`
|
|
64
|
+
|
|
65
|
+
**Rule:** Every number referenced from another document MUST include a source annotation. Only numbers native to a doc (where they originate) have no annotation.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Document Format
|
|
70
|
+
|
|
71
|
+
Every doc in `docs/` must have this frontmatter:
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
# Document Title
|
|
75
|
+
|
|
76
|
+
**Last Updated:** 2026-04-07
|
|
77
|
+
**Status:** Active | Draft | Archived
|
|
78
|
+
**Role:** source-of-truth | cascade-target
|
|
79
|
+
**Compression:** chronological | amended | none
|
|
80
|
+
|
|
81
|
+
**TL;DR:** One paragraph summary with key numbers and source references.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **Last Updated** must match today's date on any file modified in the current session.
|
|
87
|
+
- **Status:** `Active` = live, `Draft` = WIP, `Archived` = excluded from cascade.
|
|
88
|
+
- **Role:** `source-of-truth` = authoritative for its numbers. `cascade-target` = references numbers from elsewhere.
|
|
89
|
+
- **Compression:** required field. One of `chronological`, `amended`, or `none` (see Compression Modes section below).
|
|
90
|
+
- **TL;DR** must exist and contain all key metrics that appear in the doc.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Compression Modes & Timestamping Rules
|
|
95
|
+
|
|
96
|
+
Every doc declares a `Compression:` mode in frontmatter. This tells the `/distill` command (and humans) how new info gets added to the doc and how old info gets compressed when it grows too long. The mode IS the timestamping rule for new info.
|
|
97
|
+
|
|
98
|
+
### `Compression: chronological`
|
|
99
|
+
The doc is an append-only log of dated entries. Each new piece of info goes in a new dated section.
|
|
100
|
+
|
|
101
|
+
- **Timestamping rule:** every new entry MUST start with a dated heading: `## Session N (YYYY-MM-DD) — <title>`, `## YYYY-MM-DD — <title>`, or `## DD Month YYYY — <title>`. No exceptions.
|
|
102
|
+
- **Examples:** `docs/battle-plan.md` (daily log), `docs/validation/external-insights.md` (conversation journal).
|
|
103
|
+
- **`/distill` behavior:** keeps the N most recent dated sections verbatim, archives the rest into `docs/archive/<same-path>`, replaces them with a thorough summary.
|
|
104
|
+
|
|
105
|
+
### `Compression: amended`
|
|
106
|
+
The doc is a living reference. Claims are amended in place over time.
|
|
107
|
+
|
|
108
|
+
- **Timestamping rule:** every new finding that revises an existing claim MUST be added as an inline `> **[UPDATE YYYY-MM-DD · Source: ...]**` block placed immediately above the claim it modifies. Brand-new claims with no prior version don't need a stamp; they're stamped implicitly by the doc's `Last Updated` date and git history.
|
|
109
|
+
- **Examples:** `docs/validation/hypotheses.md`, `docs/market/icp-and-targets.md`, `docs/market/competitive-landscape.md`.
|
|
110
|
+
- **`/distill` behavior:** collapses old `[UPDATE]` blocks into the body text (preserving their content as integrated current-state), archives the raw blocks verbatim. Keeps the N most recent amendments per section inline.
|
|
111
|
+
|
|
112
|
+
### `Compression: none`
|
|
113
|
+
The doc is a static thesis or reference. It gets rewritten, not amended. Git history is the timeline.
|
|
114
|
+
|
|
115
|
+
- **Timestamping rule:** none. Just edit the doc and let `Last Updated` + git track changes.
|
|
116
|
+
- **Examples:** `docs/strategy/product-thesis.md`, `docs/research/domain-101.md`.
|
|
117
|
+
- **`/distill` behavior:** refuses to run. If a `none` doc has grown unwieldy, rewrite it manually or change its `Compression:` mode first.
|
|
118
|
+
|
|
119
|
+
### Why this matters
|
|
120
|
+
The TL;DR is current state, not history. It can't tell `/distill` what's new vs old. The `Compression:` mode + timestamping rule is the only mechanism that makes distillation deterministic. Skipping the timestamp on a new entry in a `chronological` or `amended` doc is a bug; it will get silently absorbed into the wrong era during distillation.
|
|
121
|
+
|
|
122
|
+
When in doubt about which mode a new doc should use: chronological logs choose `chronological`, claim trackers choose `amended`, everything else is `none`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Vault Rules
|
|
127
|
+
|
|
128
|
+
1. **Update, don't duplicate.** Amend with `> **[UPDATE YYYY-MM-DD · Source: ...]**`
|
|
129
|
+
2. **Cross-link everything.** Claims reference their source doc.
|
|
130
|
+
3. **Confidence levels:** `Unvalidated` | `Soft signal` | `Practitioner-validated` | `Data-validated`
|
|
131
|
+
4. **Source everything.** Who said it, when, confidence level.
|
|
132
|
+
5. **Minimize file count.** Append, don't create new files.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## The `/wrap-up` Protocol
|
|
137
|
+
|
|
138
|
+
When the user says `/wrap-up`, run this end-of-day sequence:
|
|
139
|
+
|
|
140
|
+
**Step 1 — Scan:** Read the battle plan. Identify all tasks for today. Categorize: done, partially done, not started, new.
|
|
141
|
+
|
|
142
|
+
**Step 2 — Present:** Show the user: "Here's today's status: [list]. Does this look right?"
|
|
143
|
+
|
|
144
|
+
**Step 3 — Prompt:** Ask: "Anything else happen today? Even small things — a reply, an accept, a thought, a link. Everything counts."
|
|
145
|
+
|
|
146
|
+
**Step 4 — Cascade:** With all info gathered, run the full cascade (Steps 0-4 above).
|
|
147
|
+
|
|
148
|
+
**Step 5 — Report:** Print:
|
|
149
|
+
- Metrics changed today (before → after)
|
|
150
|
+
- Docs updated
|
|
151
|
+
- Verification warnings (if any)
|
|
152
|
+
- Tomorrow's top priorities
|
|
153
|
+
|
|
154
|
+
**Step 6 — Commit:** Ask: "Want me to commit today's updates?" If yes, commit with message: `eod YYYY-MM-DD: [summary]`
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Documentation Vault Rules
|
|
2
|
+
|
|
3
|
+
These rules govern all documents in this vault. Follow them on every update.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Update, Don't Duplicate
|
|
8
|
+
|
|
9
|
+
Amend existing docs with timestamped updates. Never create a new file that makes an old one obsolete.
|
|
10
|
+
|
|
11
|
+
**Amendment format:**
|
|
12
|
+
|
|
13
|
+
> **[UPDATE YYYY-MM-DD · Source: person/research/call]** New finding here.
|
|
14
|
+
|
|
15
|
+
## 2. Cross-Link Everything
|
|
16
|
+
|
|
17
|
+
Claims validated or invalidated by another doc must link to it. Use relative paths:
|
|
18
|
+
`[Document Name](relative/path/to/doc.md)`
|
|
19
|
+
|
|
20
|
+
## 3. Confidence Levels on Every Claim
|
|
21
|
+
|
|
22
|
+
Mark every claim with one of:
|
|
23
|
+
- `Unvalidated` — no evidence yet
|
|
24
|
+
- `Soft signal` — directional but not confirmed (e.g., 1-2 anecdotes)
|
|
25
|
+
- `Practitioner-validated` — confirmed by someone who does this work
|
|
26
|
+
- `Data-validated` — confirmed by quantitative evidence
|
|
27
|
+
|
|
28
|
+
Always include the source: who said it, when, in what context.
|
|
29
|
+
|
|
30
|
+
## 4. Source Reference Format
|
|
31
|
+
|
|
32
|
+
**Registry metrics** (defined in `metrics.yml`):
|
|
33
|
+
`[**N**](metrics.yml#field_name)`
|
|
34
|
+
|
|
35
|
+
**Inline metrics** (from another doc):
|
|
36
|
+
`(→ source-doc.md#section-slug)`
|
|
37
|
+
|
|
38
|
+
**Rule:** Every number referenced from another document MUST include a source annotation. Only numbers native to a doc (where they originate) have no annotation.
|
|
39
|
+
|
|
40
|
+
## 5. Minimize File Count
|
|
41
|
+
|
|
42
|
+
Notes from the same person go in ONE file, appended with dates. Don't create a new file per conversation — append to `external-insights.md`.
|
|
43
|
+
|
|
44
|
+
## 6. Source Everything
|
|
45
|
+
|
|
46
|
+
Every claim needs: who said it, when, and the confidence level. Unsourced claims get `Unvalidated` until sourced.
|
|
47
|
+
|
|
48
|
+
## 7. Record and Transcribe Calls
|
|
49
|
+
|
|
50
|
+
Every call, meeting, and significant conversation should be recorded (with consent), transcribed, and integrated into the relevant docs. External input is the highest-value content in this system.
|
|
51
|
+
|
|
52
|
+
## 8. Standardized Frontmatter
|
|
53
|
+
|
|
54
|
+
Every doc in `docs/` must have:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
**Last Updated:** YYYY-MM-DD
|
|
58
|
+
**Status:** Active | Draft | Archived
|
|
59
|
+
**Role:** source-of-truth | cascade-target
|
|
60
|
+
|
|
61
|
+
**TL;DR:** One paragraph summary with sourced numbers.
|
|
62
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-metrics.sh — Verifies that metrics.yml references in docs match actual values.
|
|
3
|
+
# Supports two formats:
|
|
4
|
+
# Link format: [**N**](metrics.yml#field) or [N](metrics.yml#field)
|
|
5
|
+
# Legacy format: **N** (→ metrics.yml#field) or N (→ metrics.yml#field)
|
|
6
|
+
# Usage: tools/check-metrics.sh [docs_dir]
|
|
7
|
+
# Exit 0 if all match, exit 1 if mismatches found.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
METRICS_FILE="$REPO_ROOT/metrics.yml"
|
|
13
|
+
DOCS_DIR="${1:-$REPO_ROOT/docs}"
|
|
14
|
+
|
|
15
|
+
if [ ! -f "$METRICS_FILE" ]; then
|
|
16
|
+
echo "ERROR: metrics.yml not found at $METRICS_FILE"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
ERRORS=0
|
|
21
|
+
CHECKED=0
|
|
22
|
+
|
|
23
|
+
# Parse metrics.yml into key=value pairs (skip comments, blank lines, string values)
|
|
24
|
+
while IFS= read -r line; do
|
|
25
|
+
# Skip comments, blank lines, and string values (quoted)
|
|
26
|
+
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
27
|
+
[[ -z "$line" ]] && continue
|
|
28
|
+
[[ "$line" =~ \" ]] && continue
|
|
29
|
+
|
|
30
|
+
key=$(echo "$line" | cut -d: -f1 | tr -d ' ')
|
|
31
|
+
value=$(echo "$line" | cut -d: -f2 | tr -d ' ')
|
|
32
|
+
|
|
33
|
+
# Skip the last_updated meta field
|
|
34
|
+
[[ "$key" == "last_updated" ]] && continue
|
|
35
|
+
|
|
36
|
+
# Find all docs referencing this metric (both link and legacy formats)
|
|
37
|
+
pattern="metrics\\.yml#${key}"
|
|
38
|
+
while IFS= read -r match_file; do
|
|
39
|
+
[ -z "$match_file" ] && continue
|
|
40
|
+
CHECKED=$((CHECKED + 1))
|
|
41
|
+
|
|
42
|
+
# Extract the number from the reference
|
|
43
|
+
while IFS= read -r match_line; do
|
|
44
|
+
ref_number=""
|
|
45
|
+
|
|
46
|
+
# Format 1 (link): [**N**](metrics.yml#field)
|
|
47
|
+
if [ -z "$ref_number" ]; then
|
|
48
|
+
ref_number=$(echo "$match_line" | grep -oE '\[\*\*[0-9]+\*\*\]\(metrics\.yml#'"$key"'\)' | grep -oE '[0-9]+' || true)
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Format 2 (link, no bold): [N](metrics.yml#field)
|
|
52
|
+
if [ -z "$ref_number" ]; then
|
|
53
|
+
ref_number=$(echo "$match_line" | grep -oE '\[[0-9]+\]\(metrics\.yml#'"$key"'\)' | grep -oE '[0-9]+' || true)
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Format 3 (legacy): **N** (→ metrics.yml#field)
|
|
57
|
+
if [ -z "$ref_number" ]; then
|
|
58
|
+
ref_number=$(echo "$match_line" | grep -oE '\*\*[0-9]+\*\*[[:space:]]*\(→ metrics\.yml#'"$key"'\)' | grep -oE '[0-9]+' || true)
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Format 4 (legacy, no bold): N (→ metrics.yml#field)
|
|
62
|
+
if [ -z "$ref_number" ]; then
|
|
63
|
+
ref_number=$(echo "$match_line" | grep -oE '[0-9]+[[:space:]]*\(→ metrics\.yml#'"$key"'\)' | grep -oE '^[0-9]+' || true)
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
if [ -z "$ref_number" ]; then
|
|
67
|
+
echo "WARNING: Could not parse number for $key in $match_file"
|
|
68
|
+
echo " Line: $match_line"
|
|
69
|
+
ERRORS=$((ERRORS + 1))
|
|
70
|
+
elif [ "$ref_number" != "$value" ]; then
|
|
71
|
+
echo "MISMATCH: $key in $match_file"
|
|
72
|
+
echo " metrics.yml: $value"
|
|
73
|
+
echo " Document: $ref_number"
|
|
74
|
+
echo " Line: $match_line"
|
|
75
|
+
ERRORS=$((ERRORS + 1))
|
|
76
|
+
fi
|
|
77
|
+
done < <(grep "$pattern" "$match_file")
|
|
78
|
+
done < <(grep -rl "$pattern" "$DOCS_DIR" 2>/dev/null | grep -v '/examples/' || true)
|
|
79
|
+
done < "$METRICS_FILE"
|
|
80
|
+
|
|
81
|
+
echo ""
|
|
82
|
+
echo "=== Metrics Check ==="
|
|
83
|
+
echo "Checked: $CHECKED references"
|
|
84
|
+
echo "Errors: $ERRORS"
|
|
85
|
+
|
|
86
|
+
if [ $ERRORS -gt 0 ]; then
|
|
87
|
+
exit 1
|
|
88
|
+
else
|
|
89
|
+
echo "All metrics references match."
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|