v0.3.169 - Operator-Feedback Delivery Ledger + Batched Mark-Delivered
v0.3.169 - Operator-Feedback Delivery Ledger + Batched Mark-Delivered
v0.3.169 closes the v0.3.149 "Still Future: feedback status board" item. Operator
feedback records already carried a status field, but there was no read-only
board to aggregate delivery status and no way to commit a delivery boundary in one
action — the operator AI hand-edited each record's status through
operator-feedback-record --status, one record at a time, and lost track of "how
far did I deliver". Two commands ship together, both additive: no archive
migration, no id rewrite, no hash change.
Prime directives (unchanged)
Privacy — status and ids only. These commands aggregate and mutate delivery
STATUS metadata. They never read a feedback body and never echo feedback ref
values, title values, local absolute paths, tokens, or secrets. The record on
disk holds a feedback ref and title; both are deliberately projected out of
every ledger/mark-delivered result and receipt.Truth — metadata lifecycle only, no external submission.
deliveredhere
means "the operator marked it delivered", the same trust level as the existing
--status delivered. WOM performs no external submission and proves no human
receipt;external_submission_performedstaysfalse.
What ships
-
Read-only delivery ledger (
operator-feedback-ledger). Aliases
feedback-ledger,feedback-board. Enumeratesops/feedback/*.ymlvia
safe_archive_globand, reading ONLY status + feedback id + safe timestamps,
returns counts by status (draft/delivered/acknowledged/resolved/archived), a
pending list ofdraftfeedback ids, and the newest delivery-boundary
timestamp among delivered records. It writes nothing and requires--dry-run.
Malformed or non-mapping records are counted into anunreadablebucket and
skipped so one half-written file never fails the whole board. -
Approval-gated batched mark-delivered (
operator-feedback-mark-delivered).
Aliasfeedback-mark-delivered. In one action it marks every pendingdraft
record asdelivered, stampsdelivered_at, setsreviewed_by, and refreshes
updated_at.--dry-runpreviews which records would transition (by id and
current->new status) and writes nothing.--approve(which requires a safe
--reviewed-by) reads each record, PRESERVES every other field verbatim
(feedback ref, title, related releases, resolved_in,
body_managed_by_this_record,external_submission_performed), re-validates
the mutated record against the shipped schema, writes the transition atomically
per record, and writes a single delivery-boundary receipt under
receipts/operator-feedback/delivery-batch.<timestamp>.<batch-digest>.json
recording the ids, count, reviewer, and a per-batch content digest. The filename
carries that digest (not the whole-second timestamp alone) so two batches
committed within the same wall-clock second cannot collide and silently
overwrite each other's audit receipt.--only <id>marks a single record.Three properties hold by construction:
- Draft-only. It transitions only
draft -> delivered; acknowledged,
resolved, and archived records are never touched (byte-identical after a run). - Idempotent. Because it filters to
draftbefore transitioning, a re-run
once no drafts remain marks nothing new (delivered_count: 0, no records
rewritten). A no-op approve also writes NO receipt — the delivery-boundary
receipt is emitted only when at least one record actually transitioned, so
empty zero-delivery boundary artifacts never accumulate. - Fail-safe per record. A malformed/corrupt record in the target set is
reported and skipped; it never aborts or half-writes records already
validated.
- Draft-only. It transitions only
Why mark-delivered does NOT reuse the record writer
operator-feedback-record rebuilds the record dict entirely from CLI arguments
and never merges the on-disk record. Routing mark-delivered through it would drop
feedback_ref/title/related_releases/resolved_in (there are no flags to
pass them) and break the shipped schema's 11-field required contract. Instead
mark-delivered reads the existing YAML, mutates only the delivery fields, and
re-validates against operator-feedback.schema.json before writing. A dedicated
test asserts the mutated record still conforms to the shipped schema — the release
gates do not run schema validation, so this guarantee is pinned by pytest.
Delivery-boundary honesty
Only records stamped by mark-delivered carry delivered_at. Records that reached
delivered through the older operator-feedback-record --status delivered path
have no delivered_at, so for those the ledger's boundary falls back to their
updated_at. The ledger reports delivery_boundary_stamped_count separately and
labels the boundary as the newest available delivery timestamp, not authoritative
proof of when — or whether — anything was delivered externally.
JSON-contract and schema additions (all additive)
operator-feedback.schema.jsongains two OPTIONAL string properties,
delivered_atandacknowledged_at(not added torequired, so existing
records still validate).- New schema
wom-kit/schemas/operator-feedback-delivery-receipt.schema.json
(wom-kit/operator-feedback-delivery-receipt/v0.1) for the batch receipt, with
external_submission_performed: const falseanddelivery_is_metadata_only: const true.
Test honesty note
New tests use temp-dir fixtures copied from the fake-life archive and never touch a
real archive. They pin: the ledger aggregates counts + a pending list and does NOT
echo a seeded secret-ish feedback ref OR title; mark-delivered --dry-run previews
and writes nothing; --approve transitions only draft->delivered, stamps
delivered_at + reviewed_by, writes exactly one receipt, is idempotent (second
run no-ops AND writes no new receipt), and requires --reviewed-by; --only marks
a single record; seeded acknowledged and resolved records are byte-identical after a
run; the mutated record still conforms to the shipped schema; a malformed record is
reported as unreadable by the ledger and skipped by mark-delivered while the valid
draft still transitions and the return code stays sane; two batches committed in the
same wall-clock second keep two distinct receipts (per-batch digest in the filename);
a hand-authored record whose internal feedback_id is a secret-ish URL/token has that
id redacted to the safe file stem in the ledger pending list, the mark-delivered
output, and the receipt; and neither the mark-delivered result nor the on-disk batch
receipt contains the secret-ish ref or title.
Safety and scope
No archive migration. No id rewrite. No hash change. The ledger is read-only;
mark-delivered writes only status-metadata transitions and a receipt after
approval. delivered is a metadata stamp, never a claim of external submission.
All new commands are additive.
Upgrade
See UPGRADE.md. All changes are additive; no migration is required.