-
Notifications
You must be signed in to change notification settings - Fork 0
Realm resource URLs are always linksTo, never StringField/UrlField #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ Every field listed here is importable from a stable specifier and ready to use w | |
| | `BigIntegerField` | `'@cardstack/base/big-integer'` | For values beyond `Number.MAX_SAFE_INTEGER`. | | ||
| | `TextAreaField` | `'@cardstack/base/text-area'` | Multi-line plain text (sub-page). For paragraphs that aren't markdown. | | ||
| | `EmailField` | `'@cardstack/base/email'` | Validates as `user@domain`. Renders as `mailto:` link. | | ||
| | `UrlField` | `'@cardstack/base/url'` | Validates URL shape. Renders as `<a>` in non-edit modes. | | ||
| | `UrlField` | `'@cardstack/base/url'` | Validates URL shape. Renders as `<a>` in non-edit modes. **External URLs only** — a URL that points at a realm resource (a card instance or realm file) must be a `linksTo` / `linksToMany` field instead, never a string. | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Low — the "Details" target for a general rule is a bullet buried inside an image-specific section.
Concrete misbehavior: a model deciding how to model Suggest a short standalone subsection (sibling to "🔴 DateField vs DateTimeField — the schema-vs-value contract") with the general statement, and have the image-section bullet cross-reference it — then give Generated by Claude Code
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Claude Code 🤖] Fixed in 25caa2b. The general statement now lives in its own subsection in base-field-catalog.md ("Realm-resource URLs — always a relationship, never a string", sibling to the DateField section) with the decay list and both carve-outs; the image-section contract bullet cross-references it, and the index.md and glossary bullets name the section in their pointers. |
||
| | `PhoneNumberField` | `'@cardstack/base/phone-number'` | Country code + national number. Compound field. | | ||
| | `EthereumAddressField` | `'@cardstack/base/ethereum-address'` | Web3 address with checksum validation. | | ||
| | `ColorField` | `'@cardstack/base/color'` | Renders a color swatch + picker in edit mode. | | ||
|
|
@@ -85,9 +85,28 @@ For a `linksToMany(ImageDef)` gallery, the URL twin is `containsMany(UrlField)`: | |
| - `relationships.<field>.links.self` is for card identifiers — relative paths (`"../Theme/foo"`) or absolute realm URLs only. | ||
| - External URLs (Unsplash, S3, CDN, any `https://` URL pointing at non-card content) go in `attributes.<field>URL` on the URL-twin field. | ||
| - Uploaded card-side images go in the linked ImageDef as a normal `linksTo` relationship. | ||
| - The rule cuts both ways: a URL that points at a **realm resource** never goes in a `UrlField`/`StringField` attribute — see "Realm-resource URLs — always a relationship, never a string" below. | ||
|
|
||
| **Future direction (not implemented yet):** a single compound `Image` FieldDef that wraps either a URL or an ImageDef link and exposes a unified `.src` accessor. Until then, use the pair-of-fields approach above. | ||
|
|
||
| ### 🔗 Realm-resource URLs — always a relationship, never a string | ||
|
|
||
| The general rule, not image-specific: if a field's value is the URL of a **realm resource** — a card instance or a realm file, whether as an absolute realm URL, a relative path, or any URL a realm serves — model it as `linksTo` / `linksToMany` (a `FileDef` subtype for files), never as `contains(StringField)` or `contains(UrlField)`. This applies to any pointer field: `author`, `owner`, `relatedDoc`, `parentProject`, and so on. | ||
|
|
||
| Both versions pass lint, write, index, and render a clickable link. The string version then decays with no error: | ||
|
|
||
| - The index only tracks relationship links as dependencies — nothing re-indexes the referrer when the target changes, and no `brokenLinks` diagnostic fires when the target is deleted. | ||
| - The template cannot render the target card (`<@fields.author @format='embedded' />` needs a relationship). | ||
| - Queries cannot traverse it (`author.name` filters need a relationship). | ||
| - Relationship links are relative paths that survive a realm copy or rename; the string holds an absolute URL that keeps pointing at the old realm. | ||
|
|
||
| **Two carve-outs — string is correct there:** | ||
|
|
||
| - A `FileDef` subtype's own `id` / `url` / `sourceUrl` descriptor fields are strings that hold the realm file URL by design. Populate them as strings (`new ImageDef({ id: fileIdentifier, url: fileIdentifier, sourceUrl: fileIdentifier, ... })`) — never try to convert them into relationships. The relationship lives one level up: the card's field pointing at the `ImageDef` is the `linksTo`. | ||
| - A curated public path routed via `hostRoutingRules` (a nav target like `/about` or `/pricing` in a site config) is a routed path, not a resource identifier — decoupling the public URL from the card id is the point of the routing mechanism. `UrlField` is correct there. See the `build-site-config-with-theme` and `link-host-mode-paths` patterns. | ||
|
|
||
| Everything else `UrlField` holds should be an external (non-realm) URL. | ||
|
|
||
| ### 🔴 DateField vs DateTimeField — the schema-vs-value contract | ||
|
|
||
| The most common silent failure pattern in Boxel card families: declaring `contains(DateField)` in the .gts but writing an ISO datetime (`"2026-06-13T15:30:00Z"`) in the JSON instance, OR declaring `contains(DateTimeField)` and writing only `"2026-06-13"`. | ||
|
|
@@ -211,6 +230,7 @@ These extend `FileDef` and must be used with `linksTo`, never `contains`. See `b | |
| Need text? | ||
| ├── Single line, generic → StringField | ||
| ├── Email/URL/phone → EmailField / UrlField / PhoneNumberField | ||
| │ (UrlField = external URLs only; a realm resource URL is a linksTo, see below) | ||
| ├── Multi-line plain → TextAreaField | ||
| └── Markdown | ||
| ├── Stored on the card → MarkdownField (or RichMarkdownField for editor chrome) | ||
|
|
@@ -231,6 +251,10 @@ Need a date? | |
| Need an image / file? | ||
| └── Always linksTo(ImageDef) or specific subtype. NEVER contains(ImageDef). | ||
|
|
||
| Need to reference another card or a realm file (even if you have it as a URL)? | ||
| └── Always linksTo / linksToMany. NEVER StringField or UrlField holding the URL. | ||
| (Carve-outs: a FileDef's own id/url/sourceUrl fields; hostRoutingRules nav paths — see the section above.) | ||
|
|
||
| Need bounded choices? | ||
| └── enumField(StringField, { options: [...] }) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium — the
Skill/tree (the in-app assistant) does not get this line, so the two harnesses drift.index.md's maintainer note: "Skill guidance lives in two hand-maintained trees that nothing syncs:skills/… andSkill/… A convention change must be authored into both, or the two harnesses drift."This decision tree has a hand-maintained twin at
Skill/dev-core-concept.md:41-44:Skill/dev-core-concept.jsonpointsinstructionsSourceat./dev-core-concept.md(a local copy), not atskills/, so nothing propagates.Skill/dev-quick-reference.md:62(theUrlFieldimport listing) andSkill/dev-file-def.mdare the other two mirror sites.Concrete misbehavior: the in-app assistant picking a field type consults
Skill/dev-core-concept.md's tree, finds no rule about realm URLs, and emits@field author = contains(UrlField)— the exact bug this PR exists to prevent. Note the new workspace rule 12 does reach the assistant (Skill/boxel-workspace-cardinal-rules.jsonlinks straight at../skills/boxel-workspace-cardinal-rules/SKILL.md), so this is a partial-coverage gap rather than a total one — but the field-selection guidance the assistant actually reads is the half that's missing.Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Claude Code 🤖] Fixed in 25caa2b. The decision tree in Skill/dev-core-concept.md now carries the rule line, and the UrlField import in Skill/dev-quick-reference.md carries an external-URLs-only note. Skill/dev-file-def.md already teaches both sides (linksTo to reference a FileDef; descriptor fields as strings), so it needed no change.