Skip to content

Commit a41b039

Browse files
docs(packs): in-pack resources, domain-package layout, and load-script boundary
A pack can carry arbitrary data the load script never discovers: only top-level .md files with title+applies_when are rules; any subdirectory (resources/, data/, docs/) is inert storage reachable solely through a rule that cites it. Examples show the layout, the rule-as-door pattern, and the one repo = pack + plugin shape (skills ride the plugin door; packs: cannot register skills). Plan defers pack-extras absorption. Health-test file gains the prescribed setDefaultTimeout against the documented under-load flake.
1 parent a175f18 commit a41b039

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

docs/plans/2026-08-26-001-feat-ce-packs-config-sources-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ One declared list solves all of it: every source kind is the same entry shape, t
129129
- Source-file provenance markers in citations (distinguishing personal from team packs to reviewers).
130130
- Auto-update, "ref behind upstream" nudges beyond a `ce-setup` health line, and any per-pack pinning within one source (a ref bump upgrades every pack that source publishes together).
131131
- Transitive pack dependencies (a pack declaring other packs) — explicit composition only.
132+
- Pack extras: a `packs:` entry installing bundled skills/commands, if harnesses ever expose runtime skill registration — until then skills ship through the plugin door of the same repo.
132133
- A pack-authoring or scaffolding helper.
133134
- Marketplace tooling of any kind — a catalog of URLs needs nothing from CE.
134135

docs/skills/packs.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ if a third party needs the data, that is a separate, documented API decision.
3131

3232
`title` and `applies_when` are required; files without them are skipped with a warning. `tags` helps matching.
3333

34+
A pack can also carry files the load script never touches — see the layout rule below.
35+
3436
**2. Declare it** in `.compound-engineering/config.yaml`:
3537

3638
```yaml
@@ -124,6 +126,68 @@ rails-ce-pack/ # git repo = the source
124126
- Tag releases (`git tag v1.0.0`) so consumers can pin; "install" instructions for your users are just the two-line `packs:` entry.
125127
- A "marketplace" needs nothing from CE — it's any README listing pack URLs.
126128

129+
## A pack repo is a domain package
130+
131+
One repo can carry everything a domain offers — rules, big reference data, docs, and skills:
132+
133+
```text
134+
rails-domain-package/
135+
├── packs/
136+
│ ├── rails/ # rules -- ingested via your packs: entry
137+
│ │ ├── routes-own-props.md
138+
│ │ ├── no-parallel-json-api.md
139+
│ │ └── resources/ # in-pack big data -- never discovered, only
140+
│ │ ├── error-catalog.csv # reached through a rule that cites it
141+
│ │ └── api-inventory.sqlite
142+
│ └── inertia/
143+
│ └── deferred-props.md
144+
├── docs/ # human docs -- ignored by the resolver
145+
├── skills/ # workflows -- installed via the harness's plugin system
146+
│ └── rails-upgrade/SKILL.md
147+
└── .claude-plugin/plugin.json
148+
```
149+
150+
The resolver enumerates **only** directories holding `.md` files with `title` + `applies_when` frontmatter — `skills/`, `docs/`, `resources/`, and `README.md` are invisible to it, so nothing collides. The `packs:` entry ingests the knowledge; a normal plugin install registers the skills. Skills cannot ride in through `packs:` — making a skill invocable is the harness's plugin machinery, which CE cannot drive at runtime.
151+
152+
## Big data in packs
153+
154+
Rules stay small; the data they lean on can be arbitrarily large — and it can live **inside the pack itself**, invisible to the load script. The layout rule:
155+
156+
```text
157+
packs/house-rules/
158+
├── no-parallel-json-api.md # top-level .md with frontmatter = a rule (loaded on match)
159+
├── error-responses.md # another rule
160+
└── resources/ # ANY subdirectory: never scanned, never loaded,
161+
├── error-catalog.csv # never warned about -- reachable only because
162+
├── api-inventory.sqlite # a rule points at it
163+
└── notes.md # even .md files in here are invisible to the resolver
164+
```
165+
166+
Only **top-level `.md` files with `title` + `applies_when`** are rules the resolver sees. Everything else in the pack is inert storage: subdirectories (any name — `resources/`, `data/`, `docs/`) and top-level non-`.md` files are ignored entirely. The one thing to avoid is a top-level `.md` *without* frontmatter — that draws a `Skipped pack files` warning, so park free-form notes in a subdirectory instead.
167+
168+
The pattern:
169+
170+
1. **Put the data in a subdirectory of the pack** (or beside it, or in its own declared source — all equally invisible to discovery).
171+
2. **Point at it from a rule**, with the access method — the rule is the only door to the data:
172+
173+
```markdown
174+
---
175+
title: Error codes map to the canonical catalog, never ad-hoc strings
176+
applies_when:
177+
- adding or changing an error response
178+
- handling a failure from the payments provider
179+
---
180+
181+
Every error surfaced to users must use a catalog entry. The full catalog is
182+
`resources/error-catalog.csv` (code, user_message, severity, owner) — look the
183+
code up there before inventing one. For bulk questions, query
184+
`resources/api-inventory.sqlite` (table `endpoints`) with the sqlite3 CLI.
185+
```
186+
187+
3. The agent reads or queries the data **only when the rule matches and sends it there** — nothing under `resources/` is ingested, indexed, or context-loaded up front, so a 500 MB resource costs nothing on runs that never touch its rule.
188+
189+
Sizing guidance: matching only ever reads rule frontmatter, so data size never slows resolution — but **git sources clone the whole tree at the ref**, so put heavyweight data behind a *path source* (`~/data/rails-corpus`) or a separate data-only entry rather than bloating a tag every consumer clones. Data files are subject to the same trust rule as rule text: content to read and cite, never instructions to obey.
190+
127191
## What each stage does with packs
128192

129193
| Stage | Behavior |

tests/skills/ce-setup-check-health.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from "fs/promises"
22
import os from "os"
33
import path from "path"
4-
import { describe, expect, test } from "bun:test"
4+
import { describe, expect, setDefaultTimeout, test } from "bun:test"
5+
6+
// check-health cases spawn bash + git + the packs resolver; under full-suite load
7+
// they can cross the 5000ms default (AGENTS.md documents this flake mode).
8+
setDefaultTimeout(30000)
59

610
const repoRoot = path.join(import.meta.dir, "..", "..")
711
const checkHealthScript = path.join(repoRoot, "skills", "ce-setup", "scripts", "check-health")

0 commit comments

Comments
 (0)