recess-cli 1.3.3 → 1.6.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.
@@ -1,502 +1,72 @@
1
1
  ---
2
2
  name: recess-cli
3
- description: Safely perform Recess staff administration through the recess CLI. Use when a Recess admin asks Codex to find a kid, parent, family, enrollment, subscription, invoice, or cohort; edit the Recess Village map or manage store-catalog availability; search the curated Content Library; inspect or change a school kid's tier and capability gates; upload a kid's MAP Growth report; pause or resume billing; refund or credit an invoice item; extend a trial; cancel or restore a subscription; register or unregister a cohort against an enrollment; switch or move kids from one cohort to another; manage guide payout invoices (biweekly pay-cycle line-item changes, invoice status moves, payout recipient lookups); run class operations (take attendance, cancel or reschedule a class session, add a one-off session, end a cohort, pause cohort billing, email a cohort's families, approve or deny pending registrations); process class-cancellation credits (the "Please credit these students accordingly" Slack message — credit every registered kid for a guide-canceled session); or author learning content — build, validate, publish, and loss-safely patch a deterministic GoalTemplate, apply a template to a kid or a roster, create a goal directly on a kid, write a Mesa draft or goal workspace, capture a template snapshot, and read Mesa workspace or snapshot files.
4
- # Bundle version. Bump on every substantive edit; the CLI reports it and `doctor`
5
- # compares it against the served copy to tell an operator a refresh is available.
6
- version: 1.4.4
7
- # The lowest `recess` version this bundle is safe to install onto. Raise it ONLY
8
- # when the bundle documents a command, flag, or changed semantic that an older
9
- # binary does not have — an older CLI keeps its bundled copy instead of taking
10
- # this one. Prose, formatting, and Gotcha edits must NOT raise it; that is the
11
- # whole point of serving the bundle.
12
- minCliVersion: 1.3.3
3
+ description: Route Recess CLI work to the correct permission-scoped skill and use the shared authentication, JSON, and confirmation contracts. Use whenever an agent needs the recess command, before loading guardian learning guidance or staff-only operational guidance.
13
4
  ---
14
5
 
15
- # Recess CLI (`recess`)
6
+ # Recess CLI
16
7
 
17
- Use the installed `recess` command. Never bypass it with direct production database writes, Stripe calls, browser clicks, or hand-written API requests. Every command here operates on **live production data real families, real kids, real money.**
8
+ Use the installed `recess` command for Recess work. This small skill is shared by every CLI user. It intentionally contains no family-specific advice, staff procedures, incident notes, billing rules, or internal implementation vocabulary.
18
9
 
19
- > **Maintain this skill.** Every time you discover a new failure mode, a working invocation, or a surprising behavior, add it to the "Gotchas" notebook at the bottom (append-only, dated). When a whole workflow changes, update the matching playbook in `reference/`. The skill's source of truth lives in the monolith at `apps/admin-cli/skill/recess-cli/` — fix it there, then reinstall (`pnpm --dir apps/admin-cli run install-skill`).
20
- >
21
- > **This bundle is both published and served.** The npm package ships it (the offline, pre-auth floor), and `GET /admin/cli-skill/` serves it, so an installed CLI picks up wording and Gotcha edits via `recess setup --skill-only` **without waiting for an npm release** — the served copy just has to deploy. Bump `version:` in the frontmatter on every substantive edit. Raise `minCliVersion:` **only** when the bundle documents a command, flag, or changed semantic an older binary lacks; an older CLI then keeps its bundled copy instead. Raising it for a prose edit needlessly strands everyone who has not upgraded.
22
-
23
- ## The safety model — non-negotiable
24
-
25
- Every mutating command is two-step. Run it **without** `--confirm` first: the CLI returns `confirmation_required` (exit code 2) plus the exact `action`, `target`, and `request` body it would send, and makes **no network write**.
26
-
27
- Some previews also carry a `details` object — server-resolved facts that cannot be known offline.
28
- Today those include `enrollments create` (real price, no-charge reuse, and capacity/slot
29
- violations), `users tier set` (current/proposed capability locks and class-slot consequences), and
30
- `goal-templates patch-spec` (validated before/after hashes and protected-inventory loss),
31
- `mesa files write` (resolved workspace root, Mesa change fence, and file hashes), and
32
- `goal-templates capture-snapshot` (validated modules/files plus template/source fences); getting
33
- one costs read-only calls, never a write. **When `details` is present it is part of the preview —
34
- show it to the human too.** Approving from `action`/`request` alone while ignoring `details` is how
35
- an override gets rubber-stamped.
36
-
37
- 1. Show that preview to the human, verbatim.
38
- 2. Request explicit escalated approval for that exact action using the execution tool's approval mechanism.
39
- 3. Only after the human approves, rerun the **unchanged** command with `--confirm`.
40
-
41
- Never infer approval from the original task, prior approval, urgency, or a successful read. Never add `--confirm` yourself before the approval arrives. If any ID, amount, refund method, payer, pause date, or target changes, discard the approval and preview again.
42
-
43
- **Exceptional actions** get named explicitly in the approval request, on top of the normal preview:
44
-
45
- | Flag / command | Why it is exceptional |
46
- |---|---|
47
- | `enrollments create` | **charges a real family real money.** Quote `details.billing.effectivePriceCents` (the discounted amount actually billed, NOT `listPriceCents`), the recurrence, and the first-charge date in the approval request |
48
- | `enrollments create --force` | overrides a cohort-capacity or school-class-slot violation; name the specific warning being overridden |
49
- | `users tier set --allow-strand` | lowers a kid's class allowance below active registrations; quote `slotsUsed`, the proposed allowance, and both capability-lock lists |
50
- | `billing cancel-subscription --immediate` | no period-end grace — access ends now |
51
- | `invoices refund … --who-pays recess` | Recess absorbs the cost instead of the guide |
52
- | `cohorts end --cancel-subscriptions` | sets EVERY active enrollment's Stripe subscription to cancel at period end |
53
- | `cohorts email` | real outward email blast to families (include the recipient count from `cohorts parent-emails`) |
54
- | `events cancel` | family-facing fan-out: chat messages, parent email blast, credit-owed notes, Slack |
55
- | `payout invoices set-status` to `OPEN`/`PAID`/`CANCELED` | moves real account balances; `--send-email` additionally emails the guide |
56
- | destructive `goal-templates patch-spec` | removes subjects, recipes, plans, queue items, source URLs, or missing-coverage records from a global template; quote the exact `details.safety.removed` inventory and hashes, then use the fresh token only after approval |
57
-
58
- ## Setup, auth, and troubleshooting
59
-
60
- - Binary: `recess` (published on npm as `recess-cli`). The standard install needs no monolith checkout — `npm install -g recess-cli` then `recess setup` (installs this skill for Codex and Claude, then opens SSO). From a checkout, `pnpm --dir apps/admin-cli run install-persistent` builds a self-contained copy under `~/.recess-cli/cli/` linked from `~/.local/bin/recess`; `install-local` symlinks the checkout's live `dist/` instead — CLI-development only, and the link dies with its worktree.
61
- - Session: a **12-hour** signed admin cookie stored at `~/.recess-cli/config.json` (mode 0600). It is **not refreshable** — when it expires, a human must approve a fresh sign-in. On a headless agent, use `auth request` + `auth poll`; on a local workstation, `auth login` remains available.
62
- - Start every session with:
63
-
64
- ```bash
65
- command -v recess
66
- recess --json doctor
67
- ```
68
-
69
- `doctor` reports the config paths/origins, the auth state (it validates the stored session against `GET /auth/admin-cli/session/`), and API reachability (`/health`). Interpret it before running anything else.
70
- - If auth is missing or expired on a headless agent, start a device-authorization request:
71
-
72
- ```bash
73
- recess --json auth request --label "<agent or VM name>"
74
- ```
75
-
76
- Show the returned `approvalUrl` and `userCode` to the human. The secret device code stays only in the mode-0600 config file and must never be copied into chat or logs. After the human approves the web page, collect the session:
77
-
78
- ```bash
79
- recess --json auth poll --timeout 300
80
- ```
81
-
82
- A timed-out poll preserves the pending request, so it is safe to rerun after approval. A denied, expired, or already-used request needs a fresh `auth request`.
83
-
84
- - On a workstation with a browser, the human can instead complete:
85
-
86
- ```bash
87
- recess --json auth login # uses the built-in production OAuth client
88
- ```
89
-
90
- The login opens a browser to Recess SSO and listens on `127.0.0.1:8765` (5-minute timeout). A non-default `--callback-port` only works if that exact loopback URL is registered on the OAuth client — otherwise production rejects the redirect.
91
- - `auth status` shows the current session and whether it came from the env or the config file; `auth logout` clears the stored session.
92
-
93
- Environment overrides (all optional): `RECESS_CLI_API_ORIGIN` (default `https://api.recess.gg`), `RECESS_CLI_WEB_ORIGIN` (default `https://recess.gg`), `RECESS_CLI_OAUTH_CLIENT_ID` (overrides the built-in production client for local/staging), `RECESS_CLI_COOKIE` (session override, wins over the config file), `RECESS_CLI_CONFIG` (config path). Pointing `RECESS_CLI_API_ORIGIN` at a local server is how dev/testing works; if it is set, you are NOT talking to production — say so in previews.
94
-
95
- Auth failure modes:
96
-
97
- | Error code | Meaning | Fix |
98
- |---|---|---|
99
- | `auth_required` | no stored session | headless: `auth request` + human approval + `auth poll`; workstation: `auth login` |
100
- | `api_error` with 401 on any command | session expired or revoked | acquire a fresh session through the same headless or workstation flow |
101
- | `callback_unavailable` | port 8765 already in use | free the port or use a registered `--callback-port` |
102
- | `auth_timeout` / `auth_failed` | SSO not completed in 5 min / denied | retry the login in the browser |
103
-
104
- ## The JSON contract
105
-
106
- Always pass `--json` (it may appear anywhere in the argv). Stdout then carries exactly one JSON object:
107
-
108
- - Success: `{"ok":true,"data":{…}}` — exit code 0.
109
- - Failure: `{"ok":false,"error":{"code":"…","message":"…","details":{…}}}` — exit code 1.
110
- - Write awaiting approval: code `confirmation_required`, `details.preview` = `{action,target,request}` (plus `details` on commands with server-resolved facts, e.g. `enrollments create`), `details.requiredFlag` = `--confirm` — **exit code 2**.
111
-
112
- Without `--json`, success pretty-prints to stdout but errors go to **stderr** as text — so agents use `--json` unconditionally. Error codes you will see: `invalid_arguments`, `auth_required`, `not_found`, `api_error` (carries `details.status` and the raw API `details.body`), `confirmation_required`, `unknown_command`, `unexpected_error`, plus the auth codes above.
113
-
114
- ## Input conventions
115
-
116
- - **Money is integer cents**, always (`--amount-cents 28800` = $288.00). Parse dollar expressions yourself; never send fractional values.
117
- - **Three date shapes, not interchangeable:**
118
- - `--until` / `--trial-end`: ISO date, must be in the future (`2026-09-01`).
119
- - Payout item `--date`: bare `YYYY-MM-DD` (anchored to UTC noon so the calendar day survives timezone conversion) or a full ISO timestamp.
120
- - Event `--starts-at`: **zoneless** cohort-local wall clock, `YYYY-MM-DDTHH:MM` — a timezone offset is rejected. "3pm" means `15:00` in the cohort's timezone.
121
- - **ID and status lists are comma-separated:** `--attended id1,id2` · `--status IN_REVIEW,DRAFT`.
122
- - Search queries are positional and may span words: `recess --json users search Morgan Rivera` works, quotes optional.
123
- - **Flag parser traps:** `--flag value` and `--flag=value` are both accepted, but a value that itself starts with `--` MUST use the `=` form (`--content="-- headline --"`), or it is parsed as another flag. Misspelled flags are not rejected — an unknown `--flag` silently swallows the next token, and the real flag then errors as "Missing required". On any confusing `invalid_arguments`, re-check flag spelling first.
124
-
125
- ## The standard loop (reads resolve before writes)
126
-
127
- 1. **Resolve names to IDs.** `users search` returns the matching people, their family, every family member, and each member's enrollments and cohort registrations. Never act on a guessed ID; stop on ambiguous matches and ask the human which family or user they mean.
128
-
129
- ```bash
130
- recess --json users search "parent or kid name"
131
- recess --json enrollments list --user <kid-id>
132
- ```
133
-
134
- 2. **Read the exact current state** before proposing any write (playbooks list the reads per workflow).
135
- 3. **Collect previews** by running every planned write WITHOUT `--confirm`.
136
- 4. **Batch the escalation:** present the complete change plan — every preview, every side effect (emails, Stripe changes, notifications), every exceptional flag — as ONE approval request.
137
- 5. **Execute confirmed:** rerun each command unchanged with `--confirm`.
138
- 6. **Verify:** re-read the touched resources (`payout invoices get`, `events get`, `cohorts get`, `invoices list`, …) and check the new state matches the plan. Report the final API responses.
139
-
140
- ## Command quick reference
10
+ ## Start here
141
11
 
142
12
  ```bash
143
- # Health & auth
13
+ command -v recess
144
14
  recess --json doctor
145
- recess --json auth login|status|logout
146
-
147
- # People & enrollment context
148
- recess --json users search <name-or-id> [--limit 10]
149
- recess --json users get <user-id>
150
- recess --json users tier list-tiers
151
- recess --json users tier get <kid-id>
152
- recess --json users tier preview <kid-id> --tier <id> [--slots N]
153
- recess --json users tier set <kid-id> --tier <id> [--slots N] \
154
- --expected-updated-at <iso> [--allow-strand] [--confirm]
155
- recess --json enrollments list --user <user-id>
156
- recess --json enrollments get-for-subscription --subscription <id>
157
-
158
- # Billing & subscriptions (reference/billing.md)
159
- recess --json subscriptions list --family <family-id> [--kid <kid-id>]
160
- recess --json invoices list --subscription <subscription-id>
161
- recess --json billing pause --subscription <id> [--until ISO_DATE] [--confirm]
162
- recess --json billing resume --subscription <id> [--confirm]
163
- recess --json billing extend-trial --subscription <id> --trial-end ISO_DATE [--confirm]
164
- recess --json billing cancel-subscription --subscription <id> [--immediate] [--reason TEXT] [--restore] [--confirm]
165
- recess --json invoices refund --invoice <id> --line-item <id> --method refund|credit|tokens \
166
- [--full | --amount-cents N] [--who-pays guide|recess] [--reason TEXT] [--confirm]
167
- recess --json enrollments create --user <kid-id> --cohort <id> \
168
- [--first-charge-at ISO_DATETIME] [--send-email] [--force] [--confirm]
169
- recess --json enrollments register-cohort --enrollment <id> --user <id> --cohort <id> [--confirm]
170
- recess --json enrollments unregister-cohort --user <id> --cohort <id> [--confirm]
171
-
172
- # MAP Growth reports (reference/map-scores.md)
173
- recess --json students upload-map-scores --student <kid-id> --file <report.pdf> [--confirm]
174
-
175
- # Guide payouts (reference/payout.md)
176
- recess --json payout payruns list [--status A,B] [--schedule <id>]
177
- recess --json payout recipients list [--search <name>] [--user <id>] [--id <id>]
178
- recess --json payout invoices list [--payrun <id>] [--recipient <account-id>] [--user <id>] [--status A,B]
179
- recess --json payout invoices get <invoice-id>
180
- recess --json payout invoices set-status <invoice-id> --status IN_REVIEW|OPEN|PAID|CANCELED [--send-email] [--confirm]
181
- recess --json payout items add --invoice <id> --amount-cents N --description TEXT [--date YYYY-MM-DD] [--confirm]
182
- recess --json payout items edit <item-id> [--amount-cents N] [--description TEXT] [--date YYYY-MM-DD] [--confirm]
183
- recess --json payout items delete <item-id> [--confirm]
184
-
185
- # Class ops (reference/class-ops.md)
186
- recess --json cohorts search <query>
187
- recess --json cohorts get <cohort-id> [--events-tab ACTIVE|ENDED|CANCELED|ARCHIVED]
188
- recess --json cohorts parent-emails <cohort-id>
189
- recess --json cohorts end <cohort-id> [--cancel-subscriptions] [--confirm]
190
- recess --json cohorts pause-billing <cohort-id> --weeks 1..6 [--confirm]
191
- recess --json cohorts resume-billing <cohort-id> [--confirm]
192
- recess --json cohorts email <cohort-id> --target ALL_PARENTS|ALL_PARENTS_GUIDES --content TEXT [--confirm]
193
- recess --json events get <event-id>
194
- recess --json events take-attendance <event-id> --attended <ids> [--absent <ids>] [--excused <ids>] [--confirm]
195
- recess --json events cancel <event-id> --reason TEXT [--confirm]
196
- recess --json events set-status <event-id> --status ACTIVE|ENDED|CANCELED [--confirm]
197
- recess --json events reschedule --cohort <id> --event <id> --starts-at "YYYY-MM-DDTHH:MM" [--timezone <iana>] [--length-mins N] [--confirm]
198
- recess --json events add --cohort <id> --starts-at "YYYY-MM-DDTHH:MM" [--timezone <iana>] [--length-mins N] [--confirm]
199
- recess --json registrations approve --registration <id> [--confirm]
200
- recess --json registrations deny --cohort <id> --user <id> [--confirm]
201
-
202
- # Onboarding (reference/onboarding.md) — all keyed on family-id
203
- recess --json onboarding status <family-id>
204
- recess --json onboarding kids [--time-period-days N] [--cohort <id>] [--limit N] [--stage-filter all|scheduled|oriented|course|converted|lost]
205
- recess --json onboarding intake-session <family-id>
206
- recess --json onboarding set-stage <family-id> --stage LEGACY|PROVISIONED|PARENT_CONFIRMED|CLEARED_FOR_COHORT|COMPLETE [--confirm]
207
- recess --json onboarding set-account-state <family-id> --state ACTIVE|PENDING_PAYMENT|PAUSED|BOOTED [--note TEXT] [--confirm]
208
- recess --json onboarding attest <family-id> --condition app_downloaded|tutor_met|goals_loaded|ma_diagnostic [--revoke] [--note TEXT] [--confirm]
209
- recess --json onboarding set-intake <family-id> --session <id> --data <json> [--expected-updated-at <iso>] [--confirm]
210
- recess --json onboarding extract <family-id> --session <id> (--transcript-file <path> | --granola <ref>) [--confirm]
211
-
212
- # Recess Village map administration
213
- recess --json village render --min-x N --min-z N --max-x N --max-z N [--world village-1]
214
- recess --json village models list [--world village-1] [--query TEXT] [--archived]
215
- recess --json village models upload --file <model.glb> [--world village-1] [--name TEXT] [--confirm]
216
- recess --json village models publish|archive <model-id> [--world village-1] [--confirm]
217
- recess --json village models place <model-id> --x N --z N [--y N] [--rotation 0..3] [--world village-1] [--confirm]
218
- recess --json village models move <placement-id> --x N --z N [--y N] [--rotation 0..3] [--world village-1] [--confirm]
219
- recess --json village models remove <placement-id> [--world village-1] [--confirm]
220
-
221
- # Main Recess store catalog (status writes are restricted to Village goods)
222
- recess --json store-items list [--search TEXT] [--status ACTIVE|INACTIVE|COMING_SOON] [--item-type TYPE]
223
- recess --json store-items set-status <village-store-item-id> --status ACTIVE|INACTIVE|COMING_SOON [--confirm]
224
-
225
- # Tutor skills — the authoring know-how (served, never bundled)
226
- recess --json skills list [--query TEXT] [--category TEXT]
227
- recess --json skills get <skill-name> [--reference NAME | --all-references] [--refresh]
228
-
229
- # Curated learning-resource research (read-only)
230
- recess --json content-library search <query> [--limit 8]
231
-
232
- # Learning content (reference/goal-authoring.md)
233
- recess --json goal-templates list [--query TEXT] [--kind SIMPLE|BLUEPRINT] [--starter-only]
234
- recess --json goal-templates get <id-or-slug> [--spec-only]
235
- recess --json goal-templates versions <id-or-slug> [--version N]
236
- recess --json goal-templates validate-spec --file <path/template.json>
237
- recess --json goal-templates create --file <path/template.json> [--confirm]
238
- recess --json goal-templates patch-spec <id-or-slug> --expected-version N --patches-file <path/patches.json> [--confirm] [--confirm-destructive-changes --destructive-change-token TOKEN]
239
- recess --json goal-templates set-metadata <id-or-slug> --expected-version N [--title …] [--agent-instructions-file <path>] [--output-template-file <path>] [--confirm]
240
- recess --json goal-templates delete <id-or-slug> --expected-version N [--confirm]
241
- recess --json goal-templates snapshot-files <id-or-slug> [--path P]
242
- recess --json goal-templates capture-snapshot <id-or-slug> (--source-goal <goal-id> | --source-draft <draft-slug> --student <kid-id>) [--dry-run] [--confirm]
243
- recess --json goal-templates apply <id-or-slug> --answers-file <path> [--dry-run] [--confirm]
244
- recess --json goal-templates apply-starter <id-or-slug> --student <kid-id> [--confirm]
245
- recess --json goals list --student <kid-id>
246
- recess --json goals create --student <kid-id> --title TEXT --description TEXT [--confirm]
247
- recess --json mesa files list --student <kid-id> --goal <goal-id>
248
- recess --json mesa files read --student <kid-id> --goal <goal-id> --path P
249
- recess --json mesa files write --student <kid-id> (--goal <goal-id> | --draft <draft-slug>) (--source-dir <local-dir> | --source-file <local-file> --path P) [--message TEXT] [--confirm]
250
-
251
- # Read-only escape hatch (GET only — no raw writes exist)
252
- recess --json request get /path?query=value
253
15
  ```
254
16
 
255
- ### School tier contract
256
-
257
- `users tier get` is the read-before-write source: copy its `updatedAt` into
258
- `--expected-updated-at`, then run `preview` (or the unconfirmed `set`) with the intended tier and
259
- slot override. Show the human `slotsUsed`, the proposed allowance,
260
- `capabilitiesLockedNow`/`capabilitiesLockedAfter`, `wouldStrandRegistrations`, and `enforcementOn`.
261
- Tier names are not a capability ladder: with enforcement on, `social` (and an unassigned tier)
262
- locks `todo`, `browser`, and `tutor_chat`; the other four canonical tiers lock none. With
263
- `enforcementOn:false`, the row still changes but the kid observes no capability change until the
264
- `school-onboarding-v1` flag turns on. Never infer lock consequences from the tier name.
265
-
266
- The CLI refuses `wouldStrandRegistrations:true` by default. `--allow-strand` is an exceptional
267
- override, not a retry hint: use it only after the human explicitly approves leaving more active
268
- registrations than class slots. A 409 `STALE_WRITE` means the kid changed after the read; fetch a new
269
- token, preview again, and discard the old approval.
270
-
271
- ### Learning-content contract — load the tutor skills FIRST
17
+ `doctor` reports the active API origin, authenticated user, and CLI scope. Do not assume a role from the request. Use the scope returned by the server.
272
18
 
273
- Structurally-valid content is not good content. The `recess.gg/ai` agent writes good templates
274
- because it loads the in-product tutor skills before authoring, and `recess skills` serves you those
275
- same documents over your admin session. **Before writing any template, goal, or workspace file:**
19
+ Then load the guidance for the purpose of the work:
276
20
 
277
21
  ```bash
278
- recess --json skills get os-v2-goal-template-builder --all-references
279
- ```
280
-
281
- That is not optional politeness — the skill carries the kind decision (Simple vs Blueprint vs
282
- Prebuilt), the tutor-facing language rules, and `references/deterministic-workflow-setup.md`, which
283
- is the catalog of what the deterministic handlers can actually express. **If the requested behavior
284
- cannot be expressed by a supported handler, say so and stop.** Do not substitute something
285
- structurally valid but wrong.
286
-
287
- Which skill for which job:
288
-
289
- | Job | Load |
290
- |---|---|
291
- | Build or revise a GoalTemplate | `os-v2-goal-template-builder` (+ all references) |
292
- | Build a module-backed goal / course workspace | `os-v2-goal-builder` (+ all references) |
293
- | Create a goal for one specific kid | `goal-creation`, plus `student-research` to read them first |
294
- | Reading/ELA content | `ela-reading-goal` |
295
- | Judge whether an existing goal is any good | `goal-health-evaluation` |
296
- | How the runtime consumes what you authored | `os-v2-curriculum-runtime` |
297
- | Research an external learning platform before wiring it in | `pipeline-platform-research` |
298
- | Adapt an existing goal to new evidence | `pipeline-goal-adaptation` |
299
-
300
- These are proprietary and fetched at runtime; they are **not** in this package and must never be
301
- copied into it, into a repo, or into a chat transcript you publish.
302
-
303
- #### Tool → command mapping
304
-
305
- The skills are written for the in-product agent and name OSAgent **tools** that do not exist here.
306
- Translate as you read:
307
-
308
- | Skill says (tool) | You run (command) |
309
- |---|---|
310
- | `manage_goal_template action:"list"` | `goal-templates list` |
311
- | `manage_goal_template action:"get"` | `goal-templates get <id-or-slug>` (`--spec-only` for the spec alone) |
312
- | `manage_goal_template action:"list_versions"` / `"get_version"` | `goal-templates versions <id>` / `versions <id> --version N` |
313
- | `manage_goal_template action:"create"` | `goal-templates validate-spec --file …` then `goal-templates create --file … --confirm` |
314
- | `manage_goal_template action:"preview_setup_workflow_spec_patch"` / `"patch_setup_workflow_spec"` | `goal-templates patch-spec <id-or-slug> --expected-version N --patches-file …` (omit `--confirm` for the required server preview; destructive changes also require its exact token) |
315
- | `manage_goal_template action:"update"` (metadata) | `goal-templates set-metadata <id> --expected-version N … --confirm` |
316
- | `manage_goal_template action:"delete"` | `goal-templates delete <id> --expected-version N --confirm` |
317
- | `manage_goal_template action:"capture_snapshot"` | `goal-templates capture-snapshot <id-or-slug> --source-goal <goal-id>` or `--source-draft <slug> --student <kid-id>` |
318
- | `create_goal` | `goals create --student <kid-id> … --confirm`, or `goal-templates apply` when a template exists |
319
- | `mesa_list_files` / `mesa_read_file` | `mesa files list` / `mesa files read` |
320
- | `mesa_write_file` (batch upserts) | `mesa files write --student … (--goal … | --draft …) (--source-dir … | --source-file … --path …)` |
321
- | `load_skill` / `load_skill_reference` | `skills get <name>` / `skills get <name> --reference <ref>` |
322
- | `restore_version`, `clear_snapshot`, `mesa_edit_file`, `mesa_manage_module` | **No CLI command.** Version restore, snapshot clearing, string edits/deletes, and projected live-goal module/state mutations stay in `recess.gg/ai`. |
22
+ # Parent or guardian work: kids, schedules, goals, todos, progress, memory, Rocky
23
+ recess --json skills guardian get recess-family --all-references
323
24
 
324
- #### Research tools your own harness
25
+ # Research, author, validate, or apply a goal or reusable goal template
26
+ recess --json skills guardian get recess-goal-authoring --all-references
325
27
 
326
- The research protocols (`deterministic-workflow-setup.md` §"The research loop",
327
- `os-v2-goal-builder`'s `research-protocol.md`, `goal-creation`'s `structured-plan.md` S4 +
328
- `subagent-tasks.md`, `pipeline-platform-research`) lean on a **different** tool family. Almost none
329
- of it maps to a CLI command — you substitute your own harness, and the method transfers even though
330
- the tool names do not:
331
-
332
- | Skill says (tool) | You use |
333
- |---|---|
334
- | `search_web`, `fetch_webpage` | your own web search / page fetch |
335
- | `spawn_subagents` | your own subagents. The five researcher prompts in `goal-creation`'s `subagent-tasks.md` (curriculum-map, platforms, resources, learning-paths, prerequisites) are usable almost verbatim — they are prompts, not tool calls |
336
- | `validate_urls` | check the URLs yourself before baking them into a spec. Do not skip this: a rotted queue URL is invisible until a kid clicks it |
337
- | `search_gem_library` | `content-library search <query>` (same hybrid/vector + rerank funnel; no content or Pipeline writes) |
338
- | `search_videos`, `search_books`, `recommend_videos` | your own search |
339
- | `get_student_profile`, `read_memory`, `query_student_activity_sql` | **partial.** `users search`/`users get` give account context, `goals list` what is already authored, `mesa files` the workspace. There is **no** command for kid memory or activity SQL — for those, the separate `query-db` skill's read-only production access is the honest route, not a guess |
340
- | `search_standards`, `navigate_standards` | no CLI command |
341
- | `save_platform_research` | **no CLI command** — a researched platform profile can only be persisted from `recess.gg/ai`. Your research still informs the spec you write; it just does not get saved as a reusable profile |
342
-
343
- **The one real capability gap: `fetch_page_structure`.** The template research loop is built around
344
- it — it returns a page's `window.__NAME__` JSON globals, and `globalJsonPath` lets you drill into an
345
- oversized skill-plan config section by section (`"sections"`, then `"sections[0]"`, …) to assemble a
346
- queue across several calls without blowing your context. You have no equivalent, so you are parsing
347
- the page yourself. Two consequences worth stating out loud rather than discovering late:
348
-
349
- - Read §"The research loop" for the *method* — operational pages over reference pages, embedded JSON
350
- over anchor scraping, recurse `childSections`, dedupe by `permacode`, record unmatched rows as
351
- `missingCoverage` — and implement it with what you have. The reasoning is what matters; the tool
352
- was only ever the means.
353
- - A large skill-plan page can exceed your context in one read. Fetch it, extract the embedded JSON
354
- to a file, and walk that file section by section — do not try to hold the whole tree in the
355
- conversation. If you cannot get a clean structured extraction, **say so and stop** rather than
356
- shipping a half-mined queue: a short queue looks identical to a correct one in the spec, and the
357
- skill's own count sanity-check exists precisely because this failure is silent.
358
-
359
- #### Where the shapes genuinely differ
360
-
361
- The skills assume a conversational tool loop. This CLI is one-shot and confirm-gated, so adapt
362
- rather than pretend:
363
-
364
- - **The skill asks the tutor a question** (`ask_user_question` — e.g. the Simple/Blueprint/Prebuilt
365
- card). You have no such tool. Ask the human in your own message, in the skill's tutor-facing
366
- language, and wait for the answer before authoring.
367
- - **The skill iterates a tool call until the spec validates.** Your loop is: write the file →
368
- `validate-spec` → read `error` verbatim → fix the file → repeat. It is a read; it writes nothing
369
- and needs no approval, so iterate freely.
370
- - **The skill treats create as one step.** Here it is two: the unconfirmed run returns the
371
- SERVER-resolved `details` (`resolvedSetupHandler`, `resolvedGoalShape`, `wizardStepKeys`,
372
- `specInventory`). Put those in the approval request — they are what the template will actually do.
373
- - **Prebuilt templates are a draft → validate → capture flow.** Write the complete OS-V2 tree to a
374
- named Mesa draft, preview `capture-snapshot`, then confirm it. Capture rejects an incomplete
375
- draft and filters student `state/`, conversations, and `.recess/` bookkeeping from the frozen
376
- snapshot. Applying a MODULE_BACKED template with no snapshot still 400s correctly.
28
+ # Staff-only school operations
29
+ recess --json skills admin get recess-admin --all-references
30
+ ```
377
31
 
378
- #### The one-way rules
32
+ Use `skills guardian list` or `skills admin list` when the needed skill is not obvious. Never substitute one audience for the other. A guardian session cannot fetch the admin catalog; an admin may fetch either catalog.
379
33
 
380
- - **Every template created here is `setupMode: DETERMINISTIC_WORKFLOW`, and it can never be
381
- converted back.** There is no AI_CHAT creation path and no downgrade. The confirmation gate
382
- matters more than usual: an approval here is permanent in a way `set-metadata` is not.
383
- - **`kind` is the real choice**, not the setup mode: `SIMPLE` (description-only goal + linked todos)
384
- vs `BLUEPRINT` (a planner-built or snapshot-backed learning path). A MODULE_BACKED spec *requires*
385
- `BLUEPRINT` and the server enforces it.
386
- - **`set-metadata` cannot touch the spec, by construction.** That is the guardrail, not a gap.
34
+ ## Authentication
387
35
 
388
- Full workflow: [`reference/goal-authoring.md`](reference/goal-authoring.md).
36
+ - Workstation: `recess --json auth login` opens Recess SSO.
37
+ - Headless device authorization is staff-only: `auth request`, human approval, then `auth poll`.
38
+ - Sessions last 12 hours and are rechecked against the live user role and permissions.
39
+ - `auth status` inspects the current session; `auth logout` clears the stored session.
40
+ - The default API is production. If `RECESS_CLI_API_ORIGIN` is set, state the non-default origin before acting.
389
41
 
390
- ## Workflow playbooks
42
+ Never print or paste session cookies, device codes, or config-file contents.
391
43
 
392
- Each domain has a full playbook in this skill's `reference/` directory. Read the matching one BEFORE running that domain's writes — they carry the semantics that make the writes correct (which command notifies families, how amounts are computed, which statuses permit which operations).
44
+ ## Agent contract
393
45
 
394
- | Task | Playbook |
395
- |---|---|
396
- | **Signing a kid up for a class / enrolling / "add them to this cohort" / "the parent wants to buy X"**, subscriptions, pauses, trials, cancellations, refunds/credits/token refunds, cohort register/unregister, moving kids between cohorts | [`reference/billing.md`](reference/billing.md) |
397
- | MAP Growth report uploads | [`reference/map-scores.md`](reference/map-scores.md) |
398
- | Guide payout invoices — the biweekly "CHANGES TO MAKE EVERY 2WKS" instruction | [`reference/payout.md`](reference/payout.md) |
399
- | Attendance, class cancellations, reschedules, one-off sessions, cohort lifecycle, cohort email, registration approvals | [`reference/class-ops.md`](reference/class-ops.md) |
400
- | Class-cancellation credits — the "Please credit these students accordingly" Slack workflow | [`reference/cancellation-credits.md`](reference/cancellation-credits.md) |
401
- | Non-flexible course one-off time shift when `events reschedule` 400s (`allowFlexibleScheduling: false`) | [`reference/class-ops-reschedule.md`](reference/class-ops-reschedule.md) |
402
- | Family onboarding — stage, account state, attestation checklist, parent intake session (fill / LLM-extract) | [`reference/onboarding.md`](reference/onboarding.md) |
403
- | **Authoring learning content** — deterministic GoalTemplates, Mesa draft/goal workspace writes, snapshot capture, applying a template to a kid or roster, creating a goal on a kid | [`reference/goal-authoring.md`](reference/goal-authoring.md) |
46
+ Always pass `--json`. Stdout contains one object:
404
47
 
405
- ## Deliberately out of scope
48
+ - Success: `{"ok":true,"data":{...}}`, exit 0.
49
+ - Failure: `{"ok":false,"error":{"code":"...","message":"...","details":{...}}}`, exit 1.
50
+ - Write awaiting approval: `confirmation_required`, exit 2, with `details.preview` and `details.requiredFlag`.
406
51
 
407
- These are excluded from the CLI on purpose. If asked, direct the human to the web admin — do not improvise around the gap.
52
+ Every write is two-step:
408
53
 
409
- - **Any raw write:** there is no `request post/put/patch/delete`. `request get` exists only for read-only endpoints missing a high-level command.
410
- - **Money movement:** initiating Mercury payouts, advancing whole pay runs, generating/regenerating payout invoices → `/admin/payout` in the web admin.
411
- - **Enrollment cancellation** as a standalone action (`unregister-cohort` deliberately preserves the enrollment; only `registrations deny` cancels one, because that is what the web Deny button does).
412
- - **Cohort creation and schedule editing:** create, start, full-edit/RRULE regeneration, generate-events, guides management web admin cohort pages.
413
- - **Restoring a template version or clearing a snapshot; deleting/editing Mesa files; direct live-goal `modules/` or `state/` writes.** Use `recess.gg/ai` for these projected mutations. The CLI intentionally supports upserts only and routes structural course authoring through a draft + capture.
54
+ 1. Run without `--confirm`.
55
+ 2. Show the human the complete preview, including `preview.details`.
56
+ 3. Ask for explicit approval of that exact action.
57
+ 4. Rerun the unchanged command with `--confirm`. If the preview includes an `approvalToken`, pass it unchanged.
414
58
 
415
- ## Guardrails
59
+ Any changed target, payload, local file, server revision, amount, recipient, or consequence requires a new preview and new approval. Never infer approval from the original request.
416
60
 
417
- - Use `--json` for analysis and preserve the CLI's stable JSON envelope.
418
- - Treat the exceptional actions table above as mandatory callouts in every approval request.
419
- - For `users tier set`, quote capability lock lists rather than describing one tier as "higher" or
420
- "lower"; the mapping is binary and flag-gated. Never reuse an approval after a stale-write refresh.
421
- - `unregister-cohort` leaves the enrollment active; never substitute enrollment cancellation (or vice versa — see `registrations deny`).
422
- - **`enrollments create` vs `enrollments register-cohort` — pick wrong and you either double-charge a family or do nothing.** `create` sells a class: it makes a *new* Stripe subscription and charges the family. `register-cohort` only links a kid to a cohort against an enrollment they **already** hold, and moves no money. If the family is already paying for this course and you just need the kid in a cohort (a move, a swap, a slot they already bought), use `register-cohort`. Use `create` only when they are genuinely buying something new. When unsure, run `enrollments create` unconfirmed and read `details.reusedEnrollmentId`: if it is set, the kid already had a paid slot and `create` will correctly charge nothing — that is also your signal that `register-cohort` would have been the direct route.
423
- - `enrollments create` is the ONLY command that spends a family's money. Its unconfirmed run performs a server-side dry run and returns the real resolved price, so never quote a price from the course catalog or from memory — quote `details.billing.effectivePriceCents` — the post-discount amount, never `listPriceCents` — and mention `creditBalanceCents` when non-zero, since credits reduce the first invoice further. If `details.reusedEnrollmentId` is set, say plainly that nothing will be charged.
424
- - `enrollments create` replaces impersonating a guardian and walking their checkout. Never suggest impersonation to sign a kid up.
425
- - Never retry `enrollments create` after an `ENROLLMENT_PROVISION_FAILED` error. The Stripe subscription already exists; retrying sells a second one. Escalate to engineering with the subscription ID from the error message.
426
- - **Never author learning content without loading the tutor skills first.** A spec that passes
427
- `validate-spec` is structurally valid, not good. `validate-spec` cannot tell you the template asks
428
- a 7-year-old a question phrased for an adult, picks the wrong `kind`, or wires a platform that
429
- does not work that way — only the skills can.
430
- - `goal-templates patch-spec` accepts a JSON **array** of 1–50 `add`/`copy`/`replace`/`remove`
431
- operations. It always asks the server to apply and validate them in memory before the confirmation
432
- gate. Never manufacture a destructive token: quote `details.safety.removed`, preserve the patch
433
- file and `--expected-version`, and use only the fresh `details.safety.destructiveChangeToken`
434
- after the human approves that exact loss.
435
- - **Never write a person's name, email, Stripe object id, or a real UUID into this skill.** This
436
- bundle is published to **public npm** and served to every agent — anything added here is
437
- published. Identifiers belong in Recess API responses at runtime, never in the repo. When a Gotcha
438
- needs a real incident to be legible, keep the *mechanism* and drop the identity: "one kid's
439
- Foundations invoice", "two same-week invoices sharing their first 18 characters". A Gotcha has
440
- never needed a name to be useful, and a test fails the build on id-shaped strings.
441
- - **Never copy a tutor skill's text into a file, a repo, or anything you publish.** They are
442
- proprietary and served at runtime precisely so there is exactly one copy. Quote what you need in
443
- your reasoning; do not persist it.
444
- - Creating a goal template is creating a **global** record every future apply reads, and its
445
- deterministic setup mode is permanent. Treat the approval accordingly.
446
- - Stop on ambiguous search results and ask the human which family or user they mean.
447
- - Report the final API response and re-read the affected resource when a read command can verify the new state.
61
+ ## Shared constraints
448
62
 
449
- ## Gotchas (append-only lab notebook)
63
+ - Use high-level CLI commands. The raw escape hatch is GET-only: `recess --json request get /path`.
64
+ - Resolve names to exact IDs before writes. Stop on ambiguous matches.
65
+ - Money uses integer cents.
66
+ - Keep dates in the exact format required by command help.
67
+ - Treat a 401 or `auth_required` as an authentication problem. A transport-level `fetch failed` is a network/sandbox problem, not proof that the session expired.
68
+ - Run `recess --help` for syntax; load the audience skill for workflow meaning.
450
69
 
451
- Dated, newest last. Add an entry every time reality surprises you.
70
+ ## Updating this shared skill
452
71
 
453
- - 2026-07-16 Flag parser: an unknown/misspelled `--flag` is not rejected it silently swallows the next token as its value, and the real flag then errors "Missing required --…". Check flag spelling first on weird `invalid_arguments`.
454
- - 2026-07-16 — Values starting with `--` (e.g. email content beginning with a dash run) must use `--flag=value` form; the space form parses the value as a new flag.
455
- - 2026-07-16 — `enrollments list --user <id>` is operations-search under the hood with the ID as the search term; an ID that search can't find returns `not_found` even if the user exists but is outside the search surface.
456
- - 2026-07-16 — `payout items add` without `--date` performs one READ before the confirmation gate (fetches the invoice to compute the default item date: endDate − 1 day). Expected; not a write.
457
- - 2026-07-16 — MAP uploads run the full AI extraction pipeline server-side and can take materially longer than other writes; don't treat a slow response as a hang. `kind:"learning_statements"` with zero inserted scores is a SUCCESS (narrative NWEA report → Mesa files), not a failure.
458
- - 2026-07-16 — The 12h session is not refreshable and login is browser-interactive; an agent can never self-heal auth. Hand `recess --json auth login` to the human and wait.
459
- - 2026-07-16 — `events reschedule` returning 409 means the target slot already has an event. Only flexible-scheduling courses and ACTIVE events can be rescheduled.
460
- - 2026-07-16 — `events take-attendance` stamps `attendanceTakenAt` with the current time automatically; there is no back-dating flag.
461
- - 2026-07-16 — `payout items edit --amount-cents` sets net = total (custom items carry no platform fee). Editing an auto-generated (non-custom) item with it would overwrite net semantics — only edit custom line items.
462
- - 2026-07-16 — `request get` requires a path starting with exactly one `/`; `//host` forms are rejected (SSRF guard).
463
- - 2026-07-16 — Payout guide emails on `set-status IN_REVIEW|OPEN` fire only with `--send-email` (server default false; suppressed on staging regardless). PAID and CANCELED invoices are terminal — the server 400s any later status change.
464
- - 2026-07-17 — `billing pause` always uses Stripe `pause_collection.behavior="void"`: invoices generated during the pause are VOIDED (family never charged), never deferred for later collection; access is unaffected. Weekly subscriptions bill on Sunday-00:00 (server/UTC) anchors, so `--until` (bare date → midnight UTC = the Stripe `resumes_at`) voids every Sunday tick strictly before it and charges the first tick at/after it. Pick a mid-week `--until` between the last Sunday to skip and the next one to charge. Full rules: `reference/billing.md` § Pause timing semantics.
465
- - 2026-07-17 — Per-subscription `billing pause` writes only to Stripe; `Enrollment.pausedAt`/`pauseResumesAt` stay null (only `cohorts pause-billing` stamps them). Verify via `subscriptions list`, never the enrollment row.
466
- - 2026-07-17 — `billing pause`/`resume --confirm` returns `api_error` with `details.status: 200` even on SUCCESS: the server route (`post.pause-collection.ts`) does the Stripe update but never sends its declared `{success:true}` body, so the CLI rejects the empty 200. The write has landed — verify with `subscriptions list` (`pause_collection` + `pause_collection_resume_at`) instead of retrying blind. (Route patched 2026-07-17 to send the body; the false error persists until that deploys.) **RESOLVED 2026-07-20** — the patch is live in prod; `billing pause --confirm` now returns a clean `{"ok":true,"data":{"success":true}}`. Verify-after-write still applies, but an `api_error` here is now a real failure, not the known false alarm.
467
- - 2026-07-17 — Before any refund/credit, read the invoice's payment composition from `invoices list` (`token_deduction_cents`, `applied_balance`, `amount` vs `subtotal`, `paymentIntent.status`) and state it in the approval request — token-paid portions go back as `--method tokens`, balance-covered portions mean a "full" cash refund over-refunds. Checklist in `reference/billing.md`.
468
- - 2026-07-17 — The cancellation Slack message's "Registered students" list is a cancel-time snapshot and can under-report: a kid registered since 2025 and invoiced for the canceled week was absent from it (Honey Squad, 7/16). Build the roster live from the cohort's REGISTERED registrations when processing credits (`reference/cancellation-credits.md`).
469
- - 2026-07-17 — Mixed-composition invoices (tokens + cash) need TWO refund commands, one per portion; observed a manual pass refund the $4 token portion of a $15 line and miss the $11 cash portion. Skip kids whose line already shows `credited_amount`/`token_refunded_cents` > 0 — the manual process runs days late and may race you.
470
- - 2026-07-17 — Stripe invoice IDs can share their first ~18 chars within the same week (shape: `in_<18 identical chars>eDHTGOT1` vs `in_<the same 18 chars>HynCvkUi`) — never prefix-match invoice/line IDs; compare in full.
471
- - 2026-07-17 — `register-cohort` enforces cohort capacity server-side (400 `RA_REG_NOT_ALLOWED` "This cohort is at capacity") — the admin quiet-link path does NOT bypass it, and capacity editing is out of CLI scope (web admin). Before a batch cohort move, compare `cohorts get` `capacity` against incoming headcount; register-first/unregister-second per kid means a capacity failure leaves that kid safely in the old cohort.
472
- - 2026-07-17 — Signup billing shape: a new class signup creates an IMMEDIATE real invoice (backdated to the last Sunday anchor — line reads "Time on <course> from <Sun> until <Sun>", often with a first-week coupon) plus a $0 "Trial period" invoice; the "trial" is an anchor-reset bridge to Sunday billing (`apps/web-server/src/libs/stripe/create-subscription.ts`), NOT a free period. A recently signed-up kid can be `status: trialing` with `trial_end` a Sunday 1–2 weeks out, so a given session week may legitimately have NO invoice (family genuinely not charged) even though the kid paid the immediate invoice for an earlier week. Same-cohort same-week signups can differ (observed: one kid trial_end 7/12 and charged $25 for the 7/12 week; another trial_end 7/19, never charged for it). Before refunding a session for a new signup, read `subscriptions list` `trial_end` to learn which week each invoice actually covers — don't map "Trial period invoice" to "never paid" or the immediate invoice to the current week.
473
- - 2026-07-18 — An `install-local` symlink dies when its source worktree is deleted: every invocation exits 127 "no such file or directory". Fix by reinstalling with `pnpm --dir apps/admin-cli run install-persistent` (self-contained copy in `~/.recess-cli/cli/`), which no worktree deletion can break.
474
- - 2026-07-18 — Per-kid excused-absence credits (guide asks to credit specific absent kids; class still runs) follow the `cancellation-credits.md` mechanics minus the roster sweep: same Sunday-anchor invoice lookup, same composition check, `invoices refund --method credit --full`, reason "Excused absence - <cohort> <date>". Surface the `--who-pays` call explicitly — the guide default means the requesting guide absorbs the cost, which the human may want to override for a courtesy credit.
475
- - 2026-07-18 — A fully balance-paid invoice (`applied_balance` = -subtotal, `amount` 0, no tokens) takes `--method credit --full` cleanly: the credit note restores the consumed customer balance. The billing.md over-refund warning for `applied_balance < 0` is about CASH refunds (`--method refund`), not balance credits. Verified live on two CoLab invoices, each returning its own credit note.
476
- - 2026-07-18 — Never call a Slack credit/absence request "unprocessed" from the thread alone — processed requests routinely get no Slack reply. The read-before-assert discipline applies to volunteered recommendations and status summaries, not just writes you're about to execute. Completion evidence lives in billing state and takes TWO reads per kid: `invoices list` (a past week handled by credit shows `credited_amount` + memo) AND `subscriptions list` (a future week can be pre-handled by a billing pause, which leaves NO invoice or enrollment trace — only Stripe `pause_collection`). Observed live: flagged two requests from one family as open when both credits and a pre-emptive pause through 7/29 were already in place.
477
- - 2026-07-20 — "Skip next week" from a family is ambiguous mid-week and the two readings need OPPOSITE commands — resolve it with the human before previewing. The Sunday anchor means the week already in progress is ALREADY INVOICED AND PAID, so a pause does nothing for it (that week needs `invoices refund`); only the not-yet-issued Sunday tick can be voided by `billing pause`. Read `invoices list` for the latest `created_date` (a Sunday 00:00 UTC stamp) and the subscription's `period_end` to see exactly where the paid/unpaid boundary sits, then ask which session they mean. Observed live: Mon 7/20 request to skip "next week" on a Thursday 1-on-1 — Thu 7/23 was already paid, Thu 7/30 was not.
478
- - 2026-07-20 — A billing pause deliberately leaves the session ACTIVE on the calendar; skipping the charge and canceling the class are separate decisions with wildly different blast radii (pause = silent, `events cancel` = family email blast + chat + Slack). Ask which one the human wants rather than assuming a skipped week implies a canceled session.
479
- - 2026-07-20 — **A fully-credited MIXED invoice does NOT show `credited_amount` == the line's full amount** — the token half lands in the invoice-level `token_refunded_cents` while the line's `credited_amount` only ever reflects the CASH half. Observed on a Foundations of Science 8-11 line: a complete $30.00 credit verifies as `credited_amount: 2001` + `token_refunded_cents: 999` + `token_refundable_remaining_cents: 0` on a line whose `amount` is 3000. Read it as under-refunded and you will double-credit the family. The reliable "is this line fully made whole?" test is `credited_amount + token_refunded_cents == line amount` AND `token_refundable_remaining_cents == 0`. Note the two writes also return different envelopes: `--method credit` gives `{noteId: "cn_…"}`, `--method tokens` gives `{creditTransactionId, newBalance}` (no credit note exists for a token refund).
480
- - 2026-07-20 — Payment composition varies PER KID inside a single cohort week — never read one kid's invoice and apply that instrument to the roster. One 3-kid cancellation sweep hit all three shapes at once: fully cash-paid, fully balance-paid (`applied_balance` −3000, `amount` 0, no paymentIntent), and mixed tokens+cash. Same course, same $30 price, same Sunday invoice batch, three different correct commands (and four total writes for three kids).
481
- - 2026-07-20 — The invoice-ID prefix collision is not rare enough to ignore: within ONE kid-cohort sweep, one kid's Foundations invoice and another kid's Terraria invoice shared their first 18 characters, differing only in the final 8 (both minted in the same Sunday 00:07 UTC batch run). Scope every `invoices list` to the specific subscription and compare IDs in full — the same-second batch anchor is exactly what manufactures these near-twins.
482
- - 2026-07-21 — The earlier browser-only auth limitation is resolved for headless agents: run `auth request`, give the returned approval URL/code to a human admin, then run `auth poll`. The device secret remains in the mode-0600 config and must never be surfaced. The resulting session still expires after 12 hours and cannot refresh itself.
483
- - 2026-07-21 — **Never answer "was this cancellation refund handled?" from the DB alone.** `CreditTransactionLog` only shows `--method tokens` grants; Stripe cash/balance path (`--method credit` / `--method refund`) lands only as invoice `credited_amount` + credit-note memo via `invoices list`. Observed live on one Foundations of Science 8-11 session: two kids — one credited in cash, one against their customer balance — both looked "open" in DB/token logs while admin CLI invoices already showed `credited_amount: 3000` with a `Guide cancellation - <course> <date>` memo. Always verify completion with `recess --json invoices list --subscription <sub>` (and the mixed-invoice test `credited_amount + token_refunded_cents == line amount`).
484
- - 2026-07-21 — **Future canceled session = pause, not credit.** When the cancel lands before the Sunday that starts the canceled week, no invoice exists yet — `invoices refund` has nothing to target. Use `billing pause --until` mid-week after that Sunday to void only that tick (Scratch 'n Hack 2 Aug 4 cancel → pause the affected subscription until 2026-08-06). Documented in `reference/cancellation-credits.md` §3b. Don't route these threads to Linear; load this skill + cancellation-credits playbook immediately on `#cohort-cancellations` / "Please credit these students accordingly".
485
- - 2026-07-23 — `events reschedule` on a non-flexible course 400s (`allowFlexibleScheduling: false`) even for a simple ±10 min move (Space Technology & Rocket Launches / Starship). Check `course.allowFlexibleScheduling` on `cohorts get` **before** promising a reschedule. Staff workaround already in use: `events add` at the new cohort-local time + `events set-status CANCELED` on the original (silent — not `events cancel`). Enabling flexible scheduling is web-admin-only. Details: `reference/class-ops-reschedule.md`.
486
- - 2026-08-04 — **`fetch failed` from any command means the agent's sandbox blocked the network, not that the session died.** Observed in Codex: `command -v recess` and `recess --help` work, but `recess --json doctor` returns `{"ok":false,"error":{"code":"unexpected_error","message":"fetch failed"}}`. Codex's default `workspace-write` sandbox has no network, and every non-preview command here calls the API. Do NOT respond by re-running `auth login` / `auth request` — the session is fine. Fix the sandbox instead: in `~/.codex/config.toml` set `sandbox_mode = "workspace-write"` plus a `[sandbox_workspace_write]` table with `network_access = true` (that table must sit AFTER any bare top-level keys, or TOML folds them into it), or run `codex --sandbox danger-full-access`. A genuinely expired session looks different: a clean `auth_required` / 401 from the API, not a transport failure.
487
- - 2026-08-04 — **`skills get` serves proprietary content and is not in this package.** The tutor skills live in a private submodule and are fetched over `/admin/skills/*` behind the admin session; nothing is bundled. Two consequences: (a) a skills-repo update reaches you with no CLI release, so `--refresh` is how you defeat the ~1h local cache after someone edits a skill; (b) if `skills get` 403s, your session is not ADMIN — GUIDE/PROGRAM are refused on this family even though they pass the general `/admin` gate.
488
- - 2026-08-04 — **The template file feeds `validate-spec` and `create` unchanged.** One JSON document with the metadata AND the `setupWorkflowSpec`; the CLI drops unknown keys, so a file produced by piping `goal-templates get <id>` into an editor works — `version`, `createdById`, `updatedAt` are ignored rather than rejected. It also accepts `tags` as a comma string. What it will NOT accept: `setupWorkflowSpec` as a JSON *string* (a common serialization slip — it must be a real object) or any `setupMode` other than DETERMINISTIC_WORKFLOW.
489
- - 2026-08-04 — **Hand-writing a `setupWorkflowSpec` from memory does not work and `validate-spec` is how you find out cheaply.** The handler configs are `.strict()` — a plausible-looking `{tool, studentsStepKey}` for TOOL_GENERATED_TODO_SETUP fails with both "Required" errors AND `unrecognized_keys`. The real config needs `platformName`, `toolName` (`create_passage`|`create_writing_passage`), `goalTitleTemplate`, `todoTitle`. Read `os-v2-goal-template-builder`'s `references/deterministic-workflow-setup.md` for the catalog instead of guessing; then iterate on `validate-spec`, which writes nothing.
490
- - 2026-08-04 — **`goal-templates apply` without `--dry-run` still calls the backend once before the gate** (its own `dryRun:true`) to build the preview. That is a read, not a write, and it is the only way to know which kids get goals, which are `skipped_existing`, and what `missingCoverage` is unfilled. Show `details.counts` and `details.results` in the approval request. `apply-starter` has no dry run at all, so its preview is offline — and it 404s when the acting admin lacks the `school-onboarding-v1` flag, which reads like "template missing" but is not.
491
- - 2026-08-04 — `goal-templates get|versions|delete|apply` accept a **slug** as well as a UUID; the CLI resolves it through the list route (one extra read). `versions` without `--version` deliberately omits each version's frozen spec and agent instructions — history stays scannable; pull one with `--version N`.
492
- - 2026-08-04 — **The research protocols assume tools the CLI does not have, and one of them has no substitute.** `search_web`/`fetch_webpage`/`spawn_subagents`/`validate_urls` map cleanly onto your own harness (the five researcher prompts in `goal-creation`'s `subagent-tasks.md` are prompts, not tool calls — reusable almost verbatim). `fetch_page_structure` does not: the template research loop is designed around its JSON-globals extraction plus `globalJsonPath` drilling, which is how a huge skill-plan config gets mined section-by-section without blowing context. Extract the page's embedded JSON to a file and walk the file instead. And note what has no command at all: kid memory / activity SQL (use the `query-db` skill), `search_standards`, the gem library, and `save_platform_research` — research can inform a spec you write, but a reusable platform profile is only persistable from `recess.gg/ai`.
493
- - 2026-08-04 — **A half-mined queue is invisible.** A spec with 40 of 120 skills validates exactly like a complete one — `validate-spec` checks shape, never coverage. The skill's count sanity-check (queue length vs. what a human skimming the source page would expect) is the only guard, and you are running it by hand now that `fetch_page_structure` is unavailable. Under-length usually means you stopped recursing `childSections` too early; over-length means you did not dedupe by `permacode`. If a clean structured extraction is not achievable, stop and say so rather than shipping the partial queue.
494
- - 2026-08-04 — **This skill now updates without an npm release, but only after the server redeploys.** `recess setup --skill-only` installs the bundled copy then pulls the served one from `GET /admin/cli-skill/`. The server **memoizes** the bundle at first read (it only changes on deploy), so editing SKILL.md in a checkout does NOT change what a local dev server serves until it restarts — a genuinely confusing few minutes if you are testing the flow. `recess --json --version` reports `{cliVersion, skillVersion}`; `doctor.skill` reports whether a newer one exists and names the command, and never writes.
495
- - 2026-08-04 — **`--version` is a flag, not a noun.** Anything the arg parser sees starting with `--` lands in `flags`, leaving `positionals` empty — so a `noun`-based check for it sits behind the `!noun → print help` branch and is unreachable. Same trap for any future `--foo` top-level command: check the flag before the help branch. Observed live: `recess --json --version` printed the whole help text.
496
- - 2026-08-04 — A `Makefile install-persistent` copy is NOT the npm package: it synthesizes its own `package.json`. If that manifest lacks `version`, or `skill/` is not copied alongside `dist/`, then `--version` reports `0.0.0` and the `minCliVersion` fence **fails closed** — every served skill upgrade is silently refused with `cli_too_old`. Both are now copied; if you add another packaged artifact the CLI reads at runtime, add it to that target too.
497
- - 2026-08-04 — **`goal-templates patch-spec` performs a server preview even on a confirmed run.** The first POST is `dryRun:true`, never a write; it binds the current template version, before/after hashes, and protected removals. A destructive second POST is impossible without `--confirm-destructive-changes` and that fresh preview token. If the template or patch file changes, preview again and obtain new approval.
498
- - 2026-08-05 — **A deterministic MODULE_BACKED spec is not the course content.** `goal-templates create` can create a valid BLUEPRINT while `snapshot` remains null; Goal Preview stays empty and apply correctly says there is no valid snapshot. Build the complete tree locally, `mesa files write --draft … --source-dir …`, then `goal-templates capture-snapshot --source-draft … --student …`. Both writes server-preview first. Mesa writes are fenced to the previewed repo change; capture is fenced to that source change and the template version. Direct live-goal `modules/`/`state/` writes are deliberately blocked because those paths have DB projections.
499
- - 2026-08-05 — **The goal-audit GET cannot investigate an already-soft-deleted goal.** `request get /admin/browser/students/goals/<goal-id>/audit/` returns 404 because the handler's `ensureGoalAccess` calls `canManageGoal`, which requires `Goal.deletedAt: null` before it loads `GoalAuditLog`. The 404 is not evidence that the audit row is absent. Use the `query-db` skill's guarded read-only production query for deletion forensics.
500
- - 2026-08-05 — **`set-metadata --output-template-file` edits an AI_CHAT template's `outputTemplate`** (the goal-description payload DailyTodoGeneration consumes for description-only goals). Like `--agent-instructions-file`, it reads a local file and the server bumps the version + freezes a `GoalTemplateVersion` row. Editing a template never rewrites goals already created from it — the description was copied at goal-creation time; re-apply or edit live goals separately.
501
- - 2026-08-05 — **Content Library search now has a content/Pipeline-read-only CLI command.** `content-library search <query> [--limit 8]` calls the same hybrid/vector + rerank `/agent/search` funnel as Rocky's `search_gem_library`, returns the full fit/gist/coverage payload, and attributes the funnel's standard `offered` telemetry to `admin-cli`. It cannot inspect, ingest, file requests, curate, or edit the Pipeline; the permanent library token stays on the web server.
502
- - 2026-08-07 — **Village store goods do not live in the Village database.** They are ordinary main-Recess `StoreItem` rows with `itemType=VILLAGE_ITEM`; use `store-items list --item-type VILLAGE_ITEM` and the confirmation-gated `store-items set-status`, while `village models …` remains the separate Village-island map/model boundary. `set-status` refuses non-Village IDs and its live preflight names the item, current status, purchase count, and feed consequence before approval. A checked-in seed still owns fresh-database defaults, so an operational status edit does not replace updating that seed when the product default itself changes.
72
+ `recess setup --skill-only` reinstalls this shared router and may pull a compatible server copy. The npm package deliberately ships only this skill. Guardian and admin skills are fetched after authentication and cached locally for about one hour; use `--refresh` after a known skill edit.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Recess CLI"
3
- short_description: "Safely operate Recess admin workflows"
4
- default_prompt: "Use $recess-cli to find a family and safely perform the requested Recess admin action."
3
+ short_description: "Route safe Recess CLI work by audience"
4
+ default_prompt: "Use $recess-cli to authenticate, select the correct guardian or admin skill catalog, and safely complete the requested Recess task."
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "minCliVersion": "1.6.0"
4
+ }