fix(bonedigger-report): derive booted image name from live bootc status - #1127
kubestellar-hive[bot] wants to merge 3 commits into
Conversation
…bootc status image-info.json's image-name field is baked at build time and goes stale after a bootc rebase (e.g. bluefin -> dakota). read_boot_status() already corrected IMAGE_TAG and IMAGE_REF from the live booted ref but left IMAGE_NAME untouched, so route_issue_repo() still routed rebased hosts' reports to the pre-rebase repo. Derive IMAGE_NAME from the booted ref's repository basename, with the same jq/booted-image-snapshot fallback chain already used for tag/ref. Add BATS coverage exercising a rebased bluefin->dakota deployment. Closes #1009 Signed-off-by: scanner <scanner@users.noreply.github.com>
Hive agents work on repositories other than hive's own, and those repositories state their local rules in AGENTS.md — the cross-tool convention hive already reads and injects. Nothing told the agent which side wins a conflict, and hive's wording was the more forceful of the two every time, so the repository lost every conflict. InjectionText rendered the repo's rules under a bare header as undifferentiated prose. Hive stated the same subjects as imperatives with a verification step attached — "confirm the PR's base is that branch before you report done". Given a document that says one thing and an imperative that says another, a model follows the imperative, consistently. projectbluefin/bluefin's AGENTS.md says "All pull requests target `testing`. Never open a content PR against `main`"; hive agents opened #1275 and #1276 against main and failed its base-branch gate. projectbluefin/common#1127 and projectbluefin/review#597 died on conventional-commit title gates against hive's hardcoded "[<lane>] …" PR titles. Four dead-on-arrival PRs in one night, none of them a model error — the agents did precisely what hive instructed. Three changes, in the order they matter: State precedence in the injection. The block now leads with a declaration that the repository's instructions outrank hive's defaults on any conflict — the branch a PR targets, the title format, commit conventions, the test command, review etiquette. Two carve-outs are not the repository's to override: hive's safety and authorization rules (never merge your own PR, never bypass a write gate or the hive-open-pr path, never act as another agent), and an instruction the assignment names explicitly for that one task. An empty AGENTS.md still injects nothing at all, so the statement never appears with no rules under it. Stop asserting repo facts in the defaults. A precedence rule that has to fight hive's own confident wording on every kick is a rule that loses some of the time. The contributor task prompt's fallback wording now says to use the base the repository requires — checking its AGENTS.md, CONTRIBUTING and PR template, since a repo on a promotion model takes PRs on an integration branch rather than on its released default — and to fall back to the default branch only when nothing names one. #5729's verification step survives, pointed at the repository's requirement instead of hive's guess; so does the load-bearing "do not use the branch you find" clause. #4928 and #6081 were this same assumption at earlier stages, and each fix replaced one wrong assertion with a better one; the remaining gap was asserting at all. The prompt also now tells the agent to read the repo's AGENTS.md itself, because the contributor path never calls primeAgentsMd — on that path the repo's rules are not outranked, they are absent. Drop the hardcoded PR title. 16 policy templates per tree prescribed --title "[<lane>] …", hive's own house style projected onto every repo it touches. They now take the title from the target repository and pass --base explicitly. The [<lane>] prefix is unchanged where it is load-bearing — ISSUE titles, which classify.classifyLane routes by prefix — and it was never load-bearing for PRs: intent.Classify keys off AgentAuthor, changed paths and FeatureSignals, never the title. github.ClassifyReviewClass does read PR-title prefixes, but only to order a review queue it documents as presentational, it falls back to agent/<lane> labels, and it matches the conventional-commit word in the same pass — so the titles here keep classifying via "fix:"/"docs:"/"refactor:"/"planning:". quality's PR title had no such word, so it becomes "test: …" rather than losing its class. Both policy trees are edited together, byte-identical, as TestEmbeddedDefaultsMatchPolicySource requires. Closes #7159 Signed-off-by: Danathar <Danathar@users.noreply.github.com>
hanthor
left a comment
There was a problem hiding this comment.
The defect you're describing is real, and this is the right place to fix it. read_image_info() sets all four fields from the build-time image-info.json, and read_boot_status() was already correcting IMAGE_REF and IMAGE_TAG from the live booted ref while leaving IMAGE_NAME alone — an inconsistency that's easy to miss on a read-through. Since route_issue_repo() passes exactly "$IMAGE_NAME" "$IMAGE_TAG" to ublue-image-repo, half-corrected inputs really do route a rebased deployment's ujust report at the pre-rebase repo. Reusing the existing jq / /run/ublue-os/booted-image chain rather than adding a second source of truth is the right call, and the new test is a genuine test — I confirmed the suite goes 19 → 20 with no regressions:
$ bats tests/test_bonedigger_report.bats # merge-base fbbb193
ok 19 help intent does not collect diagnostics or create an issue
$ bats tests/test_bonedigger_report.bats # this head
ok 20 help intent does not collect diagnostics or create an issue
ok=20 fail=0
and it kills the mutants that matter most. Removing the IMAGE_NAME="$booted_name" assignment, changing ${booted_repo##*/} to ${booted_repo%%/*}, and changing ${booted_repo%%:*} to ${booted_repo##*:} each fail test 4. That's real coverage, not a smoke test.
One correctness problem, though, and it's in the parsing order.
The tag is stripped before the basename is taken:
local booted_repo="${booted_ref%%@*}"
booted_repo="${booted_repo%%:*}" # strips from the FIRST colon
local booted_name="${booted_repo##*/}"%%:* cuts at the first colon in the whole reference, but in an OCI reference the first colon can belong to a registry port rather than the tag. The basename then falls out of the registry host:
$ for ref in "ghcr.io/projectbluefin/dakota:stable" "ghcr.io/projectbluefin/dakota@sha256:dead" \
"localhost:5000/bluefin:latest" "registry.example.com:5000/projectbluefin/dakota:stable"; do
booted_repo="${ref%%@*}"; booted_repo="${booted_repo%%:*}"; pr="${booted_repo##*/}"
c="${ref%%@*}"; c="${c##*/}"; c="${c%%:*}"
printf "%-52s PR=%-22s CORRECT=%s\n" "$ref" "$pr" "$c"
done
ghcr.io/projectbluefin/dakota:stable PR=dakota CORRECT=dakota
ghcr.io/projectbluefin/dakota@sha256:dead PR=dakota CORRECT=dakota
localhost:5000/bluefin:latest PR=localhost CORRECT=bluefin
registry.example.com:5000/projectbluefin/dakota:stable PR=registry.example.com CORRECT=dakota
IMAGE_NAME becomes localhost or registry.example.com, which is strictly worse than the stale-but-plausible build-time name it replaces — ublue-image-repo will fall through to the projectbluefin/common default for a deployment it could otherwise have routed. The surrounding comment (and dakota#1328, cited two lines up) calls out "rebased or locally built deployments" specifically, and a local registry on a port is the ordinary way people produce those, so this is squarely inside the case the change is aimed at. Note the existing tag extraction two lines above already uses last-colon semantics (${booted_ref##*:}) for the same reason.
Taking the basename first makes the colon unambiguous, because a tag is the only colon that can appear in the final path component:
local booted_repo="${booted_ref%%@*}"
local booted_name="${booted_repo##*/}"
booted_name="${booted_name%%:*}"That's equivalent on every ref your test exercises and correct on the port cases too.
Second, smaller: the digest-stripping line is unverified. I deleted it —
- local booted_repo="${booted_ref%%@*}"
+ local booted_repo="$booted_ref"— and the suite stayed green:
$ bats tests/test_bonedigger_report.bats | grep -c '^not ok'
0
With that mutation a digest-pinned deployment yields IMAGE_NAME="dakota@sha256". The code is right as written; nothing proves it stays right. Digest-pinned refs are routine for bootc (bootc switch --digest, pinned rollbacks), and the case statement immediately above this block exists precisely because of them, so the gap is worth closing. Two cases added alongside test 4 cover both points — one with a …@sha256:… booted image, one with a localhost:5000/…:latest booted image asserting IMAGE_NAME is bluefin and not localhost.
mergeable_state is blocked. Everything here is a handful of lines in one function plus two test cases; happy to re-review quickly once they're in.
Generated by Claude Code
…r-report-image-name
`${booted_repo%%:*}` cut at the first colon of the whole reference, so a
registry port was mistaken for a tag: `localhost:5000/bluefin:latest`
yielded `IMAGE_NAME=localhost`. Take the basename first, then strip the
tag — in the final path component a colon can only be a tag.
Adds two regression cases: a digest-pinned booted ref (which also pins
the previously unverified `%%@*` digest strip) and a port-bearing
registry ref.
Assisted-by: Claude Opus 4.6 via GitHub Copilot
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix
system_files/bluefin/usr/libexec/bonedigger-report'sread_boot_status()already correctedIMAGE_TAGandIMAGE_REFfrom the livebootc statusbooted ref, but leftIMAGE_NAMEsourced only from the build-timeimage-info.json. After a rebase (e.g. bluefin -> dakota) that name goes stale, soroute_issue_repo()kept routingujust reportto the pre-rebase repo.This derives
IMAGE_NAMEfrom the booted ref's repository basename (same jq //run/ublue-os/booted-imagefallback chain already used for tag/ref), and adds BATS coverage for a rebased deployment.Closes #1009
Filed by scanner agent (ACMM L5 — hold-gated mode). Hold-gated: human review required.
— hive: agent=scanner backend=copilot