Skip to content

Commit 5f5b2bc

Browse files
khaliqgantclaude
andcommitted
Cross-repo dispatch + spec-lint CI
- run-overnight.sh: auto-detect **Repo:** headers and dispatch each spec to its target repo (sage/MSD/pear/nightcto/self) on a results/<program> branch, one draft PR per touched repo. Single-repo per-wave behavior preserved when no **Repo:** headers exist. Repo paths overridable via PROJECTS_ROOT / REPO_<slug>. - Add **Repo:** slug to all 14 specs. - scripts/lint-specs.sh + .github/workflows/lint-specs.yml: fail if any numbered spec is missing required sections or a valid **Repo:** header. - README: document dispatch routing + the lint/CI guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 02acd21 commit 5f5b2bc

18 files changed

Lines changed: 268 additions & 101 deletions

.github/workflows/lint-specs.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: lint-specs
2+
3+
# Guards the spec format the overnight runner + reviewer depend on: every numbered
4+
# spec must carry the required sections and a valid **Repo:** dispatch header.
5+
6+
on:
7+
push:
8+
paths:
9+
- 'specs/**'
10+
- 'scripts/lint-specs.sh'
11+
- '.github/workflows/lint-specs.yml'
12+
pull_request:
13+
paths:
14+
- 'specs/**'
15+
- 'scripts/lint-specs.sh'
16+
17+
jobs:
18+
lint:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
- name: Lint specs
23+
run: bash scripts/lint-specs.sh

README.md

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,44 @@ Flags / env:
7272
- `MAX_REVIEW_ITERS` (default 3) — review/fix loop cap
7373
- `REVIEW_CMD` / `FIX_CMD` — override the reviewer/fixer invocation (default: Ricky local with `_review.md` / `_fix.md`)
7474

75-
### Cross-repo execution (important)
75+
### Cross-repo dispatch (automatic)
7676

77-
The runner commits in whichever repo it is invoked from. Because storybook is cross-repo:
77+
Storybook is cross-repo, so each spec declares a machine-readable **`**Repo:**` slug** in its
78+
header. The runner auto-detects these and switches to **dispatch mode**: each spec is
79+
implemented in its **target repo** on a `results/storybook` branch, and a draft PR is opened
80+
**per touched repo** — no manual repo-hopping.
7881

79-
- Build the **shared pieces first** (`@code-story/schema`, `@code-story/skill`, `@code-story/react`)
80-
from waves 0–1 and the extract in `030`, publish the package, **then** run the per-product
81-
emit/mount specs (waves 2–3) from each target repo — or via Ricky cloud with that repo materialized.
82-
- The target repo for each spec is in its header and in `PROGRAM.md`'s cross-repo plan.
82+
Slugs resolve to local repo paths (sibling clones assumed):
8383

84-
Treat each wave (or each target repo's slice of a wave) as a separate Ricky handoff; let the
85-
review cycle gate each before moving on.
84+
| slug | resolves to | holds |
85+
|---|---|---|
86+
| `code-storybook` | this repo | the shared `@code-story` package (schema, skill, `react` renderer, helper personas) |
87+
| `sage` | `$PROJECTS_ROOT/sage` | `020` planning story |
88+
| `my-senior-dev` | `$PROJECTS_ROOT/../My-Senior-Dev/app` | `021` review story, `030` extract, `032` webapp surface |
89+
| `nightcto` | `$PROJECTS_ROOT/nightcto` | `022` runtime story |
90+
| `pear` | `$PROJECTS_ROOT/pear` | `031` story view |
91+
92+
`PROJECTS_ROOT` defaults to this repo's parent; override any path with `REPO_<slug>` env
93+
(e.g. `REPO_my_senior_dev=/path/to/app`). Inspect routing first with `--dry-run`:
94+
95+
```bash
96+
./scripts/run-overnight.sh storybook --dry-run # prints START <spec> -> <slug> (<repo>)
97+
```
98+
99+
Because of dependencies, the shared pieces (waves 0–1 + the `030` extract) build first in
100+
`code-storybook`; publish `@code-story`, then the per-product specs consume it. The runner
101+
walks them in order and the review cycle gates each before moving on.
102+
103+
## Spec format & CI
104+
105+
Every numbered spec must carry the sections the runner + reviewer rely on
106+
(`Goal / In scope / Out of scope / Acceptance / Review / Handoff`) and a valid `**Repo:**`
107+
slug. `scripts/lint-specs.sh` enforces this and runs in CI (`.github/workflows/lint-specs.yml`)
108+
on every push/PR touching `specs/`:
109+
110+
```bash
111+
./scripts/lint-specs.sh # OK — N specs, all have required sections + a valid **Repo:** header
112+
```
86113

87114
## Related
88115

scripts/lint-specs.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
set -uo pipefail
3+
# Lint every numbered spec for the structure the overnight runner + reviewer rely on.
4+
# Fails (exit 1) if any spec is missing a required section or the **Repo:** dispatch header.
5+
# Usage: ./scripts/lint-specs.sh [specs-glob-root] (default: ./specs)
6+
7+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
8+
SPECS_ROOT="${1:-$ROOT/specs}"
9+
10+
REQUIRED_SECTIONS=("## Goal" "## In scope" "## Out of scope" "## Acceptance" "## Review" "## Handoff")
11+
REQUIRED_HEADERS=("**Repo:**") # machine-readable dispatch target
12+
13+
fail=0
14+
count=0
15+
# numbered specs only; skip _review.md/_fix.md/PROGRAM.md
16+
while IFS= read -r f; do
17+
count=$((count+1))
18+
missing=""
19+
for s in "${REQUIRED_SECTIONS[@]}"; do
20+
grep -qF "$s" "$f" || missing="${missing}\n missing section: ${s}"
21+
done
22+
for h in "${REQUIRED_HEADERS[@]}"; do
23+
grep -qF "$h" "$f" || missing="${missing}\n missing header: ${h}"
24+
done
25+
# the Repo header must resolve to a known slug
26+
if grep -qF "**Repo:**" "$f"; then
27+
slug="$(grep -m1 -oE '\*\*Repo:\*\* [A-Za-z0-9_-]+' "$f" | awk '{print $2}')"
28+
case "$slug" in
29+
code-storybook|sage|nightcto|pear|my-senior-dev) : ;;
30+
*) missing="${missing}\n unknown **Repo:** slug: '${slug}' (allowed: code-storybook|sage|nightcto|pear|my-senior-dev)" ;;
31+
esac
32+
fi
33+
if [ -n "$missing" ]; then
34+
fail=1
35+
printf 'FAIL %s%b\n' "${f#"$ROOT"/}" "$missing"
36+
fi
37+
done < <(find "$SPECS_ROOT" -type f -name '[0-9][0-9][0-9]-*.md' | sort)
38+
39+
if [ "$count" -eq 0 ]; then echo "No numbered specs found under $SPECS_ROOT"; exit 1; fi
40+
if [ "$fail" -eq 0 ]; then
41+
echo "OK — $count specs, all have required sections + a valid **Repo:** header."
42+
else
43+
echo
44+
echo "Spec lint failed. Each numbered spec needs: ${REQUIRED_SECTIONS[*]} and a **Repo:** <slug> header."
45+
fi
46+
exit "$fail"

0 commit comments

Comments
 (0)