fix(wake): #914 digest renderer — WAKE_AGENT-prefixed ack line + digest-class locator threading (#916)
Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
This commit was merged in pull request #916.
This commit is contained in:
@@ -54,8 +54,23 @@ Commands:
|
|||||||
Local-write only; a background sync
|
Local-write only; a background sync
|
||||||
ships it (never blocks on network).
|
ships it (never blocks on network).
|
||||||
--no-sync suppresses the background ship.
|
--no-sync suppresses the background ship.
|
||||||
embed --upto N [--wake-id ID] Print the copy-run ack line to EMBED in
|
embed --upto N [--wake-id ID] [--agent A]
|
||||||
|
Print the copy-run ack line to EMBED in
|
||||||
a digest (does not perform the ack).
|
a digest (does not perform the ack).
|
||||||
|
--agent bakes an EXPLICIT
|
||||||
|
`WAKE_AGENT=A ` prefix onto the emitted
|
||||||
|
line (render-time namespace), so an
|
||||||
|
ENV-LESS copy-run still resolves to the
|
||||||
|
correct per-agent queue instead of
|
||||||
|
silently falling back to `default`
|
||||||
|
(#914). The value is shell-quoted
|
||||||
|
(`printf %q`) so it can never inject
|
||||||
|
additional shell syntax when the line
|
||||||
|
is later copy-run; the caller is
|
||||||
|
expected to have already scrubbed it
|
||||||
|
(control/ANSI/secrets) the same way
|
||||||
|
any other inlined digest value is
|
||||||
|
scrubbed.
|
||||||
status Print ledger tail + cursor state.
|
status Print ledger tail + cursor state.
|
||||||
|
|
||||||
Environment:
|
Environment:
|
||||||
@@ -210,7 +225,7 @@ cmd_consumed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd_embed() {
|
cmd_embed() {
|
||||||
local upto='' wake_id=''
|
local upto='' wake_id='' agent=''
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--upto)
|
--upto)
|
||||||
@@ -221,6 +236,10 @@ cmd_embed() {
|
|||||||
wake_id="${2:-}"
|
wake_id="${2:-}"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--agent)
|
||||||
|
agent="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "ack.sh embed: unknown option '$1'" >&2
|
echo "ack.sh embed: unknown option '$1'" >&2
|
||||||
exit 2
|
exit 2
|
||||||
@@ -236,10 +255,26 @@ cmd_embed() {
|
|||||||
# The one copy-run line a digest embeds. WAKE_ACK_CLI lets an installer point
|
# The one copy-run line a digest embeds. WAKE_ACK_CLI lets an installer point
|
||||||
# it at the deployed path; default is this script's resolved path.
|
# it at the deployed path; default is this script's resolved path.
|
||||||
local cli="${WAKE_ACK_CLI:-$SCRIPT_DIR/ack.sh}"
|
local cli="${WAKE_ACK_CLI:-$SCRIPT_DIR/ack.sh}"
|
||||||
|
# #914a: bake the render-time agent in as an EXPLICIT `WAKE_AGENT=<agent> `
|
||||||
|
# prefix, so an ENV-LESS copy-run resolves to the SAME per-agent namespace
|
||||||
|
# the digest was rendered for, instead of silently falling back to `default`
|
||||||
|
# (wake_state_dir() resolves purely from ${WAKE_AGENT:-default} at RUN time,
|
||||||
|
# which — absent this prefix — has no relation to the agent the digest was
|
||||||
|
# rendered for). `printf %q` shell-quotes the value so it is embedded as one
|
||||||
|
# opaque token and can never inject additional shell syntax into the line
|
||||||
|
# when it is later copy-run, even if the (caller-scrubbed) agent value still
|
||||||
|
# carries shell metacharacters. This is purely additive: no --agent => the
|
||||||
|
# line is byte-identical to before (existing direct callers unaffected).
|
||||||
|
local prefix=''
|
||||||
|
if [ -n "$agent" ]; then
|
||||||
|
local agent_q
|
||||||
|
printf -v agent_q '%q' "$agent"
|
||||||
|
prefix="WAKE_AGENT=${agent_q} "
|
||||||
|
fi
|
||||||
if [ -n "$wake_id" ]; then
|
if [ -n "$wake_id" ]; then
|
||||||
printf '%s consumed --upto %s --wake-id %s\n' "$cli" "$upto" "$wake_id"
|
printf '%s%s consumed --upto %s --wake-id %s\n' "$prefix" "$cli" "$upto" "$wake_id"
|
||||||
else
|
else
|
||||||
printf '%s consumed --upto %s\n' "$cli" "$upto"
|
printf '%s%s consumed --upto %s\n' "$prefix" "$cli" "$upto"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,18 +149,40 @@ _has_hard_locator() {
|
|||||||
' >/dev/null 2>&1 <<<"$1"
|
' >/dev/null 2>&1 <<<"$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# _locator_line LOCATORS_JSON — a scrubbed, one-line hard-locator rendering + a
|
# _locator_line LOCATORS_JSON — a scrubbed, one-line locator rendering + a
|
||||||
# one-targeted-call re-verify hint.
|
# 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() {
|
_locator_line() {
|
||||||
local loc="$1" repo issue sha file anchor head parts='' reverify=''
|
local loc="$1" repo issue sha file anchor head parts='' reverify=''
|
||||||
|
local remote path kind id ohash branches
|
||||||
repo="$(jq -r '.repo // ""' <<<"$loc")"
|
repo="$(jq -r '.repo // ""' <<<"$loc")"
|
||||||
issue="$(jq -r '(.issue // "") | tostring' <<<"$loc")"
|
issue="$(jq -r '(.issue // "") | tostring' <<<"$loc")"
|
||||||
sha="$(jq -r '.sha // ""' <<<"$loc")"
|
sha="$(jq -r '.sha // ""' <<<"$loc")"
|
||||||
file="$(jq -r '.file // ""' <<<"$loc")"
|
file="$(jq -r '.file // ""' <<<"$loc")"
|
||||||
anchor="$(jq -r '.anchor // ""' <<<"$loc")"
|
anchor="$(jq -r '.anchor // ""' <<<"$loc")"
|
||||||
head="$(jq -r '.head // ""' <<<"$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 "$head" ] && parts="$parts head=$(_scrub_inline "$head")"
|
||||||
[ -n "$repo" ] && parts="$parts repo=$(_scrub_inline "$repo")"
|
[ -n "$repo" ] && parts="$parts repo=$(_scrub_inline "$repo")"
|
||||||
|
[ -n "$remote" ] && parts="$parts remote=$(_scrub_inline "$remote")"
|
||||||
[ "$issue" != "" ] && parts="$parts issue=#$(_scrub_inline "$issue")"
|
[ "$issue" != "" ] && parts="$parts issue=#$(_scrub_inline "$issue")"
|
||||||
[ -n "$sha" ] && parts="$parts sha=$(_scrub_inline "$sha")"
|
[ -n "$sha" ] && parts="$parts sha=$(_scrub_inline "$sha")"
|
||||||
if [ -n "$file" ]; then
|
if [ -n "$file" ]; then
|
||||||
@@ -169,8 +191,19 @@ _locator_line() {
|
|||||||
else
|
else
|
||||||
parts="$parts file=$(_scrub_inline "$file")"
|
parts="$parts file=$(_scrub_inline "$file")"
|
||||||
fi
|
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
|
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
|
if [ -n "$sha" ] && [ -n "$file" ]; then
|
||||||
reverify="git show $(_scrub_inline "$sha"):$(_scrub_inline "$file")"
|
reverify="git show $(_scrub_inline "$sha"):$(_scrub_inline "$file")"
|
||||||
elif [ -n "$repo" ] && [ "$issue" != "" ]; then
|
elif [ -n "$repo" ] && [ "$issue" != "" ]; then
|
||||||
@@ -179,6 +212,12 @@ _locator_line() {
|
|||||||
reverify="git show $(_scrub_inline "$sha")"
|
reverify="git show $(_scrub_inline "$sha")"
|
||||||
elif [ -n "$file" ]; then
|
elif [ -n "$file" ]; then
|
||||||
reverify="re-read $(_scrub_inline "$file")"
|
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
|
fi
|
||||||
printf 'locator:%s' "$parts"
|
printf 'locator:%s' "$parts"
|
||||||
[ -n "$reverify" ] && printf '\n re-verify (ONE call): %s' "$reverify"
|
[ -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
|
# Embedded ack copy-run line (W2). CONSUMED is a consumer act; this is the
|
||||||
# exact local-write line the consumer runs after durable capture.
|
# 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'
|
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
|
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
|
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
|
fi
|
||||||
printf '%s\n' "${ack_line:-# ack unavailable}"
|
printf '%s\n' "${ack_line:-# ack unavailable}"
|
||||||
} | _redact_secrets
|
} | _redact_secrets
|
||||||
|
|||||||
@@ -34,8 +34,19 @@
|
|||||||
# burn-before-enqueue (arrow 1), W5 co-feed aliasing (arrow 2), and
|
# burn-before-enqueue (arrow 1), W5 co-feed aliasing (arrow 2), and
|
||||||
# the migration-restart silent-swallow (arrow 3, now structurally
|
# the migration-restart silent-swallow (arrow 3, now structurally
|
||||||
# impossible — allocation is always > consumed or fails loud).
|
# impossible — allocation is always > consumed or fails loud).
|
||||||
|
# 0.6.2 #914 digest.sh renderer fixes (live wake-pilot findings): (a) the
|
||||||
|
# embedded ack copy-run line now bakes an explicit
|
||||||
|
# WAKE_AGENT=<render-time-agent> prefix (shell-quoted) so an
|
||||||
|
# env-less copy-run resolves to the correct per-agent namespace
|
||||||
|
# instead of silently falling back to `default`; (b) the
|
||||||
|
# ORIENTATION locator renderer now also recognizes the locator
|
||||||
|
# vocabulary detector.sh (A1) actually emits for a digest-class
|
||||||
|
# entry (kind/id/observed_hash/remote/path), so a digest-class
|
||||||
|
# pointer carries a usable (soft) locator instead of rendering
|
||||||
|
# empty. Display-only: the ACTIONABLE-tier hard-locator FAIL-LOUD
|
||||||
|
# gate (_has_hard_locator, exit 4) is unchanged.
|
||||||
component=wake
|
component=wake
|
||||||
version=0.6.1
|
version=0.6.2
|
||||||
|
|
||||||
# Watch-list schema this component consumes, and the INCLUSIVE range of
|
# Watch-list schema this component consumes, and the INCLUSIVE range of
|
||||||
# schema_version values it supports. A wake-watch-list.json whose schema_version
|
# schema_version values it supports. A wake-watch-list.json whose schema_version
|
||||||
|
|||||||
@@ -19,6 +19,15 @@
|
|||||||
# H2 KEY BY-NAME, NEVER INLINE: the key is resolved by NAME from the credential
|
# H2 KEY BY-NAME, NEVER INLINE: the key is resolved by NAME from the credential
|
||||||
# store; no flag accepts key material; the key never leaks into output; the
|
# store; no flag accepts key material; the key never leaks into output; the
|
||||||
# same-uid threat boundary is documented. (§2.5)
|
# same-uid threat boundary is documented. (§2.5)
|
||||||
|
# D5 (#914a) EMBEDDED ACK NAMESPACE: the copy-run ack line rendered at
|
||||||
|
# WAKE_AGENT=X render time targets X EXPLICITLY, so an env-less copy-run
|
||||||
|
# still resolves to the correct per-agent namespace (never silently
|
||||||
|
# falls back to `default`). (§2.1/§2.2)
|
||||||
|
# D6 (#914b) DIGEST-CLASS LOCATOR THREADING: an ORIENTATION pointer for a
|
||||||
|
# digest-class entry (the shape detector.sh actually enqueues:
|
||||||
|
# kind/id/observed_hash/remote/path, not repo/issue/sha/file) carries a
|
||||||
|
# non-empty, usable locator — and the ACTIONABLE-tier hard-locator
|
||||||
|
# FAIL-LOUD (exit 4) is UNCHANGED for a malformed actionable claim. (§2.1)
|
||||||
#
|
#
|
||||||
# Isolated: every test runs against a fresh temp state / credential file.
|
# Isolated: every test runs against a fresh temp state / credential file.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
@@ -32,6 +41,7 @@ command -v jq >/dev/null 2>&1 || {
|
|||||||
echo "SKIP: jq not available" >&2
|
echo "SKIP: jq not available" >&2
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
# NOTE: whole-suite openssl-skip retained; unmasking D1-D6 to run in CI is tracked in #912 (they currently fail under the CI env).
|
||||||
command -v openssl >/dev/null 2>&1 || {
|
command -v openssl >/dev/null 2>&1 || {
|
||||||
echo "SKIP: openssl not available" >&2
|
echo "SKIP: openssl not available" >&2
|
||||||
exit 0
|
exit 0
|
||||||
@@ -297,6 +307,80 @@ echo "== H2: KEY BY-NAME, never inline; no key leak; same-uid boundary documente
|
|||||||
grep -qi 'off-uid' "$SIGN" || fail_msg "H2: off-uid future signer not named in sign.sh"
|
grep -qi 'off-uid' "$SIGN" || fail_msg "H2: off-uid future signer not named in sign.sh"
|
||||||
) && ok
|
) && ok
|
||||||
|
|
||||||
|
echo "== D5 (#914a): EMBEDDED ACK NAMESPACE — env-less copy-run targets the RENDER-TIME agent, not 'default' =="
|
||||||
|
(
|
||||||
|
WAKE_STATE_HOME="$(fresh_state d5)"
|
||||||
|
export WAKE_STATE_HOME
|
||||||
|
# A pending obligation observed under the 'someagent' namespace (render-time
|
||||||
|
# WAKE_AGENT is known; the bug is that the EMBEDDED line forgets it).
|
||||||
|
WAKE_AGENT=someagent "$STORE" enqueue --seq 1 --class actionable --locators '{"repo":"r","issue":41}' >/dev/null
|
||||||
|
out="$(WAKE_AGENT=someagent "$DIGEST" render)" || fail_msg "D5: render (WAKE_AGENT=someagent) failed"
|
||||||
|
ack_line="$(printf '%s\n' "$out" | awk '/^-- ACK/{f=1;next} f && NF {print; exit}')"
|
||||||
|
[ -n "$ack_line" ] || fail_msg "D5: no embedded ack line extracted from the digest"
|
||||||
|
printf '%s' "$ack_line" | grep -q 'WAKE_AGENT=someagent' ||
|
||||||
|
fail_msg "D5: embedded ack line has no explicit WAKE_AGENT=someagent prefix — an env-less copy-run silently resolves to 'default' [$ack_line]"
|
||||||
|
# Actually RUN it with NO WAKE_AGENT in the environment (the real failure
|
||||||
|
# mode: a consumer copy-pastes the line into a fresh shell).
|
||||||
|
( unset WAKE_AGENT; env -u WAKE_AGENT sh -c "$ack_line" >/dev/null 2>&1 )
|
||||||
|
rc=$?
|
||||||
|
[ "$rc" -eq 0 ] || fail_msg "D5: env-less copy-run of the embedded ack line exited non-zero (rc=$rc) [$ack_line]"
|
||||||
|
someagent_consumed="$(cat "$WAKE_STATE_HOME/someagent/consumed_seq" 2>/dev/null || echo '?')"
|
||||||
|
[ "$someagent_consumed" = "1" ] ||
|
||||||
|
fail_msg "D5: env-less copy-run did NOT advance the someagent namespace's consumed_seq (got '$someagent_consumed') — resolved to the wrong namespace"
|
||||||
|
# The 'default' namespace must stay untouched — no silent fallback.
|
||||||
|
if [ -f "$WAKE_STATE_HOME/default/consumed_seq" ]; then
|
||||||
|
default_consumed="$(cat "$WAKE_STATE_HOME/default/consumed_seq" 2>/dev/null || echo 0)"
|
||||||
|
[ "$default_consumed" = "0" ] || fail_msg "D5: env-less copy-run advanced the WRONG ('default') namespace instead of someagent"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- injection-safety: a hostile agent value must not become a shell
|
||||||
|
# injection vector when the embedded line is later copy-run, and must be
|
||||||
|
# scrubbed (control/ANSI bytes stripped) like any other inlined value.
|
||||||
|
wd="$TMP_ROOT/d5-wd"
|
||||||
|
rm -rf "$wd"
|
||||||
|
mkdir -p "$wd"
|
||||||
|
WAKE_STATE_HOME2="$(fresh_state d5b)"
|
||||||
|
# shellcheck disable=SC2016 # deliberately literal: this is the injection
|
||||||
|
# payload under test, not an expression we want the test script to expand.
|
||||||
|
nasty='$(touch INJECTED)nasty'
|
||||||
|
WAKE_AGENT="$nasty" WAKE_STATE_HOME="$WAKE_STATE_HOME2" "$STORE" enqueue --seq 1 --class actionable --locators '{"repo":"r","issue":1}' >/dev/null
|
||||||
|
out2="$(WAKE_AGENT="$nasty" WAKE_STATE_HOME="$WAKE_STATE_HOME2" "$DIGEST" render)" || fail_msg "D5: render with a hostile agent value failed"
|
||||||
|
ack_line2="$(printf '%s\n' "$out2" | awk '/^-- ACK/{f=1;next} f && NF {print; exit}')"
|
||||||
|
( cd "$wd" && unset WAKE_AGENT && env -u WAKE_AGENT WAKE_STATE_HOME="$WAKE_STATE_HOME2" sh -c "$ack_line2" >/dev/null 2>&1 )
|
||||||
|
[ -e "$wd/INJECTED" ] && fail_msg "D5: a hostile WAKE_AGENT value achieved shell injection via the embedded ack line [$ack_line2]"
|
||||||
|
true
|
||||||
|
) && ok
|
||||||
|
|
||||||
|
echo "== D6 (#914b): DIGEST-CLASS LOCATOR THREADING — ORIENTATION pointer carries its (soft) locator; ACTIONABLE hard-locator FAIL-LOUD unchanged =="
|
||||||
|
(
|
||||||
|
WAKE_STATE_HOME="$(fresh_state d6)"
|
||||||
|
export WAKE_STATE_HOME
|
||||||
|
unset WAKE_AGENT WAKE_LANE
|
||||||
|
# The REAL shape store.sh receives for a digest-class entry, per detector.sh's
|
||||||
|
# own enqueue-building jq filter (kind/id/observed_hash + whichever of
|
||||||
|
# repo/path/anchor/remote/branches the source def declares) — NOT
|
||||||
|
# repo/issue/sha/file, which is what _locator_line originally recognized.
|
||||||
|
loc="$(jq -cn '{kind:"repo", id:"r1", observed_hash:"deadbeefcafe0123456789abcdef0123456789abcdef0123456789abcdef01", remote:"example/repo"}')"
|
||||||
|
"$STORE" enqueue --seq 1 --class digest --locators "$loc" >/dev/null
|
||||||
|
out="$("$DIGEST" render)" || fail_msg "D6: render failed"
|
||||||
|
orientline="$(printf '%s\n' "$out" | grep -E '^\s*\* seq 1 \[digest\]')"
|
||||||
|
[ -n "$orientline" ] || fail_msg "D6: no ORIENTATION line found for seq 1"
|
||||||
|
printf '%s' "$orientline" | grep -qE 'locator: *$' &&
|
||||||
|
fail_msg "D6: digest-class ORIENTATION pointer rendered an EMPTY locator despite a populated .locators field [$orientline]"
|
||||||
|
# Must surface something a consumer can act on to re-verify.
|
||||||
|
printf '%s' "$orientline" | grep -qE 'remote=example/repo|id=r1|kind=repo' ||
|
||||||
|
fail_msg "D6: digest-class ORIENTATION pointer does not carry a usable locator [$orientline]"
|
||||||
|
|
||||||
|
# --- ACTIONABLE-tier hard-locator FAIL-LOUD (exit 4) must be UNCHANGED ------
|
||||||
|
WAKE_STATE_HOME="$(fresh_state d6b)"
|
||||||
|
export WAKE_STATE_HOME
|
||||||
|
"$STORE" enqueue --seq 1 --class actionable --locators '{"claim":"mergeable=true"}' >/dev/null
|
||||||
|
rc=0
|
||||||
|
"$DIGEST" render >/dev/null 2>&1 || rc=$?
|
||||||
|
[ "$rc" -eq 4 ] ||
|
||||||
|
fail_msg "D6: actionable claim with no hard locator must still FAIL-LOUD exit 4 (got $rc) — regression in the unrelated hard-locator gate"
|
||||||
|
) && ok
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if [ -s "$FAILFILE" ]; then
|
if [ -s "$FAILFILE" ]; then
|
||||||
echo "wake digest/hmac harness: FAILED ($(grep -c . "$FAILFILE") assertion(s))" >&2
|
echo "wake digest/hmac harness: FAILED ($(grep -c . "$FAILFILE") assertion(s))" >&2
|
||||||
|
|||||||
Reference in New Issue
Block a user