@@ -127,7 +115,7 @@ export default function Home() {
- incorrect generation: agent failure
- - missing-memory: under-specified surface context
+ - missing-fingerprint: under-specified surface context
- intentional product evolution
diff --git a/apps/docs/src/content/docs/cli-reference.mdx b/apps/docs/src/content/docs/cli-reference.mdx
index 8e0586e7..ac7e7866 100644
--- a/apps/docs/src/content/docs/cli-reference.mdx
+++ b/apps/docs/src/content/docs/cli-reference.mdx
@@ -26,7 +26,7 @@ product areas:
.ghost/fingerprint/prose.yml
.ghost/fingerprint/inventory.yml
.ghost/fingerprint/composition.yml
-.ghost/fingerprint/enforcement/checks.yml
+.ghost/fingerprint/checks.yml
apps/checkout/.ghost/fingerprint/manifest.yml
```
@@ -40,7 +40,7 @@ The command tables below are generated from the CLI source. Run
### Initialize - `init`
Create a `.ghost/fingerprint/` package with a manifest, raw core layer files,
-and enforcement checks. Use `--scope ` for nested package roots. Use
+and deterministic checks. Use `--scope ` for nested package roots. Use
`--monorepo` to create or preserve the root package, detect workspace child
roots, and print scoped init commands; add `--apply` to create the detected
child packages. Use
@@ -53,10 +53,9 @@ over the environment default.
```bash
ghost init
-ghost init --with-intent
ghost init --monorepo
ghost init --monorepo --apply
-ghost init --scope apps/checkout --with-intent
+ghost init --scope apps/checkout
GHOST_MEMORY_DIR=.agents/ghost ghost init
ghost init --scope apps/checkout --memory-dir .design/memory
```
@@ -64,7 +63,7 @@ ghost init --scope apps/checkout --memory-dir .design/memory
### Layer readiness - `scan`
Report whether `fingerprint/manifest.yml` is present and which useful layers it
-has: `prose`, `inventory`, and `composition`. Generated cache does not count
+has: `prose`, `inventory`, and `composition`. Raw repo signals do not count
toward inventory readiness.
@@ -86,16 +85,15 @@ Inspect the root-to-leaf fingerprint stack for one or more paths.
ghost stack apps/checkout/review/page.tsx --format json
```
-### Capture source material - `inventory`
+### Inspect repo signals - `signals`
-Emit raw signals about a frontend repo as JSON. Use this as optional source
-material under `.ghost/fingerprint/sources/cache/`.
+Emit raw signals about a frontend repo as JSON. Use this as scratch evidence
+while authoring curated fingerprint layers.
-
+
```bash
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
```
@@ -112,7 +110,7 @@ Validate a root `.ghost` fingerprint package or an individual split artifact.
```bash
ghost lint
ghost lint .ghost/fingerprint/prose.yml
-ghost lint .ghost/fingerprint/enforcement/checks.yml --format json
+ghost lint .ghost/fingerprint/checks.yml --format json
ghost lint --all
```
@@ -145,7 +143,7 @@ suggested reads, active checks, and provenance.
### Inspection - `describe`
-Print optional `.ghost/fingerprint/memory/intent.md` or a markdown section map.
+Print a markdown section map.
diff --git a/apps/docs/src/content/docs/fingerprint-authoring.mdx b/apps/docs/src/content/docs/fingerprint-authoring.mdx
index ef2c89e8..ace21668 100644
--- a/apps/docs/src/content/docs/fingerprint-authoring.mdx
+++ b/apps/docs/src/content/docs/fingerprint-authoring.mdx
@@ -70,15 +70,14 @@ Set up the Ghost fingerprint for this repo with auto-draft.
```bash
ghost scan --format json
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory . > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
ghost lint .ghost
ghost verify .ghost --root .
```
-Generated cache is source material only. It can support curated inventory, but
-it does not establish surface-composition guidance by itself. Scan frequency and
-raw cache may seed a draft, but they do not decide what the surface should do.
+Raw repo signals are source evidence only. They can support curated inventory,
+but they do not establish surface-composition guidance by themselves. Signal
+frequency may seed a draft, but it does not decide what the surface should do.
@@ -91,9 +90,7 @@ Keep each claim in the file that will make it useful later:
| `prose.yml` | Audience, goals, anti-goals, situations, principles, and experience contracts. |
| `inventory.yml` | Scopes, surface types, files, routes, libraries, assets, building blocks, exemplars, and source links. |
| `composition.yml` | Repeatable rules, layouts, structures, flows, states, content patterns, behavior, and visual arrangements. |
-| `enforcement/checks.yml` | Deterministic gates that can be checked from a diff. |
-| `memory/` | Human-approved intent and accepted or rejected rationale. |
-| `sources/cache/` | Refreshable generated observations. |
+| `checks.yml` | Deterministic gates that can be checked from a diff. |
@@ -110,7 +107,7 @@ Keep broad product-family guidance at the root. Put local obligations in the
nearest package that owns the surface.
```bash
-ghost init --scope apps/checkout --with-intent
+ghost init --scope apps/checkout
ghost stack apps/checkout
ghost lint --all
ghost verify --all
@@ -123,9 +120,7 @@ ghost verify --all
Uncommitted or unmerged fingerprint edits are drafts. Checked-in
`fingerprint/` core files are canonical.
-Use optional `fingerprint/memory/intent.md` only for human-authored or
-human-approved intent. Use `fingerprint/memory/decisions/` for rationale that
-explains why a product choice changed. Add deterministic checks sparingly, and
-only when a rule can be enforced deterministically.
+Add deterministic checks sparingly, and only when a rule can be enforced
+deterministically.
diff --git a/apps/docs/src/content/docs/getting-started.mdx b/apps/docs/src/content/docs/getting-started.mdx
index c91ef963..cd7889c9 100644
--- a/apps/docs/src/content/docs/getting-started.mdx
+++ b/apps/docs/src/content/docs/getting-started.mdx
@@ -23,10 +23,7 @@ The canonical portable fingerprint is a folder:
prose.yml
inventory.yml
composition.yml
- enforcement/checks.yml
- memory/intent.md
- memory/decisions/
- sources/cache/
+ checks.yml
```
Generation starts from `prose.yml`, `inventory.yml`, and `composition.yml`.
@@ -89,10 +86,9 @@ composition work: interviewing, reading repo evidence, drafting layer edits, and
asking you to curate the claims.
```bash
-ghost init --with-intent
+ghost init
ghost scan --format json
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
ghost lint .ghost
ghost verify .ghost --root .
```
@@ -107,9 +103,9 @@ The fingerprint records durable surface-composition guidance:
3. **Composition** - the patterns that make it intentional: rules, layouts,
structures, flows, states, content, behavior, and visual arrangements.
-Generated cache is optional source material. Curate durable prose, inventory,
-and composition into the core layer files, then use normal Git review for
-approval. For a fuller human-agent workflow, read
+Raw repo signals are optional authoring evidence. Curate durable prose,
+inventory, and composition into the core layer files, then use normal Git
+review for approval. For a fuller human-agent workflow, read
[Fingerprint Authoring](/docs/fingerprint-authoring).
@@ -134,7 +130,7 @@ After implementation, run Ghost against the same fingerprint:
```bash
ghost check --base main
-ghost review --base main --include-memory
+ghost review --base main
```
`ghost check` applies active deterministic gates from the resolved fingerprint
diff --git a/apps/docs/src/generated/cli-manifest.json b/apps/docs/src/generated/cli-manifest.json
index 1394dab6..9c55e730 100644
--- a/apps/docs/src/generated/cli-manifest.json
+++ b/apps/docs/src/generated/cli-manifest.json
@@ -1,5 +1,5 @@
{
- "generatedAt": "2026-06-22T15:49:43.175Z",
+ "generatedAt": "2026-06-23T04:21:38.371Z",
"tools": [
{
"tool": "ghost",
@@ -66,14 +66,6 @@
"takesValue": true,
"negated": false
},
- {
- "rawName": "--with-intent",
- "name": "withIntent",
- "description": "Also create optional fingerprint/memory/intent.md for human-authored or human-approved intent",
- "default": null,
- "takesValue": false,
- "negated": false
- },
{
"rawName": "--with-config",
"name": "withConfig",
@@ -242,24 +234,24 @@
},
{
"tool": "ghost",
- "name": "inventory",
- "rawName": "inventory [path]",
- "description": "Emit deterministic raw signals about a frontend repo as JSON for optional cache/source material: package manifests, language histogram, candidate config files, registry presence, top-level tree, and git remote.",
+ "name": "signals",
+ "rawName": "signals [path]",
+ "description": "Emit deterministic raw repo signals as JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, and git remote.",
"group": "advanced",
"defaultHelp": false,
- "compactName": "inventory",
- "summary": "Emit raw repo signals for optional cache material.",
+ "compactName": "signals",
+ "summary": "Emit raw repo signals for fingerprint authoring.",
"options": []
},
{
"tool": "ghost",
"name": "describe",
- "rawName": "describe [fingerprint]",
- "description": "Print a section map of fingerprint/memory/intent.md or a markdown file (line ranges + token estimates).",
+ "rawName": "describe ",
+ "description": "Print a section map of a markdown file (line ranges + token estimates).",
"group": "advanced",
"defaultHelp": false,
"compactName": "describe",
- "summary": "Print intent or markdown section ranges.",
+ "summary": "Print markdown section ranges.",
"options": [
{
"rawName": "--format ",
@@ -299,7 +291,7 @@
"group": "maintenance",
"defaultHelp": false,
"compactName": "survey",
- "summary": "Run survey/cache helpers.",
+ "summary": "Run legacy survey helpers.",
"options": [
{
"rawName": "-o, --out ",
@@ -813,14 +805,6 @@
"takesValue": true,
"negated": false
},
- {
- "rawName": "--include-memory",
- "name": "includeMemory",
- "description": "Include accepted decisions as accepted_decisions in the advisory packet (flag name retained).",
- "default": null,
- "takesValue": false,
- "negated": false
- },
{
"rawName": "--max-diff-bytes ",
"name": "maxDiffBytes",
diff --git a/docs/fingerprint-format.md b/docs/fingerprint-format.md
index 620a9b92..17bade80 100644
--- a/docs/fingerprint-format.md
+++ b/docs/fingerprint-format.md
@@ -12,16 +12,7 @@ lives under `.ghost/fingerprint/`:
prose.yml # core: surface intent
inventory.yml # core: curated material and source links
composition.yml # core: experience patterns
-
- enforcement/
- checks.yml # optional deterministic gates
-
- memory/
- intent.md # optional human-approved intent
- decisions/ # optional accepted/rejected rationale
-
- sources/
- cache/ # optional refreshable generated observations
+ checks.yml # optional deterministic gates
```
Git is the staging and approval boundary: uncommitted or unmerged edits are
@@ -51,8 +42,8 @@ summary:
goals:
- Preserve task-first documentation and product trust.
principles:
- - id: prose-before-cache
- principle: Prose captures the intent behind the surface; generated cache only explains what exists.
+ - id: prose-before-material
+ principle: Prose captures the intent behind the surface; inventory points to replaceable material.
experience_contracts:
- id: review-cites-memory
contract: Advisory review findings must cite the diff and relevant fingerprint refs.
@@ -87,17 +78,13 @@ exemplars:
why: Shows command facts and examples kept visually adjacent.
refs: [composition.pattern:reference-before-decoration]
sources:
- - id: generated-inventory
- kind: cache
- ref: sources/cache/inventory.json
- note: Refreshable observed repo facts.
- id: ghost-ui-registry
kind: registry
ref: packages/ghost-ui/public/r/registry.json
```
-Supported `inventory.sources[].kind` values are `cache`, `registry`, `file`,
-`url`, and `package`. Source links are provenance and orientation; they do not
+Supported `inventory.sources[].kind` values are `registry`, `file`, `url`, and
+`package`. Source links are provenance and orientation; they do not
make generated material canonical by themselves.
`composition.yml` captures the patterns that make a surface feel intentional:
@@ -136,7 +123,7 @@ valid, such as `inventory.exemplars[].refs`.
## Enforcement
-`fingerprint/enforcement/checks.yml` uses `ghost.checks/v1`. Checks are
+`fingerprint/checks.yml` uses `ghost.checks/v1`. Checks are
deterministic validation, not generation input.
```yaml
@@ -185,36 +172,6 @@ errors, so teams can draft gates while curation catches up. Promote only rules
that can be detected deterministically; taste stays in prose or composition
until there is a reliable detector.
-## Memory And Sources
-
-`fingerprint/memory/intent.md` is optional human-authored or human-approved
-intent: constraints, tradeoffs, audience notes, and known exceptions that
-should not be inferred from code alone.
-
-`fingerprint/memory/decisions/*.yml` stores accepted, rejected, or superseded
-surface-composition rationale using `ghost.decision/v1`:
-
-```yaml
-schema: ghost.decision/v1
-id: keep-reference-dense
-status: accepted
-title: Keep CLI reference pages dense
-claim: CLI reference pages should remain compact and task-first.
-rationale: Users arrive with a command-shaped task and need fast comparison.
-scope:
- surface_types: [reference-page]
-evidence:
- - path: apps/docs/src/content/docs/cli-reference.mdx
-decided_at: '2026-06-04T00:00:00-04:00'
-```
-
-`ghost review --include-memory` reads accepted decisions. Rejected or
-superseded decisions are history, not canonical instructions.
-
-`fingerprint/sources/cache/` is refreshable generated material. It can help an
-agent update `inventory.yml`, but cache files never count as fingerprint
-readiness by themselves.
-
## Nested Packages
Large repos can add scoped packages below the root:
@@ -264,7 +221,7 @@ ghost scan --format json
ghost lint .ghost
ghost verify .ghost --root .
ghost check --base main --format json
-ghost review --base main --include-memory
+ghost review --base main
ghost emit review-command --path apps/checkout/review/page.tsx
ghost relay gather apps/checkout/review/page.tsx
```
@@ -274,11 +231,10 @@ ghost relay gather apps/checkout/review/page.tsx
contract. Useful `inventory` means topology scopes or surface types, curated
building blocks, or exemplars. Useful `composition` means at least one pattern.
-Use generated cache when observed repo facts are useful source material:
+Use raw repo signals when observed repo facts are useful authoring evidence:
```bash
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
```
Curate durable conclusions into `prose.yml`, `inventory.yml`, or
@@ -289,9 +245,7 @@ Curate durable conclusions into `prose.yml`, `inventory.yml`, or
- Write durable surface intent in `prose.yml`.
- Write curated repo material and exemplars in `inventory.yml`.
- Write repeatable experience patterns in `composition.yml`.
-- Write deterministic gates in `enforcement/checks.yml`.
-- Write human-approved intent and rationale in `memory/`.
-- Keep generated observations in `sources/cache/`.
+- Write deterministic gates in `checks.yml`.
- Prefer typed refs over prose-only cross-links.
- Keep ids stable after review because refs and checks depend on them.
- Let Git review approve changes to canonical fingerprint layers.
diff --git a/docs/generation-loop.md b/docs/generation-loop.md
index 44046dc8..16e4a28e 100644
--- a/docs/generation-loop.md
+++ b/docs/generation-loop.md
@@ -33,20 +33,18 @@ ghost relay gather apps/checkout/review/page.tsx
2. Select relevant situations, principles, experience contracts, and patterns.
3. Inspect matching inventory exemplars as concrete anchors.
4. Use `inventory.building_blocks` as curated material.
-5. Use optional `.ghost/fingerprint/sources/cache/` only as source material.
-6. Skim active checks in `.ghost/fingerprint/enforcement/checks.yml` so
+5. Run `ghost signals` when raw repo observations would help find evidence.
+6. Skim active checks in `.ghost/fingerprint/checks.yml` so
generation avoids deterministic failures.
-7. Use optional `fingerprint/memory/intent.md`, accepted decisions, and nested
- stacks only when the project has opted into them.
+7. Use nested stacks only when the project has opted into them.
-Generated cache can help orient an agent:
+Raw repo signals can help orient an agent:
```bash
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
```
-Cache answers what exists now and does not count toward scan readiness.
+Signals answer what exists now and do not count toward scan readiness.
`prose.yml` captures the intent behind the surface. Curated inventory points to
building blocks and exemplars. `composition.yml` captures the patterns that make
the surface feel intentional.
@@ -65,13 +63,13 @@ stack and runs merged checks for each group. Only active checks can block.
`ghost review` is advisory:
```bash
-ghost review --base main --include-memory
+ghost review --base main
```
Advisory review packets include the current diff, the split fingerprint core
-layers, relevant inventory exemplars, active checks, optional accepted
-decisions, and finding categories for fixes, intentional divergence, missing
-missing grounding, experience gaps, and eval uncertainty.
+layers, relevant inventory exemplars, active checks, and finding categories for
+fixes, intentional divergence, missing fingerprint grounding, experience gaps,
+and eval uncertainty.
Review findings should cite the diff location, relevant fingerprint refs,
relevant exemplars when useful, and any active check when blocking.
@@ -83,8 +81,8 @@ smallest useful response:
- Fix the generated or changed code.
- Explain why a divergence is intentional.
-- Update the split fingerprint package or optional rationale files when the
- user asks to change the Ghost fingerprint.
+- Update the split fingerprint package when the user asks to change the Ghost
+ fingerprint.
## CI
diff --git a/docs/host-adapters.md b/docs/host-adapters.md
index cdf6ed87..30dd5d82 100644
--- a/docs/host-adapters.md
+++ b/docs/host-adapters.md
@@ -12,9 +12,8 @@ Ghost provides:
- `.ghost/fingerprint/` package loading and stack merging.
- `fingerprint/prose.yml`, `fingerprint/inventory.yml`, and
`fingerprint/composition.yml` as generation context.
-- Optional `fingerprint/enforcement/checks.yml`,
- `fingerprint/memory/intent.md`, `fingerprint/memory/decisions/`, and
- `fingerprint/sources/cache/`.
+- Optional `fingerprint/checks.yml`.
+- `ghost signals` stdout output for raw repo observations.
- `ghost check --format json` as the stable `ghost.check-report/v1` contract.
- `ghost review --format json` for advisory packets grounded in the resolved
fingerprint stack.
@@ -33,9 +32,8 @@ Host adapters provide:
- policy for when a finding blocks, comments, or remains advisory
- normal Git review for fingerprint edits
-Generated cache is optional source material. Adapters should not treat
-`.ghost/fingerprint/sources/cache/inventory.json` as canonical inventory; the
-checked-in core layer files remain the authority.
+Raw repo signals are authoring evidence, not canonical inventory. The checked-in
+core layer files remain the authority.
## Check Flow
diff --git a/docs/ideas/fingerprint-first-architecture.md b/docs/ideas/fingerprint-first-architecture.md
index 0e04f54f..645e27ea 100644
--- a/docs/ideas/fingerprint-first-architecture.md
+++ b/docs/ideas/fingerprint-first-architecture.md
@@ -29,9 +29,7 @@ consume, validate, compare, generate from, or govern that context.
`inventory.yml` points to curated material and exemplars, and
`composition.yml` captures the patterns that make it feel intentional.
- Supporting material stays subordinate to the core layers:
- `enforcement/checks.yml` validates deterministic obligations,
- `memory/intent.md` and `memory/decisions/` preserve optional human-approved
- rationale, and `sources/cache/` stores refreshable observations.
+ `checks.yml` validates deterministic obligations.
- Tools are consumers or authors of the fingerprint. They may capture,
validate, apply, govern, or compare the contract, but they do not replace it.
- Drift is governance. `check`, `review`, `ack`, `track`, and `diverge` answer
@@ -67,7 +65,7 @@ fingerprint lifecycle:
| Lifecycle | Commands and consumers | Purpose |
| --- | --- | --- |
-| Capture | `init`, `inventory`, `scan` | Create the package, gather optional source material, and report layer readiness. |
+| Capture | `init`, `signals`, `scan` | Create the package, inspect repo signals, and report layer readiness. |
| Validate | `lint`, `verify` | Check schema shape, refs, evidence, exemplars, and deterministic package quality. |
| Generate / apply | `relay gather`, host agents, future generation consumers | Give agents the upstream surface-composition contract before they build or revise. |
| Govern | `check`, `review`, `ack`, `track`, `diverge` | Validate changed surfaces, produce advisory findings, and record stance toward divergence. |
diff --git a/docs/language-fingerprints.md b/docs/language-fingerprints.md
index 2c97dd5d..8ebf67c9 100644
--- a/docs/language-fingerprints.md
+++ b/docs/language-fingerprints.md
@@ -12,7 +12,7 @@ surface-composition concern:
- `fingerprint/inventory.yml` points at copy material and external writing
standards.
- `fingerprint/composition.yml` carries copy patterns.
-- `fingerprint/enforcement/checks.yml` carries the deterministic subset.
+- `fingerprint/checks.yml` carries the deterministic subset.
This document shows the mapping. Nothing here changes the
`ghost.fingerprint/v1` schema.
@@ -143,7 +143,7 @@ Only the mechanically detectable subset belongs in `checks.yml`. The
required boilerplate today:
```yaml
-# fingerprint/enforcement/checks.yml
+# fingerprint/checks.yml
checks:
- id: no-banned-phrases
title: Banned phrases stay out of user-facing copy
diff --git a/packages/ghost-core/src/fingerprint-package.ts b/packages/ghost-core/src/fingerprint-package.ts
index 4e4fd9bf..3076032f 100644
--- a/packages/ghost-core/src/fingerprint-package.ts
+++ b/packages/ghost-core/src/fingerprint-package.ts
@@ -3,10 +3,7 @@ export const RESOURCES_FILENAME = "resources.yml" as const;
export const PATTERNS_FILENAME = "patterns.yml" as const;
export const FINGERPRINT_YML_FILENAME = "fingerprint.yml" as const;
export const CONFIG_FILENAME = "config.yml" as const;
-export const INTENT_FILENAME = "intent.md" as const;
export const FINGERPRINT_FILENAME = "fingerprint.md" as const;
-export const DECISIONS_DIRNAME = "decisions" as const;
-export const CACHE_DIRNAME = "cache" as const;
export interface FingerprintPackagePaths {
dir: string;
@@ -19,7 +16,4 @@ export interface FingerprintPackagePaths {
/** Legacy direct markdown path; not part of the canonical root bundle. */
fingerprint: string;
checks: string;
- intent: string;
- decisions: string;
- cache: string;
}
diff --git a/packages/ghost-core/src/index.ts b/packages/ghost-core/src/index.ts
index e06e8241..b8782c65 100644
--- a/packages/ghost-core/src/index.ts
+++ b/packages/ghost-core/src/index.ts
@@ -61,14 +61,11 @@ export {
} from "./embedding/index.js";
// --- Map (ghost.map/v1) ---
export {
- CACHE_DIRNAME,
CONFIG_FILENAME,
- DECISIONS_DIRNAME,
FINGERPRINT_FILENAME,
FINGERPRINT_PACKAGE_DIR,
FINGERPRINT_YML_FILENAME,
type FingerprintPackagePaths,
- INTENT_FILENAME,
PATTERNS_FILENAME,
RESOURCES_FILENAME,
} from "./fingerprint-package.js";
@@ -89,24 +86,6 @@ export {
slugifyScopeId,
type TopLevelEntry,
} from "./map/index.js";
-// --- Memory (ghost.decision/v1) ---
-export type {
- GhostDecisionDocument,
- GhostDecisionLintIssue,
- GhostDecisionLintReport,
- GhostDecisionLintSeverity,
- GhostDecisionStatus,
- GhostExperienceEvidence,
- GhostExperienceScope,
-} from "./memory/index.js";
-export {
- GHOST_DECISION_SCHEMA,
- GHOST_DECISIONS_DIRNAME,
- GhostDecisionSchema,
- GhostExperienceEvidenceSchema,
- GhostExperienceScopeSchema,
- lintGhostDecision,
-} from "./memory/index.js";
// --- Patterns (ghost.patterns/v1) ---
export type {
GhostCompositionAnatomy,
diff --git a/packages/ghost-core/src/memory/index.ts b/packages/ghost-core/src/memory/index.ts
deleted file mode 100644
index 663a16ea..00000000
--- a/packages/ghost-core/src/memory/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export { lintGhostDecision } from "./lint.js";
-export {
- GhostDecisionSchema,
- GhostExperienceEvidenceSchema,
- GhostExperienceScopeSchema,
-} from "./schema.js";
-export type {
- GhostDecisionDocument,
- GhostDecisionLintIssue,
- GhostDecisionLintReport,
- GhostDecisionLintSeverity,
- GhostDecisionStatus,
- GhostExperienceEvidence,
- GhostExperienceScope,
-} from "./types.js";
-export { GHOST_DECISION_SCHEMA, GHOST_DECISIONS_DIRNAME } from "./types.js";
diff --git a/packages/ghost-core/src/memory/lint.ts b/packages/ghost-core/src/memory/lint.ts
deleted file mode 100644
index 8ea25fec..00000000
--- a/packages/ghost-core/src/memory/lint.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { ZodIssue } from "zod";
-import { GhostDecisionSchema } from "./schema.js";
-import type {
- GhostDecisionLintIssue,
- GhostDecisionLintReport,
-} from "./types.js";
-
-export function lintGhostDecision(input: unknown): GhostDecisionLintReport {
- const result = GhostDecisionSchema.safeParse(input);
- if (!result.success) return finalize(zodIssues(result.error.issues));
- return finalize([]);
-}
-
-function zodIssues(issues: ZodIssue[]): GhostDecisionLintIssue[] {
- return issues.map((issue) => ({
- severity: "error" as const,
- rule: `schema/${issue.code}`,
- message: issue.message,
- path: formatZodPath(issue.path),
- }));
-}
-
-function formatZodPath(path: ZodIssue["path"]): string | undefined {
- if (path.length === 0) return undefined;
- return path.reduce((formatted, segment) => {
- if (typeof segment === "number") return `${formatted}[${segment}]`;
- const key = String(segment);
- return formatted ? `${formatted}.${key}` : key;
- }, "");
-}
-
-function finalize(issues: GhostDecisionLintIssue[]): GhostDecisionLintReport {
- return {
- issues,
- errors: issues.filter((issue) => issue.severity === "error").length,
- warnings: issues.filter((issue) => issue.severity === "warning").length,
- info: issues.filter((issue) => issue.severity === "info").length,
- };
-}
diff --git a/packages/ghost-core/src/memory/schema.ts b/packages/ghost-core/src/memory/schema.ts
deleted file mode 100644
index 30330331..00000000
--- a/packages/ghost-core/src/memory/schema.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { z } from "zod";
-import { GHOST_DECISION_SCHEMA } from "./types.js";
-
-const SlugIdSchema = z
- .string()
- .min(1)
- .regex(/^[a-z0-9][a-z0-9._-]*$/, {
- message:
- "id must be a slug (lowercase alphanumeric plus . _ -, leading alphanumeric)",
- });
-
-const NonEmptyStringArraySchema = z.array(z.string().min(1)).min(1);
-
-export const GhostExperienceScopeSchema = z
- .object({
- roles: NonEmptyStringArraySchema.optional(),
- scopes: NonEmptyStringArraySchema.optional(),
- surface_types: NonEmptyStringArraySchema.optional(),
- pattern_ids: NonEmptyStringArraySchema.optional(),
- paths: NonEmptyStringArraySchema.optional(),
- })
- .strict();
-
-export const GhostExperienceEvidenceSchema = z
- .object({
- path: z.string().min(1).optional(),
- survey_surface_id: z.string().min(1).optional(),
- locator: z.string().min(1).optional(),
- note: z.string().min(1).optional(),
- })
- .strict()
- .refine(
- (evidence) =>
- Boolean(
- evidence.path ||
- evidence.survey_surface_id ||
- evidence.locator ||
- evidence.note,
- ),
- {
- message:
- "evidence must include at least one of path, survey_surface_id, locator, or note",
- },
- );
-
-export const GhostDecisionSchema = z
- .object({
- schema: z.literal(GHOST_DECISION_SCHEMA),
- id: SlugIdSchema,
- status: z.enum(["accepted", "rejected", "superseded"]),
- title: z.string().min(1),
- claim: z.string().min(1),
- rationale: z.string().min(1),
- scope: GhostExperienceScopeSchema.optional(),
- evidence: z.array(GhostExperienceEvidenceSchema).min(1),
- decided_at: z.string().datetime({ offset: true }),
- })
- .strict();
diff --git a/packages/ghost-core/src/memory/types.ts b/packages/ghost-core/src/memory/types.ts
deleted file mode 100644
index 005a67e0..00000000
--- a/packages/ghost-core/src/memory/types.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-export const GHOST_DECISION_SCHEMA = "ghost.decision/v1" as const;
-
-export const GHOST_DECISIONS_DIRNAME = "decisions" as const;
-
-export type GhostDecisionStatus = "accepted" | "rejected" | "superseded";
-
-export interface GhostExperienceScope {
- roles?: string[];
- scopes?: string[];
- surface_types?: string[];
- pattern_ids?: string[];
- paths?: string[];
-}
-
-export interface GhostExperienceEvidence {
- path?: string;
- survey_surface_id?: string;
- locator?: string;
- note?: string;
-}
-
-export interface GhostDecisionDocument {
- schema: typeof GHOST_DECISION_SCHEMA;
- id: string;
- status: GhostDecisionStatus;
- title: string;
- claim: string;
- rationale: string;
- scope?: GhostExperienceScope;
- evidence: GhostExperienceEvidence[];
- decided_at: string;
-}
-
-export type GhostDecisionLintSeverity = "error" | "warning" | "info";
-
-export interface GhostDecisionLintIssue {
- severity: GhostDecisionLintSeverity;
- rule: string;
- message: string;
- path?: string;
-}
-
-export interface GhostDecisionLintReport {
- issues: GhostDecisionLintIssue[];
- errors: number;
- warnings: number;
- info: number;
-}
diff --git a/packages/ghost-core/test/memory.test.ts b/packages/ghost-core/test/memory.test.ts
deleted file mode 100644
index cb9182b6..00000000
--- a/packages/ghost-core/test/memory.test.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { describe, expect, it } from "vitest";
-import * as core from "../src/index.js";
-
-const VALID_DECISION = {
- schema: core.GHOST_DECISION_SCHEMA,
- id: "checkout-reversibility",
- status: "accepted",
- title: "Reversibility before money movement",
- claim: "Payment review must make reversibility visible before submission.",
- rationale: "Users need confidence before committing money movement.",
- scope: {
- roles: ["design", "engineering", "pm", "qa"],
- scopes: ["checkout"],
- surface_types: ["payment-review"],
- pattern_ids: ["confirmation-before-commit"],
- },
- evidence: [
- {
- path: "apps/checkout/review.tsx",
- note: "Review step exposes edit affordances before submit.",
- },
- ],
- decided_at: "2026-05-17T00:00:00.000Z",
-};
-
-describe("Ghost decision rationale schemas", () => {
- it("accepts a valid ghost.decision/v1 document", () => {
- const report = core.lintGhostDecision(VALID_DECISION);
-
- expect(report.errors).toBe(0);
- });
-
- it("rejects a decision without auditable evidence", () => {
- const report = core.lintGhostDecision({
- ...VALID_DECISION,
- evidence: [],
- });
-
- expect(report.errors).toBeGreaterThan(0);
- expect(report.issues.map((issue) => issue.path)).toContain("evidence");
- });
-
- it("does not expose proposal-era memory symbols from private core", () => {
- for (const symbol of [
- "GHOST_PROPOSAL_SCHEMA",
- "GHOST_PROPOSALS_DIRNAME",
- "GhostProposalActionSchema",
- "GhostProposalSchema",
- "lintGhostProposal",
- ]) {
- expect(core).not.toHaveProperty(symbol);
- }
- });
-});
diff --git a/packages/ghost-ui/.ghost/fingerprint/enforcement/checks.yml b/packages/ghost-ui/.ghost/fingerprint/checks.yml
similarity index 100%
rename from packages/ghost-ui/.ghost/fingerprint/enforcement/checks.yml
rename to packages/ghost-ui/.ghost/fingerprint/checks.yml
diff --git a/packages/ghost-ui/.ghost/fingerprint/inventory.yml b/packages/ghost-ui/.ghost/fingerprint/inventory.yml
index b1e76982..348962cd 100644
--- a/packages/ghost-ui/.ghost/fingerprint/inventory.yml
+++ b/packages/ghost-ui/.ghost/fingerprint/inventory.yml
@@ -30,8 +30,4 @@ building_blocks:
notes:
- Ghost UI is registry-distributed implementation inventory, not an installable npm package or consuming-product experience memory.
exemplars: []
-sources:
- - id: generated-inventory
- kind: cache
- ref: sources/cache/inventory.json
- note: Optional refreshable registry observations.
+sources: []
diff --git a/packages/ghost-ui/.ghost/fingerprint/sources/cache/.gitkeep b/packages/ghost-ui/.ghost/fingerprint/sources/cache/.gitkeep
deleted file mode 100644
index 8b137891..00000000
--- a/packages/ghost-ui/.ghost/fingerprint/sources/cache/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/packages/ghost/README.md b/packages/ghost/README.md
index dc18f4ba..52d2c56f 100644
--- a/packages/ghost/README.md
+++ b/packages/ghost/README.md
@@ -37,7 +37,7 @@ command index.
Create and validate the fingerprint package:
```bash
-ghost init --with-intent
+ghost init
ghost scan --format json
ghost lint .ghost
ghost verify .ghost --root .
@@ -53,7 +53,7 @@ Govern changes afterward:
```bash
ghost check --base main
-ghost review --base main --include-memory
+ghost review --base main
```
Install the BYOA skill bundle so your host agent can author, brief, review,
@@ -63,7 +63,7 @@ verify, remediate, and update fingerprints:
ghost skill install
```
-Advanced commands such as `inventory`, `stack`, `compare`, `ack`, `track`, and
+Advanced commands such as `signals`, `stack`, `compare`, `ack`, `track`, and
`diverge` remain available in the full command index.
Zero config for every verb. No API key is required. `OPENAI_API_KEY` /
@@ -85,12 +85,13 @@ import {
## BYOA
-Ghost is bring-your-own-agent. The CLI performs deterministic work: inventory,
-readiness reporting, linting, verification, comparison, checks, and advisory
-review packet generation. The installed `ghost` skill teaches a host agent how to
-capture canonical `.ghost/fingerprint/` surface-composition context, brief and
-generate work from it, review changes against it, verify generated UI,
-remediate issues, and suggest fingerprint edits when the user asks.
+Ghost is bring-your-own-agent. The CLI performs deterministic work: repo
+signals, readiness reporting, linting, verification, comparison, checks, and
+advisory review packet generation. The installed `ghost` skill teaches a host
+agent how to capture canonical `.ghost/fingerprint/` surface-composition
+context, brief and generate work from it, review changes against it, verify
+generated UI, remediate issues, and suggest fingerprint edits when the user
+asks.
```text
Set up the Ghost fingerprint for this repo.
diff --git a/packages/ghost/src/cli.ts b/packages/ghost/src/cli.ts
index 32246e74..f24daf51 100644
--- a/packages/ghost/src/cli.ts
+++ b/packages/ghost/src/cli.ts
@@ -233,10 +233,6 @@ export function buildCli(): ReturnType {
"--memory-dir ",
"Relative fingerprint package directory for host wrappers and stack discovery (env: GHOST_MEMORY_DIR; default: .ghost)",
)
- .option(
- "--include-memory",
- "Include accepted decisions as accepted_decisions in the advisory packet (flag name retained).",
- )
.option(
"--max-diff-bytes ",
"Maximum diff bytes to include in the review packet (default: 200000)",
@@ -266,7 +262,6 @@ export function buildCli(): ReturnType {
memoryDir:
typeof opts.memoryDir === "string" ? opts.memoryDir : undefined,
diffText,
- includeAcceptedDecisions: Boolean(opts.includeMemory),
maxDiffBytes,
});
if (opts.format === "json") {
diff --git a/packages/ghost/src/command-discovery.ts b/packages/ghost/src/command-discovery.ts
index 9345760d..b1fe396f 100644
--- a/packages/ghost/src/command-discovery.ts
+++ b/packages/ghost/src/command-discovery.ts
@@ -27,7 +27,7 @@ const GROUPS: ReadonlyArray<{
{ group: "core", title: "Core workflow" },
{ group: "advanced", title: "Advanced/package inspection" },
{ group: "compare", title: "Compare/stance" },
- { group: "maintenance", title: "Maintenance/cache" },
+ { group: "maintenance", title: "Maintenance/legacy" },
];
const COMMAND_DISCOVERY = [
@@ -102,18 +102,18 @@ const COMMAND_DISCOVERY = [
summary: "Inspect a nested fingerprint stack for repo paths.",
},
{
- name: "inventory",
+ name: "signals",
group: "advanced",
defaultHelp: false,
- compactName: "inventory",
- summary: "Emit raw repo signals for optional cache material.",
+ compactName: "signals",
+ summary: "Emit raw repo signals for fingerprint authoring.",
},
{
name: "describe",
group: "advanced",
defaultHelp: false,
compactName: "describe",
- summary: "Print intent or markdown section ranges.",
+ summary: "Print markdown section ranges.",
},
{
name: "compare",
@@ -162,7 +162,7 @@ const COMMAND_DISCOVERY = [
group: "maintenance",
defaultHelp: false,
compactName: "survey",
- summary: "Run survey/cache helpers.",
+ summary: "Run legacy survey helpers.",
},
] satisfies ReadonlyArray>;
diff --git a/packages/ghost/src/context/entrypoint-markdown.ts b/packages/ghost/src/context/entrypoint-markdown.ts
index df528297..809d3be4 100644
--- a/packages/ghost/src/context/entrypoint-markdown.ts
+++ b/packages/ghost/src/context/entrypoint-markdown.ts
@@ -1,5 +1,4 @@
import type { ContextEntrypoint, FingerprintGraphNode } from "./entrypoint.js";
-import type { PackageInventory } from "./package-context.js";
export function formatContextEntrypointMarkdown(
entrypoint: ContextEntrypoint,
@@ -17,7 +16,6 @@ export function formatContextEntrypointMarkdown(
parts.push(formatActionContract(entrypoint));
parts.push(formatReadFirst(entrypoint));
parts.push(formatValidation(entrypoint));
- parts.push(formatGeneratedCache(entrypoint.generatedCache));
parts.push(formatSuggestedReads(entrypoint));
parts.push(formatOmissions(entrypoint));
parts.push(formatUseThisContext());
@@ -103,42 +101,6 @@ function formatValidation(entrypoint: ContextEntrypoint): string {
return lines.join("\n");
}
-function formatGeneratedCache(inventory: PackageInventory): string {
- const lines = ["## Generated Cache"];
- lines.push(
- "- Generated cache is optional source material, not canonical fingerprint context.",
- );
- if (inventory.state === "missing") {
- lines.push("- State: missing.");
- return lines.join("\n");
- }
- if (inventory.state === "unreadable") {
- lines.push(
- `- State: present at \`${inventory.path}\`, but it could not be read: ${inventory.error}.`,
- );
- return lines.join("\n");
- }
- lines.push(`- State: present at \`${inventory.path}\`.`);
- pushJoined(lines, "Platform hints", inventory.summary.platform_hints, {
- code: true,
- });
- pushJoined(lines, "Build hints", inventory.summary.build_system_hints, {
- code: true,
- });
- pushJoined(lines, "Package manifests", inventory.summary.package_manifests, {
- code: true,
- });
- pushJoined(
- lines,
- "Config candidates",
- inventory.summary.candidate_config_files,
- {
- code: true,
- },
- );
- return lines.join("\n");
-}
-
function formatSuggestedReads(entrypoint: ContextEntrypoint): string {
const lines = ["## Suggested Reads"];
for (const read of entrypoint.suggestedReads) {
diff --git a/packages/ghost/src/context/entrypoint.ts b/packages/ghost/src/context/entrypoint.ts
index cccd58e4..3086a066 100644
--- a/packages/ghost/src/context/entrypoint.ts
+++ b/packages/ghost/src/context/entrypoint.ts
@@ -11,7 +11,7 @@ import {
sortNodes,
unique,
} from "./graph.js";
-import type { PackageContext, PackageInventory } from "./package-context.js";
+import type { PackageContext } from "./package-context.js";
export type {
FingerprintGraph,
@@ -53,7 +53,6 @@ export interface ContextEntrypoint {
};
suggestedReads: Array<{ path: string; reason: string }>;
omissions: Array<{ label: string; omitted: number; source: string }>;
- generatedCache: PackageInventory;
}
export interface BuildContextEntrypointOptions {
@@ -123,7 +122,6 @@ export function buildContextEntrypoint(
selected,
suggestedReads,
omissions: buildOmissions(graph, selected),
- generatedCache: context.inventory,
};
}
@@ -256,7 +254,7 @@ function expandOneHop(
}
function buildSuggestedReads(
- context: PackageContext,
+ _context: PackageContext,
selected: ContextEntrypoint["selected"],
): ContextEntrypoint["suggestedReads"] {
const reads = new Map();
@@ -280,7 +278,7 @@ function buildSuggestedReads(
}
if (selected.checks.length > 0) {
reads.set(
- "fingerprint/enforcement/checks.yml",
+ "fingerprint/checks.yml",
"active deterministic validation rules",
);
}
@@ -288,12 +286,6 @@ function buildSuggestedReads(
const path = exemplar.appliesTo.paths[0];
if (path) reads.set(path, `source surface for ${exemplar.ref}`);
}
- if (context.intent?.trim()) {
- reads.set(
- "fingerprint/memory/intent.md",
- "supplemental human-authored intent",
- );
- }
if (reads.size === 0) {
reads.set("fingerprint/prose.yml", "global fingerprint intent");
reads.set("fingerprint/inventory.yml", "topology and exemplars");
@@ -377,7 +369,7 @@ function buildOmissions(
{
label: "Active checks",
omitted: Math.max(0, totals.checks - selected.checks.length),
- source: "fingerprint/enforcement/checks.yml",
+ source: "fingerprint/checks.yml",
},
];
}
diff --git a/packages/ghost/src/context/graph.ts b/packages/ghost/src/context/graph.ts
index 5e603450..7acc453e 100644
--- a/packages/ghost/src/context/graph.ts
+++ b/packages/ghost/src/context/graph.ts
@@ -205,7 +205,7 @@ export function buildFingerprintGraph(
check.repair ? `Repair: ${check.repair}` : "",
detectorSummary(check),
].filter(Boolean),
- sourceFile: "fingerprint/enforcement/checks.yml",
+ sourceFile: "fingerprint/checks.yml",
appliesTo: applicabilityFromCheck(check),
});
addRefEdges(ref, check.derivation?.prose, "check prose derivation");
diff --git a/packages/ghost/src/context/package-context.ts b/packages/ghost/src/context/package-context.ts
index 9ed71826..a4f9709c 100644
--- a/packages/ghost/src/context/package-context.ts
+++ b/packages/ghost/src/context/package-context.ts
@@ -1,4 +1,3 @@
-import { join } from "node:path";
import { parse as parseYaml } from "yaml";
import {
type GhostChecksDocument,
@@ -12,26 +11,6 @@ import {
loadFingerprintPackage,
} from "../scan/fingerprint-package.js";
-export interface PackageInventorySummary {
- root?: string;
- platform_hints: string[];
- build_system_hints: string[];
- language_histogram: Array<{ name: string; files: number }>;
- package_manifests: string[];
- candidate_config_files: string[];
- registry_files: string[];
- top_level_tree: Array<{ path: string; kind: string; child_count: number }>;
- config?: {
- targets?: Array<{ id: string; platform?: string; roots?: string[] }>;
- libraries?: Array<{ id: string; role?: string; source?: string }>;
- };
-}
-
-export type PackageInventory =
- | { state: "missing"; path: string }
- | { state: "present"; path: string; summary: PackageInventorySummary }
- | { state: "unreadable"; path: string; error: string };
-
export interface PackageContext {
name: string;
fingerprintDir?: string;
@@ -47,19 +26,15 @@ export interface PackageContext {
};
checks?: GhostChecksDocument;
checksRaw?: string;
- intent?: string;
- inventory: PackageInventory;
}
export async function loadPackageContext(
paths: FingerprintPackagePaths,
nameOverride?: string,
): Promise {
- const [loaded, checksRaw, intent, inventory] = await Promise.all([
+ const [loaded, checksRaw] = await Promise.all([
loadFingerprintPackage(paths),
readOptional(paths.checks),
- readOptional(paths.intent),
- loadPackageInventory(paths),
]);
const fingerprint = loaded.fingerprint;
@@ -75,39 +50,20 @@ export async function loadPackageContext(
},
checks,
checksRaw,
- intent,
- inventory,
};
}
-export async function loadPackageInventory(
- paths: Pick,
-): Promise {
- const path = join(paths.cache, "inventory.json");
- const raw = await readOptional(path);
- if (!raw) return { state: "missing", path };
- try {
- return { state: "present", path, summary: summarizeInventory(raw) };
- } catch (err) {
- return {
- state: "unreadable",
- path,
- error: err instanceof Error ? err.message : String(err),
- };
- }
-}
-
function parseChecks(
raw: string,
fingerprint: GhostFingerprintDocument,
): GhostChecksDocument {
- const parsed = parseYamlSafe(raw, "fingerprint/enforcement/checks.yml");
+ const parsed = parseYamlSafe(raw, "fingerprint/checks.yml");
const report = lintGhostChecks(parsed, { fingerprint });
if (report.errors > 0) {
const first = report.issues.find((issue) => issue.severity === "error");
const suffix = first?.path ? ` @ ${first.path}` : "";
throw new Error(
- `fingerprint/enforcement/checks.yml failed lint with ${report.errors} error(s): ${
+ `fingerprint/checks.yml failed lint with ${report.errors} error(s): ${
first?.message ?? "invalid checks"
}${suffix}`,
);
@@ -115,9 +71,7 @@ function parseChecks(
const result = GhostChecksSchema.safeParse(parsed);
if (!result.success) {
- throw new Error(
- "fingerprint/enforcement/checks.yml failed schema validation.",
- );
+ throw new Error("fingerprint/checks.yml failed schema validation.");
}
return result.data as GhostChecksDocument;
}
@@ -134,81 +88,6 @@ function parseYamlSafe(raw: string, label: string): unknown {
}
}
-function summarizeInventory(raw: string): PackageInventorySummary {
- const parsed = JSON.parse(raw) as Record;
- return {
- root: typeof parsed.root === "string" ? parsed.root : undefined,
- platform_hints: stringArray(parsed.platform_hints).slice(0, 8),
- build_system_hints: stringArray(parsed.build_system_hints).slice(0, 8),
- language_histogram: recordArray(parsed.language_histogram)
- .map((entry) => ({
- name: typeof entry.name === "string" ? entry.name : "",
- files: typeof entry.files === "number" ? entry.files : 0,
- }))
- .filter((entry) => entry.name)
- .slice(0, 8),
- package_manifests: stringArray(parsed.package_manifests).slice(0, 12),
- candidate_config_files: stringArray(parsed.candidate_config_files).slice(
- 0,
- 12,
- ),
- registry_files: stringArray(parsed.registry_files).slice(0, 8),
- top_level_tree: recordArray(parsed.top_level_tree)
- .map((entry) => ({
- path: typeof entry.path === "string" ? entry.path : "",
- kind: typeof entry.kind === "string" ? entry.kind : "",
- child_count:
- typeof entry.child_count === "number" ? entry.child_count : 0,
- }))
- .filter((entry) => entry.path && entry.kind)
- .slice(0, 12),
- config: summarizeInventoryConfig(parsed.config),
- };
-}
-
-function summarizeInventoryConfig(
- value: unknown,
-): PackageInventorySummary["config"] {
- if (!value || typeof value !== "object" || Array.isArray(value)) {
- return undefined;
- }
- const record = value as Record;
- const targets = recordArray(record.targets)
- .map((entry) => ({
- id: typeof entry.id === "string" ? entry.id : "",
- ...(typeof entry.platform === "string"
- ? { platform: entry.platform }
- : {}),
- roots: stringArray(entry.roots),
- }))
- .filter((entry) => entry.id)
- .slice(0, 8);
- const libraries = recordArray(record.libraries)
- .map((entry) => ({
- id: typeof entry.id === "string" ? entry.id : "",
- ...(typeof entry.role === "string" ? { role: entry.role } : {}),
- ...(typeof entry.source === "string" ? { source: entry.source } : {}),
- }))
- .filter((entry) => entry.id)
- .slice(0, 8);
- if (targets.length === 0 && libraries.length === 0) return undefined;
- return { targets, libraries };
-}
-
-function stringArray(value: unknown): string[] {
- return Array.isArray(value)
- ? value.filter((entry): entry is string => typeof entry === "string")
- : [];
-}
-
-function recordArray(value: unknown): Array> {
- return Array.isArray(value)
- ? value.filter((entry): entry is Record =>
- Boolean(entry && typeof entry === "object" && !Array.isArray(entry)),
- )
- : [];
-}
-
const readOptional = readOptionalUtf8;
function inferPackageName(fingerprint: GhostFingerprintDocument): string {
diff --git a/packages/ghost/src/context/package-review-command.ts b/packages/ghost/src/context/package-review-command.ts
index 08825745..28208d3e 100644
--- a/packages/ghost/src/context/package-review-command.ts
+++ b/packages/ghost/src/context/package-review-command.ts
@@ -15,7 +15,7 @@ export interface EmitPackageReviewInput {
const REVIEW_FINDING_CATEGORIES = [
"fix",
"intentional-divergence",
- "missing-memory",
+ "missing-fingerprint",
"experience-gap",
"eval-uncertainty",
] as const;
@@ -25,7 +25,7 @@ const REVIEW_FINDING_CATEGORIES = [
*
* The command stays intentionally light: it tells the host agent which Ghost
* files and CLI packets to use, then includes a compact fingerprint index.
- * Full canonical truth remains in fingerprint/ core files and enforcement checks.
+ * Full canonical truth remains in fingerprint/ core files and deterministic checks.
*/
export function emitPackageReviewCommand(
input: EmitPackageReviewInput,
@@ -74,7 +74,7 @@ function packageWorkflowSection(context: PackageContext): string {
4. Inspect relevant inventory exemplars as concrete anchors for what good looks like.
5. Use inventory building blocks only as replaceable material that may help satisfy the selected prose and composition.
6. Run \`ghost check${memoryDirFlag}\` when a diff is available. Active checks are deterministic and can block.
-7. Run \`ghost review${memoryDirFlag}\` for the advisory packet when you need full diff context and fingerprint excerpts; add \`--include-memory\` only when accepted decisions matter.
+7. Run \`ghost review${memoryDirFlag}\` for the advisory packet when you need full diff context and fingerprint excerpts.
8. Cite the diff location, fingerprint core layer refs, relevant exemplars when useful, and any active check when a finding blocks.`;
}
@@ -87,9 +87,9 @@ Only findings backed by an active check should be treated as blocking. Everythin
Review only what fingerprint layers or active checks make relevant to the product surface.
-When fingerprint layers are silent, local evidence can still support advisory critique. Label those findings as provisional and non-Ghost-backed, and ground them in nearby product surfaces, local components, token or copy conventions, or optional rationale files when present. Ask the human before assessing high-risk, irreversible, privacy/security/legal, or product-surface-defining choices.
+When fingerprint layers are silent, local evidence can still support advisory critique. Label those findings as provisional and non-Ghost-backed, and ground them in nearby product surfaces, local components, or token and copy conventions. Ask the human before assessing high-risk, irreversible, privacy/security/legal, or product-surface-defining choices.
-If the diff reveals missing fingerprint grounding or layer coverage, report \`missing-memory\` or \`experience-gap\` as a review finding. Do not silently rewrite the Ghost package during review; fingerprint edits are ordinary edits that go through normal Git review.`;
+If the diff reveals missing fingerprint grounding or layer coverage, report \`missing-fingerprint\` or \`experience-gap\` as a review finding. Do not silently rewrite the Ghost package during review; fingerprint edits are ordinary edits that go through normal Git review.`;
}
function packageFingerprintIndex(context: PackageContext): string {
@@ -150,7 +150,7 @@ function formatSummary(context: PackageContext): string {
function formatSituations(situations: GhostFingerprintSituation[]): string {
if (situations.length === 0) {
- return "### Situations\n- No situations recorded yet. Treat unclear obligations as `missing-memory`.";
+ return "### Situations\n- No situations recorded yet. Treat unclear obligations as `missing-fingerprint`.";
}
const lines = ["### Situations"];
for (const situation of situations.slice(0, 8)) {
@@ -252,7 +252,7 @@ function packageChecksSection(activeChecks: GhostCheck[]): string {
if (activeChecks.length === 0) {
return `## Active Checks
-No active checks are recorded. Review remains advisory unless \`fingerprint/enforcement/checks.yml\` adds deterministic active checks.`;
+No active checks are recorded. Review remains advisory unless \`fingerprint/checks.yml\` adds deterministic active checks.`;
}
const lines = ["## Active Checks", ""];
for (const check of activeChecks.slice(0, 12)) {
@@ -271,7 +271,7 @@ No active checks are recorded. Review remains advisory unless \`fingerprint/enfo
}
if (activeChecks.length > 12) {
lines.push(
- `- ${activeChecks.length - 12} more active check(s); read \`fingerprint/enforcement/checks.yml\` before deciding whether a finding blocks.`,
+ `- ${activeChecks.length - 12} more active check(s); read \`fingerprint/checks.yml\` before deciding whether a finding blocks.`,
);
}
return lines.join("\n");
@@ -281,7 +281,7 @@ function packageReviewFooter(context: PackageContext): string {
const fingerprintDir = context.fingerprintDir ?? ".ghost";
return `---
-Generated from \`${fingerprintDir}/fingerprint/\` for ${context.name}. Re-run \`ghost emit review-command${stackFingerprintDirFlag(context)}\` after updating fingerprint core layers, enforcement checks, or optional rationale files.`;
+Generated from \`${fingerprintDir}/fingerprint/\` for ${context.name}. Re-run \`ghost emit review-command${stackFingerprintDirFlag(context)}\` after updating fingerprint core layers or deterministic checks.`;
}
function stackFingerprintDirFlag(context: PackageContext): string {
diff --git a/packages/ghost/src/fingerprint-commands.ts b/packages/ghost/src/fingerprint-commands.ts
index 7bde37a0..92de9442 100644
--- a/packages/ghost/src/fingerprint-commands.ts
+++ b/packages/ghost/src/fingerprint-commands.ts
@@ -1,5 +1,5 @@
import { readFile, stat, writeFile } from "node:fs/promises";
-import { dirname, resolve } from "node:path";
+import { basename, dirname, resolve } from "node:path";
import type { CAC } from "cac";
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
import {
@@ -34,11 +34,12 @@ import { registerInitCommand } from "./init-command.js";
import { detectFileKind, lintDetectedFileKind } from "./scan/file-kind.js";
import {
discoverGhostPackages,
+ FINGERPRINT_DIRNAME,
fingerprintPackageDisplayPath,
- inventory,
normalizeMemoryDir,
resolveMemoryDirDefault,
scanStatus,
+ signals,
} from "./scan/index.js";
import { registerEmitCommand } from "./scan-emit-command.js";
import { registerStackCommand } from "./scan-stack-command.js";
@@ -48,7 +49,7 @@ import { registerStackCommand } from "./scan-stack-command.js";
*
* Verbs author and validate the root `.ghost/` fingerprint package:
* `lint` (schema check, auto-detects file kind), `verify` (cross-artifact
- * fidelity), `describe` (section ranges + token estimates for intent or direct
+ * fidelity), `describe` (section ranges + token estimates for direct
* fingerprint markdown), `diff` (structural prose-level diff between direct
* fingerprint files), `emit` (derive review-command artifacts), and `survey`
* operations for deterministic `ghost.survey/v1`
@@ -241,14 +242,9 @@ export function registerFingerprintCommands(cli: CAC): void {
` config (config.yml): ${fmt(status.config.state)}\n`,
);
process.stdout.write(
- ` checks (fingerprint/enforcement/checks.yml): ${fmt(status.checks.state)}\n`,
- );
- process.stdout.write(
- ` cache (fingerprint/sources/cache/): ${fmt(status.cache.state)}\n`,
- );
- process.stdout.write(
- ` intent (fingerprint/memory/intent.md): ${fmt(status.intent.state)}\n\n`,
+ ` checks (fingerprint/checks.yml): ${fmt(status.checks.state)}\n`,
);
+ process.stdout.write("\n");
if (status.recommended_next) {
process.stdout.write(
`next: run the ${status.recommended_next} stage\n`,
@@ -332,16 +328,16 @@ export function registerFingerprintCommands(cli: CAC): void {
registerStackCommand(cli);
- // --- inventory ---
+ // --- signals ---
cli
.command(
- "inventory [path]",
- "Emit deterministic raw signals about a frontend repo as JSON for optional cache/source material: package manifests, language histogram, candidate config files, registry presence, top-level tree, and git remote.",
+ "signals [path]",
+ "Emit deterministic raw repo signals as JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, and git remote.",
)
.action(async (path: string | undefined) => {
try {
const target = resolve(process.cwd(), path ?? ".");
- const out = inventory(target);
+ const out = signals(target);
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
process.exit(0);
} catch (err) {
@@ -355,15 +351,13 @@ export function registerFingerprintCommands(cli: CAC): void {
// --- describe ---
cli
.command(
- "describe [fingerprint]",
- "Print a section map of fingerprint/memory/intent.md or a markdown file (line ranges + token estimates).",
+ "describe ",
+ "Print a section map of a markdown file (line ranges + token estimates).",
)
.option("--format ", "Output format: cli or json", { default: "cli" })
- .action(async (path: string | undefined, opts) => {
+ .action(async (path: string, opts) => {
try {
- const target = path
- ? resolve(process.cwd(), path)
- : resolveFingerprintPackage(undefined, process.cwd()).intent;
+ const target = resolve(process.cwd(), path);
const raw = await readFile(target, "utf-8");
const layout = layoutFingerprint(raw);
if (opts.format === "json") {
@@ -607,7 +601,6 @@ async function nestedPackageStatus(
...pkg,
fingerprint: status.fingerprint,
checks: status.checks,
- intent: status.intent,
readiness: status.readiness,
};
}),
@@ -621,7 +614,6 @@ interface NestedPackageStatus {
fingerprint_dir: string;
fingerprint: Awaited>["fingerprint"];
checks: Awaited>["checks"];
- intent: Awaited>["intent"];
readiness: Awaited>["readiness"];
}
@@ -644,7 +636,10 @@ async function loadSiblingFingerprintForChecksLint(
fileTarget: string,
): Promise {
const checksDir = dirname(fileTarget);
- const packageRoot = resolve(checksDir, "..", "..");
+ const packageRoot =
+ basename(checksDir) === FINGERPRINT_DIRNAME
+ ? resolve(checksDir, "..")
+ : resolve(checksDir, "..", "..");
try {
return (
await loadFingerprintPackage(resolveFingerprintPackage(packageRoot))
@@ -794,7 +789,7 @@ function summarizeSurveyPatterns(survey: Survey): GhostPatternsDocument {
traits: traitsForPattern(entry.value, survey),
evidence: entry.evidence,
advisory: [
- "Use as advisory composition evidence; deterministic enforcement belongs in fingerprint/enforcement/checks.yml.",
+ "Use as advisory composition evidence; deterministic checks belong in fingerprint/checks.yml.",
],
})),
advisory: {
@@ -808,7 +803,7 @@ function surveyPatternReviewExpectations(survey: Survey): string[] {
return [
"No UI surface evidence is present; do not infer product composition patterns from values, tokens, or components alone.",
"Use survey values, tokens, and components as implementation vocabulary until implemented product surfaces are observed.",
- "Treat fingerprint/memory/intent.md as human authority when present.",
+ "Treat fingerprint/prose.yml, fingerprint/inventory.yml, and fingerprint/composition.yml as the canonical authoring layers.",
];
}
@@ -819,14 +814,14 @@ function surveyPatternReviewExpectations(survey: Survey): string[] {
return [
"Treat story, fixture, and doc-example rows as component demonstration evidence, not product composition authority.",
"Cite matching composition_patterns[].evidence and survey.ui_surfaces evidence for advisory findings.",
- "Treat fingerprint/memory/intent.md as human authority when present.",
+ "Treat fingerprint/prose.yml, fingerprint/inventory.yml, and fingerprint/composition.yml as the canonical authoring layers.",
];
}
return [
"Identify the surface type before assessing composition.",
"Cite matching composition_patterns[].evidence and survey.ui_surfaces evidence for advisory findings.",
- "Treat fingerprint/memory/intent.md as human authority when present.",
+ "Treat fingerprint/prose.yml, fingerprint/inventory.yml, and fingerprint/composition.yml as the canonical authoring layers.",
];
}
diff --git a/packages/ghost/src/fingerprint.ts b/packages/ghost/src/fingerprint.ts
index 1bc83d2b..6c26a2f2 100644
--- a/packages/ghost/src/fingerprint.ts
+++ b/packages/ghost/src/fingerprint.ts
@@ -5,22 +5,17 @@ export { parseBody } from "./scan/body.js";
export type { DesignDecision } from "./scan/compose.js";
export { mergeFingerprint } from "./scan/compose.js";
export {
- CACHE_DIRNAME,
CHECKS_FILENAME,
CONFIG_FILENAME,
FINGERPRINT_COMPOSITION_FILENAME,
FINGERPRINT_DIRNAME,
- FINGERPRINT_ENFORCEMENT_DIRNAME,
FINGERPRINT_FILENAME,
FINGERPRINT_INVENTORY_FILENAME,
FINGERPRINT_MANIFEST_FILENAME,
- FINGERPRINT_MEMORY_DIRNAME,
FINGERPRINT_PACKAGE_DIR,
FINGERPRINT_PROSE_FILENAME,
- FINGERPRINT_SOURCES_DIRNAME,
FINGERPRINT_YML_FILENAME,
FINGERPRINTS_DIRNAME,
- INTENT_FILENAME,
PATTERNS_FILENAME,
RESOURCES_FILENAME,
SCOPE_SURVEYS_DIRNAME,
diff --git a/packages/ghost/src/ghost-core/fingerprint-package.ts b/packages/ghost/src/ghost-core/fingerprint-package.ts
index 61140f8a..57a60e2b 100644
--- a/packages/ghost/src/ghost-core/fingerprint-package.ts
+++ b/packages/ghost/src/ghost-core/fingerprint-package.ts
@@ -7,14 +7,8 @@ export const FINGERPRINT_MANIFEST_FILENAME = "manifest.yml" as const;
export const FINGERPRINT_PROSE_FILENAME = "prose.yml" as const;
export const FINGERPRINT_INVENTORY_FILENAME = "inventory.yml" as const;
export const FINGERPRINT_COMPOSITION_FILENAME = "composition.yml" as const;
-export const FINGERPRINT_ENFORCEMENT_DIRNAME = "enforcement" as const;
-export const FINGERPRINT_MEMORY_DIRNAME = "memory" as const;
-export const FINGERPRINT_SOURCES_DIRNAME = "sources" as const;
export const CONFIG_FILENAME = "config.yml" as const;
-export const INTENT_FILENAME = "intent.md" as const;
export const FINGERPRINT_FILENAME = "fingerprint.md" as const;
-export const DECISIONS_DIRNAME = "decisions" as const;
-export const CACHE_DIRNAME = "cache" as const;
export interface FingerprintPackagePaths {
dir: string;
@@ -23,7 +17,6 @@ export interface FingerprintPackagePaths {
prose: string;
inventory: string;
composition: string;
- enforcement: string;
fingerprintYml: string;
config: string;
resources: string;
@@ -33,9 +26,4 @@ export interface FingerprintPackagePaths {
/** Legacy direct markdown path; not part of the canonical root bundle. */
fingerprint: string;
checks: string;
- memory: string;
- intent: string;
- decisions: string;
- sources: string;
- cache: string;
}
diff --git a/packages/ghost/src/ghost-core/fingerprint/schema.ts b/packages/ghost/src/ghost-core/fingerprint/schema.ts
index 3a17c5e9..d57744c1 100644
--- a/packages/ghost/src/ghost-core/fingerprint/schema.ts
+++ b/packages/ghost/src/ghost-core/fingerprint/schema.ts
@@ -175,7 +175,6 @@ export const GhostFingerprintInventoryBuildingBlocksSchema = z
.strict();
export const GhostFingerprintInventorySourceKindSchema = z.enum([
- "cache",
"registry",
"file",
"url",
diff --git a/packages/ghost/src/ghost-core/fingerprint/types.ts b/packages/ghost/src/ghost-core/fingerprint/types.ts
index e29648f3..a45c46c5 100644
--- a/packages/ghost/src/ghost-core/fingerprint/types.ts
+++ b/packages/ghost/src/ghost-core/fingerprint/types.ts
@@ -77,7 +77,6 @@ export interface GhostFingerprintInventoryBuildingBlocks {
}
export type GhostFingerprintInventorySourceKind =
- | "cache"
| "registry"
| "file"
| "url"
diff --git a/packages/ghost/src/ghost-core/index.ts b/packages/ghost/src/ghost-core/index.ts
index 8c7435d3..e7b7dd78 100644
--- a/packages/ghost/src/ghost-core/index.ts
+++ b/packages/ghost/src/ghost-core/index.ts
@@ -115,22 +115,16 @@ export {
} from "./fingerprint/index.js";
// --- Map (ghost.map/v1) ---
export {
- CACHE_DIRNAME,
CONFIG_FILENAME,
- DECISIONS_DIRNAME,
FINGERPRINT_COMPOSITION_FILENAME,
FINGERPRINT_DIRNAME,
- FINGERPRINT_ENFORCEMENT_DIRNAME,
FINGERPRINT_FILENAME,
FINGERPRINT_INVENTORY_FILENAME,
FINGERPRINT_MANIFEST_FILENAME,
- FINGERPRINT_MEMORY_DIRNAME,
FINGERPRINT_PACKAGE_DIR,
FINGERPRINT_PROSE_FILENAME,
- FINGERPRINT_SOURCES_DIRNAME,
FINGERPRINT_YML_FILENAME,
type FingerprintPackagePaths,
- INTENT_FILENAME,
PATTERNS_FILENAME,
RESOURCES_FILENAME,
} from "./fingerprint-package.js";
@@ -152,24 +146,6 @@ export {
slugifyScopeId,
type TopLevelEntry,
} from "./map/index.js";
-// --- Memory (ghost.decision/v1) ---
-export type {
- GhostDecisionDocument,
- GhostDecisionLintIssue,
- GhostDecisionLintReport,
- GhostDecisionLintSeverity,
- GhostDecisionStatus,
- GhostExperienceEvidence,
- GhostExperienceScope,
-} from "./memory/index.js";
-export {
- GHOST_DECISION_SCHEMA,
- GHOST_DECISIONS_DIRNAME,
- GhostDecisionSchema,
- GhostExperienceEvidenceSchema,
- GhostExperienceScopeSchema,
- lintGhostDecision,
-} from "./memory/index.js";
// --- Patterns (ghost.patterns/v1) ---
export type {
GhostCompositionAnatomy,
diff --git a/packages/ghost/src/ghost-core/memory/index.ts b/packages/ghost/src/ghost-core/memory/index.ts
deleted file mode 100644
index 663a16ea..00000000
--- a/packages/ghost/src/ghost-core/memory/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export { lintGhostDecision } from "./lint.js";
-export {
- GhostDecisionSchema,
- GhostExperienceEvidenceSchema,
- GhostExperienceScopeSchema,
-} from "./schema.js";
-export type {
- GhostDecisionDocument,
- GhostDecisionLintIssue,
- GhostDecisionLintReport,
- GhostDecisionLintSeverity,
- GhostDecisionStatus,
- GhostExperienceEvidence,
- GhostExperienceScope,
-} from "./types.js";
-export { GHOST_DECISION_SCHEMA, GHOST_DECISIONS_DIRNAME } from "./types.js";
diff --git a/packages/ghost/src/ghost-core/memory/lint.ts b/packages/ghost/src/ghost-core/memory/lint.ts
deleted file mode 100644
index 8ea25fec..00000000
--- a/packages/ghost/src/ghost-core/memory/lint.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { ZodIssue } from "zod";
-import { GhostDecisionSchema } from "./schema.js";
-import type {
- GhostDecisionLintIssue,
- GhostDecisionLintReport,
-} from "./types.js";
-
-export function lintGhostDecision(input: unknown): GhostDecisionLintReport {
- const result = GhostDecisionSchema.safeParse(input);
- if (!result.success) return finalize(zodIssues(result.error.issues));
- return finalize([]);
-}
-
-function zodIssues(issues: ZodIssue[]): GhostDecisionLintIssue[] {
- return issues.map((issue) => ({
- severity: "error" as const,
- rule: `schema/${issue.code}`,
- message: issue.message,
- path: formatZodPath(issue.path),
- }));
-}
-
-function formatZodPath(path: ZodIssue["path"]): string | undefined {
- if (path.length === 0) return undefined;
- return path.reduce((formatted, segment) => {
- if (typeof segment === "number") return `${formatted}[${segment}]`;
- const key = String(segment);
- return formatted ? `${formatted}.${key}` : key;
- }, "");
-}
-
-function finalize(issues: GhostDecisionLintIssue[]): GhostDecisionLintReport {
- return {
- issues,
- errors: issues.filter((issue) => issue.severity === "error").length,
- warnings: issues.filter((issue) => issue.severity === "warning").length,
- info: issues.filter((issue) => issue.severity === "info").length,
- };
-}
diff --git a/packages/ghost/src/ghost-core/memory/schema.ts b/packages/ghost/src/ghost-core/memory/schema.ts
deleted file mode 100644
index 30330331..00000000
--- a/packages/ghost/src/ghost-core/memory/schema.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { z } from "zod";
-import { GHOST_DECISION_SCHEMA } from "./types.js";
-
-const SlugIdSchema = z
- .string()
- .min(1)
- .regex(/^[a-z0-9][a-z0-9._-]*$/, {
- message:
- "id must be a slug (lowercase alphanumeric plus . _ -, leading alphanumeric)",
- });
-
-const NonEmptyStringArraySchema = z.array(z.string().min(1)).min(1);
-
-export const GhostExperienceScopeSchema = z
- .object({
- roles: NonEmptyStringArraySchema.optional(),
- scopes: NonEmptyStringArraySchema.optional(),
- surface_types: NonEmptyStringArraySchema.optional(),
- pattern_ids: NonEmptyStringArraySchema.optional(),
- paths: NonEmptyStringArraySchema.optional(),
- })
- .strict();
-
-export const GhostExperienceEvidenceSchema = z
- .object({
- path: z.string().min(1).optional(),
- survey_surface_id: z.string().min(1).optional(),
- locator: z.string().min(1).optional(),
- note: z.string().min(1).optional(),
- })
- .strict()
- .refine(
- (evidence) =>
- Boolean(
- evidence.path ||
- evidence.survey_surface_id ||
- evidence.locator ||
- evidence.note,
- ),
- {
- message:
- "evidence must include at least one of path, survey_surface_id, locator, or note",
- },
- );
-
-export const GhostDecisionSchema = z
- .object({
- schema: z.literal(GHOST_DECISION_SCHEMA),
- id: SlugIdSchema,
- status: z.enum(["accepted", "rejected", "superseded"]),
- title: z.string().min(1),
- claim: z.string().min(1),
- rationale: z.string().min(1),
- scope: GhostExperienceScopeSchema.optional(),
- evidence: z.array(GhostExperienceEvidenceSchema).min(1),
- decided_at: z.string().datetime({ offset: true }),
- })
- .strict();
diff --git a/packages/ghost/src/ghost-core/memory/types.ts b/packages/ghost/src/ghost-core/memory/types.ts
deleted file mode 100644
index 005a67e0..00000000
--- a/packages/ghost/src/ghost-core/memory/types.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-export const GHOST_DECISION_SCHEMA = "ghost.decision/v1" as const;
-
-export const GHOST_DECISIONS_DIRNAME = "decisions" as const;
-
-export type GhostDecisionStatus = "accepted" | "rejected" | "superseded";
-
-export interface GhostExperienceScope {
- roles?: string[];
- scopes?: string[];
- surface_types?: string[];
- pattern_ids?: string[];
- paths?: string[];
-}
-
-export interface GhostExperienceEvidence {
- path?: string;
- survey_surface_id?: string;
- locator?: string;
- note?: string;
-}
-
-export interface GhostDecisionDocument {
- schema: typeof GHOST_DECISION_SCHEMA;
- id: string;
- status: GhostDecisionStatus;
- title: string;
- claim: string;
- rationale: string;
- scope?: GhostExperienceScope;
- evidence: GhostExperienceEvidence[];
- decided_at: string;
-}
-
-export type GhostDecisionLintSeverity = "error" | "warning" | "info";
-
-export interface GhostDecisionLintIssue {
- severity: GhostDecisionLintSeverity;
- rule: string;
- message: string;
- path?: string;
-}
-
-export interface GhostDecisionLintReport {
- issues: GhostDecisionLintIssue[];
- errors: number;
- warnings: number;
- info: number;
-}
diff --git a/packages/ghost/src/init-command.ts b/packages/ghost/src/init-command.ts
index 9e7db210..c25828cf 100644
--- a/packages/ghost/src/init-command.ts
+++ b/packages/ghost/src/init-command.ts
@@ -21,10 +21,6 @@ export function registerInitCommand(cli: CAC): void {
"--memory-dir ",
"Relative fingerprint package directory for host wrappers, init --scope, and default root init (env: GHOST_MEMORY_DIR; default: .ghost)",
)
- .option(
- "--with-intent",
- "Also create optional fingerprint/memory/intent.md for human-authored or human-approved intent",
- )
.option(
"--with-config",
"Also create optional config.yml for implementation roots and reference registries/libraries",
@@ -74,7 +70,6 @@ export function registerInitCommand(cli: CAC): void {
? memoryDirFromOpts(opts)
: undefined;
const initOptions = {
- withIntent: Boolean(opts.withIntent),
withConfig: Boolean(opts.withConfig || opts.reference),
reference:
typeof opts.reference === "string" ? opts.reference : undefined,
@@ -105,7 +100,6 @@ export function registerInitCommand(cli: CAC): void {
process.stdout.write(
`${JSON.stringify(
initCommandOutput(paths, {
- includeIntent: Boolean(opts.withIntent),
includeConfig: Boolean(opts.withConfig || opts.reference),
}),
null,
@@ -120,13 +114,10 @@ export function registerInitCommand(cli: CAC): void {
process.stdout.write(` prose.yml: ${paths.prose}\n`);
process.stdout.write(` inventory.yml: ${paths.inventory}\n`);
process.stdout.write(` composition.yml: ${paths.composition}\n`);
- process.stdout.write(` enforcement/checks.yml: ${paths.checks}\n`);
+ process.stdout.write(` checks.yml: ${paths.checks}\n`);
if (opts.withConfig || opts.reference) {
process.stdout.write(` config.yml: ${paths.config}\n`);
}
- if (opts.withIntent) {
- process.stdout.write(` memory/intent.md: ${paths.intent}\n`);
- }
}
process.exit(0);
} catch (err) {
@@ -144,7 +135,7 @@ function memoryDirFromOpts(opts: { memoryDir?: unknown }): string {
function initCommandOutput(
paths: ReturnType,
- options: { includeIntent: boolean; includeConfig: boolean },
+ options: { includeConfig: boolean },
): Record {
return {
dir: paths.dir,
@@ -155,6 +146,5 @@ function initCommandOutput(
composition: paths.composition,
...(options.includeConfig ? { config: paths.config } : {}),
checks: paths.checks,
- ...(options.includeIntent ? { intent: paths.intent } : {}),
};
}
diff --git a/packages/ghost/src/monorepo-init-command.ts b/packages/ghost/src/monorepo-init-command.ts
index eb35b1e8..76ca961c 100644
--- a/packages/ghost/src/monorepo-init-command.ts
+++ b/packages/ghost/src/monorepo-init-command.ts
@@ -94,7 +94,6 @@ export async function initMonorepoFingerprintPackages(options: {
return {
root: initPackageOutput(root, {
- includeIntent: Boolean(options.initOptions.withIntent),
includeConfig: Boolean(options.initOptions.withConfig),
}),
rootState,
@@ -205,7 +204,7 @@ function formatCommandArg(value: string): string {
function initPackageOutput(
paths: FingerprintPackagePaths,
- options: { includeIntent: boolean; includeConfig: boolean },
+ options: { includeConfig: boolean },
): Record {
return {
dir: paths.dir,
@@ -216,6 +215,5 @@ function initPackageOutput(
composition: paths.composition,
...(options.includeConfig ? { config: paths.config } : {}),
checks: paths.checks,
- ...(options.includeIntent ? { intent: paths.intent } : {}),
};
}
diff --git a/packages/ghost/src/review-packet.ts b/packages/ghost/src/review-packet.ts
index e9217c04..0807a6a5 100644
--- a/packages/ghost/src/review-packet.ts
+++ b/packages/ghost/src/review-packet.ts
@@ -1,13 +1,9 @@
-import type { Dirent } from "node:fs";
-import { readdir, readFile } from "node:fs/promises";
import { resolve } from "node:path";
-import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
-import { type GhostDecisionDocument, lintGhostDecision } from "#ghost-core";
+import { stringify as stringifyYaml } from "yaml";
import { buildContextEntrypoint } from "./context/entrypoint.js";
import { formatContextEntrypointMarkdown } from "./context/entrypoint-markdown.js";
import { loadPackageContext } from "./context/package-context.js";
import { parseUnifiedDiff } from "./core/index.js";
-import { readOptionalUtf8 } from "./internal/fs.js";
import { resolveFingerprintPackage } from "./scan/fingerprint-package.js";
import {
fingerprintStackToPackageContext,
@@ -26,7 +22,6 @@ export async function buildReviewPacket(options: {
packageDir?: string;
memoryDir?: string;
diffText: string;
- includeAcceptedDecisions: boolean;
maxDiffBytes?: number;
}): Promise {
return options.packageDir
@@ -37,7 +32,6 @@ export async function buildReviewPacket(options: {
async function buildSinglePackageReviewPacket(options: {
packageDir?: string;
diffText: string;
- includeAcceptedDecisions: boolean;
maxDiffBytes?: number;
}): Promise {
const paths = resolveFingerprintPackage(options.packageDir, process.cwd());
@@ -63,20 +57,15 @@ async function buildSinglePackageReviewPacket(options: {
),
},
]),
- intent: (await readOptional(paths.intent)) ?? null,
checks: context.checksRaw ?? null,
config: (await readOptionalPackageConfig(paths.config)) ?? null,
};
- if (options.includeAcceptedDecisions) {
- packet.accepted_decisions = await readAcceptedDecisions(paths.decisions);
- }
return packet;
}
async function buildStackReviewPacket(options: {
memoryDir?: string;
diffText: string;
- includeAcceptedDecisions: boolean;
maxDiffBytes?: number;
}): Promise {
const changedFiles = parseUnifiedDiff(options.diffText).map(
@@ -121,16 +110,10 @@ async function buildStackReviewPacket(options: {
),
fingerprint: first.merged.fingerprint,
context_markdown: formatReviewContextMarkdown(contextSections),
- intent: first.merged.intent,
checks: stringifyYaml(first.merged.checks, { lineWidth: 0 }),
config: config ?? null,
stacks,
};
- if (options.includeAcceptedDecisions) {
- packet.accepted_decisions = stacks
- .flatMap((stack) => stack.merged.decisions)
- .filter((decision) => decision.status === "accepted");
- }
return packet;
}
@@ -149,7 +132,7 @@ function baseReviewPacket(
finding_categories: [
"fix",
"intentional-divergence",
- "missing-memory",
+ "missing-fingerprint",
"experience-gap",
"eval-uncertainty",
],
@@ -231,9 +214,7 @@ function reviewStackFromFingerprintStack(
layer_dirs: stack.layers.map((layer) => layer.dir),
merged: {
fingerprint: stack.merged.fingerprint,
- intent: stack.merged.intent,
checks: stack.merged.checks,
- decisions: stack.merged.decisions,
},
provenance: stack.provenance,
};
@@ -260,10 +241,8 @@ interface ReviewPacket {
package_dir: string;
fingerprint: unknown;
context_markdown: string;
- intent: string | null;
checks: string | null;
config: GhostPackageConfig | null;
- accepted_decisions?: GhostDecisionDocument[];
stacks?: ReviewStackPacket[];
diff: string;
budgets: ReviewPacketBudgets;
@@ -280,9 +259,7 @@ interface ReviewStackPacket {
layer_dirs: string[];
merged: {
fingerprint: unknown;
- intent: string | null;
checks: unknown;
- decisions: GhostDecisionDocument[];
};
provenance: GhostFingerprintStack["provenance"];
}
@@ -292,28 +269,20 @@ export function formatReviewPacketMarkdown(packet: ReviewPacket): string {
Package: ${packet.package_dir}
-Review this diff as a non-blocking design-language critic. Advisory findings must be evidence-routed and must cite: ${packet.required_finding_citations.join(", ")}. Do not fail the build unless the issue is tied to an active deterministic check in fingerprint/enforcement/checks.yml. Keep findings grounded in fingerprint/prose.yml, fingerprint/inventory.yml, fingerprint/composition.yml, active deterministic checks, and optional rationale files when present; do not expand the review into unrelated audit categories.
+Review this diff as a non-blocking design-language critic. Advisory findings must be evidence-routed and must cite: ${packet.required_finding_citations.join(", ")}. Do not fail the build unless the issue is tied to an active deterministic check in fingerprint/checks.yml. Keep findings grounded in fingerprint/prose.yml, fingerprint/inventory.yml, fingerprint/composition.yml, active deterministic checks, and diff evidence; do not expand the review into unrelated audit categories.
Use these finding categories: ${packet.finding_categories.join(", ")}.
${formatReviewBudgetSection(packet)}
-When fingerprint layers are silent, local evidence can still support advisory critique. Label those findings as provisional and non-Ghost-backed, and ground them in nearby product surfaces, local components, token or copy conventions, or optional rationale files when present. Ask the human before assessing high-risk, irreversible, privacy/security/legal, or product-surface-defining choices.
+When fingerprint layers are silent, local evidence can still support advisory critique. Label those findings as provisional and non-Ghost-backed, and ground them in nearby product surfaces, local components, token or copy conventions. Ask the human before assessing high-risk, irreversible, privacy/security/legal, or product-surface-defining choices.
-If the diff exposes missing fingerprint grounding or layer coverage, report it as missing-memory or experience-gap. Do not silently rewrite the Ghost package during review; fingerprint and check edits are ordinary Git-reviewed edits.
+If the diff exposes missing fingerprint grounding or layer coverage, report it as missing-fingerprint or experience-gap. Do not silently rewrite the Ghost package during review; fingerprint and check edits are ordinary Git-reviewed edits.
${formatReviewStacksSection(packet.stacks ?? null)}
${packet.context_markdown}
-## Human Intent
-
-\`\`\`markdown
-${packet.intent ?? "_No fingerprint/memory/intent.md present. Treat fingerprint core layers as the canonical prose, inventory, and composition source._"}
-\`\`\`
-
-${formatAcceptedDecisionsSection(packet.accepted_decisions ?? null)}
-
${formatConfigSection(packet.config)}
## Diff
@@ -352,9 +321,6 @@ function formatReviewStacksSection(stacks: ReviewStackPacket[] | null): string {
lines.push(`Layers: ${stack.layer_dirs.join(" -> ")}`);
lines.push(`Merge: ${stack.provenance.merge}`);
lines.push("");
- if (stack.merged.intent?.trim()) {
- lines.push("```markdown", stack.merged.intent.trim(), "```", "");
- }
}
return `${lines.join("\n")}\n`;
@@ -375,79 +341,6 @@ function formatReviewContextMarkdown(
return lines.join("\n").trim();
}
-const readOptional = readOptionalUtf8;
-
-async function readAcceptedDecisions(
- dirPath: string,
-): Promise {
- let entries: Dirent[];
- try {
- entries = await readdir(dirPath, { withFileTypes: true });
- } catch {
- return [];
- }
-
- const decisions: GhostDecisionDocument[] = [];
- for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
- if (!entry.isFile()) continue;
- if (entry.name.startsWith(".")) continue;
- if (!/\.ya?ml$/i.test(entry.name)) continue;
-
- const path = resolve(dirPath, entry.name);
- const parsed = parseYaml(await readFile(path, "utf-8"));
- const report = lintGhostDecision(parsed);
- if (report.errors > 0) {
- const first = report.issues.find((issue) => issue.severity === "error");
- const suffix = first?.path ? ` @ ${first.path}` : "";
- throw new Error(
- `${path} failed decision lint: ${first?.message ?? "invalid decision"}${suffix}`,
- );
- }
- const decision = parsed as GhostDecisionDocument;
- if (decision.status === "accepted") decisions.push(decision);
- }
-
- return decisions;
-}
-
-function formatAcceptedDecisionsSection(
- decisions: GhostDecisionDocument[] | null,
-): string {
- if (!decisions) return "";
- if (decisions.length === 0) {
- return `## Accepted Surface-Composition Decisions
-
-_No accepted decisions found in fingerprint/memory/decisions._
-`;
- }
-
- const lines = ["## Accepted Surface-Composition Decisions", ""];
- for (const decision of decisions) {
- lines.push(`### ${decision.title}`);
- lines.push("");
- lines.push(`- **ID:** \`${decision.id}\``);
- lines.push(`- **Claim:** ${decision.claim}`);
- lines.push(`- **Rationale:** ${decision.rationale}`);
- if (decision.scope) {
- lines.push(`- **Scope:** ${formatDecisionScope(decision.scope)}`);
- }
- lines.push(
- `- **Evidence:** ${decision.evidence
- .map(
- (entry) =>
- entry.path ??
- entry.survey_surface_id ??
- entry.locator ??
- entry.note,
- )
- .join(", ")}`,
- );
- lines.push("");
- }
-
- return lines.join("\n");
-}
-
function formatConfigSection(config: GhostPackageConfig | null): string {
if (!config) {
return `## Implementation Config
@@ -463,19 +356,3 @@ ${stringifyYaml(config)}
\`\`\`
`;
}
-
-function formatDecisionScope(
- scope: NonNullable,
-): string {
- const parts: string[] = [];
- if (scope.roles?.length) parts.push(`roles=${scope.roles.join("/")}`);
- if (scope.scopes?.length) parts.push(`scopes=${scope.scopes.join("/")}`);
- if (scope.surface_types?.length) {
- parts.push(`surface_types=${scope.surface_types.join("/")}`);
- }
- if (scope.pattern_ids?.length) {
- parts.push(`pattern_ids=${scope.pattern_ids.join("/")}`);
- }
- if (scope.paths?.length) parts.push(`paths=${scope.paths.join("/")}`);
- return parts.length ? parts.join("; ") : "global";
-}
diff --git a/packages/ghost/src/scan-stack-command.ts b/packages/ghost/src/scan-stack-command.ts
index 2cfd2c41..b738e183 100644
--- a/packages/ghost/src/scan-stack-command.ts
+++ b/packages/ghost/src/scan-stack-command.ts
@@ -68,8 +68,6 @@ function formatStackJson(
merged: {
fingerprint: stack.merged.fingerprint,
checks: stack.merged.checks,
- intent: stack.merged.intent,
- decisions: stack.merged.decisions,
},
provenance: stack.provenance,
};
diff --git a/packages/ghost/src/scan/constants.ts b/packages/ghost/src/scan/constants.ts
index 96718120..ff4e44bd 100644
--- a/packages/ghost/src/scan/constants.ts
+++ b/packages/ghost/src/scan/constants.ts
@@ -21,17 +21,9 @@ export const FINGERPRINT_PROSE_FILENAME = "prose.yml";
export const FINGERPRINT_INVENTORY_FILENAME = "inventory.yml";
export const FINGERPRINT_COMPOSITION_FILENAME = "composition.yml";
-/** Purpose folders inside the portable fingerprint bundle. */
-export const FINGERPRINT_ENFORCEMENT_DIRNAME = "enforcement";
-export const FINGERPRINT_MEMORY_DIRNAME = "memory";
-export const FINGERPRINT_SOURCES_DIRNAME = "sources";
-
/** Optional data-only package configuration artifact. */
export const CONFIG_FILENAME = "config.yml";
-/** Optional filename for human-authored or human-approved intent. */
-export const INTENT_FILENAME = "intent.md";
-
/** Legacy direct fingerprint filename. Not part of the root package shape. */
export const FINGERPRINT_FILENAME = "fingerprint.md";
@@ -43,9 +35,3 @@ export const SCOPE_SURVEYS_DIRNAME = "modules";
/** Canonical filename for human-promoted deterministic gates. */
export const CHECKS_FILENAME = "checks.yml";
-
-/** Optional directory containing accepted/rejected surface-composition decisions. */
-export const DECISIONS_DIRNAME = "decisions";
-
-/** Optional directory containing generated, non-canonical caches. */
-export const CACHE_DIRNAME = "cache";
diff --git a/packages/ghost/src/scan/fingerprint-package-layers.ts b/packages/ghost/src/scan/fingerprint-package-layers.ts
index 8d4a7b45..4120870a 100644
--- a/packages/ghost/src/scan/fingerprint-package-layers.ts
+++ b/packages/ghost/src/scan/fingerprint-package-layers.ts
@@ -183,13 +183,6 @@ checks: []
`;
}
-export function templateIntent(): string {
- return `# Intent
-
-This optional file is reserved for human-authored or human-approved product intent.
-`;
-}
-
const readOptional = readOptionalUtf8;
function parseManifest(
diff --git a/packages/ghost/src/scan/fingerprint-package.ts b/packages/ghost/src/scan/fingerprint-package.ts
index f09373aa..13445b25 100644
--- a/packages/ghost/src/scan/fingerprint-package.ts
+++ b/packages/ghost/src/scan/fingerprint-package.ts
@@ -1,5 +1,4 @@
-import type { Dirent } from "node:fs";
-import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
+import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { parse as parseYaml } from "yaml";
import {
@@ -7,7 +6,6 @@ import {
type GhostFingerprintDocument,
type GhostFingerprintPackageManifest,
lintGhostChecks,
- lintGhostDecision,
MAP_FILENAME,
SURVEY_FILENAME,
} from "#ghost-core";
@@ -17,21 +15,15 @@ import {
readOptionalUtf8,
} from "../internal/fs.js";
import {
- CACHE_DIRNAME,
CONFIG_FILENAME,
- DECISIONS_DIRNAME,
FINGERPRINT_COMPOSITION_FILENAME,
FINGERPRINT_DIRNAME,
- FINGERPRINT_ENFORCEMENT_DIRNAME,
FINGERPRINT_FILENAME,
FINGERPRINT_INVENTORY_FILENAME,
FINGERPRINT_MANIFEST_FILENAME,
- FINGERPRINT_MEMORY_DIRNAME,
FINGERPRINT_PACKAGE_DIR,
FINGERPRINT_PROSE_FILENAME,
- FINGERPRINT_SOURCES_DIRNAME,
FINGERPRINT_YML_FILENAME,
- INTENT_FILENAME,
PATTERNS_FILENAME,
RESOURCES_FILENAME,
} from "./constants.js";
@@ -40,7 +32,6 @@ import {
parseSplitFingerprintForLint,
templateChecks,
templateComposition,
- templateIntent,
templateInventory,
templateManifest,
templateProse,
@@ -60,7 +51,6 @@ export interface FingerprintPackagePaths {
prose: string;
inventory: string;
composition: string;
- enforcement: string;
fingerprintYml: string;
config: string;
resources: string;
@@ -70,11 +60,6 @@ export interface FingerprintPackagePaths {
/** Legacy direct markdown path; not part of the canonical root bundle. */
fingerprint: string;
checks: string;
- memory: string;
- intent: string;
- decisions: string;
- sources: string;
- cache: string;
}
export interface LoadedFingerprintPackage {
@@ -89,7 +74,6 @@ export interface LoadedFingerprintPackage {
}
export interface InitFingerprintPackageOptions {
- withIntent?: boolean;
withConfig?: boolean;
reference?: string;
force?: boolean;
@@ -101,9 +85,6 @@ export function resolveFingerprintPackage(
): FingerprintPackagePaths {
const dir = resolve(cwd, dirArg ?? FINGERPRINT_PACKAGE_DIR);
const fingerprintDir = join(dir, FINGERPRINT_DIRNAME);
- const enforcement = join(fingerprintDir, FINGERPRINT_ENFORCEMENT_DIRNAME);
- const memory = join(fingerprintDir, FINGERPRINT_MEMORY_DIRNAME);
- const sources = join(fingerprintDir, FINGERPRINT_SOURCES_DIRNAME);
return {
dir,
fingerprintDir,
@@ -111,7 +92,6 @@ export function resolveFingerprintPackage(
prose: join(fingerprintDir, FINGERPRINT_PROSE_FILENAME),
inventory: join(fingerprintDir, FINGERPRINT_INVENTORY_FILENAME),
composition: join(fingerprintDir, FINGERPRINT_COMPOSITION_FILENAME),
- enforcement,
fingerprintYml: join(dir, FINGERPRINT_YML_FILENAME),
config: join(dir, CONFIG_FILENAME),
resources: join(dir, RESOURCES_FILENAME),
@@ -119,12 +99,7 @@ export function resolveFingerprintPackage(
survey: join(dir, SURVEY_FILENAME),
patterns: join(dir, PATTERNS_FILENAME),
fingerprint: join(dir, FINGERPRINT_FILENAME),
- checks: join(enforcement, GHOST_CHECKS_FILENAME),
- memory,
- intent: join(memory, INTENT_FILENAME),
- decisions: join(memory, DECISIONS_DIRNAME),
- sources,
- cache: join(sources, CACHE_DIRNAME),
+ checks: join(fingerprintDir, GHOST_CHECKS_FILENAME),
};
}
@@ -136,8 +111,6 @@ export async function initFingerprintPackage(
const paths = resolveFingerprintPackage(dirArg, cwd);
await Promise.all([
mkdir(paths.fingerprintDir, { recursive: true }),
- mkdir(paths.enforcement, { recursive: true }),
- ...(options.withIntent ? [mkdir(paths.memory, { recursive: true })] : []),
...(options.withConfig ? [mkdir(paths.dir, { recursive: true })] : []),
]);
const files = [
@@ -154,9 +127,6 @@ export async function initFingerprintPackage(
},
]
: []),
- ...(options.withIntent
- ? [{ path: paths.intent, content: templateIntent() }]
- : []),
];
if (!options.force) {
await assertInitDoesNotOverwrite(files.map((file) => file.path));
@@ -223,14 +193,6 @@ export async function lintFingerprintPackage(
const compositionRaw = await readOptional(paths.composition);
const configRaw = await readOptional(paths.config);
const checksRaw = await readOptional(paths.checks);
- const intentRaw = await readOptional(paths.intent);
- await lintDecisionDirectory(
- paths.decisions,
- "fingerprint/memory/decisions",
- "decision",
- lintGhostDecision,
- issues,
- );
let fingerprint: GhostFingerprintDocument | undefined;
if (manifestRaw !== undefined) {
@@ -250,92 +212,18 @@ export async function lintFingerprintPackage(
}
if (checksRaw !== undefined) {
- const checks = parseYamlSafe(
- checksRaw,
- "fingerprint/enforcement/checks.yml",
- issues,
- );
+ const checks = parseYamlSafe(checksRaw, "fingerprint/checks.yml", issues);
if (checks !== undefined) {
const checksReport = lintGhostChecks(checks, { fingerprint });
issues.push(
- ...prefixIssues(
- "fingerprint/enforcement/checks.yml",
- checksReport.issues,
- ),
+ ...prefixIssues("fingerprint/checks.yml", checksReport.issues),
);
}
}
- if (intentRaw !== undefined && intentRaw.trim().length === 0) {
- issues.push({
- severity: "warning",
- rule: "intent-empty",
- message:
- "intent.md is optional, but when present it should contain human-authored or human-approved intent.",
- path: "fingerprint/memory/intent.md",
- });
- }
-
return finalize(issues);
}
-async function lintDecisionDirectory(
- dirPath: string,
- label: "fingerprint/memory/decisions",
- itemLabel: "decision",
- lint: (input: unknown) => ReturnType,
- issues: LintIssue[],
-): Promise {
- let entries: Dirent[];
- try {
- entries = await readdir(dirPath, { withFileTypes: true });
- } catch {
- return;
- }
-
- const docs: Array<{ id: string; path: string }> = [];
- for (const entry of entries) {
- if (!entry.isFile()) continue;
- if (entry.name.startsWith(".")) continue;
- if (!/\.ya?ml$/i.test(entry.name)) continue;
-
- const relPath = `${label}/${entry.name}`;
- const raw = await readRequired(join(dirPath, entry.name), relPath, issues);
- if (raw === undefined) continue;
- const parsed = parseYamlSafe(raw, relPath, issues);
- if (parsed === undefined) continue;
-
- const report = lint(parsed);
- issues.push(...prefixIssues(relPath, report.issues));
- if (
- report.errors === 0 &&
- isRecord(parsed) &&
- typeof parsed.id === "string"
- ) {
- docs.push({ id: parsed.id, path: `${relPath}.id` });
- }
- }
-
- const seen = new Map();
- for (const doc of docs) {
- const previous = seen.get(doc.id);
- if (previous) {
- issues.push({
- severity: "error",
- rule: `${itemLabel}-id-duplicate`,
- message: `${itemLabel} id '${doc.id}' is duplicated (also at ${previous})`,
- path: doc.path,
- });
- } else {
- seen.set(doc.id, doc.path);
- }
- }
-}
-
-function isRecord(value: unknown): value is Record {
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
-}
-
async function readRequired(
path: string,
label: string,
diff --git a/packages/ghost/src/scan/fingerprint-stack.ts b/packages/ghost/src/scan/fingerprint-stack.ts
index 5f8df5ce..1b42313f 100644
--- a/packages/ghost/src/scan/fingerprint-stack.ts
+++ b/packages/ghost/src/scan/fingerprint-stack.ts
@@ -1,6 +1,6 @@
import { execFile } from "node:child_process";
import type { Dirent } from "node:fs";
-import { access, mkdir, readdir, readFile, stat } from "node:fs/promises";
+import { access, mkdir, readdir, stat } from "node:fs/promises";
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
import { promisify } from "node:util";
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
@@ -10,10 +10,6 @@ import {
type GhostCheck,
type GhostChecksDocument,
GhostChecksSchema,
- type GhostDecisionDocument,
- GhostDecisionSchema,
- type GhostExperienceEvidence,
- type GhostExperienceScope,
type GhostFingerprintComposition,
type GhostFingerprintDocument,
type GhostFingerprintEvidence,
@@ -24,15 +20,10 @@ import {
type GhostFingerprintTopology,
type GhostFingerprintTopologyScope,
lintGhostChecks,
- lintGhostDecision,
lintGhostFingerprint,
type MapFrontmatter,
} from "#ghost-core";
-import {
- loadPackageInventory,
- type PackageContext,
- type PackageInventory,
-} from "../context/package-context.js";
+import type { PackageContext } from "../context/package-context.js";
import { readOptionalUtf8 } from "../internal/fs.js";
import {
FINGERPRINT_DIRNAME,
@@ -83,9 +74,6 @@ export interface GhostFingerprintStackLayer
fingerprint_raw: string;
checks?: GhostChecksDocument;
checks_raw?: string;
- intent?: string;
- inventory: PackageInventory;
- decisions: GhostDecisionDocument[];
}
export interface GhostFingerprintStack {
@@ -96,8 +84,6 @@ export interface GhostFingerprintStack {
merged: {
fingerprint: GhostFingerprintDocument;
checks: GhostChecksDocument;
- intent: string | null;
- decisions: GhostDecisionDocument[];
};
provenance: {
merge: "child-wins-by-id";
@@ -265,7 +251,6 @@ export function buildFingerprintStack(
layers.map((layer) => layer.fingerprint),
);
const checks = mergeChecks(layers.map((layer) => layer.checks));
- const decisions = mergeById(layers.flatMap((layer) => layer.decisions));
const checkLint = lintGhostChecks(checks, {
fingerprint,
map: mapFromFingerprint(fingerprint),
@@ -287,8 +272,6 @@ export function buildFingerprintStack(
merged: {
fingerprint,
checks,
- intent: mergeIntent(layers),
- decisions,
},
provenance: {
merge: "child-wins-by-id",
@@ -305,12 +288,9 @@ export async function loadFingerprintStackLayer(
const paths = resolveFingerprintPackage(packageDir, process.cwd());
const normalizedMemoryDir = normalizeMemoryDir(memoryDir);
const root = rootForFingerprintPackageDir(paths.dir, normalizedMemoryDir);
- const [loaded, checksRaw, intent, inventory, decisions] = await Promise.all([
+ const [loaded, checksRaw] = await Promise.all([
loadFingerprintPackage(paths),
readOptional(paths.checks),
- readOptional(paths.intent),
- loadPackageInventory(paths),
- readDecisionDirectory(paths.decisions),
]);
const fingerprint = normalizeFingerprintPaths(
@@ -341,11 +321,6 @@ export async function loadFingerprintStackLayer(
fingerprint_raw: stringifyYaml(fingerprint, { lineWidth: 0 }),
...(checks ? { checks } : {}),
...(checksRaw ? { checks_raw: checksRaw } : {}),
- ...(intent ? { intent } : {}),
- inventory,
- decisions: decisions.map((decision) =>
- normalizeDecisionPaths(decision, root, repoRoot),
- ),
};
}
@@ -369,11 +344,6 @@ export function fingerprintStackToPackageContext(
fingerprintRaw: stringifyYaml(stack.merged.fingerprint, { lineWidth: 0 }),
checks: stack.merged.checks,
checksRaw: stringifyYaml(stack.merged.checks, { lineWidth: 0 }),
- intent: stack.merged.intent ?? undefined,
- inventory: stack.layers.at(-1)?.inventory ?? {
- state: "missing",
- path: `${stack.fingerprint_dir}/fingerprint/sources/cache/inventory.json`,
- },
};
}
@@ -434,7 +404,7 @@ export async function lintAllFingerprintStacks(
});
issues.push(
...prefixIssues(
- `${fingerprintPackageDisplayPath(pkg.relative_root, memoryDir)}/merged.enforcement.checks.yml`,
+ `${fingerprintPackageDisplayPath(pkg.relative_root, memoryDir)}/merged.checks.yml`,
checksReport.issues,
),
);
@@ -487,7 +457,6 @@ export async function initScopedFingerprintPackage(
scopePath: string,
cwd = process.cwd(),
options: {
- withIntent?: boolean;
withConfig?: boolean;
reference?: string;
force?: boolean;
@@ -502,7 +471,6 @@ export async function initScopedFingerprintPackage(
async function resolveAndInit(
root: string,
options: {
- withIntent?: boolean;
withConfig?: boolean;
reference?: string;
force?: boolean;
@@ -519,58 +487,10 @@ async function resolveAndInit(
}
function parseChecks(raw: string): GhostChecksDocument {
- const parsed = parseYamlSafe(raw, "fingerprint/enforcement/checks.yml");
+ const parsed = parseYamlSafe(raw, "fingerprint/checks.yml");
return GhostChecksSchema.parse(parsed) as GhostChecksDocument;
}
-async function readDecisionDirectory(
- dirPath: string,
-): Promise {
- const parsed = await readYamlFiles(dirPath);
- const docs: GhostDecisionDocument[] = [];
- for (const { path, value } of parsed) {
- const report = lintGhostDecision(value);
- if (report.errors > 0) throwFingerprintLintError(path, report.issues);
- docs.push(GhostDecisionSchema.parse(value) as GhostDecisionDocument);
- }
- return docs;
-}
-
-async function readYamlFiles(
- dirPath: string,
-): Promise> {
- let entries: Dirent[];
- try {
- entries = await readdir(dirPath, { withFileTypes: true });
- } catch {
- return [];
- }
-
- const docs: Array<{ path: string; value: unknown }> = [];
- for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
- if (!entry.isFile()) continue;
- if (entry.name.startsWith(".")) continue;
- if (!/\.ya?ml$/i.test(entry.name)) continue;
- const path = resolve(dirPath, entry.name);
- docs.push({
- path,
- value: parseYamlSafe(await readFile(path, "utf-8"), path),
- });
- }
- return docs;
-}
-
-function throwFingerprintLintError(
- path: string,
- issues: Array<{ severity: string; message: string; path?: string }>,
-): never {
- const first = issues.find((issue) => issue.severity === "error");
- const suffix = first?.path ? ` @ ${first.path}` : "";
- throw new Error(
- `${path} failed lint: ${first?.message ?? "invalid fingerprint package"}${suffix}`,
- );
-}
-
function mergeFingerprints(
fingerprints: GhostFingerprintDocument[],
): GhostFingerprintDocument {
@@ -733,16 +653,6 @@ function mergeStrings(a?: string[], b?: string[]): string[] | undefined {
return out.length ? out : undefined;
}
-function mergeIntent(layers: GhostFingerprintStackLayer[]): string | null {
- const chunks = layers
- .filter((layer) => layer.intent?.trim())
- .map(
- (layer) =>
- `# ${fingerprintPackageDisplayPath(layer.relative_root, layer.fingerprint_dir)}/fingerprint/memory/intent.md\n\n${layer.intent?.trim()}`,
- );
- return chunks.length ? `${chunks.join("\n\n")}\n` : null;
-}
-
function normalizeFingerprintPaths(
input: GhostFingerprintDocument,
baseRoot: string,
@@ -826,23 +736,6 @@ function normalizeChecksPaths(
return checks;
}
-function normalizeDecisionPaths(
- input: GhostDecisionDocument,
- baseRoot: string,
- repoRoot: string,
-): GhostDecisionDocument {
- const decision = clone(input);
- return {
- ...decision,
- scope: normalizeExperienceScopePaths(decision.scope, baseRoot, repoRoot),
- evidence: normalizeExperienceEvidence(
- decision.evidence,
- baseRoot,
- repoRoot,
- ),
- };
-}
-
function normalizeScopePaths(
scope: T | undefined,
baseRoot: string,
@@ -855,14 +748,6 @@ function normalizeScopePaths(
};
}
-function normalizeExperienceScopePaths(
- scope: GhostExperienceScope | undefined,
- baseRoot: string,
- repoRoot: string,
-): GhostExperienceScope | undefined {
- return normalizeScopePaths(scope, baseRoot, repoRoot);
-}
-
function normalizeFingerprintEvidence(
evidence: GhostFingerprintEvidence[] | undefined,
baseRoot: string,
@@ -875,18 +760,6 @@ function normalizeFingerprintEvidence(
);
}
-function normalizeExperienceEvidence(
- evidence: GhostExperienceEvidence[],
- baseRoot: string,
- repoRoot: string,
-): GhostExperienceEvidence[] {
- return evidence.map((entry) =>
- entry.path
- ? { ...entry, path: normalizePath(entry.path, baseRoot, repoRoot) }
- : entry,
- );
-}
-
function normalizePath(
path: string,
baseRoot: string,
diff --git a/packages/ghost/src/scan/index.ts b/packages/ghost/src/scan/index.ts
index 71247d1f..02e32142 100644
--- a/packages/ghost/src/scan/index.ts
+++ b/packages/ghost/src/scan/index.ts
@@ -1,10 +1,7 @@
export {
- CACHE_DIRNAME,
CONFIG_FILENAME,
FINGERPRINT_DIRNAME,
- FINGERPRINT_MEMORY_DIRNAME,
FINGERPRINT_PACKAGE_DIR,
- FINGERPRINT_SOURCES_DIRNAME,
} from "./constants.js";
export type {
DiscoveredGhostPackage,
@@ -26,7 +23,7 @@ export {
resolveGitRoot,
resolveMemoryDirDefault,
} from "./fingerprint-stack.js";
-export { inventory } from "./inventory.js";
+export { signals } from "./inventory.js";
export type { MonorepoInitCandidate } from "./monorepo-init.js";
export { detectMonorepoInitCandidates } from "./monorepo-init.js";
export type {
diff --git a/packages/ghost/src/scan/inventory.ts b/packages/ghost/src/scan/inventory.ts
index e8fb23b0..1e90997c 100644
--- a/packages/ghost/src/scan/inventory.ts
+++ b/packages/ghost/src/scan/inventory.ts
@@ -302,7 +302,7 @@ const HISTOGRAM_TOP_N = 20;
* No LLM calls, no network, no filesystem mutations. Pure reads plus a
* best-effort git invocation.
*/
-export function inventory(path: string): InventoryOutput {
+export function signals(path: string): InventoryOutput {
const root = resolve(path);
const packageManifests = collectAllManifests(root);
diff --git a/packages/ghost/src/scan/scan-status.ts b/packages/ghost/src/scan/scan-status.ts
index 6cb2f8de..17220cb2 100644
--- a/packages/ghost/src/scan/scan-status.ts
+++ b/packages/ghost/src/scan/scan-status.ts
@@ -81,8 +81,6 @@ export interface ScanStatus {
fingerprint: ScanStageReport;
config: ScanStageReport;
checks: ScanStageReport;
- intent: ScanStageReport;
- cache: ScanStageReport;
scopes?: ScanScopeReport[];
scope_error?: string;
readiness: ScanReadinessReport;
@@ -93,9 +91,7 @@ export interface ScanStatus {
* Inspect a Ghost fingerprint directory and report whether the canonical
* `fingerprint/manifest.yml` exists with useful prose, inventory, and
* composition layers.
- * Generated cache is source material, not readiness for the curated
- * inventory layer. Optional checks and rationale files are supplemental when
- * present.
+ * Optional checks are supplemental when present.
*/
export async function scanStatus(
dirPath: string,
@@ -106,21 +102,11 @@ export async function scanStatus(
const fingerprintPath = paths.fingerprintDir;
const configPath = resolve(dir, CONFIG_FILENAME);
const checksPath = paths.checks;
- const intentPath = paths.intent;
- const cachePath = paths.cache;
-
- const [
- fingerprintPresent,
- configPresent,
- checksPresent,
- intentPresent,
- cachePresent,
- ] = await Promise.all([
+
+ const [fingerprintPresent, configPresent, checksPresent] = await Promise.all([
pathExists(paths.manifest, "file"),
pathExists(configPath, "file"),
pathExists(checksPath, "file"),
- pathExists(intentPath, "file"),
- pathExists(cachePath, "directory"),
]);
const fingerprint: ScanStageReport = {
@@ -135,22 +121,12 @@ export async function scanStatus(
state: configPresent ? "present" : "missing",
path: configPath,
};
- const intent: ScanStageReport = {
- state: intentPresent ? "present" : "missing",
- path: intentPath,
- };
- const cache: ScanStageReport = {
- state: cachePresent ? "present" : "missing",
- path: cachePath,
- };
const status: ScanStatus = {
dir,
fingerprint,
config,
checks,
- intent,
- cache,
readiness: await scanReadiness(paths, fingerprintPresent),
recommended_next: fingerprintPresent ? null : "fingerprint",
};
diff --git a/packages/ghost/src/scan/verify-package.ts b/packages/ghost/src/scan/verify-package.ts
index c9801777..1b1b8c69 100644
--- a/packages/ghost/src/scan/verify-package.ts
+++ b/packages/ghost/src/scan/verify-package.ts
@@ -120,8 +120,8 @@ async function readOptionalChecks(
severity: "error",
rule: "verify-checks-read-failed",
message:
- "fingerprint/enforcement/checks.yml failed schema validation after package lint.",
- path: "fingerprint/enforcement/checks.yml",
+ "fingerprint/checks.yml failed schema validation after package lint.",
+ path: "fingerprint/checks.yml",
});
return undefined;
} catch (err) {
@@ -129,10 +129,10 @@ async function readOptionalChecks(
issues.push({
severity: "error",
rule: "verify-checks-read-failed",
- message: `fingerprint/enforcement/checks.yml could not be read as YAML: ${
+ message: `fingerprint/checks.yml could not be read as YAML: ${
err instanceof Error ? err.message : String(err)
}`,
- path: "fingerprint/enforcement/checks.yml",
+ path: "fingerprint/checks.yml",
});
return undefined;
}
diff --git a/packages/ghost/src/skill-bundle/SKILL.md b/packages/ghost/src/skill-bundle/SKILL.md
index 27b19f59..fa3ac4cd 100644
--- a/packages/ghost/src/skill-bundle/SKILL.md
+++ b/packages/ghost/src/skill-bundle/SKILL.md
@@ -20,10 +20,7 @@ materials it draws from, and the patterns that make it feel intentional.
prose.yml
inventory.yml
composition.yml
- enforcement/checks.yml
- memory/intent.md
- memory/decisions/
- sources/cache/
+ checks.yml
```
`fingerprint/` is the source of truth when it is checked in. Ordinary Git
@@ -47,12 +44,10 @@ Checks and review validate output; they are not generation input.
layer content; Ghost normalizes omitted layer files or sections internally for
checks, review, emit, and stack resolution.
-Optional support material lives under purpose folders:
-`fingerprint/enforcement/checks.yml` for deterministic gates,
-`fingerprint/memory/intent.md` for human-approved intent,
-`fingerprint/memory/decisions/` for rationale history, and
-`fingerprint/sources/cache/` for generated observations. `.ghost/config.yml`
-stays outside the portable package as local routing config.
+Optional deterministic gates live in `fingerprint/checks.yml`.
+`.ghost/config.yml` stays outside the portable package as local routing config.
+Use `ghost signals` as a stdout-only reconnaissance helper when an agent needs
+raw repo observations while authoring curated fingerprint layers.
Advanced repos may contain nested fingerprint packages such as
`apps/checkout/.ghost/`. Host wrappers may set
@@ -65,7 +60,7 @@ consume JSON and map severities into their own review or check format.
| Verb | Purpose |
|---|---|
-| `ghost init [dir]` | Create `.ghost/fingerprint/` with manifest, core layers, and enforcement checks. |
+| `ghost init [dir]` | Create `.ghost/fingerprint/` with manifest, core layers, and deterministic checks. |
| `ghost scan [dir] [--format json]` | Report fingerprint layer readiness for prose, inventory, and composition. |
| `ghost lint [file-or-dir]` | Validate a fingerprint package or artifact. |
| `ghost verify [dir] --root ` | Validate evidence paths, exemplar paths, and typed check refs. |
@@ -81,7 +76,7 @@ consume JSON and map severities into their own review or check format.
|---|---|
| `ghost init --scope ` / `--memory-dir ` | Create or resolve scoped/custom fingerprint packages for nested packages or host wrappers. |
| `ghost stack [path...]` | Inspect resolved broad-to-local fingerprint stack and merged output. |
-| `ghost inventory [path]` | Emit raw repo signals for optional generated cache/source material. |
+| `ghost signals [path]` | Emit raw repo signals for fingerprint authoring. |
| `ghost lint --all` / `ghost verify --all` | Validate nested stack merges. |
| `ghost compare [...more]` | Compare root fingerprint packages. |
| `ghost ack` / `track` / `diverge` | Record stance toward tracked drift. |
@@ -109,7 +104,7 @@ evidence-backed core layer entries, then ask the human to curate the claims.
- Treat checked-in `fingerprint/` core files as the source of truth.
- Generate from prose, inventory, and composition.
-- Run active checks from `fingerprint/enforcement/checks.yml`; only active deterministic checks block.
+- Run active checks from `fingerprint/checks.yml`; only active deterministic checks block.
- Use local evidence as provisional when fingerprint layers are silent.
- Treat auto-drafted fingerprint edits as ordinary uncommitted draft work until
the human curates them and Git review accepts them.
@@ -117,15 +112,14 @@ evidence-backed core layer entries, then ask the human to curate the claims.
- Validate with `ghost lint` and `ghost verify --root ` before declaring
fingerprint layers complete.
- Run `ghost check` for deterministic gates and `ghost review` for advisory critique.
-- Use optional config, intent, decisions, generated cache, nested stacks, and custom fingerprint
- dirs only when present or requested.
+- Use optional config, nested stacks, and custom fingerprint dirs only when
+ present or requested.
## When Fingerprint Layers Are Silent
Silent fingerprint layers do not require stopping by default. When the fingerprint does
not cover the task, proceed from nearby product surfaces, local components,
-token and copy conventions, optional rationale files when present, and ordinary
-UX reasoning when safe. Label that reasoning as provisional and
+token and copy conventions, and ordinary UX reasoning when safe. Label that reasoning as provisional and
non-Ghost-backed.
Ask a human before making high-risk, irreversible, privacy/security/legal, or
product-surface-defining choices.
@@ -135,5 +129,3 @@ product-surface-defining choices.
- Never treat advisory composition critique as a CI gate.
- Never claim provisional reasoning, local convention, or general UX reasoning as
Ghost-backed.
-- Never treat `fingerprint/memory/intent.md` as authoritative unless human-authored or human-approved.
-- Never treat rejected decisions as canonical inputs.
diff --git a/packages/ghost/src/skill-bundle/references/authoring-scenarios.md b/packages/ghost/src/skill-bundle/references/authoring-scenarios.md
index 4ca66e94..0df1812d 100644
--- a/packages/ghost/src/skill-bundle/references/authoring-scenarios.md
+++ b/packages/ghost/src/skill-bundle/references/authoring-scenarios.md
@@ -58,8 +58,8 @@ Ask only high-leverage questions that change the fingerprint:
- Where do trust, density, pacing, accessibility, recovery, or disclosure matter most?
- Are there surfaces where the same UI decision should be assessed differently?
-Use human-authored or human-approved answers in `prose.yml` and optional
-`fingerprint/memory/intent.md`. Do not treat unapproved notes as canonical.
+Use human-authored or human-approved answers in `prose.yml`. Do not treat
+unapproved notes as canonical.
When auto-draft is requested, move the interview after the starter draft and
use it to curate claims instead of asking every question up front.
@@ -71,20 +71,19 @@ stories, tests, docs, screenshots, examples, copy, tokens, assets, and UI
library references that reveal hierarchy, behavior, accessibility, trust, and
flow.
-Optional cache:
+Optional signals:
```bash
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory . > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
```
-Treat generated cache as scratch evidence. It can support curated entries in
+Treat signals as scratch evidence. They can support curated entries in
`inventory.yml`, but it does not establish surface-composition guidance by
itself.
-In auto-draft mode, always create or refresh this cache before drafting, then
-inspect the high-signal files it points to. Scan facts may seed `inventory.yml`;
-scan frequency and raw cache do not establish surface-composition guidance.
+In auto-draft mode, always gather signals before drafting, then inspect the
+high-signal files they point to. Signal facts may seed `inventory.yml`; scan
+frequency and raw signals do not establish surface-composition guidance.
## 4. Draft The Core Layers
@@ -112,9 +111,8 @@ important claims:
- keep as canonical
- soften into guidance
- reject as accidental or legacy
-- move to generated cache or scratch notes
+- move to scratch notes
- scope to a nested package
-- record as a decision
- convert into a deterministic check
Only add checks when the rule can be enforced deterministically. Subjective
@@ -163,7 +161,5 @@ canonical package.
- Never copy raw inventory into canonical layers without curation.
- Never claim scan frequency is product authority.
-- Never make `fingerprint/memory/intent.md` authoritative unless human-authored
- or human-approved.
- Never create nested packages just to mirror directory structure.
- Never turn advisory composition critique into a deterministic gate.
diff --git a/packages/ghost/src/skill-bundle/references/brief.md b/packages/ghost/src/skill-bundle/references/brief.md
index a802d4d3..dcc96d4b 100644
--- a/packages/ghost/src/skill-bundle/references/brief.md
+++ b/packages/ghost/src/skill-bundle/references/brief.md
@@ -8,14 +8,14 @@ description: Build a concise pre-generation brief from Ghost fingerprint layers.
1. Read checked-in `fingerprint/prose.yml`, `fingerprint/inventory.yml`, and `fingerprint/composition.yml`.
2. Select the relevant `prose.situations`, `prose.principles`, `prose.experience_contracts`, and `composition.patterns`.
3. Inspect matching `inventory.exemplars` as concrete generation anchors.
-4. Use optional `fingerprint/sources/cache/` when present to understand what exists.
+4. Run `ghost signals ` when raw repo observations would help you find evidence.
5. Skim active checks so generation avoids deterministic failures.
-6. Use `ghost stack `, accepted decisions, and `fingerprint/memory/intent.md` only when the repo has opted into those advanced inputs.
+6. Use `ghost stack ` when the repo has nested fingerprint packages.
Return a short brief with relevant fingerprint refs, product obligations,
-inventory exemplars and building blocks to inspect, cache facts when present,
-active checks to avoid, local evidence, and provisional assumptions when layers
+inventory exemplars and building blocks to inspect, active checks to avoid,
+local evidence, and provisional assumptions when layers
are silent.
Fingerprint edits are ordinary Git-reviewed edits to the split fingerprint
-package and optional rationale files.
+package.
diff --git a/packages/ghost/src/skill-bundle/references/capture.md b/packages/ghost/src/skill-bundle/references/capture.md
index e0b3b045..b5ed77e8 100644
--- a/packages/ghost/src/skill-bundle/references/capture.md
+++ b/packages/ghost/src/skill-bundle/references/capture.md
@@ -24,10 +24,7 @@ Ghost treats the fingerprint package as canonical.
prose.yml
inventory.yml
composition.yml
- enforcement/checks.yml
- memory/intent.md
- memory/decisions/
- sources/cache/
+ checks.yml
```
`prose.yml` captures the intent behind the surface. `inventory.yml` records
@@ -68,9 +65,8 @@ ghost init
ghost scan
```
-Use `--with-intent` only when you have human-authored or human-approved context
-to preserve. Use `--with-config --reference ` when local
-routing or a reference UI registry/library should be recorded in `.ghost/config.yml`.
+Use `--with-config --reference ` when local routing or a
+reference UI registry/library should be recorded in `.ghost/config.yml`.
### 3. Auto-Draft Mode
@@ -84,14 +80,13 @@ Auto-draft is a skill workflow, not a Ghost CLI action or flag.
1. If `.ghost/fingerprint/manifest.yml` is missing, run `ghost init`.
2. Run `ghost scan --format json`.
-3. Create generated cache:
+3. Gather raw repo signals:
```bash
- mkdir -p .ghost/fingerprint/sources/cache
- ghost inventory . > .ghost/fingerprint/sources/cache/inventory.json
+ ghost signals .
```
-4. Inspect high-signal files from the inventory, plus routes, docs, stories,
+4. Inspect high-signal files from the signals, plus routes, docs, stories,
tests, tokens, registries, assets, screenshots, and exemplars.
5. Write only the smallest evidence-backed starter entries into
`prose.yml`, `inventory.yml`, and `composition.yml`.
@@ -110,11 +105,10 @@ behavior, copy, accessibility, trust, and flow.
Optional helper:
```bash
-mkdir -p .ghost/fingerprint/sources/cache
-ghost inventory . > .ghost/fingerprint/sources/cache/inventory.json
+ghost signals .
```
-Treat generated cache as scratch material. Do not copy raw inventory into
+Treat signals as scratch observations. Do not copy raw signals into
`inventory.yml` without curation.
### 5. Write Core Layers
@@ -132,7 +126,7 @@ treating draft content as durable fingerprint guidance.
### 6. Add Checks Sparingly
-`fingerprint/enforcement/checks.yml` is the executable appendix. Add only
+`fingerprint/checks.yml` is the executable appendix. Add only
deterministic checks with typed derivation refs:
```yaml
@@ -159,7 +153,7 @@ packages exist.
## Never
- Never describe any file outside `.ghost/fingerprint/` as canonical package input.
-- Never treat generated cache as canonical inventory.
+- Never treat raw `ghost signals` output as canonical inventory.
- Never treat auto-draft as a CLI feature or a replacement for human curation.
- Never invent surface-composition obligations absent from evidence or human direction.
- Never promote subjective taste directly into checks; make it deterministic or keep it advisory.
diff --git a/packages/ghost/src/skill-bundle/references/critique.md b/packages/ghost/src/skill-bundle/references/critique.md
index 9a92aa4b..12ab8b26 100644
--- a/packages/ghost/src/skill-bundle/references/critique.md
+++ b/packages/ghost/src/skill-bundle/references/critique.md
@@ -13,10 +13,10 @@ description: Critique generated or changed UI using Ghost fingerprint layers.
inventory exemplars, active checks, and repairs where relevant.
When fingerprint layers are silent, you may use nearby product surfaces, local
-components, token and copy conventions, accepted decisions, or human intent when
-present. Label that reasoning as provisional and non-Ghost-backed.
+components, token and copy conventions. Label that reasoning as provisional and
+non-Ghost-backed.
Do not make advisory taste critique sound blocking unless an active check backs
it. If fingerprint grounding or layer coverage is missing or contradictory,
-name that as `missing-memory` or `experience-gap`; edit the Ghost package only
-when the user asks you to.
+name that as `missing-fingerprint` or `experience-gap`; edit the Ghost package
+only when the user asks you to.
diff --git a/packages/ghost/src/skill-bundle/references/patterns.md b/packages/ghost/src/skill-bundle/references/patterns.md
index 9db6f2eb..91e58b05 100644
--- a/packages/ghost/src/skill-bundle/references/patterns.md
+++ b/packages/ghost/src/skill-bundle/references/patterns.md
@@ -26,7 +26,8 @@ Add a pattern when it helps a future agent choose or review:
- a restraint rule that preserves hierarchy, density, trust, or pacing
Do not add a pattern just because a value or component exists. Put raw
-observations in `.ghost/fingerprint/sources/cache/` or keep them in scratch notes.
+observations in scratch notes; use `ghost signals` when raw repo facts help
+find candidate evidence.
## Shape
@@ -64,7 +65,7 @@ Allowed `kind` values:
- Put obligations that affect failure, disclosure, recovery, or trust in
`prose.experience_contracts`, not only `composition.patterns`.
- Put broad surface intent in `prose.principles`.
-- Add `check_refs` only when a deterministic check exists in `fingerprint/enforcement/checks.yml`.
+- Add `check_refs` only when a deterministic check exists in `fingerprint/checks.yml`.
## Validate
diff --git a/packages/ghost/src/skill-bundle/references/recall.md b/packages/ghost/src/skill-bundle/references/recall.md
index 7932dab4..70633aed 100644
--- a/packages/ghost/src/skill-bundle/references/recall.md
+++ b/packages/ghost/src/skill-bundle/references/recall.md
@@ -8,8 +8,7 @@ description: Recall applicable Ghost fingerprint layers for a task or file path.
1. Read checked-in `fingerprint/prose.yml`, `fingerprint/inventory.yml`, and `fingerprint/composition.yml` entries.
2. Select relevant prose, inventory exemplars, composition patterns, and active
checks.
-3. Use `ghost stack `, accepted decisions, and intent only when the repo
- has opted into those advanced inputs.
+3. Use `ghost stack ` when the repo has nested fingerprint packages.
4. Summarize only fingerprint refs that apply to the task.
Return:
@@ -17,7 +16,6 @@ Return:
- Applicable fingerprint refs and short claims.
- Inventory exemplars to inspect when generation or review needs a concrete anchor.
- Active checks that may affect the work.
-- Optional decisions or intent that explain why, when present.
- Any gaps where local evidence must carry the reasoning.
If the fingerprint is silent, say that plainly and continue with provisional
diff --git a/packages/ghost/src/skill-bundle/references/review.md b/packages/ghost/src/skill-bundle/references/review.md
index 2576c147..5f6c699c 100644
--- a/packages/ghost/src/skill-bundle/references/review.md
+++ b/packages/ghost/src/skill-bundle/references/review.md
@@ -16,7 +16,7 @@ ghost check --base [
```
Fix deterministic failures first. These come from active
-`fingerprint/enforcement/checks.yml` rules and are the only blocking findings.
+`fingerprint/checks.yml` rules and are the only blocking findings.
## 2. Build Advisory Context
@@ -28,14 +28,14 @@ Use the emitted packet as context. It includes:
- `fingerprint/prose.yml`, `fingerprint/inventory.yml`, and `fingerprint/composition.yml`
- curated inventory exemplars
-- active checks from `fingerprint/enforcement/checks.yml`
-- optional stack, config, intent, or accepted decision context when present or requested
+- active checks from `fingerprint/checks.yml`
+- optional stack or config context when present or requested
- the diff
## 3. Write Advisory Findings
Advisory findings are non-blocking unless tied to an active deterministic check.
-Classify each finding as `fix`, `intentional-divergence`, `missing-memory`,
+Classify each finding as `fix`, `intentional-divergence`, `missing-fingerprint`,
`experience-gap`, or `eval-uncertainty`.
Each finding must cite the diff location, relevant fingerprint core layer refs,
@@ -46,5 +46,5 @@ When fingerprint layers are silent, local evidence can still support advisory
critique. Label those findings as provisional and non-Ghost-backed.
Fingerprint edits are ordinary Git-reviewed edits to the split fingerprint
-package and optional rationale files. Do not silently rewrite the Ghost package
-during review unless the user asks for fingerprint edits.
+package. Do not silently rewrite the Ghost package during review unless the user
+asks for fingerprint edits.
diff --git a/packages/ghost/src/skill-bundle/references/schema.md b/packages/ghost/src/skill-bundle/references/schema.md
index 5ccd02f4..2216410e 100644
--- a/packages/ghost/src/skill-bundle/references/schema.md
+++ b/packages/ghost/src/skill-bundle/references/schema.md
@@ -10,10 +10,7 @@ Canonical package:
prose.yml core surface intent
inventory.yml core material and source links
composition.yml core patterns
- enforcement/checks.yml optional ghost.checks/v1 gates
- memory/intent.md optional human-approved intent
- memory/decisions/ optional ghost.decision/v1 rationale
- sources/cache/ optional generated observations
+ checks.yml optional ghost.checks/v1 gates
```
Git is the approval boundary: checked-in `fingerprint/` core files are
@@ -38,9 +35,8 @@ Use these typed refs:
- `composition.pattern:`
- `check:`
-`inventory.sources[].kind` may be `cache`, `registry`, `file`, `url`, or
-`package`.
+`inventory.sources[].kind` may be `registry`, `file`, `url`, or `package`.
-`fingerprint/enforcement/checks.yml` remains deterministic only. Ref-backed
+`fingerprint/checks.yml` remains deterministic only. Ref-backed
checks are preferred; missing or unresolved derivation refs lint as warnings.
Inventory refs can support a check but do not establish surface guidance alone.
diff --git a/packages/ghost/src/skill-bundle/references/verify.md b/packages/ghost/src/skill-bundle/references/verify.md
index b09db2b3..54233448 100644
--- a/packages/ghost/src/skill-bundle/references/verify.md
+++ b/packages/ghost/src/skill-bundle/references/verify.md
@@ -8,7 +8,7 @@ description: Verify generated UI or fingerprint edits against Ghost.
1. Run `ghost lint .ghost` and `ghost verify .ghost --root ` after
fingerprint edits.
2. Run `ghost check --base ][` after implementation changes.
-3. For advisory review, run `ghost review --base ][ --include-memory`.
+3. For advisory review, run `ghost review --base ][`.
4. For generation setup, run `ghost relay gather ` and read suggested
fingerprint files when the task widens.
5. Inspect generated UI manually or with screenshots when visual fidelity
diff --git a/packages/ghost/src/skill-bundle/references/voice.md b/packages/ghost/src/skill-bundle/references/voice.md
index d79f820d..59467f07 100644
--- a/packages/ghost/src/skill-bundle/references/voice.md
+++ b/packages/ghost/src/skill-bundle/references/voice.md
@@ -26,7 +26,7 @@ Language maps onto the existing layers; do not invent new schema. See
and omits it elsewhere. Unscoped entries reach agents only through ref
edges or the global fallback.
4. Promote only the mechanically detectable subset into
- `fingerprint/enforcement/checks.yml`:
+ `fingerprint/checks.yml`:
- Absolute rules (banned phrases, required boilerplate) become
`forbidden-regex` or `required-regex` checks with `status: active`.
- Recommendations become `status: proposed` so `ghost review` surfaces
diff --git a/packages/ghost/test/cli.test.ts b/packages/ghost/test/cli.test.ts
index 0a20e2cb..1b782dca 100644
--- a/packages/ghost/test/cli.test.ts
+++ b/packages/ghost/test/cli.test.ts
@@ -184,15 +184,15 @@ describe("ghost CLI", () => {
expect(result.stdout).toContain("Core workflow");
expect(result.stdout).toContain("Advanced/package inspection");
expect(result.stdout).toContain("Compare/stance");
- expect(result.stdout).toContain("Maintenance/cache");
+ expect(result.stdout).toContain("Maintenance/legacy");
for (const command of [
"lint [file]",
"init [dir]",
"verify [dir]",
"scan [dir]",
"stack [paths...]",
- "inventory [path]",
- "describe [fingerprint]",
+ "signals [path]",
+ "describe ",
"diff ",
"survey [...surveys]",
"relay [target]",
@@ -897,13 +897,10 @@ design_loop:
readFile(join(dir, ".ghost", "fingerprint", "manifest.yml"), "utf-8"),
).resolves.toContain("schema: ghost.fingerprint-package/v1");
await expect(
- readFile(
- join(dir, ".ghost", "fingerprint", "enforcement", "checks.yml"),
- "utf-8",
- ),
+ readFile(join(dir, ".ghost", "fingerprint", "checks.yml"), "utf-8"),
).resolves.toContain("schema: ghost.checks/v1");
const status = JSON.parse(scan.stdout);
- expect(status.cache.state).toBe("missing");
+ expect(status.cache).toBeUndefined();
const lint = await runCli(["lint"], dir);
const verify = await runCli(["verify", ".ghost", "--root", "."], dir);
@@ -1359,12 +1356,12 @@ design_loop:
it("warns for checks grounded in omitted sparse fingerprint refs", async () => {
await runCli(["init"], dir);
await writeFile(
- join(dir, ".ghost", "fingerprint", "enforcement", "checks.yml"),
+ join(dir, ".ghost", "fingerprint", "checks.yml"),
`schema: ghost.checks/v1
id: local
checks:
- - id: missing-memory-check
- title: Missing memory check
+ - id: missing-fingerprint-check
+ title: Missing fingerprint check
status: active
severity: serious
derivation:
@@ -1389,19 +1386,19 @@ checks:
expect(report.issues[0]).toMatchObject({
severity: "warning",
rule: "check-grounding-unknown",
- path: "fingerprint/enforcement/checks.yml.checks[0].derivation.prose[0]",
+ path: "fingerprint/checks.yml.checks[0].derivation.prose[0]",
});
});
it("validates standalone checks.yml derivation refs with a valid sibling fingerprint", async () => {
await writeCheckPackage(dir, { checks: false });
await writeFile(
- join(dir, ".ghost", "fingerprint", "enforcement", "checks.yml"),
+ join(dir, ".ghost", "fingerprint", "checks.yml"),
checksFileWithDerivation("prose.principle:not-recorded"),
);
const lint = await runCli(
- ["lint", ".ghost/fingerprint/enforcement/checks.yml", "--format", "json"],
+ ["lint", ".ghost/fingerprint/checks.yml", "--format", "json"],
dir,
);
@@ -1438,20 +1435,18 @@ checks:
});
it("keeps standalone checks.yml lint non-blocking when the sibling fingerprint is invalid", async () => {
- await mkdir(join(dir, ".ghost", "fingerprint", "enforcement"), {
- recursive: true,
- });
+ await mkdir(join(dir, ".ghost", "fingerprint"), { recursive: true });
await writeFile(
join(dir, ".ghost", "fingerprint", "manifest.yml"),
"not: draft\n",
);
await writeFile(
- join(dir, ".ghost", "fingerprint", "enforcement", "checks.yml"),
+ join(dir, ".ghost", "fingerprint", "checks.yml"),
checksFileWithDerivation("prose.principle:tokenized-ui-color"),
);
const lint = await runCli(
- ["lint", ".ghost/fingerprint/enforcement/checks.yml", "--format", "json"],
+ ["lint", ".ghost/fingerprint/checks.yml", "--format", "json"],
dir,
);
@@ -1465,7 +1460,7 @@ checks:
});
it("initializes a bundle and reports fingerprint capture state as json", async () => {
- const init = await runCli(["init", "--with-intent"], dir);
+ const init = await runCli(["init"], dir);
const scan = await runCli(["scan", "--format", "json"], dir);
const scanHuman = await runCli(["scan"], dir);
@@ -1476,23 +1471,19 @@ checks:
expect(init.stdout).toContain("composition.yml:");
expect(init.stdout).toContain("checks.yml:");
expect(init.stdout).not.toContain("cache/:");
+ expect(init.stdout).not.toContain("memory/intent.md:");
expect(
await readFile(
join(dir, ".ghost", "fingerprint", "manifest.yml"),
"utf-8",
),
).toContain("schema: ghost.fingerprint-package/v1");
- expect(
- await readFile(
- join(dir, ".ghost", "fingerprint", "memory", "intent.md"),
- "utf-8",
- ),
- ).toContain("# Intent");
expect(scan.code).toBe(0);
const status = JSON.parse(scan.stdout);
expect(status.fingerprint.state).toBe("present");
expect(status.proposals).toBeUndefined();
- expect(status.cache.state).toBe("missing");
+ expect(status.cache).toBeUndefined();
+ expect(status.intent).toBeUndefined();
expect(status.readiness.state).toBe("fingerprint-empty");
expect(status.readiness.layer_counts).toEqual({
prose: 0,
@@ -1515,6 +1506,12 @@ checks:
expect(scanHuman.stdout).not.toContain("memory dir:");
});
+ it("rejects removed init intent flag", async () => {
+ await expect(runCli(["init", "--with-intent"], dir)).rejects.toThrow(
+ "Unknown option `--withIntent`",
+ );
+ });
+
it("initializes a blank product scaffold with config and reference library wiring", async () => {
const init = await runCli(
[
@@ -1528,7 +1525,7 @@ checks:
dir,
);
const scan = await runCli(["scan", "--format", "json"], dir);
- const inventory = await runCli(["inventory"], dir);
+ const signals = await runCli(["signals"], dir);
await mkdir(join(dir, "packages", "ghost-ui", ".ghost"), {
recursive: true,
});
@@ -1605,19 +1602,19 @@ checks:
expect(status.readiness.state).toBe("inventory-only");
expect(status.readiness.missing_layers).toEqual(["prose", "composition"]);
- const inventoryOutput = JSON.parse(inventory.stdout);
- expect(inventoryOutput.config.libraries[0].id).toBe("ghost-ui");
+ const signalsOutput = JSON.parse(signals.stdout);
+ expect(signalsOutput.config.libraries[0].id).toBe("ghost-ui");
expect(verify.code).toBe(0);
});
- it("runs inventory, lint, and verify from the unified cli", async () => {
+ it("runs signals, lint, and verify from the unified cli", async () => {
await writeCheckPackage(dir);
- const inventory = await runCli(["inventory"], dir);
+ const signals = await runCli(["signals"], dir);
const lint = await runCli(["lint"], dir);
const verify = await runCli(["verify", ".ghost", "--root", "."], dir);
- expect(inventory.code).toBe(0);
- expect(await realpath(JSON.parse(inventory.stdout).root)).toBe(
+ expect(signals.code).toBe(0);
+ expect(await realpath(JSON.parse(signals.stdout).root)).toBe(
await realpath(dir),
);
expect(lint.code).toBe(0);
@@ -1644,7 +1641,7 @@ checks:
expect(status.fingerprint.state).toBe("present");
expect(status.checks.state).toBe("present");
expect(status.proposals).toBeUndefined();
- expect(status.cache.state).toBe("present");
+ expect(status.cache).toBeUndefined();
expect(status.readiness.state).toBe("fingerprint-partial");
expect(status.readiness.missing_layers).toEqual(["composition"]);
expect(status.readiness.reasons[0]).toContain(
@@ -1736,26 +1733,6 @@ checks:
it("emits review commands from the unified cli", async () => {
await writeCheckPackage(dir);
- await mkdir(join(dir, ".ghost", "fingerprint", "sources", "cache"), {
- recursive: true,
- });
- await writeFile(
- join(dir, ".ghost", "fingerprint", "sources", "cache", "inventory.json"),
- JSON.stringify(
- {
- root: dir,
- platform_hints: ["ios"],
- build_system_hints: ["spm"],
- language_histogram: [{ name: "swift", files: 12 }],
- package_manifests: ["Package.swift"],
- candidate_config_files: ["Code/Theme.swift"],
- registry_files: [],
- top_level_tree: [{ path: "Code/", kind: "dir", child_count: 3 }],
- },
- null,
- 2,
- ),
- );
await writeFile(
join(dir, ".ghost", "fingerprint.md"),
fingerprintWithId("local"),
@@ -2062,7 +2039,7 @@ checks:
expect(result.stdout).not.toContain("Proposal Threshold");
expect(result.stdout).toContain("provisional and non-Ghost-backed");
expect(result.stdout).not.toContain("recommend-proposal");
- expect(result.stdout).toContain("missing-memory");
+ expect(result.stdout).toContain("missing-fingerprint");
expect(result.stdout).toContain("experience-gap");
expect(result.stdout).toContain("repair or intentional-divergence");
expect(result.stdout).not.toContain("schema: ghost.fingerprint/v1");
@@ -2169,9 +2146,8 @@ libraries:
});
});
- it("review omits accepted decisions by default", async () => {
+ it("review omits removed memory fields", async () => {
await writeCheckPackage(dir);
- await writeMemoryFiles(dir);
await writeFile(
join(dir, "change.patch"),
lendingPatch("let color = CashTheme.primary"),
@@ -2189,98 +2165,30 @@ libraries:
expect(packet.proposal_types).toBeUndefined();
expect(packet.open_proposals).toBeUndefined();
expect(packet.accepted_decisions).toBeUndefined();
+ expect(packet.intent).toBeUndefined();
expect(packet.memory).toBeUndefined();
});
- it("review includes only accepted decisions when requested", async () => {
- await writeCheckPackage(dir);
- await writeMemoryFiles(dir);
- await writeFile(
- join(dir, "change.patch"),
- lendingPatch("let color = CashTheme.primary"),
- );
-
- const result = await runCli(
- [
- "review",
- "--diff",
- "change.patch",
- "--include-memory",
- "--format",
- "json",
- ],
- dir,
- );
-
- expect(result.code).toBe(0);
- const packet = JSON.parse(result.stdout);
- expect(packet.accepted_decisions).toHaveLength(1);
- expect(packet.accepted_decisions[0].id).toBe("checkout-reversibility");
- expect(packet.memory).toBeUndefined();
- expect(JSON.stringify(packet.accepted_decisions)).not.toContain(
- "rejected-decision",
- );
- expect(JSON.stringify(packet.accepted_decisions)).not.toContain(
- "saved-payment-empty-state",
- );
- });
-
- it("review --package includes only accepted decisions when requested", async () => {
- await writeCheckPackage(dir);
- await writeMemoryFiles(dir);
- await writeFile(
- join(dir, "change.patch"),
- lendingPatch("let color = CashTheme.primary"),
- );
-
- const result = await runCli(
- [
- "review",
- "--diff",
- "change.patch",
- "--package",
- ".ghost",
- "--include-memory",
- "--format",
- "json",
- ],
- dir,
- );
-
- expect(result.code).toBe(0);
- const packet = JSON.parse(result.stdout);
- expect(packet.accepted_decisions).toHaveLength(1);
- expect(packet.accepted_decisions[0].id).toBe("checkout-reversibility");
- expect(JSON.stringify(packet.accepted_decisions)).not.toContain(
- "rejected-decision",
- );
- expect(JSON.stringify(packet.accepted_decisions)).not.toContain(
- "saved-payment-empty-state",
- );
- });
-
- it("review --package points empty decision memory to the canonical path", async () => {
+ it("rejects removed review memory flag", async () => {
await writeCheckPackage(dir);
await writeFile(
join(dir, "change.patch"),
lendingPatch("let color = CashTheme.primary"),
);
- const result = await runCli(
- [
- "review",
- "--diff",
- "change.patch",
- "--package",
- ".ghost",
- "--include-memory",
- ],
- dir,
- );
-
- expect(result.code).toBe(0);
- expect(result.stdout).toContain("fingerprint/memory/decisions");
- expect(result.stdout).not.toContain(".ghost/decisions");
+ await expect(
+ runCli(
+ [
+ "review",
+ "--diff",
+ "change.patch",
+ "--include-memory",
+ "--format",
+ "json",
+ ],
+ dir,
+ ),
+ ).rejects.toThrow("Unknown option `--includeMemory`");
});
it("check routes changed files through nested stacks by default", async () => {
@@ -2714,7 +2622,7 @@ async function writeSplitFingerprintPackage(
): Promise {
const fingerprintDir = join(pkg, "fingerprint");
const doc = parseYaml(fingerprintRaw) as Record;
- await mkdir(join(fingerprintDir, "enforcement"), { recursive: true });
+ await mkdir(fingerprintDir, { recursive: true });
await Promise.all([
writeFile(
join(fingerprintDir, "manifest.yml"),
@@ -2747,12 +2655,7 @@ async function writeSplitFingerprintPackage(
stringifyYaml(doc.composition ?? { patterns: [] }),
),
...(checksRaw
- ? [
- writeFile(
- join(fingerprintDir, "enforcement", "checks.yml"),
- checksRaw,
- ),
- ]
+ ? [writeFile(join(fingerprintDir, "checks.yml"), checksRaw)]
: []),
]);
}
@@ -2781,43 +2684,6 @@ checks:
`;
}
-async function writeMemoryFiles(dir: string): Promise {
- const pkg = join(dir, ".ghost", "fingerprint", "memory");
- await mkdir(join(pkg, "decisions"), { recursive: true });
- await writeFile(
- join(pkg, "decisions", "checkout-reversibility.yml"),
- `schema: ghost.decision/v1
-id: checkout-reversibility
-status: accepted
-title: Reversibility before money movement
-claim: Payment review must make reversibility visible before final submission.
-rationale: Users need confidence before committing money movement.
-scope:
- roles: [design, engineering, pm, qa]
- scopes: [checkout]
- surface_types: [payment-review]
- pattern_ids: [confirmation-before-commit]
-evidence:
- - path: apps/checkout/review.tsx
- note: Review step exposes edit affordances before submit.
-decided_at: "2026-05-17T00:00:00.000Z"
-`,
- );
- await writeFile(
- join(pkg, "decisions", "rejected-decision.yml"),
- `schema: ghost.decision/v1
-id: rejected-decision
-status: rejected
-title: Rejected experience direction
-claim: This should not appear in drift packets.
-rationale: Rejected decisions are non-canonical rationale.
-evidence:
- - note: Rejected in design review.
-decided_at: "2026-05-17T00:00:00.000Z"
-`,
- );
-}
-
async function writeNestedCheckPackage(
dir: string,
memoryDir = ".ghost",
@@ -2827,12 +2693,6 @@ async function writeNestedCheckPackage(
join(dir, "apps", "checkout"),
memoryDir,
);
- await mkdir(join(rootMemory, "fingerprint", "sources", "cache"), {
- recursive: true,
- });
- await mkdir(join(checkoutMemory, "fingerprint", "sources", "cache"), {
- recursive: true,
- });
await mkdir(join(dir, "apps", "checkout", "review"), { recursive: true });
await mkdir(join(dir, "shared"), { recursive: true });
await writeFile(join(dir, "apps", "checkout", "review", "page.tsx"), "");
diff --git a/packages/ghost/test/context-entrypoint.test.ts b/packages/ghost/test/context-entrypoint.test.ts
index c16b94a7..28a37f16 100644
--- a/packages/ghost/test/context-entrypoint.test.ts
+++ b/packages/ghost/test/context-entrypoint.test.ts
@@ -290,10 +290,6 @@ function context(
targetPaths: ["apps/refunds/settings/page.tsx"],
layerDirs: ["/repo/.ghost", "/repo/apps/refunds/.ghost"],
fingerprintRaw: "",
- inventory: {
- state: "missing",
- path: ".ghost/fingerprint/sources/cache/inventory.json",
- },
fingerprintLayers: {
manifest: "schema: ghost.fingerprint-package/v1\nid: local\n",
},
diff --git a/packages/ghost/test/fingerprint-package.test.ts b/packages/ghost/test/fingerprint-package.test.ts
index 6015609b..8e2254fa 100644
--- a/packages/ghost/test/fingerprint-package.test.ts
+++ b/packages/ghost/test/fingerprint-package.test.ts
@@ -58,10 +58,10 @@ describe("split fingerprint package", () => {
building_blocks: {}
exemplars: []
sources:
- - id: generated-inventory
- kind: cache
- ref: sources/cache/inventory.json
- note: Refreshable observed repo facts.
+ - id: repo-signals
+ kind: file
+ ref: docs/architecture.md
+ note: Human-curated source material.
`,
);
@@ -70,9 +70,9 @@ sources:
expect(report.errors).toBe(0);
expect(loaded.fingerprint.inventory.sources[0]).toMatchObject({
- id: "generated-inventory",
- kind: "cache",
- ref: "sources/cache/inventory.json",
+ id: "repo-signals",
+ kind: "file",
+ ref: "docs/architecture.md",
});
});
@@ -84,10 +84,10 @@ sources:
building_blocks: {}
exemplars: []
sources:
- - id: generated-inventory
- kind: cache
- ref: sources/cache/inventory.json
- - id: generated-inventory
+ - id: repo-signals
+ kind: file
+ ref: docs/architecture.md
+ - id: repo-signals
kind: file
ref: tmp/inventory.json
`,
diff --git a/packages/ghost/test/fingerprint-stack.test.ts b/packages/ghost/test/fingerprint-stack.test.ts
index dcb25833..6c94ac70 100644
--- a/packages/ghost/test/fingerprint-stack.test.ts
+++ b/packages/ghost/test/fingerprint-stack.test.ts
@@ -82,11 +82,6 @@ describe("nested Ghost fingerprint stacks", () => {
(check) => check.id === "no-hardcoded-color",
)?.status,
).toBe("disabled");
- expect(
- stack.merged.decisions.find(
- (decision) => decision.id === "shared-decision",
- )?.status,
- ).toBe("rejected");
});
it("groups changed files by resolved fingerprint stack", async () => {
@@ -192,9 +187,6 @@ async function writeRootBundle(
memoryDir = ".ghost",
): Promise {
const ghost = memoryPackagePath(dir, memoryDir);
- await mkdir(join(ghost, "fingerprint", "memory", "decisions"), {
- recursive: true,
- });
await writeSplitFingerprintPackage(
ghost,
`schema: ghost.fingerprint/v1
@@ -254,19 +246,6 @@ checks:
observed_count: 3
examples:
- apps/example.tsx
-`,
- );
- await writeFile(
- join(ghost, "fingerprint", "memory", "decisions", "shared-decision.yml"),
- `schema: ghost.decision/v1
-id: shared-decision
-status: accepted
-title: Parent decision
-claim: Parent claim.
-rationale: Parent rationale.
-evidence:
- - note: Parent evidence.
-decided_at: "2026-05-17T00:00:00.000Z"
`,
);
}
@@ -276,9 +255,6 @@ async function writeChildBundle(
memoryDir = ".ghost",
): Promise {
const ghost = memoryPackagePath(root, memoryDir);
- await mkdir(join(ghost, "fingerprint", "memory", "decisions"), {
- recursive: true,
- });
await mkdir(join(root, "review"), { recursive: true });
await writeSplitFingerprintPackage(
ghost,
@@ -350,19 +326,6 @@ checks:
observed_count: 4
examples:
- review/page.tsx
-`,
- );
- await writeFile(
- join(ghost, "fingerprint", "memory", "decisions", "shared-decision.yml"),
- `schema: ghost.decision/v1
-id: shared-decision
-status: rejected
-title: Child decision
-claim: Child claim.
-rationale: Child rationale.
-evidence:
- - path: review/page.tsx
-decided_at: "2026-05-18T00:00:00.000Z"
`,
);
}
@@ -378,7 +341,7 @@ async function writeSplitFingerprintPackage(
): Promise {
const fingerprintDir = join(pkg, "fingerprint");
const doc = parseYaml(fingerprintRaw) as Record;
- await mkdir(join(fingerprintDir, "enforcement"), { recursive: true });
+ await mkdir(fingerprintDir, { recursive: true });
await Promise.all([
writeFile(
join(fingerprintDir, "manifest.yml"),
@@ -411,12 +374,7 @@ async function writeSplitFingerprintPackage(
stringifyYaml(doc.composition ?? { patterns: [] }),
),
...(checksRaw
- ? [
- writeFile(
- join(fingerprintDir, "enforcement", "checks.yml"),
- checksRaw,
- ),
- ]
+ ? [writeFile(join(fingerprintDir, "checks.yml"), checksRaw)]
: []),
]);
}
diff --git a/packages/ghost/test/fixtures/context-sandboxes/harness.ts b/packages/ghost/test/fixtures/context-sandboxes/harness.ts
index 54911930..0dde26bd 100644
--- a/packages/ghost/test/fixtures/context-sandboxes/harness.ts
+++ b/packages/ghost/test/fixtures/context-sandboxes/harness.ts
@@ -10,10 +10,8 @@ export const REPO_ROOT = resolve(
"../../../../..",
);
-export type CacheState = "missing" | "present" | "malformed";
-
export async function createSingleSurfaceSandbox(
- options: { cache?: CacheState; reorderUnrelated?: boolean } = {},
+ options: { reorderUnrelated?: boolean } = {},
): Promise {
const root = await createTempRoot("single");
await writeFiles(root, [
@@ -29,7 +27,6 @@ export async function createSingleSurfaceSandbox(
fingerprint: singleSurfaceFingerprint(options.reorderUnrelated ?? false),
checks: refundChecks(),
});
- await writeCache(root, options.cache ?? "missing");
return root;
}
@@ -178,7 +175,7 @@ async function writePackage(
options: { fingerprint: string; checks?: string },
): Promise {
const fingerprintDir = join(pkg, "fingerprint");
- await mkdir(join(fingerprintDir, "enforcement"), { recursive: true });
+ await mkdir(fingerprintDir, { recursive: true });
await writeFile(
join(fingerprintDir, "manifest.yml"),
"schema: ghost.fingerprint-package/v1\nid: local\n",
@@ -195,35 +192,13 @@ async function writePackage(
);
if (options.checks) {
await writeFile(
- join(fingerprintDir, "enforcement", "checks.yml"),
+ join(fingerprintDir, "checks.yml"),
options.checks,
"utf-8",
);
}
}
-async function writeCache(root: string, state: CacheState): Promise {
- if (state === "missing") return;
- const cacheDir = join(root, ".ghost", "fingerprint", "sources", "cache");
- await mkdir(cacheDir, { recursive: true });
- await writeFile(
- join(cacheDir, "inventory.json"),
- state === "malformed"
- ? "{nope"
- : JSON.stringify(
- {
- platform_hints: ["web"],
- build_system_hints: ["vite"],
- package_manifests: ["package.json"],
- candidate_config_files: ["apps/refunds/theme.ts"],
- },
- null,
- 2,
- ),
- "utf-8",
- );
-}
-
function proseLayer(raw: string): string {
return raw.split("# inventory")[0].replace("# prose\n", "");
}
diff --git a/packages/ghost/test/public-exports.test.ts b/packages/ghost/test/public-exports.test.ts
index 7e878292..15e31540 100644
--- a/packages/ghost/test/public-exports.test.ts
+++ b/packages/ghost/test/public-exports.test.ts
@@ -29,7 +29,7 @@ describe.runIf(hasBuiltExports)("built public exports", () => {
expect(fingerprintApi.writeContextBundle).toBeUndefined();
expect(scanApi.scanStatus).toBeTypeOf("function");
- expect(scanApi.inventory).toBeTypeOf("function");
+ expect(scanApi.signals).toBeTypeOf("function");
expect(scanApi.loadFingerprintStackForPath).toBeTypeOf("function");
expect(scanApi.initFingerprintPackage).toBeUndefined();
expect(scanApi.lintFingerprintPackage).toBeUndefined();
diff --git a/packages/ghost/test/relay.test.ts b/packages/ghost/test/relay.test.ts
index 08d094e8..9b4b2bad 100644
--- a/packages/ghost/test/relay.test.ts
+++ b/packages/ghost/test/relay.test.ts
@@ -13,7 +13,7 @@ describe("relay", () => {
});
it("gathers structured fingerprint context for a target", async () => {
- const root = await track(createSingleSurfaceSandbox({ cache: "present" }));
+ const root = await track(createSingleSurfaceSandbox());
const result = await gatherRelayContext({
cwd: root,
diff --git a/packages/ghost/test/scan-status.test.ts b/packages/ghost/test/scan-status.test.ts
index e8bf769e..d327675b 100644
--- a/packages/ghost/test/scan-status.test.ts
+++ b/packages/ghost/test/scan-status.test.ts
@@ -42,7 +42,7 @@ describe("scanStatus readiness", () => {
const status = await scanStatus(dir);
expect(status.fingerprint.state).toBe("present");
- expect(status.cache.state).toBe("missing");
+ expect("cache" in status).toBe(false);
expect(status.recommended_next).toBeNull();
expect(status.readiness.state).toBe("fingerprint-empty");
expect(status.readiness.layer_counts).toEqual({
@@ -57,7 +57,7 @@ describe("scanStatus readiness", () => {
]);
});
- it("does not count generated cache as canonical inventory readiness", async () => {
+ it("does not report sources cache as package readiness", async () => {
await mkdir(join(dir, "fingerprint", "sources", "cache"), {
recursive: true,
});
@@ -69,7 +69,7 @@ describe("scanStatus readiness", () => {
const status = await scanStatus(dir);
- expect(status.cache.state).toBe("present");
+ expect("cache" in status).toBe(false);
expect(status.readiness.state).toBe("fingerprint-empty");
expect(status.readiness.layer_counts.inventory).toBe(0);
});
@@ -213,7 +213,7 @@ composition:
const status = await scanStatus(dir);
- expect(status.cache.state).toBe("missing");
+ expect("cache" in status).toBe(false);
expect(status.readiness.state).toBe("fingerprint-ready");
expect(status.readiness.layer_counts).toEqual({
prose: 1,
]