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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Content attachment metadata API** — `attachContent()` and `attachEdgeContent()` now accept optional `{ mime, size }` metadata hints, persist logical content byte size alongside the `_content` OID, and expose `getContentMeta()` / `getEdgeContentMeta()` for structured `{ oid, mime, size }` reads without manual `_content.*` property handling. Metadata reads stay aligned with the current `_content` attachment instead of inheriting stale sibling props from later manual rewrites.
- **Streaming transitive closure traversal** — Added `transitiveClosureStream()` to the traversal stack so callers can consume reachability edges lazily as an `AsyncGenerator<{ from, to }>` without materializing the full closure array. The existing `transitiveClosure()` API remains and now collects from the stream for backward compatibility.
- **First-class sync trust configuration** — `WarpGraph.open({ trust })` and `graph.syncWith(..., { trust })` now expose an explicit public trust-config surface for sync evaluation instead of relying on hidden controller wiring alone.
- **Fluent `WarpStateV5` test builder** — Added `createStateBuilder()` in `test/helpers/stateBuilder.js` so state-heavy tests can seed nodes, edges, removals, properties, frontier state, and graph materialization through one fluent helper instead of ad hoc OR-Set/LWW mutation.
Expand All @@ -19,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **Content metadata review follow-ups** — `ContentMeta` and `ContentAttachmentOptions` are now exported as public type-only symbols, the consumer smoke test imports them directly, and the git-cas adapter docs now explicitly note that MIME/size hints are accepted for CRDT metadata but are not embedded in CAS manifests.
- **Content metadata surface manifest follow-up** — the declaration contract manifest now matches the shipped API: `attachContent()` / `attachEdgeContent()` include the optional metadata parameter for both patch builders and patch sessions, and `WarpGraph` exports `getContentMeta()` / `getEdgeContentMeta()` in the tracked public surface.
- **Roadmap reconciled after PR #67 / #68 merges** — `ROADMAP.md` and `docs/ROADMAP/COMPLETED.md` now reflect the merged pre-push gate regression work (`B168`) and the current `main` baseline before the issue-45 slice branches off.
- **Large-graph traversal memory profile** — `topologicalSort()` now has a lightweight mode that avoids retaining discovery adjacency when callers do not need it. `levels()` and `transitiveReduction()` were refactored to re-fetch neighbors on demand instead of pinning full topo adjacency in memory, reducing steady-state large-graph working sets.
- **Roadmap reconciled after B87 merge** — `ROADMAP.md` now treats the Markdown code-sample linter as merged work on `main`, advances the CI/tooling wave to start at `B88`, and records the follow-up backlog items for pre-push gate-message regression coverage (`B168`) and archived-doc status guardrails (`B169`).
- **Surface validation accounting** — The declaration surface checker now distinguishes runtime-backed exports from type-only manifest entries and understands namespace declarations, which makes the type-surface contract tighter without forcing runtime exports for pure types.
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,21 +458,28 @@ Attach content-addressed blobs to nodes and edges as first-class payloads (Paper
```javascript
const patch = await graph.createPatch();
patch.addNode('adr:0007'); // sync — queues a NodeAdd op
await patch.attachContent('adr:0007', '# ADR 0007\n\nDecision text...'); // async — writes blob
await patch.attachContent('adr:0007', '# ADR 0007\n\nDecision text...', {
mime: 'text/markdown',
}); // async — writes blob + records metadata
await patch.commit();

// Read content back
const buffer = await graph.getContent('adr:0007'); // Uint8Array | null
const oid = await graph.getContentOid('adr:0007'); // hex SHA or null
const meta = await graph.getContentMeta('adr:0007');
// { oid: 'abc123...', mime: 'text/markdown', size: 28 }

// Edge content works the same way (assumes nodes and edge already exist)
const patch2 = await graph.createPatch();
await patch2.attachEdgeContent('a', 'b', 'rel', 'edge payload');
await patch2.attachEdgeContent('a', 'b', 'rel', 'edge payload', {
mime: 'text/plain',
});
await patch2.commit();
const edgeBuf = await graph.getEdgeContent('a', 'b', 'rel');
const edgeMeta = await graph.getEdgeContentMeta('a', 'b', 'rel');
```

Content blobs survive `git gc` — their OIDs are embedded in the patch commit tree and checkpoint tree, keeping them reachable. If a live `_content` reference points at a missing blob anyway (for example due to manual corruption), `getContent()` / `getEdgeContent()` throw instead of silently returning empty bytes.
Content blobs survive `git gc` — their OIDs are embedded in the patch commit tree and checkpoint tree, keeping them reachable. `attachContent()` / `attachEdgeContent()` also persist logical content byte-size metadata automatically and will store a MIME hint when provided. Historical attachments created before metadata support, or later manual `_content` rewrites that bypass the attachment helpers, may still return `mime: null` / `size: null` from the metadata APIs until they are re-attached through the metadata-aware APIs. If a live `_content` reference points at a missing blob anyway (for example due to manual corruption), `getContent()` / `getEdgeContent()` throw instead of silently returning empty bytes.

### Writer API

Expand Down
20 changes: 9 additions & 11 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ROADMAP — @git-stunts/git-warp

> **Current version:** v14.0.0
> **Last reconciled:** 2026-03-12 (main after PR #66 merge; 26 active standalone items remain after trust/serve hardening, type-surface cleanup, large-graph traversal work, test-infra extraction, the constructor-default lint cleanup, checkpoint content-anchor batching, tree-construction determinism fuzzing, CI gate dedupe, the explicit type-only export manifest split, and the merged Markdown code-sample lint gate)
> **Last reconciled:** 2026-03-13 (main after PR #67 / #68 merges; 25 active standalone items remain after trust/serve hardening, type-surface cleanup, large-graph traversal work, test-infra extraction, the constructor-default lint cleanup, checkpoint content-anchor batching, tree-construction determinism fuzzing, CI gate dedupe, the explicit type-only export manifest split, the merged Markdown code-sample lint gate, the pre-push gate regression harness, and the missing-content blob error hardening)
> **Completed milestones:** [docs/ROADMAP/COMPLETED.md](docs/ROADMAP/COMPLETED.md)

---
Expand Down Expand Up @@ -173,7 +173,7 @@ Archived to [COMPLETED.md](docs/ROADMAP/COMPLETED.md#milestone-11--compass-ii).

## Standalone Lane (Ongoing)

32 active items sorted into priority tiers. Guiding principles: (1) harden first — correctness, memory safety, test infra, CI gates before features; (2) large-graph support is forward-looking — medium priority; (3) CI & Tooling items batch into one PR.
25 active standalone items sorted into priority tiers. Guiding principles: (1) harden first — correctness, memory safety, test infra, CI gates before features; (2) large-graph support is forward-looking — medium priority; (3) CI & Tooling items batch into one PR.

> Completed standalone items archived in [COMPLETED.md](docs/ROADMAP/COMPLETED.md#standalone-lane--completed-items).

Expand Down Expand Up @@ -204,7 +204,7 @@ P1 is complete on `v15`: B36 and B37 landed as the shared test-foundation pass,

### P2 — CI & Tooling (one batch PR)

`B83`, `B85`, `B57`, `B86`, and `B87` are now merged on `main`. The repo now runs both markdownlint and the Markdown JS/TS code-sample linter in the CI fast gate and the local `scripts/hooks/pre-push` firewall. Remaining P2 work starts at B88. That merge also promoted one follow-up item, B168, so the local hook's gate labels and quick-mode messaging now have their own regression-coverage task. B123 is still the largest item and may need to split out if the PR gets too big.
`B83`, `B85`, `B57`, `B86`, `B87`, and `B168` are now merged on `main`. The repo now runs both markdownlint and the Markdown JS/TS code-sample linter in the CI fast gate and the local `scripts/hooks/pre-push` firewall, and the hook's gate labels/quick-mode messaging now have dedicated regression coverage. Remaining P2 work starts at B88. B123 is still the largest item and may need to split out if the PR gets too big.

| ID | Item | Depends on | Effort |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------ |
Expand All @@ -219,8 +219,6 @@ P1 is complete on `v15`: B36 and B37 landed as the shared test-foundation pass,
| B128 | **DOCS CONSISTENCY PREFLIGHT** — automated pass in `release:preflight` verifying changelog/readme/guide updates for behavior changes in hot paths (materialize, checkpoint, sync). From BACKLOG 2026-02-28. | — | S |
| B12 | **DOCS-VERSION-SYNC PRE-COMMIT CHECK** — grep version literals in .md files against `package.json` | — | S |
| B43 | **VITEST EXPLICIT RUNTIME EXCLUDES** — prevent accidental local runs of Docker-only suites | — | S |
| B168 | **PRE-PUSH GATE LABEL REGRESSION TEST** — add a lightweight regression test or shared source for `scripts/hooks/pre-push` gate numbering and quick-mode messaging so local hook text cannot drift from the actual gate layout or CI ordering. From PR #66 review follow-up. | — | S |

### P3 — Type Safety & Surface

No hard dependencies. Pick up opportunistically after P2.
Expand Down Expand Up @@ -337,9 +335,9 @@ Complete on `v15`: **B80** and **B99**.

#### Wave 2: CI & Tooling (P2, one batch PR)

3. **B88, B119, B123, B128, B12, B43, B168**
3. **B88, B119, B123, B128, B12, B43**

Internal chain: **B97 already resolved** → B85 → B57. That chain is complete on `main`, and B168 remains the hook-message drift follow-up from the B87 review cycle. B123 remains the largest remaining item and may need to split out.
Internal chain: **B97 already resolved** → B85 → B57. That chain is complete on `main`, and B168 is merged as the hook-message drift follow-up from the B87 review cycle. B123 remains the largest remaining item and may need to split out.

#### Wave 3: Type Surface (P3)

Expand Down Expand Up @@ -397,11 +395,11 @@ B158 (P7) ──→ B159 (P7) CDC seek cache
| **Milestone (M12)** | 18 | B66, B67, B70, B73, B75, B105–B115, B117, B118 |
| **Milestone (M13)** | 1 | B116 (internal: DONE; wire-format: DEFERRED) |
| **Milestone (M14)** | 16 | B130–B145 |
| **Standalone** | 26 | B12, B28, B34–B35, B43, B53, B54, B76, B79, B88, B96, B98, B102–B104, B119, B123, B127–B129, B147, B152, B155–B156, B168–B169 |
| **Standalone (done)** | 61 | B19, B22, B26, B36–B37, B44, B46, B47, B48–B52, B55, B57, B71, B72, B77, B78, B80–B87, B89–B95, B97, B99–B100, B120–B122, B124, B125, B126, B146, B148–B151, B153, B154, B157–B165, B167 |
| **Standalone** | 25 | B12, B28, B34–B35, B43, B53, B54, B76, B79, B88, B96, B98, B102–B104, B119, B123, B127–B129, B147, B152, B155–B156, B169 |
| **Standalone (done)** | 62 | B19, B22, B26, B36–B37, B44, B46, B47, B48–B52, B55, B57, B71, B72, B77, B78, B80–B87, B89–B95, B97, B99–B100, B120–B122, B124, B125, B126, B146, B148–B151, B153, B154, B157–B168 |
| **Deferred** | 7 | B4, B7, B16, B20, B21, B27, B101 |
| **Rejected** | 7 | B5, B6, B13, B17, B18, B25, B45 |
| **Total tracked** | **146** total; 61 standalone done | |
| **Total tracked** | **146** total; 62 standalone done | |

### STANK.md Cross-Reference

Expand Down Expand Up @@ -505,7 +503,7 @@ B158 (P7) ──→ B159 (P7) CDC seek cache
Every milestone has a hard gate. No milestone blurs into the next.
All milestones are complete: M10 → M12 → M13 (internal) → M11 → M14. M13 wire-format cutover remains deferred by ADR 3 readiness gates.

The active backlog is **26 standalone items** sorted into **8 priority tiers** (P0–P7) with **6 execution waves**. Wave 1 is complete, and Wave 2 now starts at B88 in the CI & Tooling pack, with B168 still active as the remaining hook-message drift follow-up. See [Execution Order](#execution-order) for the full sequence.
The active backlog is **25 standalone items** sorted into **8 priority tiers** (P0–P7) with **6 execution waves**. Wave 1 is complete, and Wave 2 now starts at B88 in the CI & Tooling pack. See [Execution Order](#execution-order) for the full sequence.

Rejected items live in `GRAVEYARD.md`. Resurrections require an RFC.
`BACKLOG.md` retired — all intake goes directly into this file (policy in `CLAUDE.md`).
Expand Down
54 changes: 54 additions & 0 deletions contracts/type-surface.m8.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@
{
"name": "content",
"type": "Buffer | string"
},
{
"name": "metadata",
"type": "ContentAttachmentOptions",
"optional": true
}
],
"returns": "Promise<PatchBuilderV2>"
Expand All @@ -268,6 +273,11 @@
{
"name": "content",
"type": "Buffer | string"
},
{
"name": "metadata",
"type": "ContentAttachmentOptions",
"optional": true
}
],
"returns": "Promise<PatchBuilderV2>"
Expand Down Expand Up @@ -399,6 +409,11 @@
{
"name": "content",
"type": "Buffer | string"
},
{
"name": "metadata",
"type": "ContentAttachmentOptions",
"optional": true
}
],
"returns": "Promise<this>"
Expand All @@ -421,6 +436,11 @@
{
"name": "content",
"type": "Buffer | string"
},
{
"name": "metadata",
"type": "ContentAttachmentOptions",
"optional": true
}
],
"returns": "Promise<this>"
Expand Down Expand Up @@ -694,6 +714,16 @@
],
"returns": "Promise<Buffer | null>"
},
"getContentMeta": {
"async": true,
"params": [
{
"name": "nodeId",
"type": "string"
}
],
"returns": "Promise<ContentMeta | null>"
},
"getEdgeContentOid": {
"async": true,
"params": [
Expand Down Expand Up @@ -730,6 +760,24 @@
],
"returns": "Promise<Buffer | null>"
},
"getEdgeContentMeta": {
"async": true,
"params": [
{
"name": "from",
"type": "string"
},
{
"name": "to",
"type": "string"
},
{
"name": "label",
"type": "string"
}
],
"returns": "Promise<ContentMeta | null>"
},
"neighbors": {
"async": true,
"params": [
Expand Down Expand Up @@ -1313,6 +1361,12 @@
"ComposeWormholesOptions": {
"kind": "interface"
},
"ContentAttachmentOptions": {
"kind": "interface"
},
"ContentMeta": {
"kind": "interface"
},
"CreateBTROptions": {
"kind": "interface"
},
Expand Down
Loading
Loading