Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion commands/boxel-add-field.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ boxel:
- [ ] The new field uses `linksTo`/`linksToMany` if it points at a CardDef, `contains`/`containsMany` if FieldDef.
- [ ] Computed fields wrap field access in try/catch when crossing card boundaries.
- [ ] No self-referencing computed fields.
- [ ] Tracking marker `ⁿ` added on every changed line (if tracking mode is on).
- [ ] Changed `.gts` files passed installed npm `boxel` lint (`npx boxel file lint ... --file <local-file>` before push and `npx boxel lint <path> --realm <url>` after push).

## Failure Recovery
Expand Down
8 changes: 4 additions & 4 deletions commands/boxel-create-instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ boxel:

## Read

1. `skills/boxel/SKILL.md` — cardinal rules 13-14 (external URLs in `links.self`, `linksToMany` indexed keys) plus `references/data-management.md` for the full JSON:API instance shapes (`containsMany` arrays, empty links, `adoptsFrom`).
1. `skills/boxel/SKILL.md` — cardinal rules 12-13 (external URLs in `links.self`, `linksToMany` indexed keys) plus `references/data-management.md` for the full JSON:API instance shapes (`containsMany` arrays, empty links, `adoptsFrom`).
2. `skills/boxel/references/card-references.md` — how every `links.self` resolves (`./` / `../` / absolute; bare paths throw). Also `references/core-concept.md` "Relationship path resolution" and "`adoptsFrom.module` — URL of the .gts, NOT including the export name".
3. `skills/boxel/references/base-field-catalog.md` — DateField vs DateTimeField value contract; the image URL/ImageDef pair pattern.
4. `skills/boxel-patterns/patterns/theme-first-workflow/README.md` — theme linking via `relationships["cardInfo.theme"]`, absolute URLs for nested folders, and why Theme cards omit the self-theme relationship.
Expand All @@ -29,7 +29,7 @@ boxel:

## Procedure

1. Decide the tool: `write-text-file` (new instance, simple shape), `patch-fields` (existing instance, surgical), `patchCardInstance` (existing instance, full replace), or SEARCH/REPLACE (large or structural).
1. Decide the tool: SEARCH/REPLACE with a `(new)` marker (new instance), `patch-fields` (existing instance, surgical), or `patchCardInstance` (existing instance, full replace).
2. Compose the JSON: `"data"` envelope, `"type": "card"`, `meta.adoptsFrom` (module + name), `attributes`, `relationships` — shapes per the Read list above.
3. Include `attributes.cardInfo` unless there's a specific reason not to (it's the user's edit surface); set `relationships["cardInfo.theme"]` when this instance should carry its own theme. Theme instances themselves omit that relationship.
4. Match date string formats to the schema's field type (DateField vs DateTimeField); put external image URLs in the paired `*URL` attribute, never in a relationship.
Expand All @@ -39,10 +39,10 @@ boxel:

- [ ] `"data"` envelope; `meta.adoptsFrom` has `module` (no export-name suffix) + `name`.
- [ ] Every `links.self` starts with `./`, `../`, or `http` (see `card-references.md`).
- [ ] `linksToMany` uses indexed keys, `containsMany` is an array in attributes, empty links use `"self": null` (cardinal rule 14 + `data-management.md`).
- [ ] `linksToMany` uses indexed keys, `containsMany` is an array in attributes, empty links use `"self": null` (cardinal rule 13 + `data-management.md`).
- [ ] `attributes` is an object (never a JSON string); `relationships` is its sibling, not nested inside it.
- [ ] `attributes.cardInfo` present; theme linkage decided per `theme-first-workflow`; no self-theme on Theme instances.
- [ ] Date values match field type; image URLs in the `*URL` attribute (cardinal rule 13 + `base-field-catalog.md`).
- [ ] Date values match field type; image URLs in the `*URL` attribute (cardinal rule 12 + `base-field-catalog.md`).

## Verification after push

Expand Down
2 changes: 1 addition & 1 deletion commands/boxel-debug-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ boxel:

## Failure Recovery

- Can't reproduce → ask the user for exact reproduction steps. Boxel runtime errors often surface differently for different ages of edit-tracking state.
- Can't reproduce → ask the user for exact reproduction steps.
- Loop detected (same commands repeating) → STOP and tell the user. Don't barrel forward.
2 changes: 1 addition & 1 deletion commands/boxel-edit-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ boxel:
## Procedure

1. Read the current template — confirm which format you're editing.
2. SEARCH/REPLACE the template. Preserve `<style scoped>` blocks and tracking markers.
2. SEARCH/REPLACE the template. Preserve `<style scoped>` blocks.
3. **For `fitted`, follow `skills/boxel/references/container-query-fitted-layout.md` exactly.** Prefer `FittedCard` from `@cardstack/boxel-ui/components` for standard compositions (tune via `--fc-*` variables). When hand-rolling: single-root `.fit` grid querying the host's `fitted-card` container (no local container on the root), six height quanta, `pow()`-based typography, `minmax(0, 1fr)` body row, `min-height: 0` on grid children. Hand-rolling without these will overflow at edge sizes.
4. For multi-card delegation, use `<@fields.x />` — don't iterate `@model` then try `<@fields.x />` inside the loop (see `template-syntax.md`).
5. Use theme tokens per `boxel-ui-guidelines` — no hard-coded colors outside theme scope.
Expand Down
2 changes: 1 addition & 1 deletion commands/boxel-search-cards.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ boxel:

1. If the query is title-based and simple → `SearchCardsByTypeAndTitleCommand_a959`.
2. If the query needs filters (`eq`, `gt`, `range`) → `SearchCardsByQueryCommand_847d`.
3. Build the type reference as `{ module: <full URL>, name: <ClassName> }`. To match **all cards of a type**, use `filter: { type: ref }`. Use `on: ref` **only** to scope predicates (`eq`/`contains`/`range`) or a custom-field sort — a bare `{ on: ref }` with no predicate returns zero rows silently (Cardinal Rule 6, `boxel/references/query-systems.md`).
3. Build the type reference as `{ module: <full URL>, name: <ClassName> }`. To match **all cards of a type**, use `filter: { type: ref }`. Use `on: ref` **only** to scope predicates (`eq`/`contains`/`range`) or a custom-field sort — a bare `{ on: ref }` with no predicate returns zero rows silently (Cardinal Rule 5, `boxel/references/query-systems.md`).
4. Display results — use `show-card_566f` if the user wants to see one; just list otherwise.

## Done Criteria (self-verify)
Expand Down
14 changes: 7 additions & 7 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,19 @@ Commands route to these supporting skills. They're documented in `skills/` and a
- **`cardInfo.theme` is the per-instance override.** When set, it wins over whatever the CardDef's computed `cardTheme` would have returned. When the CardDef does NOT override `cardTheme`, `cardInfo.theme` is how a theme installs at all — but a Task that inherits its Project's theme doesn't need it on every Task instance.
- **Override `cardTitle` when there's a primary field.** Respect `cardInfo.name` first, then fall back to the primary field, then to `Untitled <DisplayName>`. See pattern `cardinfo-override-title`.
- **Build a Home app whenever you ship a card family.** When the realm contains 2+ related CardDefs (Project + Task + Person; Meet + Swimmer + Club + Result; Show + Listing + Venue), ship a `Home` CardDef alongside them: `prefersWideFormat = true`, brand masthead, one `@context.searchResultsComponent` section per CardDef in the family. The user lands there and sees the realm at a glance — no manual indexing required. See pattern [`app-card-home-with-search`](skills/boxel-patterns/patterns/app-card-home-with-search/README.md). Skip only for single-card utilities.
- **Lint is mandatory for Boxel code work** (Cardinal Rule 10): `npx boxel file lint <path> --realm <url> --file <local-file>` before push, `npx boxel lint <path> --realm <url>` after. Prefer `npx boxel` over bare `boxel` (stale shims); there is no `npx boxel check`. Commands, clean-output criteria, and the no-shell assistant variant: [`lint-workflow.md`](skills/boxel/references/lint-workflow.md).
- **Lint is mandatory for Boxel code work** (Cardinal Rule 9): `npx boxel file lint <path> --realm <url> --file <local-file>` before push, `npx boxel lint <path> --realm <url>` after. Prefer `npx boxel` over bare `boxel` (stale shims); there is no `npx boxel check`. Commands, clean-output criteria, and the no-shell assistant variant: [`lint-workflow.md`](skills/boxel/references/lint-workflow.md).
- **Public-repo path hygiene.** This workspace is a public repo. Never commit absolute local filesystem paths, local usernames, or machine-specific checkout paths into tracked docs, scripts, generated bundle comments, or learning notes. Use placeholders (`/path/to/boxel`, `/path/to/bxl`, `[local checkout]`) or env vars (`BOXEL_MONOREPO`). Local paths are fine in transient shell commands, not in persisted files.
- **Don't reach for `cancel-indexing`.** Slow indexing is not stuck indexing. Sample the indexed-card count for 5+ minutes; rising = leave it alone. `cancel-indexing --cancel-pending` discards the realm's queued TODO list and makes things worse. Last-resort recovery: `boxel file touch <one card>` → base `cancel-indexing` (never `--cancel-pending`) → `full-reindex-realm`. See [`skills/boxel-environment/references/indexing-operations.md`](skills/boxel-environment/references/indexing-operations.md).
- **Fresh-realm push semantics.** `boxel realm push` of 30+ files via `/_atomic` can silently drop indexing jobs even when the push reports success. For > 50 instances, push kit-by-kit and verify each batch indexes (`npx boxel search` returns the expected count) before pushing the next. See indexing-operations.md "Post-push verification gate" and "The `/_atomic` batch trap".
- **Never inline media/binary in card JSON** (Cardinal Rule 11). No `data:`/`blob:`/base64/media bytes in any string field or JSON attribute. Store media as a realm file via `linksTo(FileDef/ImageDef/PngDef)`; generated bytes go through `WriteBinaryFileCommand` first.
- **🔴 `DateField` value = `YYYY-MM-DD` (no `T`); `DateTimeField` value = ISO datetime with `T`** (Cardinal Rule 12). A mismatch passes lint and indexes, then crashes at render (`RangeError: Invalid time value`). `*At` suffix → DateTimeField; `*Date`/`*On`/`dob` → DateField. Details: [`base-field-catalog.md`](skills/boxel/references/base-field-catalog.md).
- **🚨 Never put an external URL in `relationships.<field>.links.self`** (Cardinal Rule 13) — the indexer fetches it expecting a card, the failed parse poisons the JSONB write, and the **whole realm's indexing transaction rolls back**. For external image URLs use the pair pattern `linksTo(ImageDef)` + `contains(UrlField)`; recipe in [`base-field-catalog.md`](skills/boxel/references/base-field-catalog.md) "Image fields — the URL/ImageDef pair pattern".
- **🚨 `linksToMany` JSON uses indexed top-level keys** (`"activityFeed.0": { "links": { "self": "../foo" } }`), never an array under `links.self` (Cardinal Rule 14). The array shape is rejected with `"instance ... is not a card resource document"`.
- **Never inline media/binary in card JSON** (Cardinal Rule 10). No `data:`/`blob:`/base64/media bytes in any string field or JSON attribute. Store media as a realm file via `linksTo(FileDef/ImageDef/PngDef)`; generated bytes go through `WriteBinaryFileCommand` first.
- **🔴 `DateField` value = `YYYY-MM-DD` (no `T`); `DateTimeField` value = ISO datetime with `T`** (Cardinal Rule 11). A mismatch passes lint and indexes, then crashes at render (`RangeError: Invalid time value`). `*At` suffix → DateTimeField; `*Date`/`*On`/`dob` → DateField. Details: [`base-field-catalog.md`](skills/boxel/references/base-field-catalog.md).
- **🚨 Never put an external URL in `relationships.<field>.links.self`** (Cardinal Rule 12) — the indexer fetches it expecting a card, the failed parse poisons the JSONB write, and the **whole realm's indexing transaction rolls back**. For external image URLs use the pair pattern `linksTo(ImageDef)` + `contains(UrlField)`; recipe in [`base-field-catalog.md`](skills/boxel/references/base-field-catalog.md) "Image fields — the URL/ImageDef pair pattern".
- **🚨 `linksToMany` JSON uses indexed top-level keys** (`"activityFeed.0": { "links": { "self": "../foo" } }`), never an array under `links.self` (Cardinal Rule 13). The array shape is rejected with `"instance ... is not a card resource document"`.
- **🚨 Building a kit is a sequential checklist with verification gates — lint is NOT the gate.** The seven gates, in order: (1) Stage-0 planning (thunk-by-default `() => Class` for kit-internal links; per-format content matrices), (2) import audit (base fields are **default** exports; never `ImageDef` from `/base/image`), (3) CDN-verify every icon, (4) push per-file — no atomic batches on fresh realms, (5) module-load probe (`get-card-type-schema` must return `status: ready`), (6) typed-search count gate (`boxel search` is the truth source, not lint), (7) render smoke test per CardDef. Exact commands and failure signatures: [`indexing-operations.md`](skills/boxel-environment/references/indexing-operations.md).
- **Query traps that silently return zero rows** (Cardinal Rules 6–8): `filter: { type: ref }` to select all cards of a type — never a bare `{ on: ref }` (`on` only scopes predicates); custom sort fields require `on: ref`; build refs with `codeRef()` and import the `realmURL` Symbol from `@cardstack/runtime-common` (never `Symbol.for('realmURL')`). Details: [`query-systems.md`](skills/boxel/references/query-systems.md).
- **Query traps that silently return zero rows** (Cardinal Rules 5–7): `filter: { type: ref }` to select all cards of a type — never a bare `{ on: ref }` (`on` only scopes predicates); custom sort fields require `on: ref`; build refs with `codeRef()` and import the `realmURL` Symbol from `@cardstack/runtime-common` (never `Symbol.for('realmURL')`). Details: [`query-systems.md`](skills/boxel/references/query-systems.md).
- **Format choice = who owns the cell size, not what the cell looks like.** `@format='embedded'` lets the child decide its height — use for lists, feeds, roster rows. `@format='fitted'` makes the child fill a parent-controlled box — use for uniform tile grids (portraits, calendar cells). Picking fitted for a list with short content leaves empty boxes below each row. The fix is the format choice, upstream of any CSS. See "Picking the format" in [`delegated-render-control.md`](skills/boxel-ui-guidelines/references/delegated-render-control.md).
- **Include `attributes.cardInfo` on instances when practical.** Even with all null values, the `cardInfo` object lets the user edit name/summary/theme later through the UI. It's required when the CardDef uses the default `cardTheme` pass-through AND you want a theme set per-instance.
- Read before writing. Many `.gts` files have edit-tracking markers; reading first preserves them.
- Read before writing. Fetch a file’s current contents before a SEARCH/REPLACE edit so the SEARCH block matches exactly.
- Use SEARCH/REPLACE for `.gts` edits. `write-text-file` is forbidden for `.gts` (UI freezes; tool calls don't stream).
- One CardDef per file. FieldDefs and helpers can co-locate.
- Theme variables only — no hard-coded colors in templates. All colors live in the Theme card's `cssVariables`.
Expand Down
2 changes: 1 addition & 1 deletion skills/boxel-create-edit-cards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Read:
1. `../boxel-environment/references/card-tool-selection.md` — the create/edit tool tables, quick decision tree, file naming, and path rules.
2. `../boxel-environment/SKILL.md` — the master decision tree (Step 4 covers data tasks).

For `.gts` edits, `source-code-editing` defines the SEARCH/REPLACE format. Never use `write-text-file` for `.gts`.
`source-code-editing` defines the SEARCH/REPLACE format used to create and edit files (`.gts` and `.json` alike). Never use `write-text-file`.
10 changes: 5 additions & 5 deletions skills/boxel-environment/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ You are the orchestrator of the Boxel AI Assistant. You decide which host comman
### Step 4 — Data task

```
├─ New .json instance? → write-text-file (never for .gts)
├─ New .json instance? → SEARCH/REPLACE with (new) marker
├─ Clone + modify? → copy-card → patch-fields
├─ Long markdown field (>500 chars)? → ApplyMarkdownEditCommand_c112
├─ Small/targeted change? → patch-fields_3e67
Expand All @@ -62,7 +62,7 @@ You are the orchestrator of the Boxel AI Assistant. You decide which host comman

Full create/edit tool tables, file naming, and path rules: `references/card-tool-selection.md`.

> **⚠️ Streaming rule:** NEVER use `write-text-file` for `.gts` files. Tool calls don't stream — the whole payload must be generated before the user sees anything, so the UI looks frozen. SEARCH/REPLACE streams visibly. (`write-text-file` is OK for `.json` instances.)
> **⚠️ Streaming rule:** Create and edit files with SEARCH/REPLACE — avoid `write-text-file`. Tool calls don't stream — the whole payload must be generated before the user sees anything, so the UI looks frozen. SEARCH/REPLACE streams visibly for `.gts` and `.json` alike.

### Step 5 — Search / find

Expand All @@ -78,13 +78,13 @@ Full create/edit tool tables, file naming, and path rules: `references/card-tool
├─ INTERACT MODE:
│ ├─ Display card → show-card_566f
│ ├─ Create card / definition → switch-submode_dd88 (submode: "code", createFile: true, codePath: realmUrl + filename), then SEARCH/REPLACE
│ ├─ Switch to code → switch-submode_dd88 (submode: "code")
│ └─ Open workspace → open-workspace_1696
│ ├─ Switch to code → switch-submode_dd88 (submode: "code"; pass codePath to target a specific realm — a bare switch stays in the current realm)
│ └─ Open workspace → open-workspace_1696 (lands in interact mode)
├─ CODE MODE:
│ ├─ Preview card + module → preview-format_cb94
│ ├─ Open file in editor → update-code-path-with-selection_f749
│ ├─ Switch to interact → switch-submode_dd88 (submode: "interact")
│ └─ Open workspace → open-workspace_1696
│ └─ Open workspace → open-workspace_1696 (⚠️ exits code mode — to change realm and stay in code mode, switch-submode with a codePath in that realm)
└─ EITHER MODE:
└─ Toggle mode → switch-submode_dd88
```
Expand Down
17 changes: 8 additions & 9 deletions skills/boxel-environment/references/calling-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

**⚠️ CRITICAL CLARIFICATION:**
- Level 3 is NOT called "parameters" — it's just the key-value pairs INSIDE `attributes`
- The keys at level 3 vary per command (e.g., `cardId` for show-card, `realm` + `path` for write-text-file)
- The keys at level 3 vary per command (e.g., `cardId` for show-card, `submode` + `codePath` for switch-submode)
- `attributes` is ALWAYS a JSON object `{ }`, NEVER a string

### 🔴 THE ONE TRUE FORMAT (Copy This Exactly)
Expand Down Expand Up @@ -106,22 +106,21 @@ Before generating ANY command call, verify:
}
```

### Example 2: write-text-file
### Example 2: switch-submode (multiple attributes)
```json
{
"name": "write-text-file_e5a1",
"name": "switch-submode_dd88",
"payload": {
"description": "Create product instance file",
"description": "Open code mode on a new file in the target realm",
"attributes": {
"realm": "https://realm-url/",
"path": "Product/item.json",
"content": "{\"data\": {...}}",
"overwrite": true
"submode": "code",
"createFile": true,
"codePath": "https://realm-url/product-catalog.gts"
}
}
}
```
**Note:** `content` is a STRING containing serialized JSON or other escaped text content
**Note:** `codePath` pins code mode to the target realm — a bare `submode` switch stays in whatever realm the UI last showed

### Example 3: Empty attributes (when no params needed)
```json
Expand Down
Loading