record: one enriched record per verified delivery (#39) #76

Merged
larandar merged 1 commit from agent.teyla/Jostoph:tango/39-the-delivery-record into main 2026-08-13 05:40:49 +00:00
Contributor

record: one enriched record per verified delivery (#39)

Today every ruling is stateless: one event, one judgment. This is the
first piece of Epic 1.1 — the delivery record — the design law it
settles on: all steward state is derivable from the delivery stream.

In scope

  • src/record.rs: a JSON-lines write-ahead log distinct from the audit
    trail (#31). One DeliveryRecord per verified delivery — payload
    digest (SHA-256), facts mechanically lowered from the payload (the
    same event/subject/field/nests shape jostoph::deduce::Facts
    already produces), and a reference to the ruling reached
    (event_id + verdict — not a copy of the Decision, which stays
    the audit trail's alone).
  • RECORD_SCHEMA_VERSION, independent of policy::SCHEMA_VERSION: a
    record whose version this binary does not understand is refused
    outright by RecordLog::all, never silently skipped the way a torn
    audit line is.
  • Idempotence by construction: RecordLog::append loads every
    delivery_id already on disk at open() and is a no-op for one it
    has seen — the forge's own retries, or a resent delivery, append
    nothing a second time.
  • jostoph replay --record-log FILE [--repo R] [--since TAG]: reads
    the log, rebuilds the fact view — a deduplicated, sorted union of
    every fact — and prints it. Nothing else reads records; the ruling
    path and the future jostoph curation are meant to read only this
    view.
  • serve --record-log FILE: wires a RecordLog into the webhook
    listener. Extraction happens exactly once, in the one place a
    delivery is known to be verified — after the HMAC signature check,
    once the decision is reached — and a failure to append is loud
    (tracing::error!) but never turns a verified delivery into a
    refused one.

Out of scope

  • Any rule reading the facts (Epic 2.1) — this issue only records.
  • Signing and chaining (S1/S2): every field is read with
    #[serde(default)], so a future record carrying a signature or a
    chain link still parses as one of today's.
  • Business-specific facts — closing keywords (F2), touched paths and
    subjects (F3), new kinds (F4), change-ids (F5). This issue extracts
    only what every delivery mechanically carries; those are additive.
  • Multi-repo rollout (E1) and the seal (S4): --repo filters today's
    one shared log by the payload's own repository.full_name;
    --since cuts ordinally by delivery_id. Both are named provisional
    in the CLI help and in record.rs's doc comment — S4's tag
    annotation and E1's per-repo store give them their durable meaning.

Boundary

  • Facts are extracted only from verified deliveries — the generic
    lowering runs on exactly what the payload carried, nothing inferred.
  • Replay is deterministic: replay folds facts into a BTreeSet, so
    the view has one order regardless of record order, and running it
    twice over the same records yields byte-identical output.

Acceptance, as it ran

  • cargo test: 62 tests, 61 pass (12 new, all green); the one failure
    (policy::tests::every_finding_lands_in_one_comment) is pre-existing
    on main at d25ffa0 — confirmed by stashing this change and
    re-running — and unrelated to this issue's scope.
  • cargo fmt --check and cargo clippy --all-targets -- -D warnings
    clean.
  • nix flake check (eval) passes; nix build .#checks.build fails
    only on the same pre-existing test, confirmed identically on
    unmodified main.
  • Manual end-to-end: jostoph serve --record-log against a real
    HMAC-signed delivery appends one record; resending the identical
    x-forgejo-delivery id appends nothing; jostoph replay reconstructs
    the fact view; --repo correctly includes/excludes by
    repository.full_name.

Closes #39

record: one enriched record per verified delivery (#39) Today every ruling is stateless: one event, one judgment. This is the first piece of Epic 1.1 — the delivery record — the design law it settles on: all steward state is derivable from the delivery stream. **In scope** - `src/record.rs`: a JSON-lines write-ahead log distinct from the audit trail (#31). One `DeliveryRecord` per verified delivery — payload digest (SHA-256), facts mechanically lowered from the payload (the same `event`/`subject`/`field`/`nests` shape `jostoph::deduce::Facts` already produces), and a reference to the ruling reached (`event_id` + `verdict` — not a copy of the `Decision`, which stays the audit trail's alone). - `RECORD_SCHEMA_VERSION`, independent of `policy::SCHEMA_VERSION`: a record whose version this binary does not understand is refused outright by `RecordLog::all`, never silently skipped the way a torn audit line is. - Idempotence by construction: `RecordLog::append` loads every `delivery_id` already on disk at `open()` and is a no-op for one it has seen — the forge's own retries, or a resent delivery, append nothing a second time. - `jostoph replay --record-log FILE [--repo R] [--since TAG]`: reads the log, rebuilds the fact view — a deduplicated, sorted union of every fact — and prints it. Nothing else reads records; the ruling path and the future `jostoph curation` are meant to read only this view. - `serve --record-log FILE`: wires a `RecordLog` into the webhook listener. Extraction happens exactly once, in the one place a delivery is known to be verified — after the HMAC signature check, once the decision is reached — and a failure to append is loud (`tracing::error!`) but never turns a verified delivery into a refused one. **Out of scope** - Any rule reading the facts (Epic 2.1) — this issue only records. - Signing and chaining (S1/S2): every field is read with `#[serde(default)]`, so a future record carrying a signature or a chain link still parses as one of today's. - Business-specific facts — closing keywords (F2), touched paths and subjects (F3), new kinds (F4), change-ids (F5). This issue extracts only what every delivery mechanically carries; those are additive. - Multi-repo rollout (E1) and the seal (S4): `--repo` filters today's one shared log by the payload's own `repository.full_name`; `--since` cuts ordinally by `delivery_id`. Both are named provisional in the CLI help and in `record.rs`'s doc comment — S4's tag annotation and E1's per-repo store give them their durable meaning. **Boundary** - Facts are extracted only from verified deliveries — the generic lowering runs on exactly what the payload carried, nothing inferred. - Replay is deterministic: `replay` folds facts into a `BTreeSet`, so the view has one order regardless of record order, and running it twice over the same records yields byte-identical output. **Acceptance, as it ran** - `cargo test`: 62 tests, 61 pass (12 new, all green); the one failure (`policy::tests::every_finding_lands_in_one_comment`) is pre-existing on `main` at `d25ffa0` — confirmed by stashing this change and re-running — and unrelated to this issue's scope. - `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` clean. - `nix flake check` (eval) passes; `nix build .#checks.build` fails only on the same pre-existing test, confirmed identically on unmodified `main`. - Manual end-to-end: `jostoph serve --record-log` against a real HMAC-signed delivery appends one record; resending the identical `x-forgejo-delivery` id appends nothing; `jostoph replay` reconstructs the fact view; `--repo` correctly includes/excludes by `repository.full_name`. Closes #39
Today every ruling is stateless: one event, one judgment. This is the
first piece of Epic 1.1 — the delivery record — the design law it
settles on: all steward state is derivable from the delivery stream.

**In scope**
- `src/record.rs`: a JSON-lines write-ahead log distinct from the audit
  trail (#31). One `DeliveryRecord` per verified delivery — payload
  digest (SHA-256), facts mechanically lowered from the payload (the
  same `event`/`subject`/`field`/`nests` shape `jostoph::deduce::Facts`
  already produces), and a reference to the ruling reached
  (`event_id` + `verdict` — not a copy of the `Decision`, which stays
  the audit trail's alone).
- `RECORD_SCHEMA_VERSION`, independent of `policy::SCHEMA_VERSION`: a
  record whose version this binary does not understand is refused
  outright by `RecordLog::all`, never silently skipped the way a torn
  audit line is.
- Idempotence by construction: `RecordLog::append` loads every
  `delivery_id` already on disk at `open()` and is a no-op for one it
  has seen — the forge's own retries, or a resent delivery, append
  nothing a second time.
- `jostoph replay --record-log FILE [--repo R] [--since TAG]`: reads
  the log, rebuilds the fact view — a deduplicated, sorted union of
  every fact — and prints it. Nothing else reads records; the ruling
  path and the future `jostoph curation` are meant to read only this
  view.
- `serve --record-log FILE`: wires a `RecordLog` into the webhook
  listener. Extraction happens exactly once, in the one place a
  delivery is known to be verified — after the HMAC signature check,
  once the decision is reached — and a failure to append is loud
  (`tracing::error!`) but never turns a verified delivery into a
  refused one.

**Out of scope**
- Any rule reading the facts (Epic 2.1) — this issue only records.
- Signing and chaining (S1/S2): every field is read with
  `#[serde(default)]`, so a future record carrying a signature or a
  chain link still parses as one of today's.
- Business-specific facts — closing keywords (F2), touched paths and
  subjects (F3), new kinds (F4), change-ids (F5). This issue extracts
  only what every delivery mechanically carries; those are additive.
- Multi-repo rollout (E1) and the seal (S4): `--repo` filters today's
  one shared log by the payload's own `repository.full_name`;
  `--since` cuts ordinally by `delivery_id`. Both are named provisional
  in the CLI help and in `record.rs`'s doc comment — S4's tag
  annotation and E1's per-repo store give them their durable meaning.

**Boundary**
- Facts are extracted only from verified deliveries — the generic
  lowering runs on exactly what the payload carried, nothing inferred.
- Replay is deterministic: `replay` folds facts into a `BTreeSet`, so
  the view has one order regardless of record order, and running it
  twice over the same records yields byte-identical output.

**Acceptance, as it ran**
- `cargo test`: 62 tests, 61 pass (12 new, all green); the one failure
  (`policy::tests::every_finding_lands_in_one_comment`) is pre-existing
  on `main` at `d25ffa0` — confirmed by stashing this change and
  re-running — and unrelated to this issue's scope.
- `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings`
  clean.
- `nix flake check` (eval) passes; `nix build .#checks.build` fails
  only on the same pre-existing test, confirmed identically on
  unmodified `main`.
- Manual end-to-end: `jostoph serve --record-log` against a real
  HMAC-signed delivery appends one record; resending the identical
  `x-forgejo-delivery` id appends nothing; `jostoph replay` reconstructs
  the fact view; `--repo` correctly includes/excludes by
  `repository.full_name`.

Closes #39
larandar deleted branch tango/39-the-delivery-record 2026-08-13 05:40:49 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Ting/Jostoph!76
No description provided.