Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7e6cf57
docs: cycle 0010 design + FULL-COVERAGE invariant + coverage ratchet
flyingrobots Apr 6, 2026
e39e28e
test: 163 tests for 5 controllers — StrandController, ProvenanceContr…
flyingrobots Apr 6, 2026
b764b7d
test: 202 tests for PatchController, SyncController, QueryController
flyingrobots Apr 6, 2026
70adf53
test: add comprehensive ComparisonController unit tests (61 cases)
flyingrobots Apr 6, 2026
f5b07b6
test: 123 tests for MaterializeController + ComparisonController
flyingrobots Apr 6, 2026
4875fb6
chore: ratchet auto-update 92.12% → 92.86% lines
flyingrobots Apr 6, 2026
a77cedf
test: 129 unit tests for StrandService
flyingrobots Apr 6, 2026
bc436ee
test: 52 unit tests for ConflictAnalyzerService
flyingrobots Apr 6, 2026
7375c93
chore: ratchet auto-update 93.1% → 93.8% lines
flyingrobots Apr 6, 2026
74c7624
test: 53 tests for WarpApp, WarpCore, WarpRuntime gaps
flyingrobots Apr 6, 2026
d33462e
chore: ratchet auto-update 93.8% → 94.18% lines
flyingrobots Apr 6, 2026
f0a9322
test: restore coverage ratchet and log open options debt
flyingrobots Apr 6, 2026
de1c5d9
test: ratchet coverage to 94.5
flyingrobots Apr 6, 2026
2985df8
test: raise coverage to 94.87 and gate ratchet updates
flyingrobots Apr 6, 2026
f3e43a2
docs: require end-of-turn commits in agents guide
flyingrobots Apr 6, 2026
08e21bb
test: raise coverage to 95.17
flyingrobots Apr 6, 2026
7f9d209
test: expand conflict analyzer coverage
flyingrobots Apr 6, 2026
6a01a31
test: drive graph traversal coverage to 95.52
flyingrobots Apr 6, 2026
b54faa0
test: drive query stack coverage to 95.91
flyingrobots Apr 6, 2026
8f8922a
docs: record dag pathfinding raw error smell
flyingrobots Apr 6, 2026
ac0a379
docs: queue service decomposition follow-ups
flyingrobots Apr 6, 2026
b1694c9
test: drive dag pathfinding coverage to 96.12
flyingrobots Apr 6, 2026
55335aa
test: drive conflict analyzer coverage to 96.32
flyingrobots Apr 6, 2026
da6f2b0
test: drive strand service coverage to 96.39
flyingrobots Apr 6, 2026
a6dc223
test: drive runtime coverage to 96.49
flyingrobots Apr 6, 2026
6a01f22
test: raise coverage to 96.66
flyingrobots Apr 6, 2026
cb1c613
test: raise coverage to 96.75
flyingrobots Apr 6, 2026
c24e466
test: raise coverage to 96.86
flyingrobots Apr 6, 2026
6e1f231
test: raise coverage to 96.97
flyingrobots Apr 6, 2026
7ceff26
test: raise coverage to 97.29
flyingrobots Apr 6, 2026
d990d90
test: raise coverage to 97.66
flyingrobots Apr 6, 2026
f0a4e50
docs: close cycle 0010
flyingrobots Apr 6, 2026
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
68 changes: 68 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# AGENTS.md

## Session Start

- Think usage is agent-specific:
- Claude agents use `claude-think`.
- Gemini agents use `gemini-think`.
- Other agents should avoid using think for now.
- Treat Think as memory and coordination, not as repo truth. Anchor claims to files, commands, commits, or tests.

## Git Safety

- NEVER amend commits. Make a new commit instead.
- NEVER rebase.
- NEVER force any git operation.
- NEVER use destructive cleanup or history rewrite commands like `git reset --hard`, `git clean -f`, `git checkout .`, or `git restore .`.
- This repo stores graph data as Git commits; rewriting history can destroy user data.
- At the end of each turn, stage only the specific files written in that turn. Do not use `git add -A` by default.
- If you wrote files in the turn, commit them in that turn. Do not leave your own edits staged but uncommitted.

## Process

- Read [METHOD.md](METHOD.md) and follow it.
- Backlog lives in `docs/method/backlog/` with lanes `inbox/`, `asap/`, `up-next/`, `cool-ideas/`, and `bad-code/`.
- As you work, feel free to file concrete jank, stank, or correctness smells under `docs/method/backlog/bad-code/`.
- As you work, feel free to file speculative improvements or design sparks under `docs/method/backlog/cool-ideas/`.
- Prefer small, precise backlog notes over leaving useful discoveries only in chat.
- Cycles live in `docs/design/<NNNN-slug>/`.
- Retros live in `docs/method/retro/<NNNN-slug>/`.
- Signposts are `docs/BEARING.md` and `docs/VISION.md`; update them at cycle boundaries, not mid-cycle.
- Zero tolerance for brokenness: if you encounter an error or warning in your path, fix it or surface it explicitly.

## Engineering Doctrine

- Read `docs/SYSTEMS_STYLE_JAVASCRIPT.md` before making design-level changes.
- Runtime truth wins. If something has invariants, identity, or behavior, it should exist as a runtime-backed type.
- Validate at boundaries and constructors. Constructors establish invariants and do no I/O.
- Prefer `instanceof` dispatch over tag switching.
- No `any`. Use `unknown` only at raw boundaries and eliminate it immediately.
- No boolean trap parameters. Use named option objects or separate methods.
- No magic strings or numbers when a named constant should exist.
- Hexagonal architecture is mandatory. `src/domain/` does not import host APIs or Node-specific globals.
- Wall clock is banned from `src/domain/`. Time must enter through a port or parameter.
- Domain bytes are `Uint8Array`; `Buffer` stays in infrastructure adapters.

## Repo Context

- `@git-stunts/git-warp` is a multi-writer graph database stored on top of Git.
- Graph data is stored as commits pointing at Git's empty tree (`4b825dc642cb6eb9a060e54bf8d69288fbee4904`).
- Writers append independent patch chains; materialization deterministically merges them through CRDTs.

## Tests and Coverage

- Useful commands:
- `npm run test:local`
- `npm run test:coverage`
- `npm run lint`
- `npm run typecheck`
- Coverage ratchet policy:
- Only `npm run test:coverage` is allowed to update coverage thresholds.
- Targeted or ad hoc coverage runs must not rewrite `vitest.config.js`.
- Critical multi-writer regression suite: `test/unit/domain/WarpGraph.noCoordination.test.js`.

## Release Hygiene

- Full runbook: `docs/method/release.md`.
- Releases require matching versions in `package.json` and `jsr.json`.
- Update `CHANGELOG.md` for externally meaningful changes.
160 changes: 160 additions & 0 deletions docs/design/0010-100-percent-coverage/100-percent-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Cycle 0010 — 100% Code Coverage

**Status:** PARTIAL

**Date:** 2026-04-05

## Sponsors

- **Human:** James Ross
- **Agent:** Claude (Opus)

## Hill

Establish the FULL-COVERAGE invariant and install the CI ratchet.
Write tests for the highest-risk untested code until coverage reaches
100% lines or the cycle reaches a natural break.

## Playback questions

### Agent questions

1. Does `vitest --coverage` report 100% line coverage?
2. Is there a CI-enforceable threshold that prevents regression?
3. Are the untested giants (StrandService, ConflictAnalyzerService,
controllers) now covered?
4. Do the new tests verify behavior, not implementation?

### Human questions

1. Do the tests catch real bugs?
2. Is the coverage number honest (no `/* v8 ignore */` cheats)?

## Baseline (2026-04-05)

| Metric | Value |
|--------|-------|
| Lines | 85.46% |
| Branches | 75.03% |
| Functions | 88.93% |
| Statements | 85.14% |

### Zero-coverage source files (domain)

| File | LOC | Risk |
|------|-----|------|
| `ConflictAnalyzerService.js` | 2582 | Critical — conflict resolution |
| `StrandService.js` | 2060 | Critical — strand lifecycle |
| `ComparisonController.js` | 1212 | High — graph comparison |
| `MaterializeController.js` | 1010 | High — materialization orchestration |
| `QueryController.js` | 946 | High — query dispatch |
| `SyncController.js` | 684 | High — sync orchestration |
| `PatchController.js` | 526 | High — patch lifecycle |
| `WarpCore.js` | 504 | High — plumbing API |
| `CheckpointController.js` | 431 | Medium — checkpoint orchestration |
| `WarpApp.js` | 319 | Medium — product API |
| `ForkController.js` | 294 | Medium — fork operations |
| `SubscriptionController.js` | 248 | Medium — event subscriptions |
| `ProvenanceController.js` | 243 | Medium — provenance queries |
| `StrandController.js` | 182 | Low — strand delegation |
| **Total** | **12,278** | |

## Strategy

### Phase 1 — Install the ratchet

- Add `@vitest/coverage-v8` as devDependency
- Configure vitest coverage thresholds at current baseline (85%)
- Add coverage check to pre-push hook
- Write the FULL-COVERAGE invariant

### Phase 2 — Test the controllers (smallest first)

Controllers are thin delegation layers. They're the fastest path to
coverage gains. Order by LOC ascending:

1. StrandController (182 LOC)
2. ProvenanceController (243 LOC)
3. SubscriptionController (248 LOC)
4. ForkController (294 LOC)
5. CheckpointController (431 LOC)
6. PatchController (526 LOC)
7. SyncController (684 LOC)
8. QueryController (946 LOC)
9. MaterializeController (1010 LOC)
10. ComparisonController (1212 LOC)

### Phase 3 — Test the strand services

The heaviest files. These need deep understanding of strand and
conflict semantics:

11. StrandService (2060 LOC)
12. ConflictAnalyzerService (2582 LOC)

### Phase 4 — Test WarpApp / WarpCore / WarpRuntime

These are integration-level — they orchestrate controllers. May
already get incidental coverage from controller tests.

13. WarpApp (319 LOC)
14. WarpCore (504 LOC)
15. WarpRuntime (1037 LOC)

## Non-goals

- Branch coverage. Line coverage first. Branch coverage is the
follow-on ratchet.
- Mutation testing. That's a separate invariant.
- Test the CLI commands (`bin/cli/commands/`). CLI tests are in BATS.
- Test the visualization barrel files (`index.js` re-exports).

## Accessibility / assistive reading posture

Not applicable — test-only cycle.

## Localization / directionality posture

Not applicable.

## Agent inspectability / explainability posture

Tests are the most inspectable artifact an agent can produce. Each
test file documents the behavior contract of the service it covers.

## Hard gates

- Coverage must not decrease from baseline (ratchet)
- noCoordination suite: 7/7
- Existing test suite: all passing
- No `/* v8 ignore */` suppressions

## Result (2026-04-06)

| Metric | Baseline | Final |
|--------|----------|-------|
| Lines | 85.46% | 97.66% |
| Branches | 75.03% | 87.45% |
| Functions | 88.93% | 96.57% |
| Statements | 85.14% | 97.25% |

### What shipped

- FULL-COVERAGE invariant established in `docs/invariants/full-coverage.md`
- Coverage ratchet installed and later corrected so it only updates on
global `npm run test:coverage`
- Controllers covered end-to-end
- StrandService, ConflictAnalyzerService, WarpApp, WarpCore, and
WarpRuntime all materially covered
- Multiple remaining residue clusters were backlogged explicitly instead
of hidden behind `/* v8 ignore */`

### End state

The cycle reached a natural break at 97.66% line coverage.

The ratchet and the tests are honest. The remaining gap is now mostly:

- import-time / environment-coupled fallback machinery
- defensive tails after normalization or exhaustive loops
- visualization/rendering code that is now a likely product-surface cut
38 changes: 38 additions & 0 deletions docs/invariants/full-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# FULL-COVERAGE

## What must remain true?

Every source file in `src/` must have 100% line coverage from the
unit test suite.

## Why does it matter?

This is a database engine. Untested code is unverified code. 12,278
lines of critical-path code (strand services, controllers, runtime)
shipped with zero tests. The noCoordination suite proves CRDT
semantics, but it cannot prove that individual services handle their
edge cases — error paths, cancellation, boundary conditions,
configuration variants. A correctness bug in an untested service
can corrupt graph state silently.

100% line coverage is not 100% correctness. But 0% line coverage is
0% evidence. The invariant closes the evidence gap.

## How do you check?

```bash
npx vitest run test/unit/ --coverage --coverage.thresholds.lines=100
```

## Baseline

2026-04-05: 85.46% lines, 75.03% branches, 88.93% functions.

## Ratchet

Coverage may only increase. Each cycle that touches source files
must not reduce the coverage percentage. The CI gate enforces this
via `--coverage.thresholds.lines`.

Once 100% is reached, the threshold is locked and any PR that drops
below fails CI.
16 changes: 16 additions & 0 deletions docs/method/backlog/asap/DX_timeoutms-missing-from-type-surface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# timeoutMs missing from WarpApp.open type surface

`timeoutMs` is accepted at runtime by `WarpApp.open()` but is not
declared in `index.d.ts`. TypeScript rejects it:

```
error TS2353: Object literal may only specify known properties,
and 'timeoutMs' does not exist in type '{ graphName: string;
persistence: GraphPersistencePort; writerId: string; ... }'.
```

Found by: graft (flyingrobots/graft) during v0.4.0 WARP Level 1
integration.

Fix: either add `timeoutMs?: number` to the open options type, or
remove the runtime support if it's not a public option.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# PROTO: ConflictAnalyzerService pipeline decomposition

## Legend

PROTO — protocol/domain structural improvement

## Problem

`ConflictAnalyzerService.js` is the largest file in the repo at ~2582
lines. It currently mixes at least five distinct jobs in one module:

- request normalization and filter parsing
- frontier/strand context resolution
- op record and target identity construction
- candidate collection and conflict classification
- trace assembly, note generation, filtering, and snapshot hashing

This violates the Systems Style doctrine in
`docs/SYSTEMS_STYLE_JAVASCRIPT.md`:

- P1: domain concepts with invariants should have runtime-backed forms
- P3: behavior belongs on the type that owns it
- module scope is the first privacy boundary, not the whole service file

The current shape is a long helper-function corridor around one thin
service class. That makes the file hard to test in layers, hard to
review, and too easy to accidentally couple unrelated phases.

## Proposal

Split the analyzer into an explicit pipeline:

- `ConflictAnalysisRequest` or `parseConflictAnalyzeOptions()`:
boundary parsing and normalized filter construction
- `ConflictFrameLoader`:
frontier/strand resolution and patch-frame loading
- `ConflictRecordBuilder`:
receipt-to-record conversion, target identity, effect digests
- `ConflictCandidateCollector`:
supersession/redundancy/eventual-override candidate generation
- `ConflictTraceAssembler`:
grouping, note generation, filtering, and snapshot hashing

Keep `ConflictAnalyzerService` as the facade/orchestrator that wires
those collaborators together.

Also promote the load-bearing plain-object concepts to runtime-backed
forms where they actually carry invariants or behavior:

- normalized analysis request
- conflict target
- conflict resolution
- conflict trace

## Sequencing

Do **not** mix this refactor into the current coverage push.

Recommended order:

1. Finish coverage on the existing analyzer behavior.
2. Lock behavior with tests.
3. Extract one pipeline phase at a time behind the current public API.

## Impact

- Smaller, phase-local tests
- Cleaner ownership of conflict-analysis steps
- Less shape-soup in the analyzer core
- Lower risk when changing one phase of the pipeline

## Related

- `docs/method/backlog/bad-code/CC_conflict-analyzer-god-object.md`
- `docs/method/backlog/bad-code/PROTO_conflict-analyzer-dead-branches.md`

Loading
Loading