fix(wake): #914 digest renderer — WAKE_AGENT-prefixed ack line + digest-class locator threading
Some checks failed
ci/woodpecker/pr/ci Pipeline failed

(a) the embedded ack copy-run line (digest.sh -> ack.sh embed) now bakes an
    explicit WAKE_AGENT=<render-time-agent> prefix (scrubbed via _scrub_inline,
    shell-quoted via printf %q), so an env-less copy-run resolves to the
    correct per-agent namespace instead of silently falling back to 'default'.

(b) _locator_line now also recognizes the locator vocabulary detector.sh (A1)
    actually emits for a digest-class entry (kind/id/observed_hash/remote/path
    /branches, not repo/issue/sha/file), so a digest-class ORIENTATION pointer
    carries a usable (soft) locator instead of rendering empty. Display-only:
    _has_hard_locator and the ACTIONABLE-tier hard-locator FAIL-LOUD (exit 4)
    are unchanged.

RED-FIRST: new D5/D6 groups added to test-wake-digest-hmac.sh, verified RED
against unmodified digest.sh/ack.sh and GREEN after the fix (stash/pop
diffed). H1/H2 (openssl-dependent) are now individually SKIPped instead of
short-circuiting the whole file, so D1-D6 (including the new tests) still run
and pass when openssl is masked from PATH.

Touches only digest.sh + ack.sh + their test harness + the wake manifest
version bump; store.sh/detector.sh/reconcile.sh untouched (avoids collision
with the concurrent #908 work).

Closes #914
Part of #892
This commit is contained in:
mosaic-coder
2026-07-26 00:12:54 -05:00
parent 2378665eaf
commit 2fb15b3aa7
4 changed files with 206 additions and 15 deletions

View File

@@ -149,18 +149,40 @@ _has_hard_locator() {
' >/dev/null 2>&1 <<<"$1"
}
# _locator_line LOCATORS_JSON — a scrubbed, one-line hard-locator rendering + a
# one-targeted-call re-verify hint.
# _locator_line LOCATORS_JSON — a scrubbed, one-line locator rendering + a
# one-targeted-call re-verify hint, where available.
#
# #914b: covers BOTH locator vocabularies actually in use:
# - the HARD-locator shape (§2.1, gated by _has_hard_locator, unchanged):
# repo+issue | 40-hex sha | file(:anchor).
# - the shape detector.sh (A1) actually builds for a `digest`-class entry
# (see detector.sh's enqueue-locators jq filter): kind/id/observed_hash +
# whichever of repo/path/anchor/remote/branches the source def declares.
# None of kind/id/observed_hash/remote/path/branches were recognized here
# before, so a real digest-class pointer rendered a bare empty "locator:"
# line despite the entry carrying real (soft, non-hard) locator data. This
# is display-only: it does NOT feed _has_hard_locator, so the
# ACTIONABLE-tier hard-locator FAIL-LOUD gate is untouched.
_locator_line() {
local loc="$1" repo issue sha file anchor head parts='' reverify=''
local remote path kind id ohash branches
repo="$(jq -r '.repo // ""' <<<"$loc")"
issue="$(jq -r '(.issue // "") | tostring' <<<"$loc")"
sha="$(jq -r '.sha // ""' <<<"$loc")"
file="$(jq -r '.file // ""' <<<"$loc")"
anchor="$(jq -r '.anchor // ""' <<<"$loc")"
head="$(jq -r '.head // ""' <<<"$loc")"
remote="$(jq -r '.remote // ""' <<<"$loc")"
path="$(jq -r '.path // ""' <<<"$loc")"
kind="$(jq -r '.kind // ""' <<<"$loc")"
id="$(jq -r '(.id // "") | tostring' <<<"$loc")"
ohash="$(jq -r '.observed_hash // ""' <<<"$loc")"
branches="$(jq -r '(.branches // []) | if length > 0 then join(",") else "" end' <<<"$loc" 2>/dev/null || true)"
[ -n "$kind" ] && parts="$parts kind=$(_scrub_inline "$kind")"
[ "$id" != "" ] && parts="$parts id=$(_scrub_inline "$id")"
[ -n "$head" ] && parts="$parts head=$(_scrub_inline "$head")"
[ -n "$repo" ] && parts="$parts repo=$(_scrub_inline "$repo")"
[ -n "$remote" ] && parts="$parts remote=$(_scrub_inline "$remote")"
[ "$issue" != "" ] && parts="$parts issue=#$(_scrub_inline "$issue")"
[ -n "$sha" ] && parts="$parts sha=$(_scrub_inline "$sha")"
if [ -n "$file" ]; then
@@ -169,8 +191,19 @@ _locator_line() {
else
parts="$parts file=$(_scrub_inline "$file")"
fi
elif [ -n "$path" ]; then
if [ -n "$anchor" ]; then
parts="$parts path=$(_scrub_inline "$path"):$(_scrub_inline "$anchor")"
else
parts="$parts path=$(_scrub_inline "$path")"
fi
fi
# One-targeted-call re-verify hint.
[ -n "$branches" ] && parts="$parts branches=$(_scrub_inline "$branches")"
# observed_hash is a content hash (e.g. sha256 of the polled source), NOT a
# git commit SHA — kept distinct from `sha` so it never impersonates one or
# feeds the `git show <sha>` re-verify hint below.
[ -n "$ohash" ] && parts="$parts observed_hash=$(_scrub_inline "$ohash")"
# One-targeted-call re-verify hint (best available, most-specific first).
if [ -n "$sha" ] && [ -n "$file" ]; then
reverify="git show $(_scrub_inline "$sha"):$(_scrub_inline "$file")"
elif [ -n "$repo" ] && [ "$issue" != "" ]; then
@@ -179,6 +212,12 @@ _locator_line() {
reverify="git show $(_scrub_inline "$sha")"
elif [ -n "$file" ]; then
reverify="re-read $(_scrub_inline "$file")"
elif [ -n "$path" ]; then
reverify="re-read $(_scrub_inline "$path")"
elif [ -n "$kind" ] && [ "$id" != "" ]; then
reverify="re-poll source $(_scrub_inline "$kind")/$(_scrub_inline "$id")"
elif [ -n "$remote" ]; then
reverify="re-read $(_scrub_inline "$remote")"
fi
printf 'locator:%s' "$parts"
[ -n "$reverify" ] && printf '\n re-verify (ONE call): %s' "$reverify"
@@ -350,12 +389,24 @@ cmd_render() {
# Embedded ack copy-run line (W2). CONSUMED is a consumer act; this is the
# exact local-write line the consumer runs after durable capture.
#
# #914a: thread the RENDER-TIME agent into the embedded line as an EXPLICIT
# WAKE_AGENT=<agent> prefix (scrubbed through the same _scrub_inline path
# as every other inlined value — ack.sh additionally shell-quotes it before
# embedding, so it can never become a shell-injection vector when the line
# is later copy-run). Without this, an env-less copy-run resolves
# wake_state_dir() to `${WAKE_AGENT:-default}` = `default`, which silently
# targets the WRONG per-agent namespace (or refuses) unless the consumer's
# ambient shell happens to already export the same WAKE_AGENT. If render
# itself was env-less (agent=="default"), baking "default" is no worse than
# today — the fix wins the common case where WAKE_AGENT was set at render.
printf '\n-- ACK (copy-run; local-write only, never blocks on network) --\n'
local ack_line
local ack_line agent_scrubbed
agent_scrubbed="$(_scrub_inline "$agent")"
if [ -n "$wake_id" ]; then
ack_line="$("$ACK_SH" embed --upto "$observed" --wake-id "$wake_id" 2>/dev/null || true)"
ack_line="$("$ACK_SH" embed --upto "$observed" --agent "$agent_scrubbed" --wake-id "$wake_id" 2>/dev/null || true)"
else
ack_line="$("$ACK_SH" embed --upto "$observed" 2>/dev/null || true)"
ack_line="$("$ACK_SH" embed --upto "$observed" --agent "$agent_scrubbed" 2>/dev/null || true)"
fi
printf '%s\n' "${ack_line:-# ack unavailable}"
} | _redact_secrets