Skip to content

Commit 3a83e0c

Browse files
mcp-tool-shopclaude
andcommitted
feat(swarm): Phase 5-7 — Feature pass (scaffold, reference consumer, graduation)
Stage A-D landed a fastidious authoring + verification floor with no ceiling — nothing scaffolded new tools, nothing showed how to consume the IR, nothing read graduation.targetDate. Phase 5 audit identified 3 CRIT/HIGH "small" features that close the ceiling. new-tool scaffold (FT-001, CRITICAL): - marketing/scripts/new-tool.mjs — `npm run new-tool -- <id>` - Validates id against namespace pattern (lowercase, kebab-case) - Generates marketing/data/tools/<id>.json from template: - All required fields populated with placeholders - Claims default to status: aspirational + graduation block (60-day default targetDate) - 1 example message per channel (web-blurb, readme-snippet, hn-post, x-post, linkedin-post) - Placeholder press.boilerplate + targeting - Updates marketing.index.json - Refuses overwrite, rolls back on failure - Replaces CONTRIBUTING.md "use existing as reference" prose Reference consumer (FT-002, CRITICAL): - examples/consume.mjs — runnable example for downstream sites - Walks lockfile → verifies every file's sha256 → walks index → prints per-tool summaries (name, oneLiner, status counts, messages, press boilerplate) - --json mode for programmatic consumption - --root flag for alternate trees - Deliberately uses Node core APIs only — no producer-module imports (downstream consumers won't have them on classpath) - examples/README.md explains scope and consumer contract Graduation report (FT-005, HIGH): - marketing/scripts/graduation-report.mjs — `npm run graduation` - Reads claim.graduation.targetDate (added in Stage C/D) - Buckets aspirational claims: overdue / due this month / on track / untracked - Exit codes: 0 = none overdue, 1 = at least one overdue (CI-gate-able) - --json mode for structured output - Makes graduation block load-bearing instead of dark inventory Test coverage: - 73 → 89 tests (+16) across 16 suites - test/new-tool.test.mjs (6 tests): scaffold creates valid file, index updated, refuses overwrite, rejects bad ids - test/consume.test.mjs (4 tests): smoke, integrity check on tampered data, --json mode, schemaVersion forward-compat warning - test/graduation-report.test.mjs (6 tests): live-repo smoke, date-driven exit codes, untracked aspirational claims Test stability: - npm test serialized to --test-concurrency=1 (was racing on shared lockfile mutations between consume.test and gen-lock.test; livestate-mutating tests will be migrated to temp-trees in a follow-up). 5/5 consecutive runs: 89/89 pass. Doc updates: - README Scripts reference: 3 new rows - README Consuming MarketIR: pointer to examples/consume.mjs - CONTRIBUTING: scaffold workflow replaces hand-copy - CHANGELOG [Unreleased] Added: 3 new bullets - ARCHITECTURE Components + Extension points: new entries Final state: validate ✓ lock:check ✓ 89/89 tests ✓ fmt:check ✓ Repo now has both floor (verification) and ceiling (extension). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 174d704 commit 3a83e0c

12 files changed

Lines changed: 1765 additions & 13 deletions

ARCHITECTURE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ Humans only edit files in `marketing/data/**` and `marketing/manifests/evidence.
7070
| `marketing/scripts/gen-lock.mjs` | The lockfile generator. Deterministic — same inputs, byte-identical output. |
7171
| `marketing/scripts/hash-file.mjs` | Single-file hash utility. Used when registering new evidence. |
7272
| `marketing/scripts/_paths.mjs` | Shared module for path resolution + safety guards (`assertSafeRef`, `assertSafePath`). |
73+
| `marketing/scripts/new-tool.mjs` | Scaffold script. Generates a boilerplate tool record under `marketing/data/tools/` so new tools start from a known-valid shape. |
74+
| `marketing/scripts/graduation-report.mjs` | Aspirational claim graduation report. Lists claims by `graduationTarget` (overdue / due this month / on track). |
75+
| `examples/consume.mjs` | Reference consumer. Walks the lockfile, verifies every hash, and prints tool summaries. Mirrors what the site bridge does. |
7376

7477
---
7578

@@ -143,6 +146,8 @@ The architecture is intentionally minimal. When new requirements arrive, these a
143146
- **Deprecation metadata** (planned) — when a schema field is deprecated, an explicit `deprecatedSince` field would let consumers warn their own users with version context. Currently, deprecation lives only in the schema's `description` text.
144147
- **`x-*` extension fields** (considering) — for consumer-specific metadata that should not pollute the canonical schema. Mirrors the JSON Schema convention.
145148

149+
For consumers building their own ingestion pipeline, **`examples/consume.mjs` is the canonical reference consumer**. It demonstrates the lockfile-first walk + sha256 verification end-to-end and is the recommended starting point before re-implementing the recipe in another stack.
150+
146151
See [ROADMAP.md](ROADMAP.md) for the full set.
147152

148153
---

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ Dogfood swarm wave 2 (2026-05-15) landed cross-cutting hardening.
1717
- CI: minimal-permissions block on GitHub Actions workflows.
1818
- Repo ownership: `.github/CODEOWNERS`.
1919
- Scripts: `marketing/scripts/_paths.mjs` shared-paths module (single source of truth for repo path resolution across scripts).
20+
- Scripts: `marketing/scripts/new-tool.mjs` — scaffold script for adding new tool entries (`npm run new-tool -- <id>`).
21+
- Scripts: `marketing/scripts/graduation-report.mjs` — overdue aspirational claim report (`npm run graduation`).
22+
- Examples: `examples/consume.mjs` — reference consumer demonstrating how to fetch, verify, and walk the marketing IR.
2023
- Docs: top-level README Testing section.
2124

2225
### Changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ For one-line examples of every script, see the **Scripts reference** section in
3939

4040
## Adding a new tool
4141

42-
1. Create `marketing/data/tools/<tool-id>.json` (e.g., `marketing/data/tools/foo-bar.json`).
43-
2. Use the existing `zip-meta-map.json` as a structural reference. Required top-level fields: `schemaVersion`, `id`, `name`, `positioning`, `audienceRefs`, `claims`, `messages`, `press`, `targeting`.
42+
1. Run `npm run new-tool -- <tool-id>` to scaffold the boilerplate. Then edit the file, claim by claim. Run `npm run validate` after each meaningful change.
43+
2. Required top-level fields: `schemaVersion`, `id`, `name`, `positioning`, `audienceRefs`, `claims`, `messages`, `press`, `targeting`.
4444
3. The tool's `id` must follow the namespace pattern `tool.<slug>` (see README's "IDs are stable and permanent" section).
4545
4. Add a `tools` entry referencing the new file in `marketing/data/marketing.index.json`. Files not reachable from the index are rejected as orphans by the validator.
4646
5. Run `npm run lock` to regenerate the lockfile so the new tool is pinned.

README.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,18 @@ npm test
139139

140140
One-line examples for every script in `package.json`:
141141

142-
| Command | What it does |
143-
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
144-
| `npm run validate` | Validate the entire dataset against the schema and structural invariants. No args. Exits non-zero on any failure. |
145-
| `npm run lock` | Regenerate `marketing/manifests/marketing.lock.json` in place from current data. No args. |
146-
| `npm run lock:check` | Regenerate the lockfile in CI mode and fail if it differs from what's committed. No args. Used by CI. |
147-
| `npm test` | Run the full test suite via the Node built-in test runner. |
148-
| `npm run fmt:check` | Verify Prettier formatting across the repo. Exits non-zero if anything would be reformatted. |
149-
| `npm run fmt` | Apply Prettier formatting in place. |
150-
| `npm run hash -- marketing/evidence/<file>` | Print the sha256 + bytes of one file. Use this when registering a new evidence artifact in `evidence.manifest.json`. The `--` is required so npm forwards the path argument. |
142+
| Command | What it does |
143+
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
144+
| `npm run validate` | Validate the entire dataset against the schema and structural invariants. No args. Exits non-zero on any failure. |
145+
| `npm run lock` | Regenerate `marketing/manifests/marketing.lock.json` in place from current data. No args. |
146+
| `npm run lock:check` | Regenerate the lockfile in CI mode and fail if it differs from what's committed. No args. Used by CI. |
147+
| `npm test` | Run the full test suite via the Node built-in test runner. |
148+
| `npm run fmt:check` | Verify Prettier formatting across the repo. Exits non-zero if anything would be reformatted. |
149+
| `npm run fmt` | Apply Prettier formatting in place. |
150+
| `npm run hash -- marketing/evidence/<file>` | Print the sha256 + bytes of one file. Use this when registering a new evidence artifact in `evidence.manifest.json`. The `--` is required so npm forwards the path argument. |
151+
| `npm run new-tool -- <id>` | Scaffold a new tool entry under `marketing/data/tools/`. Generates the boilerplate JSON file and reminds you to add the index entry. The `--` is required so npm forwards the id argument. |
152+
| `npm run graduation` | Report aspirational claim graduation status (overdue / due this month / on track). Use this to see which aspirational claims are nearing or past their `graduationTarget` date. No args. |
153+
| `node examples/consume.mjs` | Reference consumer that walks the lockfile, verifies every hash, and prints tool summaries. Pass `--json` for machine-readable output. Mirrors what the public site bridge does at build time. |
151154

152155
---
153156

@@ -208,7 +211,7 @@ Consumers should resolve files through the lockfile rather than hard-coding path
208211
3. For each entry in the lockfile, fetch the referenced file (e.g., from `https://raw.githubusercontent.com/mcp-tool-shop/mcpt-marketing/<tag>/<path>`) and verify its sha256 matches the lockfile entry.
209212
4. Abort the build on any hash mismatch — the data is no longer self-consistent.
210213

211-
The site bridge (`fetch-marketir.mjs`) is the reference implementation of this recipe.
214+
The site bridge (`fetch-marketir.mjs`) is the reference implementation of this recipe. See `examples/consume.mjs` for a runnable reference consumer that demonstrates the lockfile-first walk, sha256 verification, and tool summarization end-to-end without leaving this repo.
212215

213216
### Aliases and deprecation
214217

examples/README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# MarketIR consumer examples
2+
3+
This directory holds reference implementations of downstream consumers of the
4+
MarketIR data set. They are documentation that runs.
5+
6+
## Contents
7+
8+
| File | Purpose |
9+
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
10+
| `consume.mjs` | Reference consumer — reads the lockfile, verifies sha256 integrity for every locked file, walks the index, and prints a per-tool summary. |
11+
12+
## When to read these examples
13+
14+
- You are building a downstream consumer (e.g. the public mcptoolshop.com
15+
site, a Slack notifier, a static-site generator integration) and want a
16+
worked example of the lockfile-verify-then-walk pattern.
17+
- You are evaluating whether to depend on this dataset and want a 5-minute
18+
proof that the contract works the way the README claims.
19+
- You are debugging a "why does my snapshot diverge from the lockfile" issue
20+
and want a known-good reference implementation to diff against.
21+
22+
## When NOT to copy these examples wholesale
23+
24+
The example uses **relative filesystem paths** to keep the demo runnable from
25+
a fresh checkout with no setup. A production consumer should:
26+
27+
- Use a **stable URL** (e.g. `raw.githubusercontent.com/<tag>/<path>`) instead
28+
of relative paths, so the consumer is not coupled to a local checkout layout.
29+
- **Cache aggressively** — every IR file is content-addressed via the
30+
lockfile's sha256, so a sha256 match is a byte-for-byte cache hit.
31+
- **Validate against the published JSON Schema** rather than trusting the
32+
field shape produced by the example.
33+
- **Pin to a specific tag** rather than fetching from `main` so a producer-side
34+
edit cannot change the bytes you depend on without your knowledge.
35+
36+
## Running consume.mjs
37+
38+
From the repo root:
39+
40+
```bash
41+
node examples/consume.mjs # human-readable summary
42+
node examples/consume.mjs --json # structured JSON to stdout
43+
node examples/consume.mjs --root <p> # alternate marketing/ tree
44+
node examples/consume.mjs --help # full options
45+
```
46+
47+
Exit codes:
48+
49+
- `0` — Walked + verified everything successfully.
50+
- `1` — Integrity failure (sha256 mismatch / unreadable file / schema mismatch).
51+
52+
## What `consume.mjs` actually does
53+
54+
1. Reads `marketing/manifests/marketing.lock.json` (the entry point).
55+
2. Checks the lockfile's `schemaVersion` against the supported MAJOR; warns
56+
on mismatch.
57+
3. For each entry in `lock.files[]`, recomputes `sha256` + `bytes` from disk
58+
and compares to the lockfile entry. This is the **load-bearing** step —
59+
without it, the lockfile is "polite suggestion," not "tamper-evident."
60+
4. Reads `marketing/data/marketing.index.json` and walks `tools[]`,
61+
`audiences[]`, `campaigns[]`.
62+
5. For each tool, prints: name, oneLiner, status counts (proven /
63+
aspirational / deprecated), first 2 messages, and a press boilerplate
64+
snippet.
65+
66+
## When the consumer contract changes
67+
68+
If you add a new file under `marketing/`, the lockfile-walk step automatically
69+
picks it up — no consumer change needed.
70+
71+
If you change the **shape** of the IR (e.g. rename a field, add a new top-level
72+
section), update `consume.mjs` to demonstrate the new field. Drift between the
73+
schema and this example is a contract-surface bug; the docs domain owns
74+
flagging it during review.

0 commit comments

Comments
 (0)