jacquardSnapshot

← snapshot

9545 bytes
# jacquard — Architecture

Status: milestone 1 (library + narrated demo) complete. Everything below the
"Unblock conditions" heading is working Rust; everything in that section is
not built yet and is listed with an owner.

## 1. The one-paragraph version

jacquard is a content-addressed version DAG in which design decisions and
human attestations are objects alongside code, provenance (human / agent /
mixed) is part of every snapshot's hashed identity, a fail-closed gate
refuses to promote work whose blast radius touches unattested decisions, and
a rendezvous registry — structurally unable to hold content — matches
similar work across organisations and introduces the people doing it. Every
one of those claims is bound to a mechanism that fails the build when
violated: isolation is a property of the Cargo dependency graph, the
privacy boundary is a trait bound plus the absence of a dependency edge, and
settlement is a typestate with a single producing path.

## 2. The finding that shaped everything

| | |
|---|---|
| Original frame | "Record design rationale so it isn't lost" (the ADR tradition) |
| This design | Rationale capture fails when it is homework; it works when the *system* asks at the moment the knowledge exists, and when the record has consequences (the gate) and provenance (verbatim vs synthesized, in the object model) |
| Why | Decades of design-rationale tools (IDEF6, gIBIS) died of authoring cost. An interviewer drafts; a human signs a short verbatim account; the gate makes the record load-bearing without punishing anyone — misses are parked work, never demerits |

## 3. Trust planes

Three planes, declared per-crate in `[package.metadata.jacquard]` and proven
by `cargo xtask boundary` over the `cargo metadata` resolve graph (normal +
build deps; dev-deps exempt):

```
  neutral      jac-core, jac-sketch, xtask
               vocabulary only; may reach nothing above itself

  content      jac-object, jac-decision, jac-gate, jac-git, jac-repo, jac-demo
               may name repository content

  rendezvous   jac-rendezvous
               digests only;  ──X──▶  content   (the forbidden edge)
```

Rules: a `rendezvous` crate may never transitively reach a `content` crate;
a `neutral` crate may never reach either non-neutral plane; any crate's
`forbids` list is enforced as written. The rendezvous privacy claim is
enforced twice by independent mechanisms — the graph rule above, and the
`DigestSafe` trait bound on registry payloads (`jac-core/src/transfer.rs`,
with `compile_fail` doctests) — because a control that silently stops firing
is worse than no control.

## 4. Crate map

| Crate | Plane | Role |
|---|---|---|
| `jac-core` | neutral | Typed ids, BLAKE3 digests with tag-byte domain separation, typed object ids, injectable clocks, identity/provenance vocabulary, `RepoPath`, **`DigestSafe`** |
| `jac-sketch` | neutral | MinHash over 3-word shingles, 64 lanes; `Similarity` as permille (`Eq`/`Ord`, no floats); `ContentSketch`/`DecisionSketch` newtypes |
| `jac-object` | content | Blob / Tree (sorted at construction) / Snapshot (provenance inside the hash); canonical encoding; `ObjectStore` port + re-hashing memory fake; `diff_trees` |
| `jac-decision` | content | `Decision` (+ `RationaleFamily` taxonomy), `Attestation` (human-only constructor), `DecisionRecord<Unsettled/Settled>` sealed typestate, `DecisionLedger` port + fake |
| `jac-gate` | content | `evaluate(ledger, changed) -> Verdict`; `Verdict::default()` is `Blocked` |
| `jac-git` | content | `GitInterop` port; `GitCommitImage` with `Jacquard-*` trailers; `FakeGit` proving id-identical round trips |
| `jac-repo` | content | The engine: commit, branch, propose, attest, promote (fast-forward, gated), sketch — the one sanctioned content→digest crossing |
| `jac-rendezvous` | rendezvous | `Registry` port + memory fake: publish, find-similar (never self), broker introductions |
| `jac-demo` | content | Narrated 11-scene walkthrough; links every plane; ships nowhere |
| `xtask` | neutral | `boundary`, `lint-layout`, `ci` — controls, not conveniences |

## 5. Data model

All objects are identified by `blake3(tag byte ‖ length-prefixed fields)`.
The tag byte (`ObjectTag`) separates kinds; length prefixes make field
boundaries unambiguous; trees are sorted and duplicate-free at construction
so equal directories hash equal.

- **Snapshot** = tree + parents + author + **provenance** + message +
  timestamp. Provenance in the hash is the "cannot be quietly relabelled"
  mechanism.
- **Decision** = title + rationale (may be agent-drafted) + rationale
  families (alternatives / constraints / missed-abstraction / deferred-work /
  confidence-risk — the interview taxonomy) + proposer + scope
  (segment-wise path prefixes) + timestamp. Broadening a scope mints a new
  decision: people attested to the narrow one.
- **Attestation** = decision id + human id + verbatim statement (1..=2000
  chars) + timestamp. Constructor takes `&HumanIdentity` only.

## 6. Failure scenarios

| # | Scenario | Safeguard | Test |
|---|---|---|---|
| F1 | Agent work relabelled human after the fact | Provenance inside the content address | `encode::tests::provenance_changes_identity`; demo scene 3 |
| F2 | An agent attests a decision | No constructor accepts an agent identity | `compile_fail` doctest on `Attestation` |
| F3 | Code writes `DecisionRecord<Settled>` directly | No constructor for `Settled`; fields private; sealed states | `compile_fail` doctest on `DecisionRecord` |
| F4 | A verdict nobody computed is treated as a pass | `Verdict::default()` is `Blocked { NotEvaluated }` | `default_verdict_is_blocked` |
| F5 | A future verdict variant admits by accident | Consumers match `Admitted { .. }` literally; wildcard is the blocked path | `Repo::promote` (`if let`) |
| F6 | The registry acquires a content dependency | Plane rule + `forbids`, both fired in the negative test | `cargo xtask boundary` (verified to fail on an injected edge) |
| F7 | A content-bearing type becomes publishable | `DigestSafe` bound; no blanket impl | `compile_fail` doctest in `jac-rendezvous` |
| F8 | `src/auth-x` governed by a `src/auth` decision (prefix bug) | Segment-wise `RepoPath::is_under` | `prefix_is_segment_wise`; `out_of_scope_unsettled_decisions_do_not_block` |
| F9 | Round trip through git drops provenance | Trailers carry provenance/author/timestamp; import re-derives the id | `round_trip_preserves_identity`; tamper test |
| F10 | Provenance dishonest at creation | — (unmitigated; see unblock conditions) | — |
| F11 | `mod.rs` slips in when the clippy lint fails to fire | Independent xtask grep | `cargo xtask lint-layout` |

## 7. Acceptance claims (mapped to demo scenes)

| # | Claim | Where |
|---|---|---|
| AT-1 | Same tree, different provenance ⇒ different snapshot id | scene 3; `encode` tests |
| AT-2 | Agent proposes; decision is structurally unsettled | scene 4 |
| AT-3 | Unsettled decision in blast radius blocks promotion; ref does not move | scene 5; `the_whole_gate_story` |
| AT-4 | Human attestation settles; same promotion admits | scenes 6–7 |
| AT-5 | Only digests reach the registry; total registry knowledge is printable and boring | scene 8 |
| AT-6 | Independent similar work found across orgs; introduction brokered content-free | scene 9 |
| AT-7 | git round trip yields the identical snapshot id | scene 10; `jac-git` tests |
| AT-8 | An agent cannot attest | discharged by the compiler (compile_fail) |

## 8. Honest scoping

Stated wherever the claims are made, and repeated here:

- Provenance is **unforgeable after the fact**, not verified at creation.
- `HumanIdentity` is a type-level invariant, **not authentication**.
- The gate binds **declared scopes only**; ungoverned paths admit with
  `decisions_checked: 0`, reported honestly.
- MinHash leaks **resemblance by design** — that is the product. It cannot
  leak content, and an adversary with a candidate text can still test it
  against a published sketch.
- Promotion is **fast-forward only**; merge is future work, not a position.

## 9. Unblock conditions

Each has an owner; none is optional before the first real deployment.

1. **Identity & signing** — provenance and attestations verified at
   creation (keys, signatures over object ids), turning F10 from unmitigated
   to mitigated. Owner: jdetle.
2. **The interviewer** — the platform component that elicits rationale at
   promotion time: trigger engine with a hard interruption budget,
   significance classifier, question taxonomy over `RationaleFamily`,
   author-approved synthesis into `Decision` objects. The substrate stores
   what it produces; the interviewer itself is a service around it. Owner:
   jdetle.
3. **Real git adapter** — `GitInterop` over gitoxide or the CLI, with the
   trailer format frozen as a wire format. Owner: jdetle.
4. **Registry transport** — a network adapter for `Registry`, with
   authenticated org identity and rate limits; the payload type already
   bounds what it can ever carry. Owner: jdetle.
5. **Three-way merge** — real merge with conflicts; until then `promote`
   refuses non-fast-forward honestly. Owner: jdetle.
6. **Persistent stores** — on-disk `ObjectStore`/`RefStore`/`DecisionLedger`
   adapters. Owner: jdetle.

## 10. Recorded dissent

None yet — which the adversarial-review process treats as a warning sign,
not a comfort. The first review's target list is seeded in
[the review TLDR](../adversarial-review/reviews/TLDR.md).