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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/delete-dormant-entrypoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@anarchitecture/ghost": patch
---

Remove the dormant context-selection machinery (the Job 2 path-selection graph,
`buildContextEntrypoint`, `buildSelectedContext`, and selection-reasons) that was
inert since the coordinate removal and orphaned once `review` moved onto the
surface rails. Internal cleanup; no public surface change.
9 changes: 9 additions & 0 deletions .changeset/external-contract-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@anarchitecture/ghost": minor
---

Bindings can reference an external contract: a `.ghost.bind.yml` `contract:` now
accepts an npm package name (`@scope/brand`) in addition to `.` (in-repo),
resolved from `node_modules`. `ghost verify` checks the external contract
resolves and that each bound surface exists in it. External fingerprint loading
for grounding remains a follow-up.
9 changes: 9 additions & 0 deletions .changeset/remove-validate-one-check-format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@anarchitecture/ghost": minor
---

Collapse to one check format. Remove `ghost.validate/v1`, the `validate.yml`
facet, the `ghost check` deterministic gate, and the `./govern` export. Ghost
now has a single check format — markdown `ghost.check/v1`, routed by surface
(`ghost checks`) and grounded by the fingerprint. `parseUnifiedDiff` moved to a
neutral module; the `drift` stance ledger is unchanged.
10 changes: 10 additions & 0 deletions .changeset/review-on-surfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@anarchitecture/ghost": minor
---

Rebuild `ghost review` on the surface rails: it now resolves the diff's touched
surfaces (via bindings), selects the markdown checks governing them, and grounds
each in the fingerprint slice — instead of emitting `validate.yml` and a
path-selection context packet. The advisory-review JSON replaces
`fingerprint` / `context_markdown` / `checks` / `stacks` with `touched_surfaces`,
`routed_checks`, and `grounding`. `ghost check` remains the deterministic gate.
48 changes: 2 additions & 46 deletions apps/docs/src/generated/cli-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-06-26T05:19:12.918Z",
"generatedAt": "2026-06-26T12:58:29.867Z",
"tools": [
{
"tool": "ghost",
Expand Down Expand Up @@ -140,7 +140,7 @@
"tool": "ghost",
"name": "scan",
"rawName": "scan [dir]",
"description": "Report sparse fingerprint package contribution facets: intent, inventory, composition, validate, and the next BYOA step.",
"description": "Report sparse fingerprint package contribution facets: intent, inventory, composition, and the next BYOA step.",
"group": "core",
"defaultHelp": true,
"compactName": "scan",
Expand Down Expand Up @@ -631,50 +631,6 @@
}
]
},
{
"tool": "ghost",
"name": "check",
"rawName": "check",
"description": "Run active ghost.validate/v1 gates from the resolved fingerprint stack against a git diff.",
"group": "core",
"defaultHelp": true,
"compactName": "check",
"summary": "Run active deterministic gates against a diff.",
"options": [
{
"rawName": "--base <ref>",
"name": "base",
"description": "Git ref to diff against (default: HEAD)",
"default": null,
"takesValue": true,
"negated": false
},
{
"rawName": "--diff <patch>",
"name": "diff",
"description": "Unified diff file to check instead of running git diff. Use '-' for stdin.",
"default": null,
"takesValue": true,
"negated": false
},
{
"rawName": "--package <dir>",
"name": "package",
"description": "Exact fingerprint package directory; bypasses stack discovery",
"default": null,
"takesValue": true,
"negated": false
},
{
"rawName": "--format <fmt>",
"name": "format",
"description": "Output format: markdown or json",
"default": "markdown",
"takesValue": true,
"negated": false
}
]
},
{
"tool": "ghost",
"name": "review",
Expand Down
21 changes: 20 additions & 1 deletion docs/ideas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,15 @@ buildable Layer 2 design. They agree; read them as a sequence.
entanglements: `relay` and `review` share `context/` machinery (partition,
don't delete wholesale), and `survey` is a command *and* a module (delete the
command surface only). `review` / `emit` / `validate-v1` / the survey module
left for later cuts.
left for later cuts. **Shipped** (`c12f8f1`) — the cutover (Phases 1–8) is
complete.
- `polish-roadmap.md` — sequences the four deferred post-cutover cuts. Key
finding: they are not independent. `review`/`emit` sit on both `validate.yml`
and the dormant Job 2 entrypoint, so **Cut A** (move `review`/`emit` onto
`gather`+`checks`) is the keystone that unblocks **Cut B** (delete the dormant
entrypoint) and **Cut C** (`validate/v1` positioning). **Cut D** (external
contract references in bindings) is independent. The `ghost-core/survey` module
removal is held back as a deeper, separate excavation.

## Independent, still live

Expand All @@ -154,3 +162,14 @@ buildable Layer 2 design. They agree; read them as a sequence.
- Delete notes that only describe superseded package splits, removed commands,
or dead routing/coordinate models after their useful decisions are folded
into current docs.
- `polish-cut-c-plan.md` — execution spec for Cut C, escalated to full removal:
one check format. Deletes `ghost.validate/v1`, `validate.yml`, the `ghost
check` detector gate, and the `./govern` export; rescues `parseUnifiedDiff`
into a neutral module first; preserves the `drift` stance ledger (cleanly
separable from the detector gate). Markdown `ghost.check/v1` becomes the single
check format.
- `polish-cut-d-plan.md` — execution spec for Cut D: external contract references
in bindings. A `.ghost.bind.yml` `contract:` accepts `.` (in-repo) or an npm
package name resolved from `node_modules`; `ghost verify` checks the external
contract resolves and its bound surfaces exist. Resolution + validation only;
external fingerprint loading for grounding is deferred.
108 changes: 108 additions & 0 deletions docs/ideas/polish-cut-c-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
status: exploring
---

# Polish Cut C plan: collapse to one check format (remove ghost.validate/v1)

Decision (settled by the user): **two check formats make no sense — default down
to one, the markdown `ghost.check/v1`.** This escalates Cut C from the roadmap's
"keep the deterministic gate" (Option 1) to **full removal of `ghost.validate/v1`
and the `ghost check` deterministic-gate command** (Option 2).

Governance is now entirely: `ghost checks` (route + ground markdown checks) and
`ghost review` (the advisory packet over the same). The agent evaluates; Ghost
never runs a detector.

## What dies

- **`ghost.validate/v1`**: `ghost-core/checks/{schema,types,lint,routing}.ts`,
`GhostValidateSchema`, `GhostCheck`, `routeGhostValidateForPath`, the
`validate.yml` facet and its file-kind/dispatch.
- **`ghost check`** (the deterministic gate) and **`ghost drift ... check`'s**
detector path — `core/check.ts`'s detector evaluation, `runGhostDriftCheck`,
`inline-color-literals`, `gate.ts` if detector-only.
- The **`./govern`** public export and `govern.ts` (it re-exports the check
runner).
- `validate.yml` from `ghost init` scaffolding and the package paths/loader.

## The two things to rescue first (read before deleting)

1. **`parseUnifiedDiff` lives in `core/check.ts`** and is imported by the *new*
`review-packet.ts` and `checks-command.ts`. It is generic diff parsing, not
validate logic. **Move it** to a neutral home (e.g. `scan/diff.ts` or
`core/diff.ts`) before deleting `core/check.ts`, and repoint the two callers.
2. **`drift` is two things.** `ghost drift status` / `ghost drift check` operate
on the **stance ledger** (`.ghost-sync.json`, tracked-fingerprint identity) —
that is *not* the detector gate and must survive. Only the
`validate.yml`-detector evaluation inside `core/check.ts` dies. Confirm which
parts of `core/` are detector-only vs. drift-ledger before cutting.

## Open decision in this cut

**Does `ghost check` (the command name) survive, repurposed?** Today `check`
runs deterministic detectors against a diff. With detectors gone, the natural
"check a diff" verb is `ghost checks` (markdown routing + grounding).
Recommendation: **delete `ghost check`** (singular, the detector gate) and let
`ghost checks` (plural, the markdown router) be the diff-checking verb. Note the
near-collision in the changeset; it is intentional (the plural replaces the
singular).

## Steps

1. **Rescue `parseUnifiedDiff`** to a neutral module; repoint `review-packet` and
`checks-command`; drop it from the `core` public surface if it was exported.
2. **Delete the detector gate:** `ghost check` command block in `cli.ts`,
`core/check.ts`'s detector path, `inline-color-literals.ts`, and any
detector-only helpers in `core/`. Preserve the drift-ledger path
(`drift status` / `drift check` over `.ghost-sync.json`).
3. **Delete `ghost-core/checks/`** (schema, types, lint, routing) and its
`#ghost-core` re-exports.
4. **Remove `validate.yml`** from `ghost init` scaffolding, `FingerprintPackagePaths`,
the loader, file-kind detection + dispatch, scan-status/contribution, and
verify-package.
5. **Remove the `./govern` export** from `package.json` and delete `govern.ts`;
update `public-exports.test.ts`.
6. **Update the skill bundle / docs** to state one check format: markdown
`ghost.check/v1`, routed by surface and grounded by the fingerprint.
7. Regenerate the manifest; fill the major changeset.

## Scope boundary (what Cut C does NOT do)

- **Keeps `ghost checks` / `ghost review` / `ghost.check/v1`** — the surviving
governance surface.
- **Keeps `drift` (stance ledger)** — unrelated to detectors.
- **Keeps `ghost-core/survey`** — still its own deferred excavation.
- No new check behavior; this is removal + the diff-parser rescue.

## Tests

- Delete `ghost check` / `validate.yml` test cases (`checks.test.ts`,
`checks-grounding.test.ts`, the cli detector cases).
- `public-exports.test.ts`: drop `./govern` and validate exports.
- Confirm `review` / `checks` still parse diffs after the `parseUnifiedDiff`
move.
- `drift status` / `drift check` (ledger) stay green.
- Full `pnpm test` + `pnpm check` green.

## Changeset

`major` — removes the `ghost check` command, the `./govern` export, the
`ghost.validate/v1` schema and `validate.yml` facet. Note that `ghost checks`
(markdown) is the single remaining check format.

## Process notes

- **Rescue `parseUnifiedDiff` first, as its own step**, so the new commands never
break during the deletion.
- Separate the drift-ledger code from the detector code in `core/` before
cutting — the compiler is the worklist once the gate command is gone.
- This is the largest polish cut (~24 files reference the surface); expect a
Phase-3-style ripple. Delete, then chase to green.
- Mind the terminology guard on changeset/skill prose.

## Read-back

Cut C succeeds if `ghost.validate/v1`, `validate.yml`, the `ghost check`
detector gate, and the `./govern` export are gone; `parseUnifiedDiff` survives in
a neutral home so `review`/`checks` still work; the drift stance ledger is
untouched; and Ghost has exactly one check format — markdown `ghost.check/v1`.
79 changes: 79 additions & 0 deletions docs/ideas/polish-cut-d-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
status: exploring
---

# Polish Cut D plan: external contract references in bindings

The last deferred cut. Today a `.ghost.bind.yml` only supports `contract: .`
(the in-repo root contract); lint hard-rejects anything else. Cut D lets a
binding reference an **external contract** — a published brand package — so a
repo can bind its local paths to surfaces defined by `@scope/brand` in
`node_modules`.

## Scope (from the roadmap, held tight)

- **npm-name references only.** `contract: @scope/brand` or `contract: brand`.
Arbitrary resource-id resolvers (needing host config) are deferred.
- **No new version machinery.** `ack` / `track` already model stance toward a
moving reference; do not reinvent pinning here.
- The cut is **resolution + validation**, not a new runtime.

## The finding that bounds it

The `contract:` field is currently *informational*: lint only checks it is `.`,
and discovery (`readExplicitBinding`) takes the binding's surface ids on faith —
it never cross-checks them against the contract's `surfaces.yml`. And
`gather`/`checks`/`review` operate on the *local* package; composing an external
contract's content already works via `gather --package node_modules/<name>/.ghost`.

So Cut D's real, bounded value is: **resolve the referenced contract and validate
that the bound surfaces exist in it.** Nothing else needs to change.

## What it builds

1. **Schema/lint** accept a contract reference: `.` (in-repo) or an npm package
name (`@scope/name` or `name`). Replace the hard `binding-contract-unsupported`
error with: `.` is always fine; an npm-name is fine *syntactically*;
anything else (a path, a URL, a resource id) is still rejected for now.
2. **A contract resolver** (`scan/contract-resolver.ts`): given a reference and a
starting dir, return the contract's `.ghost/` directory.
- `.` → the in-repo contract (root `.ghost/`, the existing behavior).
- npm name → the nearest `node_modules/<name>/.ghost/` walking up from the
binding's directory. Returns `null` when unresolved.
3. **Verify integration**: a binding with an external `contract:` is validated —
the referenced package resolves and each bound `surface` exists in that
contract's `surfaces.yml`. Unresolved package or unknown surface → a verify
error (`binding-contract-unresolved` / `binding-surface-unknown`).

## What it does NOT do

- **No external fingerprint loading in `gather`/`checks`/`review`.** They stay
local; `--package` already reaches an external package's `.ghost/`. Following a
binding to auto-load an external contract's *content* for grounding is a larger
follow-up, explicitly deferred.
- **No resource-id resolvers, no version pinning, no network fetch.** npm
resolution is filesystem-only (`node_modules`); installing the package is the
host's job.
- The in-repo `contract: .` path is unchanged.

## Steps

1. Add an npm-name matcher to the binding schema/lint; relax the contract check
to accept `.` or a valid npm name, reject the rest.
2. Write `resolveContractDir(reference, fromDir, repoRoot)` in `scan/` — `.` and
npm-name resolution, filesystem-only, `null` on miss.
3. In `verify-package` (or a binding verifier), for each `.ghost.bind.yml` with a
non-`.` contract: resolve it, read its `surfaces.yml`, and assert each bound
surface exists; emit verify errors otherwise.
4. Tests: npm-name lint accept/reject; resolver finds `node_modules/<name>/.ghost`
and returns null when absent; verify flags an unknown surface / unresolved
package; `contract: .` still works unchanged.
5. Update the binding docstring + skill/schema reference to document external
references. Changeset `minor` (additive).

## Read-back

Cut D succeeds if a `.ghost.bind.yml` can declare `contract: @scope/brand`,
Ghost resolves it from `node_modules` and validates the bound surfaces exist in
that contract, the in-repo `.` path is unchanged, and external fingerprint
loading for grounding is explicitly left as a follow-up.
Loading