Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
### Relevant ADR(s)

<!-- List all applicable ADR IDs. If none, explicitly state "None". -->
- [ ] ADR-00XX (Worktree Independence and Materialization Architecture)
- [ ] ADR-00XY (Graph-Native Content, Deterministic Materialization, and Workspace Bridge)
- [ ] ADR-0002 (Worktree Independence and Materialization Architecture)
- [ ] ADR-0003 (Graph-Native Content, Deterministic Materialization, and Workspace Bridge)
- [ ] None

### Compliance Declaration
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- **ADR-0004: Content Attachments Belong in git-warp** — Decision record establishing that CAS-backed content-on-node is a git-warp substrate responsibility, not a git-mind domain concern. Aligns with Paper I's `Atom(p)` attachment formalism (#252)
- **Chalk formatting for `extension list`** — `formatExtensionList()` renders extension names in cyan bold, versions dimmed, `[builtin]` in yellow / `[custom]` in magenta, consistent with all other CLI commands (#265)
- **Prefix collision detection** — `registerExtension()` now checks incoming domain prefixes against all registered extensions and throws a descriptive error on overlap. Idempotent re-registration of the same extension name is still allowed (#264)
- **Imperative views declared in extension manifests** — `milestone` and `progress` views added to the roadmap manifest; `traceability`, `coverage`, and `onboarding` views added to the architecture manifest. Purely declarative — makes `extension list` show the full picture (#268)
- **`git mind extension remove <name>` subcommand** — Removes a custom extension from the registry. Throws on built-in or non-existent extensions. `--json` output supported. `removeExtension()` exported from public API (#263)
- **JSON Schema contracts for extension CLI output** — 4 new schemas in `docs/contracts/cli/`: `extension-list`, `extension-validate`, `extension-add`, `extension-remove`. Valid samples added to the contract test harness (#262)
- **Deferred items documented in ROADMAP** — #261 (ephemeral registration) and #269 (`--extension` flag) documented with rationale and recommended H2 slot

### Changed

- **Upgraded `@git-stunts/git-warp`** from v11.3.3 to v11.5.0
- **`registerBuiltinExtensions()` memoized** — Module-level `builtInsLoaded` flag prevents redundant YAML file reads on repeated invocations within the same process (#266)
- **Test count** — 537 tests across 28 files (was 527)

## [3.2.0] - 2026-02-17

### Changed
Expand Down
14 changes: 14 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,20 @@ NEXUS ←── ORACLE │

These items are not assigned to a milestone yet. They'll be scheduled based on user feedback and priorities.

### Extension persistence & ephemeral loading (deferred from M12 polish)

Two issues were filed during the M12 extension polish pass and intentionally deferred:

- **#261 — Ephemeral registration: extension add doesn't persist across invocations.**
`git mind extension add <path>` registers for the current process only. The fix requires a persistence mechanism — a lockfile (`.git/git-mind/extensions.yaml`), graph-stored config, or git-config entries. Each option has different tradeoffs for portability, discoverability, and merge semantics. This also changes the CLI boot sequence for ALL commands (startup must load user extensions after built-ins), so it needs careful design.

- **#269 — `--extension <path>` flag for single-invocation loading.**
A workaround for #261: load an extension for one command only (`git mind view my-view --extension ./ext.yaml`). Useful for CI/CD pipelines that inject custom domain logic. Deferred because this is cleaner to design after #261's persistence exists — the flag would be "like `extension add` but ephemeral", which is only meaningful once `add` is actually persistent.

**Recommended slot:** H2 (CONTENT + MATERIALIZATION) planning. Both issues naturally fall into the extension lifecycle story — persistence is a prerequisite for the extension marketplace vision (H4). Design the persistence mechanism during H2 kickoff, implement as the first H2 deliverable so that all subsequent extension work (content system extensions, materializer extensions) benefits from proper registration.

### Other backlog items

- `git mind onboarding` as a guided walkthrough (not just a view)
- Confidence decay over time (edges rot if not refreshed)
- View composition (combine multiple views)
Expand Down
13 changes: 10 additions & 3 deletions bin/git-mind.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Usage: git mind <command> [options]
*/

import { init, link, view, list, remove, nodes, status, at, importCmd, importMarkdownCmd, exportCmd, mergeCmd, installHooks, processCommitCmd, doctor, suggest, review, diff, set, unsetCmd, extensionList, extensionValidate, extensionAdd } from '../src/cli/commands.js';
import { init, link, view, list, remove, nodes, status, at, importCmd, importMarkdownCmd, exportCmd, mergeCmd, installHooks, processCommitCmd, doctor, suggest, review, diff, set, unsetCmd, extensionList, extensionValidate, extensionAdd, extensionRemove } from '../src/cli/commands.js';
import { parseDiffRefs, collectDiffPositionals } from '../src/diff.js';
import { createContext } from '../src/context-envelope.js';
import { registerBuiltinExtensions } from '../src/extension.js';
Expand Down Expand Up @@ -94,6 +94,8 @@ Commands:
--json Output as JSON
add <manifest> Load and register an extension
--json Output as JSON
remove <name> Unregister an extension by name
--json Output as JSON

Edge types: implements, augments, relates-to, blocks, belongs-to,
consumed-by, depends-on, documents`);
Expand Down Expand Up @@ -377,7 +379,7 @@ switch (command) {
const extFlags = parseFlags(args.slice(2));
switch (subCmd) {
case 'list':
await extensionList(cwd, { json: extFlags.json ?? false });
extensionList(cwd, { json: extFlags.json ?? false });
break;
case 'validate': {
const validatePath = args.slice(2).find(a => !a.startsWith('--'));
Expand All @@ -389,9 +391,14 @@ switch (command) {
await extensionAdd(cwd, addPath, { json: extFlags.json ?? false });
break;
}
case 'remove': {
const removeName = args.slice(2).find(a => !a.startsWith('--'));
extensionRemove(cwd, removeName, { json: extFlags.json ?? false });
break;
}
default:
console.error(`Unknown extension subcommand: ${subCmd ?? '(none)'}`);
console.error('Usage: git mind extension <list|validate|add>');
console.error('Usage: git mind extension <list|validate|add|remove>');
process.exitCode = 1;
}
break;
Expand Down
135 changes: 135 additions & 0 deletions docs/adr/ADR-0004.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# ADR-0004: Content Attachments Belong in git-warp

- **Status:** Accepted
- **Date:** 2026-02-20
- **Deciders:** Git Mind maintainers
- **Related:** ADR-0002, ADR-0003, WARP Paper I (aion-paper-01), M13A VESSEL-CORE roadmap milestone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

ADR-0002 and ADR-0003 references are unresolvable from the current index.

The README ADR index uses placeholder headings ADR-00XX and ADR-00XY — there are no entries numbered ADR-0002 or ADR-0003 that a reader can follow. Consider either linking directly to the ADR files or updating the README headings to use their real numbers so cross-references work.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adr/ADR-0004.md` at line 6, The "Related" references in ADR-0004.md
point to ADR-0002 and ADR-0003 which are unresolvable because the ADR README
index uses placeholder headings like "ADR-00XX" and "ADR-00XY"; fix by updating
the ADR index (README) to use the real ADR numbers or by changing the Related
line in ADR-0004.md to link directly to the actual ADR files (or their correct
numbers) so the cross-reference resolves; search for the README ADR index
headings "ADR-00XX"/"ADR-00XY" and either rename them to the real identifiers or
update ADR-0004.md’s Related entry to the true filenames/URLs for ADR-0002 and
ADR-0003.

- **Upstream impact:** git-warp, git-cas

---

## 1. Context

The M13A (VESSEL-CORE) roadmap milestone calls for a content-on-node system: the ability to attach rich content (documents, specs, narratives) to graph nodes, stored as content-addressed blobs, with full CRDT versioning and time-travel.

The original plan placed this system entirely in git-mind: a `ContentStore` abstraction, a `_content` property convention, `git mind content set/show` commands, storage policy engine, and provenance receipts.

On review, the storage primitive — attaching a content-addressed blob to a graph node — is not a domain concern. It is a graph-level operation that belongs in git-warp.

### 1.1 The Paper Says So

WARP Paper I defines a WARP graph as `(S, α, β)` where:

- `S` is a finite directed multigraph (the skeleton)
- `α : V_S → WARP` assigns an **attachment** to every vertex
- `β : E_S → WARP` assigns an **attachment** to every edge

Attachments are full WARP graphs or **atoms**: `Atom(p)` for some payload `p ∈ P`, where P is "the stuff we are not going to model internally (bytestrings, floats, external object IDs, ...)".

Currently, git-warp models nodes and edges with flat key-value properties. There is no first-class concept of an attachment payload. Content-addressed blob attachment is the concrete realization of `Atom(p)` — it gives nodes (and potentially edges) the ability to carry opaque payload data, exactly as the paper's formalism requires.

### 1.2 The Layering Argument

If content attachment lives in git-warp:

- **Time-travel works for free.** Content SHAs are properties; `materialize({ ceiling })` already handles property-level time-travel.
- **Multi-writer merge works for free.** CRDT conflict resolution on the SHA property follows existing semantics.
- **Observer scoping works for free.** Content visibility follows node visibility.
- **Provenance is already tracked.** Every property mutation carries tick/writer metadata.
- **Any tool on git-warp gets it.** Not just git-mind — any future consumer of the WARP graph API can attach and retrieve content.

If content attachment lives in git-mind, all of the above must be re-implemented or worked around at the application layer, duplicating guarantees that already exist in the substrate.

---

## 2. Decision

**Content attachment is a git-warp responsibility, not a git-mind responsibility.**

git-warp should:

1. **Install `git-cas` as a dependency** — providing content-addressed blob storage backed by the git object store.
2. **Expose an API for attaching content to nodes** — this could be as simple as a property convention (e.g., a CAS key stored as a node property), or a dedicated method on the patch/node API. The exact API design is git-warp's decision.
3. **Expose content read/retrieval** — given a node, retrieve its attached content blob.

git-mind's role becomes a thin CLI/UX layer:

- `git mind content set <node> <file>` — CLI command that calls the git-warp attachment API
- `git mind content show <node>` — CLI command that reads and displays attached content
- `git mind content edit <node>` — editor integration, crash recovery, conflict handling (M13B)
- Storage policy (MIME thresholds, size limits) — domain-level configuration
- Materialization templates that consume attached content (M14)

---

## 3. Alternatives Considered

### 3A: Content system entirely in git-mind (original M13A plan)

git-mind implements `ContentStore` over git plumbing, manages `_content` property convention, handles CAS operations directly.

**Rejected because:**
- Duplicates CRDT/time-travel/observer guarantees already in git-warp
- Other git-warp consumers cannot benefit
- Thickens the git-mind layer with storage plumbing that isn't domain logic
- Diverges from the paper's formalism, which places attachments at the graph level

### 3B: Content as plain git-warp properties (no CAS, inline values)

Store content directly as property values on nodes.

**Rejected because:**
- Property values are not designed for large payloads (documents, images)
- No deduplication across nodes with identical content
- No streaming/chunking for large files
- Misses the `Atom(p)` semantics — atoms are opaque external references, not inline data

---

## 4. Consequences

### For git-warp

- New dependency: `git-cas`
- New API surface: content attachment on nodes (and potentially edges)
- Closer alignment with Paper I's `(S, α, β)` formalism
- git-warp's README/docs should reference the attachment concept

### For git-mind

- M13A scope shrinks significantly — git-mind provides CLI/UX, not storage
- M13B (content editing UX) is unaffected — editor integration remains a git-mind concern
- Existing property-based workflows are unaffected
- The `_content` property convention (if that's the API shape) is documented as a git-warp concern, not a git-mind convention

### For the roadmap

- M13A splits: upstream work in git-warp (attachment API), then thin git-mind CLI layer
- M14 (FORGE/materialization) benefits — the materialization pipeline reads content via the same git-warp API, no git-mind-specific content abstraction needed
- M16 (CITADEL/trust) benefits — trust-scoped content visibility is just trust-scoped property visibility

---

## 5. Relationship to the Paper

| Paper concept | git-warp realization |
|---|---|
| `Atom(p)` — opaque payload | CAS blob referenced by SHA |
| `α(v)` — vertex attachment | Content property on node |
| `β(e)` — edge attachment | Content property on edge (future) |
| `P` — set of atomic payloads | Git object store (content-addressed) |
| Depth-0 attachment | A node whose attachment is a single blob |
| Deeper attachments | Future: nested WARP graphs as attachments (not in scope for this decision) |

This ADR addresses the depth-0 case: nodes carry `Atom(p)` payloads via CAS. The full recursive attachment model (`α(v)` mapping to arbitrary WARP graphs) is a future concern — but this decision establishes the correct layering so that deeper attachments can be added later without architectural rework.

---

## 6. Open Questions (for git-warp)

These are implementation details for the git-warp maintainers:

1. **API shape:** Dedicated `node.attach(blob)` / `node.content()` methods, or a property convention like `_content: <cas-sha>`? Either works; the former is more discoverable.
2. **Edge attachments:** Should edges also support content attachment from day one, or is node-only sufficient for the first iteration?
3. **Integrity verification:** Should `getContent()` verify the SHA on read, or trust the object store?
4. **MIME / metadata:** Should git-warp store content metadata (MIME type, size, encoding) alongside the CAS reference, or leave that to consumers like git-mind?
27 changes: 21 additions & 6 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Use ADRs for decisions that are hard to reverse, cross-cut multiple subsystems,

## ADR Index

## ADR-00XX — Worktree Independence and Materialization Architecture
**Status:** Accepted
## [ADR-0002](./ADR-0002.md) — Worktree Independence and Materialization Architecture
**Status:** Accepted
**Date:** 2026-02-15

### What it establishes
Expand All @@ -24,8 +24,8 @@ Defines the core separation model: **worktree-aware, never worktree-bound**.

---

## ADR-00XY — Graph-Native Content, Deterministic Materialization, and Workspace Bridge
**Status:** Accepted
## [ADR-0003](./ADR-0003.md) — Graph-Native Content, Deterministic Materialization, and Workspace Bridge
**Status:** Accepted
**Date:** 2026-02-15

### What it adds
Expand All @@ -41,10 +41,10 @@ Turns the separation model into an adoption-ready product path without breaking

---

## What changed from ADR-00XX to ADR-00XY
## What changed from ADR-0002 to ADR-0003

1. **From principle to execution:**
ADR-00XX defined boundaries; ADR-00XY defines how users actually work within them.
ADR-0002 defined boundaries; ADR-0003 defines how users actually work within them.

2. **Editing UX became first-class:**
The project now explicitly treats editing ergonomics as a top adoption risk.
Expand Down Expand Up @@ -88,6 +88,21 @@ Recommended sections:

---

## [ADR-0004](./ADR-0004.md) — Content Attachments Belong in git-warp
**Status:** Accepted
**Date:** 2026-02-20

### What it establishes
- Content-on-node (CAS-backed blob attachment) is a **git-warp** responsibility, not git-mind.
- git-warp should install `git-cas` and expose an API for attaching content-addressed blobs to nodes.
- git-mind provides the CLI/UX layer (`content set/show/edit`) on top.
- This aligns git-warp with Paper I's `(S, α, β)` formalism — nodes carrying `Atom(p)` payloads.

### Why it matters
Prevents git-mind from duplicating CRDT, time-travel, observer, and provenance guarantees that already exist in the substrate. Makes the attachment primitive available to any git-warp consumer, not just git-mind.

---

## Quick Contribution Rules

- Keep ADRs concise but specific.
Expand Down
23 changes: 23 additions & 0 deletions docs/contracts/cli/extension-add.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/neuroglyph/git-mind/docs/contracts/cli/extension-add.schema.json",
"title": "git-mind extension add --json",
"description": "Extension registration result from `git mind extension add --json`",
"type": "object",
"required": ["schemaVersion", "command", "name", "version", "views", "lenses"],
"additionalProperties": false,
"properties": {
"schemaVersion": { "type": "integer", "const": 1 },
"command": { "type": "string", "const": "extension-add" },
"name": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 },
"views": {
"type": "array",
"items": { "type": "string" }
},
"lenses": {
"type": "array",
"items": { "type": "string" }
}
}
}
46 changes: 46 additions & 0 deletions docs/contracts/cli/extension-list.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/neuroglyph/git-mind/docs/contracts/cli/extension-list.schema.json",
"title": "git-mind extension list --json",
"description": "Registered extensions from `git mind extension list --json`",
"type": "object",
"required": ["schemaVersion", "command", "extensions"],
"additionalProperties": false,
"properties": {
"schemaVersion": { "type": "integer", "const": 1 },
"command": { "type": "string", "const": "extension-list" },
"extensions": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "version", "builtin", "views", "lenses"],
"additionalProperties": false,
"properties": {
"name": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 },
"description": { "type": "string" },
"builtin": { "type": "boolean" },
"views": {
"type": "array",
"items": {
"type": "object",
"required": ["name"],
"additionalProperties": false,
"properties": {
"name": { "type": "string", "minLength": 1 },
"description": { "type": "string" },
"prefixes": { "type": "array", "items": { "type": "string" } },
"edgeTypes": { "type": "array", "items": { "type": "string" } },
"requireBothEndpoints": { "type": "boolean" }
}
}
},
"lenses": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
}
}
15 changes: 15 additions & 0 deletions docs/contracts/cli/extension-remove.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/neuroglyph/git-mind/docs/contracts/cli/extension-remove.schema.json",
"title": "git-mind extension remove --json",
"description": "Extension removal result from `git mind extension remove --json`",
"type": "object",
"required": ["schemaVersion", "command", "name", "version"],
"additionalProperties": false,
"properties": {
"schemaVersion": { "type": "integer", "const": 1 },
"command": { "type": "string", "const": "extension-remove" },
"name": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 }
}
}
Loading
Loading