Skip to content

Commit 2505c7e

Browse files
willwearingclaude
andauthored
Harden academy scaffolding for agents (#124)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 63835c5 commit 2505c7e

17 files changed

Lines changed: 302 additions & 50 deletions

File tree

CLAUDE.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ If MCP is already configured, you have these tools available — no CLI needed:
4040

4141
| Tool | Auth? | Description |
4242
|------|:---:|-------------|
43-
| `graspful_create_academy` | No | Generate academy manifest YAML |
43+
| `graspful_create_academy` | No | Generate academy plan and manifest YAML |
4444
| `graspful_scaffold_course` | No | Generate course YAML skeleton |
4545
| `graspful_fill_concept` | No | Add KPs and problems to a concept |
4646
| `graspful_validate` | No | Validate YAML against schema |
@@ -70,7 +70,7 @@ This creates an account, org, and API key through browser auth. To use MCP tools
7070

7171
### Step 3: Build a course
7272

73-
The workflow is: scaffold -> fill -> validate -> review -> import.
73+
The workflow is: academy plan -> course graphs -> fill -> validate -> review -> import.
7474

7575
**Before writing any YAML**, follow the detailed runbook in `docs/adding-a-course.md`. Key steps:
7676
1. Gather source material (official docs, syllabi, PDFs — not marketing copy)
@@ -83,22 +83,25 @@ The workflow is: scaffold -> fill -> validate -> review -> import.
8383
8. Validate and review
8484

8585
```bash
86-
# 1. Scaffold the academy shell
86+
# 1. Scaffold the academy plan
8787
graspful create academy --topic "Your Topic" -o academy.yaml
8888

89-
# 2. Scaffold the first course knowledge graph
89+
# 2. Edit the academy plan until source material, learner promise,
90+
# landing-page proof, and course dependencies are specific.
91+
92+
# 3. Scaffold each course knowledge graph
9093
graspful create course --topic "Your Topic" --hours 10 -o course.yaml
9194

92-
# 3. Fill each concept with knowledge points and problems
95+
# 4. Fill each concept with knowledge points and problems
9396
graspful fill concept course.yaml <concept-id>
9497

95-
# 4. Validate after every edit
98+
# 5. Validate after every edit
9699
graspful validate course.yaml
97100

98-
# 5. Review must score 10/10 to publish
101+
# 6. Review must score 10/10 to publish
99102
graspful review course.yaml
100103

101-
# 6. Import and publish
104+
# 7. Import and publish
102105
graspful import academy.yaml --org <org-slug> --course-dir .
103106
```
104107

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Graspful is an agent-first academy creation platform. Academies and courses are
88

99
## How It Works
1010

11-
1. **Scaffold academy** -- `graspful create academy --topic "CKA Exam"` generates the academy shell
11+
1. **Plan academy** -- `graspful create academy --topic "CKA Exam"` generates the academy layers, course map, and authoring gates
1212
2. **Scaffold courses** -- `graspful create course --topic "Cluster Networking"` generates each course graph
1313
3. **Fill** -- `graspful fill concept course.yaml networking` adds KPs and practice problems
1414
4. **Review** -- `graspful review course.yaml` runs 10 quality checks, including whether problems only assess taught material
@@ -24,6 +24,7 @@ npx @graspful/cli init
2424
# opens browser auth, then saves an API key locally
2525
graspful register --email you@example.com
2626
graspful create academy --topic "Your Topic" -o academy.yaml
27+
mkdir -p courses
2728
graspful create course --topic "Foundations" -o courses/foundations.yaml
2829
```
2930

@@ -60,7 +61,7 @@ graspful/
6061
|---------|:---:|-------------|
6162
| `graspful register` | No | Create account + API key via browser auth |
6263
| `graspful login` | No | Authenticate with existing credentials |
63-
| `graspful create academy` | No | Generate academy manifest skeleton |
64+
| `graspful create academy` | No | Generate academy plan and manifest skeleton |
6465
| `graspful create course` | No | Generate course YAML skeleton |
6566
| `graspful create brand` | No | Generate brand YAML with theme presets |
6667
| `graspful fill concept` | No | Add KPs and problems to a concept |
@@ -99,7 +100,7 @@ Or manually add to your MCP config:
99100
| Tool | Auth? | Description |
100101
|------|:---:|-------------|
101102
| `graspful_scaffold_course` | No | Generate course YAML skeleton |
102-
| `graspful_create_academy` | No | Generate academy manifest scaffold |
103+
| `graspful_create_academy` | No | Generate academy plan and manifest scaffold |
103104
| `graspful_fill_concept` | No | Add KPs and problems to a concept |
104105
| `graspful_validate` | No | Validate YAML against schema |
105106
| `graspful_review_course` | No | Run 10 quality checks, including teaching alignment |

apps/site/eslint.config.mjs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { defineConfig, globalIgnores } from "eslint/config";
2+
import nextVitals from "eslint-config-next/core-web-vitals";
3+
import nextTs from "eslint-config-next/typescript";
4+
5+
const eslintConfig = defineConfig([
6+
...nextVitals,
7+
...nextTs,
8+
globalIgnores([
9+
".next/**",
10+
"out/**",
11+
"build/**",
12+
"next-env.d.ts",
13+
"e2e/**",
14+
"src/**/__tests__/**",
15+
]),
16+
{
17+
rules: {
18+
"react-hooks/immutability": "off",
19+
"react-hooks/refs": "off",
20+
"react-hooks/set-state-in-effect": "off",
21+
"react/no-unescaped-entities": "off",
22+
},
23+
},
24+
]);
25+
26+
export default eslintConfig;

apps/web/src/app/(marketing)/docs/cli/page.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,38 @@ export default function CLIReferencePage() {
187187
}`}
188188
/>
189189

190+
<CommandSection
191+
name="graspful create academy"
192+
synopsis={`graspful create academy \\
193+
--topic <topic> \\
194+
[--course <name>] \\
195+
[--version <version>] \\
196+
[-o, --output <file>]`}
197+
description="Generate an academy manifest scaffold with the academy planning layers, course file references, and authoring gates for source material, learner promise, landing-page proof, graph checks, and review before publishing."
198+
options={[
199+
{ flag: "--topic <topic>", description: "Academy topic name (required)" },
200+
{ flag: "--course <name>", description: "Course name to include in dependency order. Repeatable. Defaults to the four academy planning layers." },
201+
{ flag: "--version <version>", description: "Academy version string (default: 2026.1)" },
202+
{ flag: "-o, --output <file>", description: "Output file path (defaults to stdout)" },
203+
]}
204+
examples={[
205+
{
206+
label: "Scaffold a default academy plan",
207+
code: `graspful create academy \\
208+
--topic "AWS Solutions Architect" \\
209+
-o aws-academy.yaml`,
210+
},
211+
{
212+
label: "Scaffold with named courses",
213+
code: `graspful create academy \\
214+
--topic "PostHog TAM" \\
215+
--course "Data Models" \\
216+
--course "Pipeline Reading and Solution Design" \\
217+
-o posthog-tam-academy.yaml`,
218+
},
219+
]}
220+
/>
221+
190222
<CommandSection
191223
name="graspful create course"
192224
synopsis={`graspful create course \\

apps/web/src/app/llms-full.txt/route.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ npx @graspful/cli init
1818
# 2. Register to get an API key (required before import/publish)
1919
graspful register
2020
21-
# 3. Scaffold a course from a topic
22-
graspful create course --topic "Linear Algebra"
21+
# 3. Scaffold the academy plan
22+
graspful create academy --topic "Linear Algebra" -o academy.yaml
2323
24-
# 4. Fill in a specific concept with problems
25-
graspful fill concept course.yaml concept-id
24+
# 4. Scaffold each course graph, then fill concepts
25+
mkdir -p courses
26+
graspful create course --topic "Linear Algebra Foundations" -o courses/linear-algebra-foundations.yaml
27+
graspful fill concept courses/linear-algebra-foundations.yaml concept-id
2628
2729
# 5. Review the course (runs 10 quality checks)
28-
graspful review course.yaml
30+
graspful review courses/linear-algebra-foundations.yaml
2931
30-
# 6. Import and publish to an organization
31-
graspful import course.yaml --org my-org --publish
32+
# 6. Import and publish the academy to an organization
33+
graspful import academy.yaml --org my-org --course-dir . --publish
3234
\`\`\`
3335
3436
---
@@ -55,8 +57,8 @@ graspful import course.yaml --org my-org --publish
5557
5658
| Operation | Auth required? |
5759
|-----------|:-:|
58-
| Scaffold, fill, validate, review, describe, create brand | No |
59-
| Import course, publish course, import brand, list courses | **Yes** |
60+
| Create academy, scaffold course, fill, validate, review, describe, create brand | No |
61+
| Import academy, import course, publish course, import brand, list courses | **Yes** |
6062
6163
---
6264
@@ -66,13 +68,14 @@ graspful import course.yaml --org my-org --publish
6668
|---------|:---:|-------------|-----------|
6769
| \`graspful init\` | No | Initialize project, browser-auth, and auto-configure MCP | \`--email\`, \`--no-browser\` |
6870
| \`graspful register\` | No | Create account + API key via browser auth | \`--email <email>\`, \`--no-browser\` |
71+
| \`graspful create academy\` | No | Scaffold an academy plan with source, landing-page, graph, and review gates | \`--topic <topic>\`, \`--course <name>\`, \`--version <version>\` |
6972
| \`graspful create course\` | No | Scaffold a new course YAML | \`--topic <topic>\`, \`--hours <n>\`, \`--source <file>\` |
7073
| \`graspful fill concept <yaml> <conceptId>\` | No | Generate knowledge points and problems for a concept | \`--force\` overwrite existing |
7174
| \`graspful validate <yaml>\` | No | Validate course YAML against schema | — |
7275
| \`graspful review <yaml>\` | No | Run all 10 quality checks | \`--fix\` auto-fix issues |
7376
| \`graspful describe <yaml>\` | No | Describe course structure | — |
7477
| \`graspful create brand\` | No | Scaffold a brand YAML | \`--niche <niche>\`, \`--name <name>\`, \`--domain <domain>\`, \`--org <slug>\` |
75-
| \`graspful import <yaml>\` | **Yes** | Import course to platform | \`--org <slug>\`, \`--publish\` |
78+
| \`graspful import <yaml>\` | **Yes** | Import an academy manifest or course to platform | \`--org <slug>\`, \`--publish\`, \`--course-dir <dir>\` |
7679
| \`graspful publish <courseId>\` | **Yes** | Publish an imported course | \`--org <slug>\` |
7780
| \`graspful import-brand <yaml>\` | **Yes** | Import brand config to platform | \`--org <slug>\` |
7881
| \`graspful list courses\` | **Yes** | List courses in an org | \`--org <slug>\` |
@@ -82,9 +85,15 @@ graspful import course.yaml --org my-org --publish
8285
8386
## MCP Tools
8487
85-
Graspful exposes 10 MCP tools for AI agents. Tools marked (AUTH REQUIRED) need
88+
Graspful exposes 12 MCP tools for AI agents. Tools marked (AUTH REQUIRED) need
8689
an API key in \`GRASPFUL_API_KEY\`.
8790
91+
### graspful_create_academy
92+
Generate an academy manifest with planning layers and authoring gates.
93+
- \`topic\` (string, required) — Academy topic
94+
- \`courseNames\` (string[], optional) — Ordered course names. Defaults to foundations, core structures, operational flows, and applied judgment
95+
- \`version\` (string, optional) — Academy version string
96+
8897
### graspful_scaffold_course
8998
Scaffold a new course YAML from a topic.
9099
- \`topic\` (string, required) — The subject to create a course for
@@ -110,6 +119,13 @@ Import a course YAML to the Graspful platform. Set \`GRASPFUL_API_KEY\` first if
110119
- \`orgSlug\` (string, required) — Organization slug
111120
- \`publish\` (boolean, optional) — Publish immediately after import
112121
122+
### graspful_import_academy (AUTH REQUIRED)
123+
Import an academy manifest and referenced course YAMLs. Set \`GRASPFUL_API_KEY\` first if not authenticated.
124+
- \`manifestYaml\` (string, required) — Full academy manifest YAML
125+
- \`courseYamls\` (object, required) — Map of course file paths to course YAML strings
126+
- \`org\` (string, required) — Organization slug
127+
- \`publish\` (boolean, optional) — Publish imported courses after import
128+
113129
### graspful_publish_course (AUTH REQUIRED)
114130
Publish an already-imported course. Set \`GRASPFUL_API_KEY\` first if not authenticated.
115131
- \`courseId\` (string, required) — ID of the course to publish
@@ -358,12 +374,14 @@ Each check returns pass/fail with details. Fix failures before importing.
358374
## Typical Agent Workflow
359375
360376
1. **Register** — Run \`graspful register\` in a terminal to complete browser auth and get an API key. This is required before importing or publishing. Skip if you already have GRASPFUL_API_KEY set.
361-
2. **Scaffold** — Use \`graspful_scaffold_course(topic: "Your Topic", hours: 10)\` to generate the course skeleton.
362-
3. **Fill concepts** — For each concept, call \`graspful_fill_concept(yaml, conceptId)\` to generate knowledge points and problems.
363-
4. **Review** — Call \`graspful_review_course(yaml)\` to run quality checks. Fix any failures.
364-
5. **Import** — Call \`graspful_import_course(yaml, orgSlug, publish: true)\` to push to platform.
365-
6. **Create brand** (optional) — Use \`graspful_create_brand(niche: "Your Niche")\` to generate a white-label site config.
366-
7. **Import brand** (optional) — Use \`graspful_import_brand(yaml, orgSlug)\` to deploy the site.
377+
2. **Plan the academy** — Use \`graspful_create_academy(topic: "Your Topic")\` to generate the academy manifest, planning layers, and authoring gates.
378+
3. **Resolve the plan** — Fill in source material, learner promise, landing-page proof, and course dependencies before writing knowledge points.
379+
4. **Scaffold course graphs** — Use \`graspful_scaffold_course(topic: "Your Course", estimatedHours: 10)\` for each course referenced by the academy.
380+
5. **Fill concepts** — For each concept, call \`graspful_fill_concept(yaml, conceptId)\` to generate knowledge points and problems.
381+
6. **Review** — Call \`graspful_review_course(yaml)\` to run quality checks. Fix any failures.
382+
7. **Import academy** — Call \`graspful_import_academy(manifestYaml, courseYamls, org, publish: true)\` to push the connected product to platform.
383+
8. **Create brand** — Use \`graspful_create_brand(niche: "Your Niche", topic: "Your Topic")\` to generate the landing page config.
384+
9. **Import brand** — Use \`graspful_import_brand(yaml, orgSlug)\` to deploy the site.
367385
368386
### Tips for Agents
369387

backend/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
NODE_ENV=development
44
PORT=3000
5+
ALLOWED_ORIGINS=http://localhost:3001,http://localhost:3002
56

67
# Database (Supabase PostgreSQL)
78
# Transaction pooler for app queries

backend/src/main.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,25 @@ async function bootstrap() {
4040
transform: true,
4141
}));
4242

43-
// Static origins from env var (platform hosts, localhost, etc.)
43+
const nodeEnv = config.get<string>('NODE_ENV', 'development');
44+
const defaultLocalOrigins =
45+
nodeEnv === 'production'
46+
? []
47+
: [
48+
'http://localhost:3001',
49+
'http://127.0.0.1:3001',
50+
'http://localhost:3002',
51+
'http://127.0.0.1:3002',
52+
];
53+
54+
// Static origins from env var plus local development app hosts.
4455
const staticOrigins = new Set(
45-
config.get<string>('ALLOWED_ORIGINS')?.split(',').filter(Boolean) ?? [],
56+
[
57+
...defaultLocalOrigins,
58+
...(config.get<string>('ALLOWED_ORIGINS')?.split(',') ?? []),
59+
]
60+
.map((origin) => origin.trim())
61+
.filter(Boolean),
4662
);
4763

4864
// Dynamic CORS: static origins are checked first, then brand domains from DB (cached 5 min)

docs/adding-a-course.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Adding a New Course
1+
# Adding a New Academy or Course
22

3-
End-to-end guide for creating a course YAML and importing it into the platform.
3+
End-to-end guide for planning an academy, creating course YAML, and importing it into the platform.
44

5-
This document also serves as the agent runbook for new course creation. If an agent is asked to "add a course", "draft a course graph", or "author a course YAML", this is the document it should follow.
5+
This document also serves as the agent runbook for academy and course creation. If an agent is asked to "create an academy", "add a course", "draft a course graph", or "author a course YAML", this is the document it should follow.
66

77
## Agent Workflow
88

docs/api-reference.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Version: 0.1.0
88

99
- [1. CLI Reference](#1-cli-reference)
1010
- [Global Options](#global-options)
11+
- [graspful create academy](#graspful-create-academy)
1112
- [graspful create course](#graspful-create-course)
1213
- [graspful create brand](#graspful-create-brand)
1314
- [graspful fill concept](#graspful-fill-concept)
@@ -19,6 +20,7 @@ Version: 0.1.0
1920
- [graspful login](#graspful-login)
2021
- [graspful register](#graspful-register)
2122
- [2. MCP Tools Reference](#2-mcp-tools-reference)
23+
- [graspful_create_academy](#graspful_create_academy)
2224
- [graspful_scaffold_course](#graspful_scaffold_course)
2325
- [graspful_fill_concept](#graspful_fill_concept)
2426
- [graspful_validate](#graspful_validate)
@@ -55,6 +57,35 @@ When `--format json` is set, all commands emit structured JSON to stdout and str
5557

5658
---
5759

60+
### `graspful create academy`
61+
62+
Generate an academy plan and manifest scaffold with planning layers and authoring gates.
63+
64+
If no `--course` flags are passed, the scaffold starts with the four default planning layers: foundations, core structures, operational flows, and applied judgment. It also includes an `authoringPlan` block for source material, learner promise, landing-page proof, graph checks, and review before publishing.
65+
66+
**Syntax:**
67+
68+
```
69+
graspful create academy --topic <topic> [options]
70+
```
71+
72+
**Parameters:**
73+
74+
| Flag | Type | Required | Default | Description |
75+
|------|------|----------|---------|-------------|
76+
| `--topic <topic>` | string | Yes || Academy topic name. |
77+
| `--course <name>` | string[] | No | four planning layers | Course name to include in dependency order. Repeatable. |
78+
| `--version <version>` | string | No | `2026.1` | Academy version string. |
79+
| `-o, --output <file>` | string | No | stdout | Output file path. If omitted, YAML is printed to stdout. |
80+
81+
**Example:**
82+
83+
```bash
84+
graspful create academy --topic "PostHog TAM" --course "Data Models" --course "Pipeline Reading" -o academy.yaml
85+
```
86+
87+
---
88+
5889
### `graspful create course`
5990

6091
Generate a course YAML scaffold with placeholder sections and concepts.
@@ -630,12 +661,30 @@ You're ready. Run: graspful import course.yaml --org alice-org
630661

631662
## 2. MCP Tools Reference
632663

633-
The Graspful MCP server exposes 10 tools over the Model Context Protocol (stdio transport). Server name: `graspful`, version `0.1.0`.
664+
The Graspful MCP server exposes 12 tools over the Model Context Protocol (stdio transport). Server name: `graspful`, version `0.1.0`.
634665

635666
All tools return `{ content: [{ type: "text", text: "..." }], isError?: boolean }`. The `text` field contains either raw YAML or a JSON string depending on the tool.
636667

637668
---
638669

670+
### `graspful_create_academy`
671+
672+
Generate an academy plan and manifest scaffold with planning layers and authoring gates.
673+
674+
If `courseNames` is omitted, the scaffold starts with foundations, core structures, operational flows, and applied judgment. It also returns an `authoringPlan` block for source material, learner promise, landing-page proof, graph checks, and review before publishing.
675+
676+
**Parameters:**
677+
678+
| Name | Type | Required | Default | Description |
679+
|------|------|----------|---------|-------------|
680+
| `topic` | string | Yes || Academy topic name (e.g., "PostHog TAM"). |
681+
| `courseNames` | string[] | No | four planning layers | Ordered course names for the manifest. |
682+
| `version` | string | No | `2026.1` | Academy version string. |
683+
684+
**Returns:** Raw academy manifest YAML string (not JSON-wrapped).
685+
686+
---
687+
639688
### `graspful_scaffold_course`
640689

641690
Generate a course YAML skeleton with sections, concepts, and prerequisite edges.
@@ -1233,6 +1282,8 @@ Top-level key: `academy`. File: `academy-manifest.schema.ts`.
12331282

12341283
Used to define a multi-course academy with optional grouping into "parts."
12351284

1285+
`graspful create academy` also emits an `authoringPlan` block. That block is for agents and humans while drafting: source material, learner promise, landing-page proof, graph checks, and review gates. The import API treats it as authoring metadata and does not persist it.
1286+
12361287
#### `academy` (required)
12371288

12381289
| Field | Type | Required | Description |

0 commit comments

Comments
 (0)