Skip to content

🌱 Forward-merge v4 into v5 through 201c69c8a (20 commits) - #7545

Merged
clubanderson merged 21 commits into
v5from
sync/v4-to-v5
Sep 18, 2026
Merged

clubanderson merged 21 commits into
v5from
sync/v4-to-v5

Conversation

@clubanderson

Copy link
Copy Markdown
Member

🌱 Forward-merge v4 into v5 through 201c69c (20 commits)

Carries v4 e74d45e..201c69c (v4.54.2, v4.55.0, v4.55.1) into v5,
including #7510 (cmd/hive domain extraction), #7532 (manager.go split),
#7527/#7528 (review routing + breadth-first slots), #7523 (token-access
audit backport of v5 #6303), #7515 (branch-protection facts on blocked
PR pills), #7535, #7539, #7520.

Resolution notes:

  • cmd/hive, pkg/agent: declaration-level 3-way merge against base
    ff7630e; v5 seams (spokeStatePath, SetHubPushedDashboardURL,
    applyModeUnscheduledAlert, mutation boundary, runDuplicateSweepIfDue)
    preserved. F24 tombstone intact.
  • pkg/github/client.go: v5 fetchPRs signature (RepoPRBreakdown) + v4
    BaseRef/prBaseRef; added Client.requiredStatusCheckContexts shim so
    v4's protection_facts.go compiles against v5, where the sweep lives in
    pkg/github/automerge.
  • pkg/github/token_access_audit{,_test}.go: v4 side (its backport of
    v5's own 🔒 security: make the token-access audit log non-writable by agents #6303, now using testutil.Eventually).
  • pkg/github/automerge/f3_trusted_merger_source_test.go: v4's
    f3ReadPackage path adjusted one level deeper for v5's subpackage.
  • pkg/review/prompts.go: v4 routing instruction + mentionableAuthor
    added; v5's [architect] refactor: delete dead review prompt wrappers (BuildPerspectivePrompts, BuildSequentialPrompt in pkg/review/prompts.go) #6212 deletion of dead BuildPerspectivePrompts /
    BuildSequentialPrompt kept.
  • pkg/agent tests: v4's normalizeModelName cases rewritten against v5's
    normalizeModelNameForBackend (v5 removed the wrapper in 🐛 fix: remove dead manager tmux launch layer #5795).
  • docs/knowledge-curator.md: v5 promotion-only body + v4's
    knowledge.git_sources section (code exists on v5). roadmap.md: v4
    rows. api-reference.md: v5 table, citations re-resolved (--fix).
  • .github/workflows/v2-ci.yml: v5 UID-isolation step + v4's fuller
    audit-log step comment.

Refs #7463

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Danathar and others added 20 commits September 17, 2026 22:39
…7516)

A PR GitHub reports as not mergeable hovered as the bare GitHub enum —
"not mergeable on GitHub (blocked)" — even when classifyMergeEligibility
had just been handed the exact gate that made GitHub say blocked
("CI failing: build, lint", "awaiting review approval", "held: …") and
threw it away in blockedOrOutstanding. On a protected branch that
discarded reason is the one that mattered, so the operator had to open
the PR on GitHub — the trip the pill was meant to save.

Step 1 of #7515, no new API calls:

- blockedOrOutstanding keeps the sweep's reason for MergeableNo:
  "blocked — CI failing: build, lint", "blocked — awaiting review
  approval", "blocked — held: …", "blocked — intent verification: …",
  "blocked — CI pending".
- The review gate now runs before the GitHub-says-no return, so a PR
  blocked for want of a review carries that reason. Both paths file a
  MergeableNo PR in the skip bucket; only the wording changes.
- When every sweep gate passes and GitHub still says blocked, say
  "blocked — all sweep gates pass; a branch-protection rule is
  unsatisfied" until step 2 can name the rule.
- Plain-language wording for the rest: "has merge conflicts with v4 —
  needs a rebase", "behind v4 — needs an update from the base branch",
  "draft — mark ready for review to enter the sweep", "mergeability not
  yet computed by GitHub — re-checked next tick".
- PullRequest gains BaseRef (base_ref), read from the list payload the
  enumeration already has, so the wording can name the base branch.

The frontend needs no change: prMergeNote already shows a blocked
verdict's reason verbatim. Fixtures in the pill test move to the new
wording and pin that "blocked — CI failing: build, lint" reaches the
tooltip without GitHub's raw state appended.

Tests: merge_verdict_7515_test.go pins every row of the issue's table as
an exact string and fails on the parent commit (all 14 cases); the 7478
table is updated for the new wording; fetchPRs is checked to populate
BaseRef.

Refs #7515

Signed-off-by: Douglas Baggett <doug.baggett@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v4.54.2 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>
…self (#7520)

ROADMAP.md gains a 'v6 — Future Line (not open)' section and
src/docs/roadmap.md gains a matching Later row: no v6 branch exists,
v6-designated work is design-only, the line opens after the v5 GA bar
(#6016) via the same RFC gate that governs v5, and the one existing
v6-designated design (github-mention-triggers.md, #7483) is indexed.

Closes #7519

Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
Co-authored-by: sec-check <sec-check@hive.kubestellar.io>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A hive at ACMM L5 cannot merge, so the only possible outcome of a review
is a human acting on it. Reviewers already produce well-cited comments
with requires_human and reject verdicts, but nothing carried those to a
person: a blocking finding landed in a queue of hundreds looking exactly
like routine review noise, and the two reviews posted on the projectbluefin
spoke contained zero mentions and no way to filter for "a human must
decide this".

Add a routing block to the publish half of the kick. A requires_human or
reject verdict must open the comment with a pinned
`**HUMAN DECISION NEEDED**` marker, which is the string a maintainer
triaging a long queue can actually search on; its whole value is being
identical everywhere, so it is pinned by test.

Mention the PR author only when the author is a person. On a hive fleet
most PRs are agent-authored, so the author is an App ("app/<name>") or a
bot ("<name>[bot]"), and @-mentioning either notifies no one while still
reading as though the review had been routed somewhere. That false signal
is worse than no mention, so for those authors the prompt explicitly
suppresses the mention and leans on the marker instead.

Also ask the reviewer to report the limits of its own review. An honest
"I could not verify X" is more useful than a confident guess, and leaving
it out is how an unreviewed change gets waved through on the strength of
an automated approval.

Routing rides the existing review.post_comments switch, so a hive that
has not opted into commenting sees no prompt change.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PlanDispatch spends a fixed parallel-review budget in PR order, and an
uncapped PR takes as many slots as it has missing perspectives. On a short
queue that is exactly right - fanning every perspective out at once is the
review swarm's designed behavior. On a deep one it inverts the intent: the
head of the queue absorbs the entire budget, the PRs behind it get nothing
this cycle, and adding reviewers buys more opinions on one PR rather than
coverage across many. A spoke with hundreds of open PRs is the case where
that matters, because there the scarce thing is PRs looked at, not depth
per PR.

Add review.max_perspectives_per_pr, which caps perspectives dispatched to
one PR per cycle. It loses no coverage: a perspective skipped this cycle is
still missing next cycle and gets dispatched then, so the cap schedules
depth rather than dropping it. It also bounds how many comments a single PR
can collect at once, which starts to matter now that reviewers publish
their verdicts.

Zero means no cap, so every existing hive keeps the behavior it has. The
cap is subordinate to MaxParallelReviews, which still bounds total
concurrency.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ackport #6303 to v4) (#7523)

Backport of #6303 (merged to v5) to the v4 release line, which still ships
the vulnerable dev:node 0664 token-access.jsonl: every agent UID is in
group node, so any prompt-injected agent could truncate the trail of its
own token use, rewrite lines to blame a peer, or forge entries
(CWE-284/CWE-345). Fixed on v5 on 2026-09-08; v4 has released ~20 versions
since (HEAD is v4.54.0) without it.

Deviations from the v5 commit, forced by branch divergence:
- src/cmd/hive/notifywire.go does not exist on v4; the
  StartTokenAccessAuditWatcher call is wired into cmd/hive/main.go next to
  the existing PrepareRequestDirs call (same placement rationale).
- .github/workflows/v2-ci.yml step ('Entrypoint token-access audit log not
  agent-writable') is NOT included: this agent tier cannot push workflow
  changes. The test script src/deploy/test_entrypoint_token_access_audit.sh
  IS included; wiring it into CI needs a human (see tracking issue).

Verified: go build ./cmd/hive ./pkg/github ./pkg/dashboard, go vet, and
go test ./pkg/github -run TokenAccess all pass on this branch.
bin/test_git_credential_hive.sh: 21/22 pass; the one failure is the known
live-host non-hermeticity (the UID map resolves the harness UID to the
real agent, overriding the claimed name — the forge-guard working as
designed, not a defect).

Refs #6287

Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: Andy Anderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ss of existing spokes

#7457 fixed the 401 on /api/contribute/me (#7453) in two halves: code on
the hub and the spoke, and two nginx annotations — auth-url and
auth-response-headers — on the hive-contribute Ingress in
k8sManifestTemplate. The code half rolled out with the next image. The
Ingress half is `kubectl apply`ed only by provisionHive, so every hosted
spoke provisioned before the merge still serves /api/contribute through
an Ingress with no auth-url: nginx never asks the hub who is calling,
X-Hive-User never arrives, and /api/contribute/me answers 401 to the
hive's own signed-in owner on a spoke whose served_sha is well past the
fix (hosted-projectbluefin-common-nmq5, #7517).

Add the missing re-apply as a hub reconcile on the NET_ADMIN /
per-hive-env pattern (contribute_ingress_reconcile.go):

- Every 15 minutes, for each hosted hive on an nginx cluster (OpenShift-
  Route clusters render Routes, not this Ingress; pull-only clusters have
  no kubectl path), read the live hive-contribute Ingress and merge-patch
  the two annotations on when absent or stale. Only the drifted keys go
  in the patch, so the issuer, timeouts and any vanity-host marks are
  untouched. A converged Ingress is a Debug no-op; an unparseable one is
  a Warn and no patch, never a blind one. An annotation patch rolls no
  pod, so there is no per-cycle cap.
- The expected values come from the same inputs the template renders
  from (hubPublicURL, hive ID); TestContributeIngressReconcileMatchesThe-
  Template renders the template and pins them equal, so the sweep and
  provisioning cannot disagree about what converged means.
- Wired into the SHA poller next to its siblings; a test pins the call
  site so the lane cannot be written, tested and never run (#2674).
- Sweep accounting: a filter that selects nobody on a populated registry
  warns, and a sweep that patched or failed anything logs a summary.

docs/security-model.md records the general rule the issue asks for: a
template change to an existing object reaches only spokes provisioned
after it, so it needs a reconcile path or a re-provision note.

Not covered here: the sign-in bounce the issue mentions in passing — its
redirect chain has not been captured yet, and the issue itself says it
may be a separate cookie-scope question.

Fixes #7517

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Douglas Baggett <doug.baggett@gmail.com>
…cile

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: clubanderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: clubanderson <andy@clubanderson.com>
GitHub folds every unsatisfied branch-protection rule into the single word
"blocked", so a PR pill that hovered as "blocked — all sweep gates pass; a
branch-protection rule is unsatisfied" (the step-1 placeholder from #7516)
still sent the operator to GitHub to learn which rule — the trip the pill
exists to save.

The sweep now collects the two facts GitHub's REST PR payload does not
carry, both at enumeration time and neither per PR:

  - GitHub's own reviewDecision plus each reviewer's current position, from
    ONE GraphQL query per repository per pass (paginated 100 PRs at a time);
  - the base branch's required status-check set — from the operator's
    auto_merge.required_checks when installed, which costs no API call at
    all — compared against the check runs EnrichCIStatus already walks.

github.PullRequest.BranchProtectionBlockReason is the one place that turns
those facts into words. It is deliberately conservative: it returns ok=false
whenever nothing on hand identifies a rule, and it refuses to claim a
required check "has not reported" on a repository where no required context
was seen as a check run at all, because there the context is a commit status
and every one of them would look absent.

The dashboard needs no frontend change: prMergeNote already renders a
blocked verdict's reason verbatim.

Refs #7515

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>
The warm-cache restore is only a win when it is faster than the
compile it replaces. On some cluster nodes the download from the
Actions cache backend crawls — observed 16-minute 'Restore warm Go
build cache' steps (run 35299669695, test agent 3/5 and dashboard
shuffle 2/2) against a ~2-minute cold compile, making the two slow
jobs the run's 21-minute long poles. The actions/cache default gives
each segment 10 minutes; cap it at 1 so a slow restore aborts and the
job builds from scratch instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: clubanderson <andy@clubanderson.com>
…out of manager.go (#7532)

Verbatim block moves matching pre-adoption v5 file boundaries.
manager.go: 4,321 -> 1,793 lines.

Part-of: #7303

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v4.55.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>
TestProbeCopilotModelsSDK_AbsentHelperYieldsSentinel and
TestProbeCopilotModelsSDK_HelperNotInstalled exec'd whatever lived at the
production helper path (/usr/local/bin/copilot-models.mjs). They handled two
states — helper absent (assert sentinel) or helper works (skip) — but not the
third: helper installed without stored copilot auth, the normal state on live
agent hosts, where the helper exits 1 ('Not authenticated'), the sentinel
check fatals, and the entire pkg/dashboard suite goes red.

Fix: copilotSDKHelperPath becomes a var with an in-package test seam
(setCopilotSDKHelperPathForTest, mirroring knowledge.SetBaseDirForTest).
Both tests repoint it at a nonexistent temp path so the absence assertion
holds on every host, and a new TestProbeCopilotModelsSDK_FailingHelperIsNotAbsent
covers the present-but-failing exec path (#7365's signature) with a stub
script, hermetically. No production behavior change.

Signed-off-by: hive-quality[bot] <hive-quality[bot]@users.noreply.github.com>
Co-authored-by: hive-quality[bot] <hive-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… 8,072 lines) (#7510)

* 🌱 cmd/hive: extract five domains from main.go into their own files

main.go was 9,795 lines of package main, mixing unrelated business logic
with the process entrypoint. The file this issue is named after,
main_helpers.go, is already gone (dissolved across #7241..#7308), but the
other half of #7238 — "package main concentrates business logic" — still
held: main.go alone was 9,795 of the package's 12,059 non-test lines.

This is a stage. Five self-contained domains move out, each into one file
in the same package main:

  appkeyfile.go        GitHub App private-key file management: where
                       per-App-ID and per-hive key files live, how a key is
                       written, resolved, fingerprinted, and how a missing
                       key is described.
  selfupgrade.go       self-upgrade bookkeeping: the upgrade marker and
                       last-outcome files, the retry/backoff budget, and the
                       boot-time reconciliation of the two.
  login_scan.go        login-required scanning: detecting a backend dropping
                       to an interactive login prompt and debouncing that
                       across sightings so one noisy line cannot pause an
                       agent.
  merge_eligibility.go merge eligibility: hold gates, required-check state,
                       bucket classification, the trusted-merger authz
                       binding, and the merge-eligible report.
  intent_verdicts.go   intent verdicts: PR/issue evidence gathering,
                       alignment summary, the advisory record, and the
                       intent-verdicts report.

main.go: 9,795 -> 8,072 lines. The diff on main.go is 1,723 deletions and
0 additions: nothing was edited in place.

Pure code motion. Declarations were moved byte-identically, with their doc
comments; nothing was renamed, reformatted, or "improved". Verified with a
structural declaration diff that parses every top-level func (including
methods), type, const and var — including members of grouped const(...) and
var(...) blocks — from the original main.go and from the union of the six
resulting files, keyed by name and compared byte-exactly:

  BEFORE files: 1  declarations: 242
  AFTER  files: 6  declarations: 242
  LOST: 0   ADDED: 0   CHANGED: 0   DUPLICATE: 0
  RESULT: PURE CODE MOTION

Cross-checked against go/ast: 241 unique declaration names before and
after, identical sets.

One grouped var block (ciFailingPath, intentVerdictsPath) is deliberately
left in main.go: its two members belong to different domains and splitting
the group would have meant rewriting a declaration rather than moving it.

Design-doc file:line citations that this motion shifted are updated by
content anchoring — each cited line was located by its exact text in the
new tree (see #7493 for why this drift keeps recurring). The App-key and
trustedMergerFunc citations now name their new files. Pre-existing drift in
citations unrelated to this move is left alone rather than silently
rewritten.

main.go remains 8,072 lines and #7238 stays open; the remaining bulk is
main() and runEvalCycle, tracked under #7232.

Refs #7238

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 test(github): let the F3 trusted-merger guard follow trustedMergerFunc out of main.go

TestF3AuthorizerIsWiredInMain read cmd/hive/main.go and looked for
trustedMergerFunc in it. This PR moves that declaration to
cmd/hive/merge_eligibility.go, so the guard failed with "trustedMergerFunc
not found -- it was renamed or removed", which reads as a lost fix when
this is pure code motion.

The func-body assertions now scan every non-test .go file in cmd/hive via
a new f3ReadPackage helper, so the guard follows the declaration wherever
the ongoing cmd/hive split puts it next. The package, not the filename, is
the unit that matters for "does this declaration still exist".

The wiring assertion stays pinned to cmd/hive/main.go on purpose: that one
is about where startup installs the authorizer, and it should fail if the
call moves out of startup.

Teeth verified by mutation, each a genuine assertion failure:
  - lowering the floor to config.RoleRead -> "no longer requires at least
    config.RoleMerger"
  - deleting the SetMergerAuthorizer call from main.go -> "does not install
    the trusted-merger authorizer ... INERT"
  - renaming trustedMergerFunc -> "not found -- it was renamed or removed"

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 📖 docs: correct five cmd/hive file:line citations this split left stale

Two shifts landed after the citations in this PR were first computed, and
each moved a cited line without moving the citation.

The new files' header comments were relocated below the `package main`
clause, which shifted every line in appkeyfile.go and merge_eligibility.go
up by one. And the v4 merge grew main.go by ~570 lines via #7512, which
moved runHub.

Re-anchored by content, not arithmetic: each citation's target was read out
of v4's main.go and located by its exact text in the merged tree. Verified
by reading every cited line back and matching it against the symbol the
prose names:

  appkeyfile.go:47-48     -> "Vars rather than consts so tests can point ..."
  appkeyfile.go:50        -> spokeProvisionedAppKeyPath = "/secrets/..."
  appkeyfile.go:51        -> spokeAppKeyPath            = "/data/..."
  merge_eligibility.go:50 -> func trustedMergerFunc(
  main.go:8104            -> func runHub(

The thirteen main.go citations this PR already rewrote were re-checked the
same way and are correct as they stand; they are untouched here.

src/scripts/check-api-reference-citations.sh still exits 0.

Refs #7238

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…7526) (#7539)

/api/status (and the SSE replay frame a reconnecting tab receives) serves
the cached s.status snapshot, which is only replaced by UpdateStatusIfFresh
at the end of an eval cycle — after the whole-fleet GitHub enumeration. On a
rate-limited 16-repo spoke that cycle runs far past eval_interval_s, so
agents the manager started (state=running assigned on the same code path
that logs "audit: agent started") still read state=stopped for 10-15+
minutes and the dashboard paints a healthy fleet red.

BroadcastAgentStatus already carries correct in-memory liveness on a 10s
tick with no GitHub calls, but only fanned it out over SSE. It now also
patches the agent block (Agents/HiddenAgents/ConfiguredAgents) of the cached
snapshot, copy-on-write so handleStatus — which marshals its loaded pointer
outside statusMu — can never see a torn payload. The refresh runs ahead of
the redundant-frame skip, so a skipped broadcast still freshens the cache.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v4.55.1 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>
@clubanderson clubanderson added the no-changelog No changelog fragment required label Sep 18, 2026
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Sep 18, 2026
@kubestellar-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign danathar for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Carries v4 e74d45e..201c69c (v4.54.2, v4.55.0, v4.55.1) into v5,
including #7510 (cmd/hive domain extraction), #7532 (manager.go split),
#7527/#7528 (review routing + breadth-first slots), #7523 (token-access
audit backport of v5 #6303), #7515 (branch-protection facts on blocked
PR pills), #7535, #7539, #7520.

Resolution notes:
- cmd/hive, pkg/agent: declaration-level 3-way merge against base
  ff7630e; v5 seams (spokeStatePath, SetHubPushedDashboardURL,
  applyModeUnscheduledAlert, mutation boundary, runDuplicateSweepIfDue)
  preserved. F24 tombstone intact.
- pkg/github/client.go: v5 fetchPRs signature (RepoPRBreakdown) + v4
  BaseRef/prBaseRef; added Client.requiredStatusCheckContexts shim so
  v4's protection_facts.go compiles against v5, where the sweep lives in
  pkg/github/automerge.
- pkg/github/token_access_audit{,_test}.go: v4 side (its backport of
  v5's own #6303, now using testutil.Eventually).
- pkg/github/automerge/f3_trusted_merger_source_test.go: v4's
  f3ReadPackage path adjusted one level deeper for v5's subpackage.
- pkg/review/prompts.go: v4 routing instruction + mentionableAuthor
  added; v5's #6212 deletion of dead BuildPerspectivePrompts /
  BuildSequentialPrompt kept.
- pkg/agent tests: v4's normalizeModelName cases rewritten against v5's
  normalizeModelNameForBackend (v5 removed the wrapper in #5795).
- docs/knowledge-curator.md: v5 promotion-only body + v4's
  knowledge.git_sources section (code exists on v5). roadmap.md: v4
  rows. api-reference.md: v5 table, citations re-resolved (--fix).
- .github/workflows/v2-ci.yml: v5 UID-isolation step + v4's fuller
  audit-log step comment.

Refs #7463

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Andy Anderson <andy@clubanderson.com>
@clubanderson
clubanderson merged commit c66a944 into v5 Sep 18, 2026
55 of 57 checks passed
@kubestellar-prow
kubestellar-prow Bot deleted the sync/v4-to-v5 branch September 18, 2026 05:06
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution! Your PR has been merged.

We'd love to hear how your experience was: share feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. no-changelog No changelog fragment required size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants