From 9e359dc6a5dd591d62ad07106aab2f51eedf578e Mon Sep 17 00:00:00 2001 From: letur Date: Sat, 12 Sep 2026 23:11:31 +0200 Subject: [PATCH] fix(queue): accept the no-mistakes alias in check, task_publish and --publish `queue.sh check` compared a record's publish method against PUBLISH_METHODS directly, so every task recorded before #74 renamed `no-mistakes` to `attested` failed the control plane's gate for good. Two other readers had the same drift: `task_publish` let such a record fall back to the operator's default (a silent downgrade to `pr` on a fresh clone), and `add --publish no-mistakes` was refused by argparse. All three now go through publish_method(), which stays the one owner of the alias. Claude-Session: https://claude.ai/code/session_01Hfb2qxCc8oS1bdcmAXgyBs --- scripts/lib/queue.py | 13 ++++++--- scripts/queue-selftest.sh | 57 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/scripts/lib/queue.py b/scripts/lib/queue.py index ee9fb47..f91a03a 100644 --- a/scripts/lib/queue.py +++ b/scripts/lib/queue.py @@ -3249,8 +3249,9 @@ def task_publish(task: Task) -> tuple[str, str | None]: """ method, how = policy_publish_default() block = task.doc.get("publish") or {} - if block.get("method") in PUBLISH_METHODS: - method, how = block["method"], block.get("how") + declared = publish_method(block.get("method")) + if declared in PUBLISH_METHODS: + method, how = declared, block.get("how") text = str(how).strip() if how else "" return method, text or None @@ -6520,7 +6521,10 @@ def cmd_check(args) -> int: if not d.get(key): problems.append(f"{ref}: missing {key}") pub = d.get("publish") or {} - if "method" in pub and pub["method"] not in PUBLISH_METHODS: + # Through publish_method(), like every other reader: a record older + # than the rename is never rewritten, so refusing its word here would + # fail this check for good. + if "method" in pub and publish_method(pub["method"]) not in PUBLISH_METHODS: problems.append( f"{ref}: publish method {pub['method']!r} is not one of " + ", ".join(sorted(PUBLISH_METHODS)) @@ -6641,6 +6645,9 @@ def build_parser() -> argparse.ArgumentParser: ) a.add_argument( "--publish", + # argparse applies `type` before `choices`, so the retired word is + # folded into its shape first and never reaches the record. + type=publish_method, choices=sorted(PUBLISH_METHODS), help="what this task must PRODUCE. Defaults to the publish block in " "POLICY.md's frontmatter, and to `pr` when there is none", diff --git a/scripts/queue-selftest.sh b/scripts/queue-selftest.sh index d9403b0..4ce18af 100755 --- a/scripts/queue-selftest.sh +++ b/scripts/queue-selftest.sh @@ -1497,6 +1497,45 @@ expect "and show reports the method it was read as" "publish: attested" "$st out="$($QUEUE check 2>&1)" expect "and check is happy with a record that declares nothing" "ok" "$out" +# (f2) A record written before `no-mistakes` was renamed `attested`. Every +# reader folds the old word into the new one, so the validator must too: +# records are never rewritten once archived, and a check that refused +# them turned the control plane's own gate red for good. Its own queue, +# because the second record here is invalid on purpose. + +aliasq="$tmp/alias-queue" +aq() { FLEET_QUEUE_DIR="$aliasq" $QUEUE "$@"; } +retag() { + python3 - "$1" "$2" <<'PY' +import sys + +import yaml + +path, method = sys.argv[1], sys.argv[2] +doc = yaml.safe_load(open(path)) +doc["publish"]["method"] = method +open(path, "w").write(yaml.safe_dump(doc, sort_keys=False)) +PY +} + +aq topic add renamed --prompt 'records from before the method was renamed' >/dev/null +aq add renamed legacy-word --title 'Dispatched when the method was no-mistakes' \ + --repo /tmp/repo-a --branch fix/legacy-word --publish attested >/dev/null +retag "$aliasq/renamed/01-legacy-word/task.yaml" no-mistakes + +out="$(aq check 2>&1 || true)" +expect "check accepts the retired no-mistakes spelling" "queue check: ok" "$out" +refute "and does not call it an unknown method" "is not one of" "$out" + +aq add renamed bogus-word --title 'A method nothing defines' \ + --repo /tmp/repo-a --branch fix/bogus-word --publish attested >/dev/null +retag "$aliasq/renamed/02-bogus-word/task.yaml" carrier-pigeon + +out="$(aq check 2>&1 || true)" +expect "check still rejects a method that is no method and no alias" \ + "publish method 'carrier-pigeon' is not one of" "$out" +refute "and does not reject the alias alongside it" "'no-mistakes'" "$out" + # (g) No `gh` on PATH at all. Its own queue and its own PATH, so the answer # cannot depend on anything else that happens to be pending. @@ -1599,6 +1638,24 @@ expect "a task recorded under the old tool name still reads as its shape" \ "alias=attested" "$alias_reads" expect "and every other method is untouched by that" "kept=pr" "$alias_reads" +# The helper is not the claim; the READERS are. This clone's default is `pr`, +# so a reader that skips the alias falls back to it and downgrades the check, +# where the operator's `attested` elsewhere in this file would hide that. +if out="$(bq add unconfigured said-old-word --title 'Added with the retired word' \ + --repo /tmp/repo-a --branch fix/said-old-word --publish no-mistakes --number 02 2>&1)"; then + pass "add --publish no-mistakes is still accepted" +else + fail "add --publish no-mistakes is still accepted" "$out" +fi +refute "and nothing writes the old word into the record" "no-mistakes" \ + "$(cat "$tmp/bare-queue/unconfigured/02-said-old-word/task.yaml" 2>&1)" + +bq add unconfigured recorded-old-word --title 'Recorded before the rename' \ + --repo /tmp/repo-a --branch fix/recorded-old-word --publish attested --number 03 >/dev/null +retag "$tmp/bare-queue/unconfigured/03-recorded-old-word/task.yaml" no-mistakes +expect "a record saying no-mistakes is verified as attested, not the clone's pr" \ + "publish: attested" "$(bq show unconfigured/03-recorded-old-word 2>&1)" + # (i) The stale attestation the pipeline caused ITSELF, told apart from every # other one. `attested` writes the attestation while it opens the pull # request and can then push its own CI fixes on top, which leaves the body