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
5 changes: 5 additions & 0 deletions .changeset/rename-retrieval-field-to-for.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": minor
---

The node retrieval frontmatter field is `for`: the situation or activity the guidance is for. `context` and `description` are rejected at validation with a pointer to `for`; gather/pull output, the embed API, coverage reporting (`withoutFor`), and the lint rule (`node-for-missing`) all use `for`.
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ glossary.md # the author's category vocabulary + what each kind means
checks/ # optional review assertions; never a node source
```

The **corpus is flat**. A node is a markdown file: a `context` in
frontmatter (the retrieval payload), optional `materials`, and brand guidance in
the prose body. A node's identity is its filename minus `.md`; its kind is the
The **corpus is flat**. A node is a markdown file: a `for` payload in
frontmatter (the retrieval payload: the situation or activity the guidance is
for), optional `materials`, and brand guidance in the prose body. A node's identity is its filename minus `.md`; its kind is the
filename prefix before the first dot, declared in the glossary. There is no
hierarchy, no inheritance, no edges; nesting into folders is a browsing
convenience only.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ repeatable commands for scaffolding, validation, retrieval, and review.
The package is a **flat set of nodes**. The optional `cover:` in
`manifest.yml` may name any node; `ghost gather` inlines it before the menu.
The default skeleton calls that node `brand`, but the filename is not reserved.
A node is one markdown file: a `context` in frontmatter, optional
`materials`, and brand guidance in the prose body. `description` remains a
deprecated read alias for one release, and `ghost validate` warns on its use.
A node is one markdown file: a `for` payload in frontmatter (the situation or
activity the guidance is for), optional `materials`, and brand guidance in the
prose body.

```markdown
---
context: Placing, sizing, or choosing a logo lockup or glyph.
for: Placing, sizing, or choosing a logo lockup or glyph.
materials:
- brand/logo-lockup.svg
- brand/logo-glyph.svg
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/anti-goal.generic-ai-product.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Any visual surface at risk of generic AI-product spectacle or card chrome.
for: Any visual surface at risk of generic AI-product spectacle or card chrome.
materials:
- apps/docs/src/pages/index.astro
- apps/docs/src/styles/marked-doc.css
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/brand.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Any expression of the ghost brand.
for: Any expression of the ghost brand.
materials:
- apps/docs/src/styles/marked-doc.css
- apps/docs/src/pages/index.astro
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/composition.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Composing any ghost page or adapting its layout across viewport sizes.
for: Composing any ghost page or adapting its layout across viewport sizes.
materials:
- apps/docs/src/components/DocSection.astro
- apps/docs/src/components/SectionWrapper.astro
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/docs-site.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Translating the ghost visual expression into another surface or medium.
for: Translating the ghost visual expression into another surface or medium.
materials:
- apps/docs/src/pages/index.astro
- apps/docs/src/components/Hero.astro
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/mark.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Using yellow, selection, intervention, or active-choice states.
for: Using yellow, selection, intervention, or active-choice states.
materials:
- apps/docs/src/styles/marked-doc.css
- apps/docs/src/components/docs/gather-demo.tsx
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/pattern.motion.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Anything that moves, transitions, reveals in sequence, or changes position or emphasis.
for: Anything that moves, transitions, reveals in sequence, or changes position or emphasis.
materials:
- apps/docs/src/components/docs/gather-demo.tsx
- apps/docs/src/styles/marked-doc.css
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/pattern.specimen.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: A reader needs to inspect an example, table, code sample, comparison, palette, diagnostic, or interactive result.
for: A reader needs to inspect an example, table, code sample, comparison, palette, diagnostic, or interactive result.
materials:
- apps/docs/src/pages/index.astro
- apps/docs/src/components/docs/gather-demo.tsx
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/principles.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Any ghost visual expression, independent of surface, medium, or implementation.
for: Any ghost visual expression, independent of surface, medium, or implementation.
materials:
- apps/docs/src/pages/index.astro
- apps/docs/src/components/DocSection.astro
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/.ghost/visual-system.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
context: Choosing or implementing color, typography, spacing, shape, elevation, interaction, or accessibility for ghost.
for: Choosing or implementing color, typography, spacing, shape, elevation, interaction, or accessibility for ghost.
materials:
- apps/docs/src/styles/marked-doc.css
- apps/docs/src/styles/docs.css
Expand Down
16 changes: 8 additions & 8 deletions apps/docs/src/components/docs/gather-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { useEffect, useMemo, useRef, useState } from "react";

const nodes = [
{ id: "brand", context: "any task that should express this brand" },
{ id: "voice", context: "anything with words" },
{ id: "motion", context: "anything that moves on screen" },
{ id: "brand", for: "any task that should express this brand" },
{ id: "voice", for: "anything with words" },
{ id: "motion", for: "anything that moves on screen" },
{
id: "email.transactional",
context: "money moved and the reader is checking",
for: "money moved and the reader is checking",
},
{
id: "layout.spacing",
context: "laying out a page",
for: "laying out a page",
},
{ id: "never.ai-defaults", context: "any first-draft visual surface" },
{ id: "never.ai-defaults", for: "any first-draft visual surface" },
{
id: "logo.usage",
context: "placing or sizing the mark",
for: "placing or sizing the mark",
},
] as const;

Expand Down Expand Up @@ -118,7 +118,7 @@ export function GatherDemo() {
>
<div className="font-bold">{node.id}</div>
<div>
{node.context}
{node.for}
{node.id === "brand" ? (
<span className="text-[var(--doc-middle)]">
{" "}
Expand Down
12 changes: 6 additions & 6 deletions apps/docs/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const steeringDiagnoses = [
signal: "delivery gap",
read: "the relevant guidance existed but did not reach the task",
repair:
"add the node, sharpen its description, or inspect why it was skipped",
"add the node, sharpen its `for` payload, or inspect why it was skipped",
},
{
signal: "correction gap",
Expand All @@ -44,7 +44,7 @@ const guidanceExamples = [
label: "visual decision",
path: "pattern.crop-with-intent.md",
body: `---
description: Photography. Gather when a composition uses a photo.
for: Photography. Gather when a composition uses a photo.
materials:
- brand/photography/**
---
Expand All @@ -60,7 +60,7 @@ Reject cautious full-object framing and collages that avoid choosing a focal poi
label: "product pattern",
path: "condition.blocked-progress.md",
body: `---
description: Blocked progress. Gather when someone cannot continue a task.
for: Blocked progress. Gather when someone cannot continue a task.
materials:
- src/components/error-state/**
---
Expand All @@ -76,7 +76,7 @@ If the person cannot resolve the problem, state what happens next. Do not end on
label: "exact material",
path: "asset.color-roles.md",
body: `---
description: Exact color roles and values. Gather before assigning color.
for: Exact color roles and values. Gather before assigning color.
materials:
- src/styles/brand-tokens.css
---
Expand Down Expand Up @@ -245,12 +245,12 @@ Ink carries content. Signal marks selection. Correction marks review.
<p>
Gather shows the complete menu before the agent chooses. If useful
guidance does not reach the work, you can see whether the node was
missing, its description was unclear, or the agent skipped it.
missing, its <code>for</code> payload was unclear, or the agent skipped it.
</p>

<h3 class="pt-4 font-bold lowercase">efficiency</h3>
<p>
Gather represents every node with a compact ID and description.
Gather represents every node with a compact ID and <code>for</code> payload.
Pull loads the full prose and materials only for the selected
nodes. The agent sees the shape of the whole brand while keeping
generation context small and free of unrelated instructions.
Expand Down
4 changes: 2 additions & 2 deletions docs/purposes.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ into folders is a browsing convenience only.
| `manifest.yml` | Schema version and package id; the package's anchor. |
| `glossary.md` | The author's dictionary: every term with defined meaning in the corpus. ghost ships no fixed vocabulary. |
| Prose nodes (`<kind>.<slug>.md`, `<slug>.md`) | Durable brand guidance; each body answers why (the stance), with what (the materials), or how it is assembled (the patterns). Altitude lives in prose; narrower guidance names its condition. |
| Node frontmatter | `context` (retrieval payload) and optional `materials` (explicit repo-relative file paths or supported external locators, with optional retrieval notes; see the schema reference for the supported schemes). |
| Node frontmatter | `for` (retrieval payload) and optional `materials` (explicit repo-relative file paths or supported external locators, with optional retrieval notes; see the schema reference for the supported schemes). |
| `checks/` | Optional review assertions binding to nodes with `references`. Never a node source and never generation input. |

One resolution mechanism, read-only:
Expand Down Expand Up @@ -78,7 +78,7 @@ Two rules keep the reservation honest:
1. **Retrieval needs pushed into the shape.** When selection feels imprecise, the
temptation is to encode routing in data: proliferating filename kinds until
they become destinations, or turning the glossary into a dispatch table.
*Fix: `context` is the retrieval payload; sharpen contexts, show the
*Fix: `for` is the retrieval payload; sharpen `for` payloads, show the
menu, let the agent pick.*

2. **Filing by destination.** A node authored as `for-emails.md` smuggles a
Expand Down
14 changes: 7 additions & 7 deletions packages/context-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ measures whether the right guidance even makes it into context. Selection is
the unit. No generation, ever.

`ghost gather` does no filtering: the menu is always the whole catalog, and
selection happens in the model's head against each node's `context`.
So what this bench actually tests is whether contexts are good enough
retrieval payloads — a node with a vague context is invisible at
selection happens in the model's head against each node's `for` payload.
So what this bench actually tests is whether `for` payloads are good enough
retrieval payloads — a node with a vague `for` is invisible at
selection time no matter how good its body is.

## Run
Expand All @@ -30,15 +30,15 @@ else `ghost` on PATH).
## Screens

**package** — the catalog rendered as the selection surface the model
sees: id, kind, context, material count, coverage line. Click a node to
see its real `ghost pull` output in a drawer. Review contexts as
retrieval payloads, not file contents; a node with no context is flagged
sees: id, kind, `for` payload, material count, coverage line. Click a node to
see its real `ghost pull` output in a drawer. Review `for` payloads as
retrieval payloads, not file contents; a node with no `for` is flagged
as invisible.

**bench** — type an ask (or run the whole asks suite), fire N single-shot
selection trials, and read the heatmap: nodes × asks, each cell the
fraction of trials that selected the node. Solid column = confident
context. Speckled = coin-flip. Empty row = dead node. Blue outline =
`for` payload. Speckled = coin-flip. Empty row = dead node. Blue outline =
the ask's expected set. Scores above the map: consistency (mean pairwise
Jaccard), mean per-trial precision and recall, poison-selection rate, unknown
ids, and nodes ever selected.
Expand Down
4 changes: 2 additions & 2 deletions packages/context-control/lib/model.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function fakeModel() {
const selected = [];
for (const entry of menu) {
const nodeTokens = tokens(
[entry.id.replaceAll(/[.-]/g, " "), entry.kind, entry.context]
[entry.id.replaceAll(/[.-]/g, " "), entry.kind, entry.for]
.filter(Boolean)
.join(" "),
);
Expand Down Expand Up @@ -101,7 +101,7 @@ function selectUser(ask, menu, cover) {
const flags = [entry.materials ? `${entry.materials} materials` : null]
.filter(Boolean)
.join(", ");
return `- ${entry.id}${entry.kind ? ` [${entry.kind}]` : ""}${flags ? ` (${flags})` : ""}: ${entry.context ?? "(no context)"}`;
return `- ${entry.id}${entry.kind ? ` [${entry.kind}]` : ""}${flags ? ` (${flags})` : ""}: ${entry.for ?? "(no for payload)"}`;
});
const coverLine = cover
? `Cover already in context: ${cover.id}\n\n${cover.body}\n\n`
Expand Down
8 changes: 4 additions & 4 deletions packages/context-control/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ <h1><b>context-control</b> · selection is the unit</h1>
</div>
<div class="legend">
Each column is one ask; each cell is the fraction of trials that selected the node.
Solid = confident context. Speckled = coin-flip. Empty row = invisible node.
Solid = confident `for` payload. Speckled = coin-flip. Empty row = invisible node.
Blue outline = in the ask's expected set. The cover is already in context and
is not part of the menu. Selection runs the ghost skill's recall protocol;
live agents additionally carry task
Expand Down Expand Up @@ -168,12 +168,12 @@ <h1><b>context-control</b> · selection is the unit</h1>
const c = data.coverage;
$("#coverage").textContent =
`${c.nodes} nodes · ${c.concrete} concrete` +
(c.withoutContext ? ` · ${c.withoutContext} without context` : "");
(c.withoutFor ? ` · ${c.withoutFor} lack for` : "");
$("#menuRows").innerHTML = state.menu.map((n) => `
<tr class="node" data-id="${n.id}">
<td class="id">${n.id}${n.materials ? `<span class="badge mat">${n.materials} mat</span>` : ""}</td>
<td class="kind">${n.kind ?? ""}</td>
<td>${n.context ?? '<span style="color:var(--danger)">no context — invisible at selection time</span>'}</td>
<td>${n.for ?? '<span style="color:var(--danger)">no for payload — invisible at selection time</span>'}</td>
</tr>`).join("");
document.querySelectorAll("tr.node").forEach((row) => {
row.onclick = () => openDrawer(row.dataset.id);
Expand All @@ -189,7 +189,7 @@ <h1><b>context-control</b> · selection is the unit</h1>
const node = data.nodes?.[0];
drawer.innerHTML = `<button class="close">close ×</button><h2>${id}</h2>` +
(node
? `<p style="color:var(--dim)">${node.context ?? ""}</p><hr style="border:0;border-top:1px solid var(--line)" />${escapeHtml(node.body)}`
? `<p style="color:var(--dim)">${node.for ?? ""}</p><hr style="border:0;border-top:1px solid var(--line)" />${escapeHtml(node.body)}`
: `<p class="flag">pull returned nothing (${escapeHtml(JSON.stringify(data))})</p>`);
drawer.querySelector(".close").onclick = () => drawer.classList.add("hidden");
}
Expand Down
8 changes: 4 additions & 4 deletions packages/ghost/src/commands/gather-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ function menuCoverageLine(menu: GhostGatherResult): string {
`${coverage.nodes} nodes`,
`${coverage.concrete} carry payloads (${payloadParts.join(", ")})`,
];
if (coverage.withoutContext > 0) {
parts.push(`${coverage.withoutContext} lack context`);
if (coverage.withoutFor > 0) {
parts.push(`${coverage.withoutFor} lack \`for\` payloads`);
}
return parts.join(" · ");
}
Expand All @@ -125,7 +125,7 @@ function formatMenuMarkdown(menu: GhostGatherResult): string {
if (menu.ask) {
lines.push(
"Complete, unfiltered, unranked list from the ghost package. ghost has not selected nodes for this ask.",
"Pull every node whose context indicates its stated situation applies and whose guidance, material, structure, or refusal governs the work. Skip inapplicable nodes. Topic overlap alone is not applicability. Do not add nodes for completeness or omit applicable nodes to meet a count.",
"Pull every node whose `for` payload indicates its stated situation applies and whose guidance, material, structure, or refusal governs the work. Skip inapplicable nodes. Topic overlap alone is not applicability. Do not add nodes for completeness or omit applicable nodes to meet a count.",
"Next: `ghost pull <id> [<id>…]`.",
"If nothing applies, name the package's silence, follow the cover silence posture, and do not invent ghost-backed guidance.",
"",
Expand All @@ -147,7 +147,7 @@ function formatMenuMarkdown(menu: GhostGatherResult): string {
for (const entry of menu.nodes) {
const kind = entry.kind ? ` _(${entry.kind})_` : "";
lines.push(`- \`${entry.id}\`${kind}`);
if (entry.context) lines.push(` - ${entry.context}`);
if (entry.for) lines.push(` - ${entry.for}`);
if (entry.materials !== undefined) {
lines.push(` - materials: ${entry.materials}`);
}
Expand Down
6 changes: 2 additions & 4 deletions packages/ghost/src/commands/pull-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@ function formatPullJson(
nodes: result.nodes.map((node) => ({
id: node.id,
...(node.kind !== undefined ? { kind: node.kind } : {}),
...(node.context
? { context: node.context, description: node.context }
: {}),
...(node.for ? { for: node.for } : {}),
...(node.declaredMaterials !== undefined
? {
materials: inlineMaterials
Expand All @@ -134,7 +132,7 @@ function formatPullMarkdown(result: GhostPullResult): string {
for (const node of result.nodes) {
const kind = node.kind ? ` _(${node.kind})_` : "";
const lines = [`# \`${node.id}\`${kind}`];
if (node.context) lines.push("", `> ${node.context}`);
if (node.for) lines.push("", `> ${node.for}`);
lines.push("", node.body.trim());
if (node.materials !== undefined && node.materials.length > 0) {
lines.push("", "Materials:");
Expand Down
9 changes: 4 additions & 5 deletions packages/ghost/src/embed/gather.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function gatherContract(ask: string | undefined): GhostGatherContract {
selection: {
basis: "applicability",
instruction: ask
? "Pull every node whose context indicates its stated situation applies and whose guidance, material, structure, or refusal governs the work; skip inapplicable nodes."
? "Pull every node whose `for` payload indicates its stated situation applies and whose guidance, material, structure, or refusal governs the work; skip inapplicable nodes."
: "Bare gather is catalog inspection. Do not treat the menu as task grounding until an ask is supplied; when grounding a task, pull every applicable node and skip inapplicable nodes.",
topicOverlapAloneIsApplicability: false,
addForCompleteness: false,
Expand All @@ -76,8 +76,8 @@ export function gatherContract(ask: string | undefined): GhostGatherContract {
export function menuCoverage(
menu: readonly CatalogMenuEntry[],
): GhostGatherCoverage {
const withoutContext = menu.filter(
(entry) => !entry.context || entry.context.trim().length === 0,
const withoutFor = menu.filter(
(entry) => !entry.for || entry.for.trim().length === 0,
).length;
return {
nodes: menu.length,
Expand All @@ -87,8 +87,7 @@ export function menuCoverage(
fencedExamples: menu.filter((entry) => entry.hasFencedExample).length,
skeletons: menu.filter((entry) => entry.hasSkeleton).length,
},
withoutContext,
undescribed: withoutContext,
withoutFor,
};
}

Expand Down
4 changes: 1 addition & 3 deletions packages/ghost/src/embed/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ export async function pullGhostNodes(
nodes: pulledNodes.map(({ node, materials }) => ({
id: node.id,
...(node.kind !== undefined ? { kind: node.kind } : {}),
...(node.context
? { context: node.context, description: node.context }
: {}),
...(node.for ? { for: node.for } : {}),
...(node.materials !== undefined
? { declaredMaterials: [...node.materials] }
: {}),
Expand Down
Loading
Loading