Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ce2bda2
fix(conformance): read "(0 …)" and "not found" only where mxcli print…
claude Oct 20, 2026
8427edc
fix(monkey): recognise non-English sign-out controls, configurable pe…
claude Oct 20, 2026
f5310f6
fix(status): brace variables before an em-dash so --brief prints NEXT…
claude Oct 20, 2026
160d180
fix(page-fidelity): score a popup wireframe's dialog, not the page be…
claude Oct 20, 2026
5a73904
fix(portability): stop [ \t] in sed/grep brackets trimming a trailing…
claude Oct 20, 2026
7eedb5c
fix(progress-report): one nav group, one id=stamp, no build-plan.html…
claude Oct 20, 2026
75d474f
fix(conformance): read "Error:" on the first line only (#470 review)
claude Oct 20, 2026
4818e28
Merge origin/master into fix/conformance-absent-marker
claude Oct 20, 2026
d751f8d
fix(monkey): whole-caption match for non-English sign-out, keep proje…
claude Oct 20, 2026
7f95020
Merge origin/master into fix/monkey-signout-i18n
claude Oct 20, 2026
22ac22e
fix(page-fidelity): switch to the dialog boundary only with popup evi…
claude Oct 20, 2026
6496213
Merge origin/master into fix/page-fidelity-popup
claude Oct 20, 2026
1aa6aeb
fix(progress-report): review fixes for #486 (scroll margin, unknown s…
claude Oct 20, 2026
bab64c5
fix(monkey): correct the master FAIL count to 9 (#481 review)
claude Oct 20, 2026
6605557
fix(portability): anchor the sed/grep tab lint to the command word (#…
claude Oct 20, 2026
0149cb8
Merge PR #486 into integration/2026-10-20
claude Oct 20, 2026
d103df7
Merge PR #480 into integration/2026-10-20
claude Oct 20, 2026
6c97adf
Merge PR #482 into integration/2026-10-20
claude Oct 20, 2026
1de6904
Merge PR #481 into integration/2026-10-20
claude Oct 20, 2026
742a3de
Merge PR #484 into integration/2026-10-20
claude Oct 20, 2026
c89c049
Merge PR #485 into integration/2026-10-20
claude Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m
## Unreleased

- fix(gates): **a decision taken during a stage no longer reads as that stage's approval** (#477). The #207 flag prefixed a FAIL with `APPROVED OVER A FAILING GATE` whenever any CONFIRMED row carried the stage's number, so a Stage-5 build with `| 5 | Sub-agents run on Sonnet | CONFIRMED |` was flagged, and `status.sh` told the lead to re-open an approval nobody gave while the real gap (PENDING obligations) sat underneath. The register has no dedicated exit row, so `gate-check.sh` now counts a stage as approved on either of two pieces of evidence in the Decisions table: an explicit exit row ("Stage N approved", "Stage 4 — approved with conditions", "Approved: Stage N …", "Gate approved", or "<artifact> approved" for a fixed artifact list such as "Build plan approved"), or the stage having CONFIRMED rows of its own plus a CONFIRMED decision for a later stage up to 6 (the #207 field case); an early Stage-7 "no cutover" row or an N/A/skip row is not evidence. The Stage 3/4 gate checks and every verdict and exit code are unchanged. `test-bug03-gates.sh` T17 (16 cases, checked under gawk, mawk and original-awk): 7 FAIL on master, 47/47 with the fix. — MendixMau
- fix(progress-report): **the top bar is one group of section links (Stages, Build, Pages, Tips) instead of stage chips plus links; the page carries one `id=stamp`, and the header drops the build-plan.html note.** The sticky bar loses the `Stage P 0 … 6` chips, which repeated the stage list right below it, and gains a `Stages` link to that list; the `#stage-N` card anchors stay for links from other pages, and the header now states the current stage. Five elements shared `id=stamp` (invalid HTML); the "Last updated" span keeps it and the others get their own ids plus a `data-stamp` marker, which `build-plan-status.sh --refresh` now reads (with `id=stamp`, so pages written before this still compare) to leave an unchanged page alone. The filter now depends on both scripts: a project whose `bin/build-plan-status.sh` is locally edited keeps the old `id=stamp` filter after sync, so the page is rewritten on every refresh; such a project needs `sync-project.sh --only-bin build-plan-status.sh --upgrade-bin build-plan-status.sh` alongside this. "replaces the former build-plan.html" is gone from the header sub-line. Fixes #479 items 1–3 — MendixMau (review 2026-10-20)
- fix(conformance): **a definition that merely contains "(0 " or "not found" is no longer scored ABSENT** (#470). `conformance-check.sh` matched both markers anywhere in mxcli's answer, so a built microflow whose documentation read "HttpStatus (0 = no response or time-out)" went STALE (five false STALEs in one module), and so does every `describe navigation` with a not-found page, which mxcli prints as " not found page <Page>". It now reads an error only on the first non-blank line (`Error:…`, the old start-of-output meaning; enumeration, scheduled-event and image-collection docs print raw, so a doc line may start `Error:`), and an empty set only when the last non-blank line is exactly the `(0 …)` summary (also `(0 workflow group(s))`); CRLF output reads the same. New fixture `tests/wave2/test-conformance-absent-marker.sh` replays two verbatim captures already in the repo (the v0.25.0 navigation describe of mxcli's public test app, and a v0.23.0 microflow describe with the issue's doc sentence spliced in), plus an enumeration with a raw `Error:` doc line and CRLF-only cases, both layouts; 6 FAIL on master, 24/24 with the fix. — jopterhorst
- fix(page-fidelity): **a popup wireframe is scored against its dialog, not the page drawn behind it** (#471). A popup wireframe draws the caller's page behind a scrim (`.popup-stage > .shell-behind + .dialog-scrim > .dialog`); `page-fidelity.js` looked for `div.main` first and landed in the shell, so a popup whose fields and bindings all matched scored 29% in the field, every miss being the caller's page head, h1 and card. With popup evidence (a `.popup-stage` or `.dialog-scrim` in the wireframe, or a page whose MDL names a popup layout) the scorer now scores the `.dialog` only (class word, so `dialog-head` does not count), or the stage minus `.shell-behind` when there is no `.dialog`. Without that evidence nothing changes, so a full-page wireframe that draws an inline `.dialog` confirm (the design system's own component) scores exactly as before, and a bare-`.dialog` popup wireframe still reads `<body>`. `test-page-fidelity-mocks.sh` gains the popup cases on a verbatim v0.23.0 describe of a real popup page and a wireframe drawn in the issue's shape, plus layout-only evidence and the full-page-plus-inline-confirm regression case: 18% and 8 FAIL on master, 100% and 78/78 with the fix. — jopterhorst
- fix(monkey): **the monkey no longer signs itself out of a non-English app** (#469). `monkey.js` recognised only English sign-out captions, so in a Dutch app it clicked "Afmelden", landed on `login.html`, scored a false CRASH and fuzzed nothing more (seed 204262061: 3 observations, 1 crash-class; 24 and 0 with the label excluded). The default now also covers Dutch, German, French, Spanish, Portuguese and Italian sign-out captions, matched as the whole caption so ordinary verbs stay fuzzable ("Ziek afmelden", "Vom Kurs abmelden", and sign-in captions such as "Aanmelden"); a project adds its own in `project.config.js` `signOutPattern` or `SIGN_OUT_PATTERN`, added to the default, never replacing it, with its own regex flags kept. `rapidTab` no longer presses Enter while focus is on a sign-out control (top bar, nav item, aria-label or title, each tested on its own), the route by which a layout control outside `.mx-page` was reached. `test-monkey-left-app.sh` gains the issue's captions, sign-in near-misses, the over-match captions, the project pattern and its flags, and a rapidTab guard scoped to that branch (it fails on a blind-Enter mutation): 9 FAIL on master, 22/22 with the fix. — jopterhorst
- fix(progress-report): **the build plan is shown once: the phase cards are the plan.** Expanding a card now shows its full rows (#, Kind, Step, Produces/Proves, Depends on, State, Scripts) and Claims, which only the duplicate "Full plan tables" block carried; that block is gone and views A (script folders) and B (module proof) are their own section. The `now` phase renders open with a selected style, opening another phase closes the previous one (accordion; `#phase-N` links still open their card), and a viewer's saved open/closed state survives the 30 s reload and wins over the default (#474) — field feedback from a requirements-driven project at Stage 5 — MendixMau (field feedback 2026-10-20)

- fix(status): **`status.sh --brief` no longer aborts with "LAST_FAIL<byte>: unbound variable" before printing NEXT (#467).** Two NEXT lines wrote a bare `$LAST_FAIL`/`$NOT_DONE_PASS` straight before an em-dash; in a single-byte locale (and on macOS bash 3.2 even under `en_US.UTF-8`) bash reads the dash's first byte as part of the name and `set -u` kills the run. Braced there and at the two other sites with the same idiom (`project-bin/constants-audit.sh`'s `…`, a case pattern in `test-runbook-slice.sh`). `check-portability.sh` now flags any bare `$NAME` followed by a non-ASCII byte, in every file including the meta-exempt ones (matched with `LC_ALL=C`, so the check does not depend on the locale it guards against; `# portability-ok:` opts out). New `tests/wave2/test-status-bare-var.sh`: the lint cases on any machine, plus both NEXT rules run under an ISO-8859-1 locale where one exists (reproduced on bash 5.2 with a compiled `en_US.ISO-8859-1`: both rules aborted on master, both print after). — jopterhorst
- fix(portability): **`[ \t]` in a sed or grep bracket no longer trims a trailing "t" (#468).** BSD/macOS sed reads `[ \t]` as "space, backslash or t" (only GNU sed expands `\t` there, and grep never does), so `context-pack.sh` printed `Mod.ENUM_Product` as `Mod.ENUM_Produc` and called it "not in the folder plan"; `closeout.sh`'s cell trim cut "bulk import" to "bulk impor" and "test" to "est"; `entry-mode.sh` read an answer written `answered:<TAB>migration` as no entry mode at all; and `gate-check.sh`'s intake section count matched `##t…` but never `##<TAB>`. All five sites now use `[[:blank:]]`, and a test fixture's `[^\t]` uses a real tab (`$'…'`). `check-portability.sh` flags `\t` inside a bracket that follows `sed`/`grep` on the line (awk expands `\t` everywhere and is not flagged; a `$'…'` string is skipped). Reproduced on GNU sed through a `sed --posix` shim, which reads the bracket the BSD way: the captured context-pack golden brief (`Sales.Product`, `Sales.ACT_ApprovalStep_Reject`) failed 4 of 4 new BSD-sed checks on master and passes after; new `tests/wave2/test-sed-tab-bracket.sh` covers `closeout.sh`, `entry-mode.sh` and the lint (6 of 9 fail on master). — jopterhorst
## v2026.10.19

- new(progress-report): **the progress report gains a Project pages section: a compact folder tree of the HTML pages found in the project's output folders (gate dashboard, analysis reports, blueprint, design system, process map, wireframes, UI reviews, test reports), each a relative link from `architecture/` with its modified date.** Pages are found by scanning the project's files on every render, never from a stored list; only the top-level folders in `PAGE_DIRS` are scanned (each named with its producer; a symlinked one is skipped), `node_modules`, `.mpr-snapshots`, `deployment`, `mprcontents`, `theme`, `.git`, vendor and dot folders are never entered, a folder with more than 8 pages folds behind a count, every folder keeps its row and lists at most 100 links (`+K more`), and the report never lists itself. Dates are shown as days, so the section is unchanged on the same day unless a listed page changed and a `--refresh` leaves the page alone. Test: `tests/wave2/test-progress-report-pages.sh` (single and two-tree layouts, Windows hrefs) — MendixMau (request 2026-10-19)
Expand Down
30 changes: 30 additions & 0 deletions bin/check-portability.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ while IFS= read -r f; do
report "$f" 1 "CRLF line endings" "bash fails on line 1. Ensure .gitattributes has *.sh text eol=lf."
fi

# 2. A bare $NAME followed directly by a non-ASCII byte (an em-dash, an ellipsis). In a
# single-byte locale bash reads that byte as part of the variable NAME (macOS bash 3.2 did
# it under en_US.UTF-8 too), so set -u aborts with "NAME<byte>: unbound variable" (#467:
# status.sh --brief died before printing NEXT). Checked before is_meta: no file is exempt
# from this one. Matched byte-wise (LC_ALL=C) so the result does not depend on the locale
# it is meant to guard against. Fix: brace it, ${NAME}—.
while IFS= read -r hit; do
[ -n "$hit" ] || continue
case "$hit" in *portability-ok:*) continue ;; esac
report "$f" "${hit%%:*}" "bare \$NAME followed by a non-ASCII character" \
'Brace the name: ${NAME}. A single-byte locale (and bash 3.2) reads the first byte as part of the name; set -u then aborts.'
done <<EOF
$(code_lines "$f" | LC_ALL=C grep -E '^[0-9]+:.*\$[A-Za-z_][A-Za-z0-9_]*[^[:print:][:cntrl:]]')
EOF

is_meta "$f" && continue

while IFS= read -r hit; do
Expand Down Expand Up @@ -157,6 +172,21 @@ while IFS= read -r f; do
fi ;;
esac

# \t inside a sed/grep bracket expression is not a tab. BSD/macOS sed reads [ \t] as
# "space, backslash or t" (GNU sed alone expands it), and grep reads it that way on
# every platform, so a trim stripped a trailing "t": Mod.ENUM_Product became
# Mod.ENUM_Produc in context-pack.sh (#468). awk expands \t in a regex everywhere, so
# only a bracket that FOLLOWS sed/grep on the line counts; a bracket inside a $'...'
# string holds a real tab and is skipped.
case "$txt" in
*sed*'\t'*|*grep*'\t'*)
if grep -Eq -- '(^|[^[:alnum:]_])(sed|grep)([[:space:]]|$)[^|]*\[[^]]*\\t' <<<"$txt" \
&& ! grep -Eq -- "\\\$'[^']*\\[[^]]*\\\\t" <<<"$txt"; then
report "$f" "$ln" "\\t inside a sed/grep bracket expression (BSD: backslash or t, not tab)" \
'Use [[:blank:]] (space or tab) or [[:space:]], or a real tab via $'"'"'\t'"'"'.'
fi ;;
esac

case "$txt" in
*'grep -P'*|*'grep -qP'*|*'grep -oP'*)
report "$f" "$ln" "grep -P is GNU-only" \
Expand Down
2 changes: 1 addition & 1 deletion bin/gate-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ check_stage_P() {
else if (l ~ /^unverified/ && l ~ /how to verify/) ok=1
}
END { flush(); print bad }' "$f")
total=$(grep -c '^##[ \t]' "$f")
total=$(grep -c '^##[[:blank:]]' "$f")

# Always name the file that was graded. A Stage P line that does not is what let the
# wrong-file bug survive for weeks: the output was locally coherent and globally wrong.
Expand Down
4 changes: 2 additions & 2 deletions bin/lib/closeout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ _co_checkpoints() {
esac
}

_co_trim() { printf '%s' "$1" | sed -E 's/^[ \t]+//; s/[ \t]+$//; s/^\*+//; s/\*+$//; s/^[ \t]+//; s/[ \t]+$//'; }
_co_trim() { printf '%s' "$1" | sed -E 's/^[[:blank:]]+//; s/[[:blank:]]+$//; s/^\*+//; s/\*+$//; s/^[[:blank:]]+//; s/[[:blank:]]+$//'; }
# Truncate to N chars on a word boundary, with an ellipsis. Cells in a real register run to
# 2,000 characters; the block is a recap, the register is the record.
_co_trunc() {
Expand All @@ -117,7 +117,7 @@ _co_trunc() {
}
# Stage cell → bare stage token ("Stage 3" → 3, "P" → P). Empty when it is not a stage.
_co_stage_of() {
local s; s="$(_co_trim "$1" | sed -E 's/^[Ss]tage[ \t]*//')"
local s; s="$(_co_trim "$1" | sed -E 's/^[Ss]tage[[:blank:]]*//')"
case "$s" in P|p) echo P ;; [0-7]) echo "$s" ;; *) echo "" ;; esac
}
# First word of a status cell, upper-cased, punctuation stripped — the same "controlled
Expand Down
4 changes: 2 additions & 2 deletions bin/lib/entry-mode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ entry_mode_token() {
# doing it after would let that stripping treat "(2026-09-01)" as if it opened at the
# start of the value and eat everything from "assumed (" onward, value included. The
# register's bare value ("migration") has no such prefix and passes through untouched.
v="$(printf '%s' "$v" | sed -E 's/^[ \t>*_-]*(answered|answer|a|decision|assumed)[ \t]*(\([^)]*\))?[ \t]*:[ \t]*//')"
v="$(printf '%s' "$v" | sed -E 's/^[[:blank:]>*_-]*(answered|answer|a|decision|assumed)[[:blank:]]*(\([^)]*\))?[[:blank:]]*:[[:blank:]]*//')"
v="${v%%(*}" # drop a trailing parenthetical: "migration (not greenfield)"
v="${v%%,*}" # drop anything after the first comma
v="${v%%;*}" # ...or semicolon
# Trim leading/trailing noise, INCLUDING markdown emphasis markers on both ends
# ("**migration**", "_greenfield_") — the old trailing trim only stripped spaces,
# tabs and a period, so a bolded or italicised value never matched anything.
v="$(printf '%s' "$v" | sed 's/^[ \t>*_-]*//; s/[ \t.*_-]*$//')"
v="$(printf '%s' "$v" | sed 's/^[[:blank:]>*_-]*//; s/[[:blank:].*_-]*$//')"
full="$v"
# Documented multi-word phrases resolve on the WHOLE value, before the first-word
# truncation below: the runbook's own label for the fourth mode is "Change an existing
Expand Down
4 changes: 2 additions & 2 deletions bin/status.sh
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ next_action() {
esac
if [ "$UNSYNCED" -gt 0 ]; then echo "ba-agent flushes $UNSYNCED UNSYNCED marker(s) in PROJECT.md — every gate is blocked until then (conversion-runbook.md §3b)"; return; fi
case "$COH" in *"DUE"*) echo "run process-coherence-pass.md — the cross-module seam check is due"; return ;; esac # process-coherence-pass.md
if [ -n "$LAST_FAIL" ]; then echo "last gate FAILED for: $LAST_FAIL— fix and re-exec (docs/BUILD-LOG.md, .mpr-snapshots/last-mxbuild-errors.json)"; return; fi # iterative-build-loop.md Gate: BUILD
if [ -n "$LAST_FAIL" ]; then echo "last gate FAILED for: ${LAST_FAIL}— fix and re-exec (docs/BUILD-LOG.md, .mpr-snapshots/last-mxbuild-errors.json)"; return; fi # iterative-build-loop.md Gate: BUILD
if [ -n "$DONE_STALL" ]; then echo "$DONE_STALL — done- needs the coverage checklist; run bin/coverage-preflight.sh to see what blocks it (iterative-build-loop.md)"; return; fi
if [ -n "$NOT_DONE_PASS" ]; then echo "gate-passed but not done-: $NOT_DONE_PASS— walk the happy path + coverage checklist, then git mv to done- (iterative-build-loop.md step 13-15)"; return; fi
if [ -n "$NOT_DONE_PASS" ]; then echo "gate-passed but not done-: ${NOT_DONE_PASS}— walk the happy path + coverage checklist, then git mv to done- (iterative-build-loop.md step 13-15)"; return; fi
if [ -n "$OB_NEXT" ]; then echo "discharge: $OB_NEXT (bin/lib/obligations.tsv names the artifact each owes)"; return; fi
[ -n "$GC_NEXT" ] && { echo "$GC_NEXT"; return; }
echo "nothing overdue — take the next script in architecture/build-plan.md"
Expand Down
4 changes: 3 additions & 1 deletion project-bin/build-plan-status.sh
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,10 @@ if [ "$WRITE_HTML" -eq 1 ]; then
if "$PY" "$GEN" "$ROOT" "${GEN_ARGS[@]}" >/dev/null 2>"$OUT.err.$$"; then
# Two renders of the same state differ only in the stamp lines. A refresh that changed
# nothing leaves the file alone, or every exec.sh run would dirty the working tree.
# Stamp lines carry a data-stamp attribute (one of them also id=stamp); 'id=stamp' is kept in
# the pattern so a page written before #479, with id=stamp on every such line, still compares.
if [ "$REFRESH" -eq 1 ] && [ -f "$OUT" ] \
&& [ "$(grep -v 'id=stamp' "$OUT")" = "$(grep -v 'id=stamp' "$OUT_TMP")" ]; then
&& [ "$(grep -vE 'id=stamp|data-stamp' "$OUT")" = "$(grep -vE 'id=stamp|data-stamp' "$OUT_TMP")" ]; then
rm -f "${OUT_TMP:?}"
else
mv -f "$OUT_TMP" "$OUT"
Expand Down
29 changes: 25 additions & 4 deletions project-bin/conformance-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,31 @@ probe() {
esac
if [ $rc -ne 0 ]; then echo "ABSENT"; return; fi
if [ -z "$(printf '%s' "$out" | tr -d '[:space:]')" ]; then echo "ABSENT"; return; fi
case "$out" in
Error:*|*"not found"*) echo "ABSENT"; return ;;
*"(0 "*) echo "ABSENT"; return ;; # empty result set, e.g. "(0 entities)"
esac
# Read the two absence markers only where mxcli prints them, never in the definition body.
# A substring match read a DESCRIBE whose documentation said "HttpStatus (0 = no response or
# time-out)" as an empty set, and the verbatim v0.25.0 `describe navigation` of mxcli's public
# test app carries " not found page Pages.Vehicle_Overview" (a navigation setting): both
# scored ABSENT, so built rows went STALE (#470).
# Error — mxcli puts its error FIRST (stderr is merged above, and `-c` pairs it with exit 1,
# which the rc test already scored); so only the first non-blank line is read, the
# old `Error:*` start-of-output meaning. Not any line: a body line CAN start at
# column 0 — enumerations, scheduled events and image collections print their
# documentation raw inside `/** … */` (one ` * ` on the first line only), and a
# multi-line string prints raw too, so a doc line "Error: …" is still PRESENT.
# (0 …) — a table's summary is printed as its own LAST line, `fmt.Fprintf("\n%s\n", Summary)`
# with Summary = "(%d entities)", "(%d workflow group(s))", …; a definition ends in
# `/`, `;` or a grant, never in a parenthesised count.
# CR stripped for a Windows-built mxcli (printed "\n%s\n", so nothing else trails the summary).
# Here-strings, not `printf | grep -q`: under pipefail
# that pipe can lose to SIGPIPE on a long definition (test-pipefail-grep-q.sh).
local first last
out="$(tr -d '\r' <<<"$out")"
first="$(sed -n '/[^[:space:]]/{p;q;}' <<<"$out")"
case "$first" in Error:*) echo "ABSENT"; return ;; esac
last="$(sed '/^[[:space:]]*$/d' <<<"$out" | tail -n 1)"
if grep -Eq '^\(0 .*\)$' <<<"$last"; then # no trailing class: the CR strip above is what lets CRLF output match
echo "ABSENT"; return # empty result set, e.g. "(0 entities)"
fi
echo "PRESENT"
}

Expand Down
2 changes: 1 addition & 1 deletion project-bin/constants-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ while IFS= read -r line; do
# four sentences — and printing it whole turns a 30-row table into a wall. The register is
# the place to read it; this column only has to prove a reason exists.
short=$(printf '%s' "$reason" | cut -c1-58)
[ "${#reason}" -gt 58 ] && short="$short…"
[ "${#reason}" -gt 58 ] && short="${short}…"

ROWS="$ROWS$(printf '%-13s %-44s %-10s %-8s %s' "$verdict" "$qname" "$type" "$state" "$short")
"
Expand Down
Loading
Loading