feat(wake): W3 — cumulative-state digest renderer + non-circular HMAC signer #904

Merged
Mos merged 1 commits from feat/wake-digest-hmac into main 2026-07-26 00:32:13 +00:00
Owner

Builds on the merged W2 store/ack (EPIC #892). Adds A3 (digest.sh) and A5 (sign.sh) under packages/mosaic/framework/tools/wake/, honoring CONVERGED-DESIGN exactly. One coherent PR.

A3 — digest.sh cumulative-state digest renderer

CONVERGED-DESIGN contract Implementation
§2.1 CUMULATIVE-STATE (state-since-CONSUMED, not a delta) Renders the FULL unacked set since consumed_seq from the durable pending-inbox (store.sh drain). Two still-pending changes both render.
§2.1 TWO-TIER TRUST — orientation who / lane / board-head + changed-obligation list (observed_seq + locators); decides the no-op case with zero tool calls.
§2.1 TWO-TIER TRUST — actionable Every consequential fact rendered ONLY as a CLAIM@seq claim-to-verify ("VERIFY LIVE, do NOT act on this line"); never auto-actioned. Self-sufficiency never exempts a live gate.
§2.1 HARD LOCATORS Every actionable claim must carry repo+issue# / 40-char SHA / file:anchor + a one-call re-verify hint. Missing/imprecise locator = fail-loud (exit 4, nothing emitted).
§2.1 BOUNDING / INJECTION / SECRETS Source free-text only inside a delimited, length-capped, ANSI/bidi/zero-width-stripped untrusted block; secret-canary redaction over any inlined content; whole-digest backstop. Embeds the W2 ack copy-run line.
§1.2/§2.3 draws from the store Reads the coalesced durable pending-inbox via store.sh drain (or --from-file/--stdin).

A5 — sign.sh non-circular HMAC signer

CONVERGED-DESIGN contract Implementation
§2.5 non-circular tuple wake_id generated INDEPENDENTLY at emit (not derived from, not a member of, the signed field-tuple). wake_mac = HMAC(key, wake_id ‖ agent_identity ‖ mission_generation ‖ observed_seq ‖ emit_ts ‖ content_hash) — over wake_id PLUS the fields; the MAC is never its own input. Envelope splits wake_id / signed{…} / wake_mac to make non-circularity visible.
§2.5 fills the placeholder sign-entry fills the hmac field W2 left as "" and attaches the independent wake_id; content_hash binds {observed_seq,class,emit_ts,locators}.
§2.5 key by-name Key resolved BY NAME from the operator credential store (.wake.hmac_keys.<name>); never inlined, never echoed, no flag accepts key material; unresolvable name = fail-loud.
§2.5 threat boundary Same-uid boundary documented in-file (same-uid attacker can read the key / observe argv → out of scope); off-uid signer named as the future §4 G6 gate.

Subcommands: sign, sign-digest, sign-entry, verify, verify-entry.

RED-FIRST evidence

test-wake-digest-hmac.sh (wired into test:framework-shell, after the W2 harness). Each invariant was verified to go RED on a targeted regression of the implementation:

Test Invariant RED demo (mutation → failing assertion)
D1 cumulative-state drain→tail -1 (delta) → "OLDER change (issue #11) dropped"
D2 hard-locator fail-loud gate forced true → "must FAIL, but render succeeded / expected exit 4 got 0"
D3 two-tier render calls git → "orientation no-op made a live tool call"
D4 scrub _scrub_ctrlcat → "ANSI ESC / bidi / C0 survived the scrub"
H1 non-circular HMAC wake_id derived-fixed → "not independently generated"; drop content_hash → "tampering content_hash did NOT break the MAC"
H2 key-by-name-never-inline add --key flag → "accepted an inline-key flag"

Gates

  • test:framework-shell full chain: green (rc=0), new harness passes 6/6.
  • shellcheck -x clean on digest.sh, sign.sh, test-wake-digest-hmac.sh.
  • Firewall: grep -niE 'jason|woltje|jarvis' = 0; no $HOME/src defaults; sanitization gate passes. Operator-agnostic (XDG/env only).
  • prettier --check clean on the edited package.json.

Out of scope (untouched): detector (W4), FN-oracle/reconciler (W5), beacon (W6), installer (W7).

Part of #892

Builds on the merged W2 store/ack (EPIC #892). Adds **A3** (`digest.sh`) and **A5** (`sign.sh`) under `packages/mosaic/framework/tools/wake/`, honoring CONVERGED-DESIGN exactly. One coherent PR. ## A3 — `digest.sh` cumulative-state digest renderer | CONVERGED-DESIGN contract | Implementation | |---|---| | §2.1 CUMULATIVE-STATE (state-since-CONSUMED, not a delta) | Renders the FULL unacked set since `consumed_seq` from the durable pending-inbox (`store.sh drain`). Two still-pending changes both render. | | §2.1 TWO-TIER TRUST — orientation | who / lane / board-head + changed-obligation list (observed_seq + locators); decides the no-op case with **zero tool calls**. | | §2.1 TWO-TIER TRUST — actionable | Every consequential fact rendered ONLY as a `CLAIM@seq` claim-to-verify ("VERIFY LIVE, do NOT act on this line"); never auto-actioned. Self-sufficiency never exempts a live gate. | | §2.1 HARD LOCATORS | Every actionable claim must carry repo+issue# / 40-char SHA / file:anchor + a one-call re-verify hint. Missing/imprecise locator = **fail-loud (exit 4, nothing emitted)**. | | §2.1 BOUNDING / INJECTION / SECRETS | Source free-text only inside a delimited, length-capped, ANSI/bidi/zero-width-stripped untrusted block; secret-canary redaction over any inlined content; whole-digest backstop. Embeds the W2 ack copy-run line. | | §1.2/§2.3 draws from the store | Reads the coalesced durable pending-inbox via `store.sh drain` (or `--from-file`/`--stdin`). | ## A5 — `sign.sh` non-circular HMAC signer | CONVERGED-DESIGN contract | Implementation | |---|---| | §2.5 non-circular tuple | `wake_id` generated INDEPENDENTLY at emit (not derived from, not a member of, the signed field-tuple). `wake_mac = HMAC(key, wake_id ‖ agent_identity ‖ mission_generation ‖ observed_seq ‖ emit_ts ‖ content_hash)` — over wake_id PLUS the fields; the MAC is never its own input. Envelope splits `wake_id` / `signed{…}` / `wake_mac` to make non-circularity visible. | | §2.5 fills the placeholder | `sign-entry` fills the `hmac` field W2 left as `""` and attaches the independent `wake_id`; content_hash binds `{observed_seq,class,emit_ts,locators}`. | | §2.5 key by-name | Key resolved BY NAME from the operator credential store (`.wake.hmac_keys.<name>`); never inlined, never echoed, no flag accepts key material; unresolvable name = fail-loud. | | §2.5 threat boundary | Same-uid boundary documented in-file (same-uid attacker can read the key / observe argv → out of scope); off-uid signer named as the future §4 G6 gate. | Subcommands: `sign`, `sign-digest`, `sign-entry`, `verify`, `verify-entry`. ## RED-FIRST evidence `test-wake-digest-hmac.sh` (wired into `test:framework-shell`, after the W2 harness). Each invariant was verified to go **RED** on a targeted regression of the implementation: | Test | Invariant | RED demo (mutation → failing assertion) | |---|---|---| | D1 | cumulative-state | drain→`tail -1` (delta) → "OLDER change (issue #11) dropped" | | D2 | hard-locator fail-loud | gate forced true → "must FAIL, but render succeeded / expected exit 4 got 0" | | D3 | two-tier | render calls `git` → "orientation no-op made a live tool call" | | D4 | scrub | `_scrub_ctrl`→`cat` → "ANSI ESC / bidi / C0 survived the scrub" | | H1 | non-circular HMAC | wake_id derived-fixed → "not independently generated"; drop content_hash → "tampering content_hash did NOT break the MAC" | | H2 | key-by-name-never-inline | add `--key` flag → "accepted an inline-key flag" | ## Gates - `test:framework-shell` full chain: **green** (rc=0), new harness passes 6/6. - `shellcheck -x` clean on `digest.sh`, `sign.sh`, `test-wake-digest-hmac.sh`. - Firewall: `grep -niE 'jason|woltje|jarvis'` = 0; no `$HOME/src` defaults; sanitization gate passes. Operator-agnostic (XDG/env only). - `prettier --check` clean on the edited `package.json`. Out of scope (untouched): detector (W4), FN-oracle/reconciler (W5), beacon (W6), installer (W7). Part of #892
jason.woltje added 1 commit 2026-07-26 00:19:58 +00:00
feat(wake): W3 — cumulative-state digest renderer + non-circular HMAC signer
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
d3318c5910
Builds on the merged W2 store/ack (EPIC #892). Adds A3 (digest.sh) and A5
(sign.sh) under packages/mosaic/framework/tools/wake/, honoring CONVERGED-DESIGN.

A3 — digest.sh (cumulative-state digest renderer), §2.1:
- CUMULATIVE-STATE: renders the FULL unacked set since consumed_seq from the
  durable pending-inbox (state-since-CONSUMED, not an event delta).
- TWO-TIER TRUST: orientation tier (who/lane/board-head + changed-obligation
  list with observed_seq + locators) decides the no-op case with ZERO tool
  calls; actionable tier renders every consequential fact ONLY as a
  CLAIM-TO-VERIFY, point-in-time-at-seq — never auto-actioned.
- HARD LOCATORS: every actionable claim must carry repo+issue#/40-char SHA/
  file:anchor; a missing locator is fail-loud (exit 4, nothing emitted).
- BOUNDING/INJECTION/SECRETS: source free-text is quoted only inside a
  delimited, length-capped, ANSI/bidi/zero-width-stripped untrusted block;
  secret-canary redaction over any inlined content; embeds the W2 ack line.

A5 — sign.sh (non-circular HMAC signer), §2.5:
- wake_id generated INDEPENDENTLY at emit (not derived from, and not a member
  of, the signed field-tuple); wake_mac = HMAC(key, wake_id || agent_identity
  || mission_generation || observed_seq || emit_ts || content_hash) — over
  wake_id PLUS the fields, genuinely non-circular (the MAC is never its own
  input). Fills the `hmac` placeholder W2 left in store entries.
- Key resolved BY NAME from the credential store, never inlined, never echoed;
  no flag accepts key material. Same-uid threat boundary documented; off-uid
  signer named as a future gate.

RED-FIRST tests (test-wake-digest-hmac.sh, wired into test:framework-shell):
cumulative-state, hard-locator fail-loud, two-tier (zero-call orientation +
claim-to-verify), scrub (secret-canary + ANSI/bidi/zero-width), non-circular
HMAC (independent wake_id + tamper-breaks-MAC), key-by-name-never-inline. Each
verified to go RED on a targeted regression. shellcheck clean; operator-agnostic
(XDG/env only). manifest.txt bumped to 0.2.0.

Part of #892

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb
Author
Owner

Record of Review — PR #904 W3 (digest renderer + non-circular HMAC signer; Part of #892)

VERDICT: APPROVE. Reviewer: independent subagent a0499b1d (opus, per CONVERGED-DESIGN §2.1/§2.5) — ≠ builder (mosaic-coder). REVIEWED-HEAD (full-40): d3318c5910eed3ea48d94b58f8ef3f28e9962c42.

Per-criterion (all 8 PASS, re-derived)

  1. Cumulative-state — 2 pending actionables both render (not a delta); D1 red on a true delta mutation.
  2. Two-tier / zero-live-calls — no-op path renders NO-OP with ZERO tool calls (git/gh/tea/curl/ssh tripwired); actionable = CLAIM@seq framed UNTRUSTED-VERIFY-LIVE.
  3. Hard-locator fail-loud — no/imprecise locator → exit 4, 0 bytes stdout. Reproduced.
  4. Scrub — ANSI/bidi/ZWSP/BOM/C0 stripped (LC_ALL=C byte-exact); secret canaries → [REDACTED]; delimited length-capped UNTRUSTED block; whole-digest backstop. Both dimensions red-first.
  5. ★ NON-CIRCULAR HMAC (§2.5) — matches EXACTLY. Reviewer MANUALLY recomputed HMAC(key, wake_id‖agent‖gen‖seq‖ts‖content_hash) = wake_mac. wake_id independently generated (fresh openssl rand — 2 emits over identical fields → different wake_ids) + OUTSIDE signed{} + prepended-to-BIND; genuinely non-circular (wake_mac never its own input). Tamper any signed field / forge wake_id / wrong key → all TAMPERED. All H1 angles red. The brief paraphrase was the wrong gloss; §2.5 formula ships.
  6. Key by-name never-inline — only --key-name (a NAME); inline-key flags rejected; resolved from credential store; unresolvable → fail-loud; no key leak; same-uid boundary + openssl-argv caveat documented, off-uid G6 gate named.
  7. Red-first + wiring — D1-D4/H1×3/H2×2 all reproduced genuinely red; harness wired into test:framework-shell 6/6; W2 harness still green (no regression).
  8. Gates — shellcheck clean; firewall 0; verify-sanitized pass; prettier clean; Part of #892; mergeable; CI TERMINAL-GREEN at head.

Non-blocking hardening (tracked as #905, NOT a merge blocker)

Validator/render asymmetry: a hand-crafted top-level .claim entry via --stdin/--from-file could render without a locator (escaping exit-4). NOT reachable via canonical --from-store (store never emits top-level .claim); trust framing intact (locator-completeness gap, not trust bypass). Fast-follow #905.
GO for id-11 + merge at FULL-40 d3318c5910eed3ea48d94b58f8ef3f28e9962c42. Non-executor.

# Record of Review — PR #904 W3 (digest renderer + non-circular HMAC signer; Part of #892) **VERDICT: APPROVE.** **Reviewer:** independent subagent `a0499b1d` (opus, per CONVERGED-DESIGN §2.1/§2.5) — ≠ builder (`mosaic-coder`). **REVIEWED-HEAD (full-40):** `d3318c5910eed3ea48d94b58f8ef3f28e9962c42`. ## Per-criterion (all 8 PASS, re-derived) 1. **Cumulative-state** — 2 pending actionables both render (not a delta); D1 red on a true delta mutation. 2. **Two-tier / zero-live-calls** — no-op path renders NO-OP with ZERO tool calls (git/gh/tea/curl/ssh tripwired); actionable = `CLAIM@seq` framed UNTRUSTED-VERIFY-LIVE. 3. **Hard-locator fail-loud** — no/imprecise locator → exit 4, 0 bytes stdout. Reproduced. 4. **Scrub** — ANSI/bidi/ZWSP/BOM/C0 stripped (LC_ALL=C byte-exact); secret canaries → [REDACTED]; delimited length-capped UNTRUSTED block; whole-digest backstop. Both dimensions red-first. 5. **★ NON-CIRCULAR HMAC (§2.5) — matches EXACTLY.** Reviewer MANUALLY recomputed HMAC(key, wake_id‖agent‖gen‖seq‖ts‖content_hash) = wake_mac. wake_id independently generated (fresh openssl rand — 2 emits over identical fields → different wake_ids) + OUTSIDE signed{} + prepended-to-BIND; genuinely non-circular (wake_mac never its own input). Tamper any signed field / forge wake_id / wrong key → all TAMPERED. All H1 angles red. The brief paraphrase was the wrong gloss; §2.5 formula ships. 6. **Key by-name never-inline** — only --key-name (a NAME); inline-key flags rejected; resolved from credential store; unresolvable → fail-loud; no key leak; same-uid boundary + openssl-argv caveat documented, off-uid G6 gate named. 7. **Red-first + wiring** — D1-D4/H1×3/H2×2 all reproduced genuinely red; harness wired into test:framework-shell 6/6; W2 harness still green (no regression). 8. **Gates** — shellcheck clean; firewall 0; verify-sanitized pass; prettier clean; Part of #892; mergeable; **CI TERMINAL-GREEN** at head. ## Non-blocking hardening (tracked as #905, NOT a merge blocker) Validator/render asymmetry: a hand-crafted top-level `.claim` entry via --stdin/--from-file could render without a locator (escaping exit-4). NOT reachable via canonical --from-store (store never emits top-level .claim); trust framing intact (locator-completeness gap, not trust bypass). Fast-follow #905. **GO for id-11 + merge at FULL-40 `d3318c5910eed3ea48d94b58f8ef3f28e9962c42`. Non-executor.**
Mos approved these changes 2026-07-26 00:32:11 +00:00
Mos left a comment
First-time contributor

GO — Gate-16 id-11 stamp, pinned to exact FULL head d3318c5910.
Basis: verbatim RoR (comment 18901) — independent contract review APPROVE (all 8 criteria vs CONVERGED-DESIGN §2.1/§2.5, red-first reproduced). The §2.5 HMAC question is ADJUDICATED with manual recomputation: wake_id independently generated, outside the signed field-set, prepended-to-bind — non-circularity is generation-independence, not input-exclusion (builder/Mos/reviewer concordant; the brief gloss was the artifact). Two-tier trust, hard-locator fail-loud, scrub + secret-canary, key-by-name all verified. #905 tracked as non-blocking locator-completeness hardening on non-canonical input paths. CI terminal-green. W3 of EPIC #892. Named executor: Mos (id-11), squash pinned.

GO — Gate-16 id-11 stamp, pinned to exact FULL head d3318c5910eed3ea48d94b58f8ef3f28e9962c42. Basis: verbatim RoR (comment 18901) — independent contract review APPROVE (all 8 criteria vs CONVERGED-DESIGN §2.1/§2.5, red-first reproduced). The §2.5 HMAC question is ADJUDICATED with manual recomputation: wake_id independently generated, outside the signed field-set, prepended-to-bind — non-circularity is generation-independence, not input-exclusion (builder/Mos/reviewer concordant; the brief gloss was the artifact). Two-tier trust, hard-locator fail-loud, scrub + secret-canary, key-by-name all verified. #905 tracked as non-blocking locator-completeness hardening on non-canonical input paths. CI terminal-green. W3 of EPIC #892. Named executor: Mos (id-11), squash pinned.
Mos merged commit 10d957d095 into main 2026-07-26 00:32:13 +00:00
Mos deleted branch feat/wake-digest-hmac 2026-07-26 00:32:14 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#904