Skip to content

πŸ› fix: atomic runtime config save + fresh legacy fallback (#17862) - #17867

Merged
clubanderson merged 2 commits into
v5from
fix/atomic-runtime-config-save
Nov 12, 2026
Merged

clubanderson merged 2 commits into
v5from
fix/atomic-runtime-config-save

Conversation

@clubanderson

Copy link
Copy Markdown
Member

Fixes #17862.

Problem

Config.Save wrote /etc/hive/hive.yaml and /data/hive.yaml.runtime in place with O_TRUNC. On hosted-available-vllmd-03 (liveness-killed ~14x/hr since Nov 7) a kill between truncate and write left a 0-byte runtime file; the entrypoint's -s guard fell back to /data/hive.yaml.bak - frozen at the Aug 2026 rename on every fleet hive - and the hive booted acmm_level 3 / 6 agents instead of the owner's level 6. No log line said so.

Fix

  • writeFileAtomic (pkg/config/atomic_write.go): temp file in the target's directory -> fsync -> rename onto the EvalSymlinks-resolved target, so the πŸ› /etc/hive/hive.yaml is an emptyDir on hosted spokes β€” edits don't survive restartΒ #17468 /etc/hive/hive.yaml -> runtime link survives (TestSaveThroughSymlinkKeepsLink stays green). If rename is refused (single-file Docker bind mount, EBUSY) it falls back to the previous in-place write, so those deployments behave exactly as before.
  • Both Save writes use it; runtime stays 0600, the source path keeps its existing mode.
  • entrypoint: hive_refresh_legacy_fallback copies a YAML-valid (has project:) runtime config over hive.yaml.bak via tmp+mv on every clean boot, only when the new name is the boot source - so the fallback is at most one boot stale and the Docker/LXC "only copy" worry never applies. hive_runtime_config_read now WARNs loudly on stderr when it falls back (names the 0-byte truncation, fallback age, and flags a fallback older than the damaged runtime file with pointers to .dashboard/audit.jsonl).
  • Hub copy-config template comment and RuntimeConfigFileLegacy doc updated.

Tests

  • atomic_write_test.go: content+mode replacement, missing-file create, symlink target preserved with no temp in the link dir, and TestSaveSurvivesKillMidWrite (reader racing 200 Saves never observes an empty/partial file - fails against the old O_TRUNC write).
  • deploy/test_entrypoint_runtime_config.sh: fallback WARN emitted / steady state silent; refresh happens from a valid runtime, not from an invalid or project-less one, not when booting from legacy; legacy is never removed and only moved-over inside the refresh helper. 18/18 pass locally.

Evidence

Incident write-up for vllmd-03 in the issue; Deployment/PVC//data were intact, only the runtime config reverted.

Config.Save wrote /etc/hive/hive.yaml and /data/hive.yaml.runtime in place
with O_TRUNC. A liveness SIGKILL between truncate and write left a 0-byte
runtime file; the entrypoint's -s check then fell back to /data/hive.yaml.bak,
frozen at the Aug 2026 rename on every hive, and vllmd-03 booted level 3
instead of 6.

- writeFileAtomic: temp file in the target's dir, fsync, rename onto the
  EvalSymlinks-resolved target so the #17468 link survives; falls back to
  the old in-place write when rename is refused (single-file bind mounts).
- Both Save writes use it; runtime stays 0600, source keeps its mode.
- entrypoint: hive_refresh_legacy_fallback copies a YAML-valid runtime
  config over hive.yaml.bak on clean boots (tmp+mv), only when the new name
  is the boot source; hive_runtime_config_read WARNs loudly on fallback and
  flags a fallback older than the damaged runtime file.
- Tests: writer unit tests incl. symlink + no-partial-read race; shell
  suite covers WARN, refresh, parse guard, legacy-boot no-op.

Fixes #17862

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
@kubestellar-prow kubestellar-prow Bot added dco-signoff: yes Indicates the PR's author has signed the DCO. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 12, 2026
@clubanderson

Copy link
Copy Markdown
Member Author

/lgtm
/approve

@kubestellar-prow

Copy link
Copy Markdown
Contributor

@clubanderson: you cannot LGTM your own PR.

Details

In response to this:

/lgtm
/approve

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubestellar-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: clubanderson

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

The pull request process is described 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

@kubestellar-prow kubestellar-prow Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Nov 12, 2026
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Open PR overlap check

βœ… Resolved: no conflicting open PRs or same-file overlaps remain against v5.

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Mutation testing

ℹ️ 1 mutant(s) on added lines were SKIPPED because gremlins v0.6.0 only honours the first lines added lines of each default-context hunk, so non-contiguous edits in one hunk are partly missed. This does not affect the verdict.

File Line Mutation
src/pkg/config/config.go 7808 CONDITIONALS_NEGATION

All 18 mutant(s) in changed files were killed.

Verdict: pass (#17349).

@hivecommons-hive

hivecommons-hive Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Hive status β€” updated in place; edits do not notify. Full history is in the Activity Monitor.

@clubanderson this PR is held at stage/071-needs-author and the hive will not merge it yet.

Failing checks:

  • coverage

Next: the hive does not push to your branch. Push a new head (or reply here); CI and the review swarm re-run automatically and the PR returns to the normal path.

Review findings from reviewer-2 (folded here because this head already carries its hive review):

Read atomic_write.go, the saveLocked changes, the entrypoint helpers, and the copy-config template at v5. The core change looks correct to me; one gap in the fallback worth a look.

Verified

  • writeFileAtomic resolves the link with EvalSymlinks before choosing the rename target (src/pkg/config/atomic_write.go:37-42), so on hosted spokes the /etc/hive/hive.yaml -> /data/hive.yaml.runtime link from saas_provision.go:3472 survives β€” the rename lands on the PVC file, not on the emptyDir link. The template only creates that link when the runtime file is -s (non-empty), so the dangling-link case where EvalSymlinks returns ErrNotExist and the rename would replace the link itself is not produced by the hosted path.
  • Temp file is created in the target's directory, chmod'd before any data is written, fsync'd, then renamed; syncDir is best-effort after. Rename failure (EBUSY on a single-file bind mount) falls back to the old O_TRUNC write (atomic_write.go:71-79), which is what those deployments had before.
  • sourceConfigMode uses os.Stat (follows the link) so a tightened source-file mode is preserved; the explicit post-write Chmod(0600) on the runtime file is correctly dropped since the mode is now applied to the fresh inode.
  • hive_refresh_legacy_fallback is gated on $1 = $HIVE_CONFIG_RUNTIME and -s (entrypoint.sh, new function), so it only runs when the new name is this boot's source β€” the legacy file is never the only copy when it is overwritten, which is the Docker/LXC concern the old never-write rule protected. The copy is tmp + mv -f in the same directory, so the fallback cannot itself be truncated by a kill. hive_harden_runtime_config exists at entrypoint.sh:617.

One gap (low): the in-place fallback does not cover a failed CreateTemp

atomic_write.go:44-48: if the target's directory is not writable but the file is (root-owned /etc/hive with a user-owned hive.yaml, or a read-only dir with a writable file), os.CreateTemp fails and the function returns that error β€” writeFileInPlace is only reached on a failed rename. The previous OpenFile(O_WRONLY|O_TRUNC) would have succeeded there. On Kubernetes this is absorbed by the existing tolerance at config.go:7836-7847 (source failure is logged and Save returns nil when runtime + overlay were written), so it is a behaviour change only where the runtime layer also fails. Since the PR already accepts in-place as an acceptable fallback, extending it to an EACCES/EROFS from CreateTemp would close the gap cheaply. Suggestion, not a blocker.

Minor: TestSaveSurvivesKillMidWrite races a reader against concurrent Saves rather than simulating a kill β€” it does pin "no reader observes a partial file", which is the property that matters, just noting the name over-promises slightly.

stage 080-awaiting-merge-admission β†’ 071-needs-author: required-check-failing:coverage

@hivecommons-hive

Copy link
Copy Markdown
Contributor
1273a7e

ℹ️ Sentinel notice β€” informational; author is trusted (owner), merge is not blocked

Hive flagged this PR (author @clubanderson, head 1273a7e37d86) because it matches behaviors that can override security controls, escalate privileges or damage the codebase. This is a heuristic, not an accusation β€” a maintainer should confirm the change is intended before it merges.

  • sensitive_path β€” changes 2 sensitive path(s) (Touches a sensitive path (OWNERS, workflows, policies, hive config, security docs, …))
    • src/deploy/entrypoint.sh
    • src/deploy/test_entrypoint_runtime_config.sh

Hive did not add the sentinel-alert label because this author is trusted. The finding remains recorded for audit and the dashboard Security tab; set sentinel.trusted_authors_block: true to require the blocking label for trusted authors too.

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read writeFileAtomic, both saveLocked call sites, the hub copy-config init command and the entrypoint resolver against v5. This looks correct to me; no defects found.

What I verified

  • Symlink case (#17468): the init container only creates /etc/hive/hive.yaml -> /data/hive.yaml.runtime when the target is non-empty (src/pkg/hub/saas_provision.go:3472, [ -s … ] guard), so EvalSymlinks in atomic_write.go:41-46 always resolves to an existing target and the rename lands on the PVC file, never on the link. The ErrNotExist branch (dangling link replaced by a regular file) is therefore unreachable from that template.
  • Bind-mount fallback: rename(2) onto a mount point fails with EBUSY; atomic_write.go:76-84 then performs the old O_TRUNC write, so single-file Docker mounts keep their inode exactly as before.
  • Mode handling: sourceConfigMode (config.go:7834) uses os.Stat, which follows the link, so on hosted spokes the source write preserves the PVC file's 0600 rather than widening it to 0644. The explicit post-create Chmod that was removed from the runtime branch is now covered by tmp.Chmod(mode) before the rename (atomic_write.go:54).
  • Entrypoint: hive_refresh_legacy_fallback only fires when $1 == $HIVE_CONFIG_RUNTIME and that file is non-empty and parses with a project map, so the legacy file is never the sole copy when it is overwritten; the copy goes through ${…}.tmp.$$ + mv -f. Cleanup registered in the shell test matches this.

Info (not a defect): the YAML parse guard depends on import yaml. src/Dockerfile:712-714 installs PyYAML best-effort (|| echo WARN), so an image where that install failed would log "does not parse as a hive config" on every boot and never refresh the fallback β€” a graceful degradation back to today's behaviour, but worth knowing when reading boot logs.

The PR body's claims match the diff (atomic write on both Save targets, loud WARN on fallback, refresh only from the new name). coverage is the only red check; nothing in the diff looks responsible for that beyond the new file needing its tests counted.

β€” hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive

Copy link
Copy Markdown
Contributor

CI: coverage is the only red check β€” pkg/config landed at 89.9% against a 90% floor (run 43167441822). PR-local: the drop comes from the error/fallback branches in the new atomic_write.go, which the four tests there never reach.

Not pushing to your branch (human-authored PR); suggested additions to src/pkg/config/atomic_write_test.go that cover the unexercised branches with no os.Rename faking:

// EvalSymlinks error that is NOT ErrNotExist (ENOTDIR) must surface, not be
// treated as "missing target".
func TestWriteFileAtomicRejectsPathThroughRegularFile(t *testing.T) {
	dir := t.TempDir()
	file := filepath.Join(dir, "plain")
	if err := os.WriteFile(file, []byte("x"), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := writeFileAtomic(filepath.Join(file, "child.yaml"), []byte("y"), 0o600); err == nil {
		t.Fatal("expected error for path through a regular file")
	}
}

// Unwritable target directory: CreateTemp fails and the error is reported.
func TestWriteFileAtomicUnwritableDir(t *testing.T) {
	if os.Geteuid() == 0 {
		t.Skip("root ignores directory modes")
	}
	dir := t.TempDir()
	if err := os.Chmod(dir, 0o500); err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = os.Chmod(dir, 0o700) })
	if err := writeFileAtomic(filepath.Join(dir, "hive.yaml"), []byte("y"), 0o600); err == nil {
		t.Fatal("expected error when temp file cannot be created")
	}
}

// The rename fallback is kept for bind mounts; pin it directly.
func TestWriteFileInPlace(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hive.yaml")
	if err := os.WriteFile(path, []byte("old-longer-content"), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := writeFileInPlace(path, []byte("new"), 0o600); err != nil {
		t.Fatal(err)
	}
	got, _ := os.ReadFile(path)
	if string(got) != "new" {
		t.Fatalf("content = %q, want new", got)
	}
	if fi, _ := os.Stat(path); fi.Mode().Perm() != 0o600 {
		t.Fatalf("mode = %v, want 0600", fi.Mode().Perm())
	}
	if err := writeFileInPlace(filepath.Join(dir, "missing", "hive.yaml"), []byte("x"), 0o600); err == nil {
		t.Fatal("expected error for missing parent dir")
	}
}

sourceConfigMode for a missing path (0o644 default) is also worth one assertion if the floor is still tight after these.


🐝 Hive Agent: scanner | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown

β€” hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive

Copy link
Copy Markdown
Contributor

CI triage (hive scanner, comment-only on a contributor-authored branch): the only red check is coverage (v2 Tests) β€” COVERAGE GATE FAILED: config: 89.9% (floor 90%). Overall ./pkg is 89.7%, ./crux 92.9%, both fine; it is just the pkg/config package landing 0.1pt under its floor after the new atomic-save / legacy-fallback branches. One small table-driven test covering the error paths of the new save helper (rename failure, tmp-file write failure, fresh-fallback when the legacy file is absent) should clear it. Baseline check: v5's own coverage job is not red, so this is PR-local.


🐝 Hive Agent: scanner | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown

β€” hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88

Adds tests for missing target dir, ENOTDIR resolve error, dangling
symlink, rename+fallback double failure (no temp leak), in-place
fallback, syncDir tolerance and sourceConfigMode, lifting pkg/config
back over its 90% coverage floor.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
@clubanderson
clubanderson merged commit 05b4c6c into v5 Nov 12, 2026
86 of 87 checks passed
@kubestellar-prow
kubestellar-prow Bot deleted the fix/atomic-runtime-config-save branch November 12, 2026 08:46
clubanderson added a commit that referenced this pull request Nov 12, 2026
* πŸ› agents: Copilot CLI ET_EXEC binary fails with ENOEXEC on kernel 6.12 β€” exec via ld.so + startup preflight (#17737)

Fixes #17729. Refs #17642.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.221.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.221.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* fix: warn once per agent about user identity in agent gitconfig (#17742)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› fix: name ENOEXEC instead of dumping binary stderr in model discovery error (#17756)

* πŸ› fix: name ENOEXEC instead of dumping binary stderr in model discovery error

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› fix: import unicode/utf8 and keep ANSI-coloured stderr out of the ENOEXEC check

cli_models.go used utf8.ValidString without importing unicode/utf8, so the
dashboard package failed to build. isBinaryStderr also treated ESC (0x1b) as
a binary control byte, so coloured stderr would have been mislabelled as an
ENOEXEC dump; ESC is now allowed alongside the whitespace controls, with a
regression test.

Hive-Fix-Attempt: 1/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ✨ crux: Substrate as the 4th sandbox runtime (opt-in, preflight-gated) (#17741)

* ✨ crux: Substrate as the 4th sandbox runtime (opt-in, preflight-gated)

Add Agent Substrate (CNCF Sandbox) as sandbox.runtime: substrate behind
the existing sandbox.Launcher seam, mirroring the OpenShell runtime
(ADR-0026 phase 2).

- crux/sandbox/substrate*.go: SubstrateLauncher (ActorTemplate + Actor
  per kick, atenet-egress policy derived from the proxy host set +
  allow_hosts, workspace push/run/pull through an in-actor bridge over
  the atenet-router mTLS CONNECT tunnel, snapshot_on_idle suspends
  instead of deleting), real gRPC client constructed lazily, Control
  API preflight registered into crux/config.
- crux/sandbox/substrateapi: trimmed, wire-compatible proto subset of
  ate.control.v1 generated in-repo (no go.ate.dev/substrate import:
  it requires Go 1.27 and is very large). Zero new Go modules.
- crux/sandbox/substratebridge: the HTTP bridge served inside the actor.
- crux/config/agent_config_substrate.go: sandbox.substrate.* leaves with
  per-agent merge; pkg/config gate warnings (digest-pinned image).
- Settings parity allowlist entries for the new leaves (#17699).

Fixes #17697
Part of #17695

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* changelog: fragment for #17741

Generated by changelog-fragment-guard from the PR title (#5675).
Edit the wording freely, or replace this file with the no-changelog
label if the change is not user-visible.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* 🌱 changelog: make the substrate fragment an entry bullet

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 changelog: drop the auto-generated duplicate fragment

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 substrateapi: drop a never-true nil comparison flagged by staticcheck

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Signed-off-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* πŸ“– docs: ADR-0026 phase 2 β€” Agent Substrate runtime operator and deploy notes (#17745)

* πŸ“– docs: ADR-0026 phase 2 β€” Agent Substrate runtime operator and deploy notes

Amend ADR-0026 with the Substrate implementation notes (contract mapping,
deviations from the reference model, cluster prerequisites and which hive
clusters qualify, when to choose it over OpenShell); add the operator
section to sandbox-isolation.md, the integrations table row, deploy/README
pointer and a new deploy/substrate.md with install and verification steps.

Part of #17695

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* changelog: fragment for #17745

Generated by changelog-fragment-guard from the PR title (#5675).
Edit the wording freely, or replace this file with the no-changelog
label if the change is not user-visible.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* 🌱 changelog: make the substrate docs fragment an entry bullet

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 changelog: drop the auto-generated duplicate fragment

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ“– docs: fix the ADR-0002 link in the Substrate section

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Signed-off-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* πŸ”– release: v5.221.1

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.221.1 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* fix: stop read-only lanes retrying hive:unauthorized label removal (#17743)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: reconcile and expire effect-ledger ops stuck in Unknown (#17746)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› fix: guard nil config in stageEngineOptions before reading WorkflowsDir (#17765)

stageEngineOptions dereferenced cfg.Data.WorkflowsDir as an argument before any nil check. Because stageEngine is a package-level sync.Once, the panic only fired when a nil-config caller was first in the process, making it order-dependent. Build the workflow store only when cfg != nil.

Closes #17736

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› Skip archived repositories during GFI curation (#17739)

* Fix GFI curation for archived repositories

Hive-Run: hivecommons/hive#17734
Hive-Plan: archived-repository GFI recovery
Hive-Spec: good-first-issue#archived-repositories
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

* fix: correct changelog fragment category

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

---------

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: hivecommons-hive[bot] <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.222.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.222.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› marketplace: resume CNCF catalogue seeding across hub restarts and let spokes trigger refresh (#17773)

* πŸ› marketplace: resume CNCF catalogue seeding across hub restarts and let spokes trigger refresh

Root cause of the one-entry catalogue on hive.hivecommons.dev: GitHub
tarballs lead with a pax_global_header entry, which Go's tar reader
surfaces as a header. ExtractTarGz counted it as a second top-level name,
returned dest instead of the wrapped "<owner>-<repo>-<sha>/" directory,
and ingest.ReadTree then saw every path with the wrapper prefix β€” no
GitHub Actions workflow matched. Only tektoncd/pipeline got through, via
the content-based Tekton sniff. Skip pax headers when extracting.

Seeding is now resumable: at startup the hub ingests every corpus repo
that is due (no record, record older than 24h or in an older format,
error older than 1h) instead of only when the catalogue is empty, so a
restart mid-seed continues and a flapping hub does not re-crawl fresh
repos. Records carry a format version; bumping it (format 2 for the
tarball fix) re-ingests everything on the next boot. The last run summary
is persisted so status.lastRefresh survives restarts; status gains
pendingRepos/lastRefreshErrors and ?verbose=1 (spoke or admin) lists the
failing repos with their errors.

POST /api/marketplace/refresh accepts the same credentials as publish
(hub admin session or spoke heartbeat bearer + X-Hive-ID); spokes are
rate-limited (one full run/hour, 10 ?repo= runs/min β†’ 429 + Retry-After),
admins are not. The spoke proxy passes 429/409 through with the hub's
message. The crawl sleeps on 403/429 Retry-After (secondary limits) and
logs progress every 25 repos.

Refs hivecommons/hive#17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› marketplace: update refresh tooltip in static-src and regenerate index.html

Refs hivecommons/hive#17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: bound consecutive stale-snapshot drops in the status builder (#17744)

* fix: bound consecutive stale-snapshot drops in the status builder

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ“– docs: update staleDropStarvationThreshold comment for bounded publish

The constant's doc comment still described the pre-PR contract (drop
logged at Warn). It now marks the point where the build is published
anyway to bound staleness.

Hive-Fix-Attempt: 1/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: ci-maintainer <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ“– docs: resync api-reference citations after server.go route shift

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Signed-off-by: ci-maintainer <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ› fix: log Prow ok-to-test rejection at WARN only when first noted (#17754)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* ✨ crux: Substrate preflight prover (operator, CRDs, gVisor RuntimeClass, feature gates) (#17766)

* ✨ crux: Substrate preflight prover (operator, CRDs, gVisor RuntimeClass, feature gates)

Extends the sandbox.runtime: substrate preflight with the cluster facts
behind a failing kick: operator CRDs and workloads, ready WorkerPools and
their sandbox classes, gVisor (SandboxConfig or runsc RuntimeClass) and
KVM availability, node kernels, the certificates.k8s.io APIs behind the
PodCertificateRequest and ClusterTrustBundle feature gates, and an mTLS
CONNECT probe of atenet-router. Skipped outside a cluster; one warning
when RBAC is missing, with a manifest that grants it.

Fixes #17698

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* fix(crux/sandbox): egress policy atespace + in-place workspace clear found by #17697 kick

The first real kick through Substrate v1.0.0 (kind, gVisor) surfaced two
defects in the launcher path: CreateActorEgressPolicy was rejected with
'egress_policy.metadata.atespace: Required value', and the bridge's PUT
/workspace failed with EBUSY because os.RemoveAll targeted the durableDir
mount point itself. Set the atespace on the singleton policy and clear the
workspace directory's contents in place.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* test(crux/sandbox): run the kube/router preflight halves only in a Pod wired for Substrate

CI runs in Pods, so /var/run/secrets/kubernetes.io/serviceaccount/token
exists there and the kube/router halves of the prover ran during the
Control-API-only tests (crux/sandbox and crux/agent). The prover now
requires both the Kubernetes token and hive's projected Substrate token
(opts.token_file, /var/run/secrets/ate/token by default) before probing the
cluster; the Kubernetes token path is a package variable the sandbox test
package pins to a nonexistent file, with a helper that flips both markers on
for the tests covering the in-cluster branches.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: openshell sandbox names and driver_config fit gateway 0.1.8 limits (#17780)

Prototype on LKE (#17643) hit four gateway rejections before a kick ran:
sandbox names over MAX_ROUTABLE_NAME_LEN (19), the reserved 'workspace'
volume name, default-denied pod labels/annotations, and the missing Agent
Sandbox prerequisite. Names are now hk-<slug>-<base36 unix> (fnv tag when
the slug does not fit), the claim mounts as hive-workspace, and the deploy
doc records the operator settings the measurements depended on
(allowed_pod_labels, workspaceStorageClass, sandboxUid, auth modes) plus
the landlock-in-LSM preflight lesson and the measured numbers.

Refs #17643, #17642

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.222.1

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.222.1 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› marketplace: stream repository tarballs and keep only pipeline files (#17793)

The marketplace fetcher extracted each default-branch tarball to disk via
complexity.ExtractTarGz under a 256 MiB whole-tree cap, so large monorepos
(kubernetes/kubernetes, kserve/kserve, meshery/meshery, shipwright-io/build,
spidernet-io/spiderpool) failed with "archive exceeds size cap" and never
reached the catalogue. ingest.ReadTarball now streams the archive and keeps
only the paths ReadTree would select (shared selectTreeFile), 1 MiB per
file and 32 MiB in total, discarding everything else without buffering;
the complexity cap is untouched for its own callers.

GET /api/marketplace/status now includes failing:[{repo,error,checkedAt}]
without ?verbose=1 (repo names and fetch errors carry no secrets), so an
operator with a plain PAT can see why repos fail. Four corpus entries that
GitHub now redirects are updated to their current names.

Refs hivecommons/hive#17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: redact proxy userinfo in upstreamproxy URL errors (#17731)

* πŸ› fix: redact proxy userinfo in upstreamproxy URL errors

parseProxyURL formatted the raw proxy URL into its rejection errors, and
configureUpstreamProxy logs that error, so a malformed URL carrying the
proxy's Basic credential put the password in the hive JSON log on every
boot. logscrub's basic-auth category covers Authorization headers only,
not URL userinfo, so nothing downstream masked it. Use url.Redacted() on
the parsed form, strip the userinfo segment when url.Parse itself fails,
and drop the *url.Error wrapper whose message repeats the full URL.

Fixes #17730

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* chore: re-key merge request after transient lint-pending refusal

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* ✨ benchmarks: per-PR estimated cost in /api/benchmarks/export (#17791)

Records now carry cost.basis (tokens_x_pricing | unknown), cost.usd_est,
token share, launches and price_table_date, attributed from the spoke's
launch-cost collector: the swarm's PR ref for reviewer records, the
governor's issue ref of an issue the PR closed for author records (both
ref spellings). Vendored benchmarks schema refreshed; converter and
dashboard lookup tests; docs cost section.

Fixes #17790
Refs #17694

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.223.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.223.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* ✨ marketplace: seed curated models and ship the CNCF TechDocs assessment workflow (#17798)

Curated marketplace tier gets a source of truth in the binary:
pkg/hub/marketplace/curated/*.yaml is embedded, strictly decoded, checked
for the curated invariants (provenance curated, a version, nothing left
inferred), compiled through bundle.Export and published at tier curated
on hub startup and at the start of every refresh run when that
name@version is absent. Bad files are logged and skipped, never fatal;
edits without a version bump are refused as conflicts, so a new version
is a new file. Seeded keys are reported in lastRefresh.curated.

The first curated model is cncf-techdocs-assessment@curated-2026.11.1:
Nate W.'s AI-assisted TechDocs assessment process (spec at
nate-double-u/cncf-techdocs@882be6f, intake template @ad91530), scoped to
cncf/techdocs. Issues travel requested β†’ accepted β†’ draft-open (β†’
escalated) β†’ closed/declined; deliverable PRs travel drafting β†’ in-review
β†’ stakeholder-review β†’ awaiting-merge β†’ merged with checks-failing and
discarded branches. Every gate (/accept, /decline, /verify, /ready,
/confirm, /approve, /discard, /abort) is released by a human role; the
drafter and verifier agents stop at gates. Checks are the repo's
format/link/spell-check workflows plus the manual provenance block. The
rules evaluator is exercised over hand-written Facts for each step.

cncf/techdocs is not a landscape project, so the corpus package gains an
operator-added extra.json merged into Repos(); the ingested-unreviewed
version now sits next to the curated one.

Docs: marketplace section of docs/design/workflow-models.md (curated
seeding, where a model goes, how versions work) and
docs/workflows/techdocs-assessment.md for the TechDocs team.

Refs hivecommons/hive#17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: classify repeated agent-lifecycle log lines instead of filing unclassified selfreports (#17757)

* πŸ› fix: classify repeated agent-lifecycle log lines instead of filing unclassified selfreports

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* test: expect agent_lifecycle_repeated class for non-held kick failures

TestPostRestartKickHoldLogsWithoutFleetReport asserted the generic
unclassified class; 'failed to send kick' is now classified as
agent_lifecycle_repeated. Held kicks still produce no observation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ› fix(marketplace): refuse and downgrade model-provenance curated claims from non-admin bundles (#17764)

* πŸ› fix(marketplace): refuse and downgrade model-provenance curated claims from non-admin bundles

The hub checked only the envelope Tier when deciding whether a publisher may
assert curated/built-in; the model's own spec.meta.provenance is hash-covered
and stored verbatim. Spoke import preferred the model's provenance over the
envelope tier, so a registered spoke could publish a bundle every importer
badged as curated. The hub now refuses such bundles from non-admins (it
cannot rewrite the hash-covered model) and importers honour a vouched model
provenance only when the envelope asserts the same tier.

Fixes #17763

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* fix(marketplace): express importedProvenance as an if so the mutation gate sees its coverage

Go's cover tool does not instrument the case expressions of a tagless
switch, only the case bodies, so gremlins reported the three conditionals
on the switch as NOT COVERED even though TestImportedProvenance exercises
every branch. Fold the switch into a single if with identical semantics;
if conditions sit inside the preceding coverage block and are attributed
correctly.

No behaviour change: empty provenance still takes the envelope tier, and a
curated/built-in claim the envelope does not assert still falls back to it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ› fix: hub digest reads a stale stage-tracker body instead of blocking on a rebuild (#17785)

* πŸ› fix: let the hub activity digest read a stale stage-tracker body instead of blocking on a rebuild

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* πŸ§ͺ test: wait for the hub digest rebuild with testutil.Eventually instead of a sleep loop

TestSleepRatchet failed on CI (196 time.Sleep calls in tests, baseline 195):
the new stale-read test polled stageTrackerBuild with a hand-rolled
time.Sleep loop. Use testutil.Eventually, which is the helper the ratchet
points to.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ› fix: log issue-claims GitHub side-effect retries below WARN (#17786)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› fix: detect orphaned queued workflow runs in automerge held-run resolution (#17787)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* ✨ dashboard: surface agent_sandbox.substrate and per-agent sandbox.substrate settings (#17788)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› claude auth: share refreshed logins across per-agent .claude homes without sharing memory (#17767)

* πŸ› claude auth: share refreshed logins across per-agent .claude homes without sharing memory

Since the per-agent CLI homes landed, each per-UID agent has a real
~/.claude directory with only .credentials.json symlinked to the shared
login at /data/home/.claude/.credentials.json. Claude Code refreshes its
token by writing a temp file in ~/.claude and renaming it over
.credentials.json, then chmod 0600. The rename replaces the symlink with
a private, agent-owned file. bridgeHomeEntry refuses to clobber a regular
file, so nothing ever re-links it; the refreshed access token and the
rotated refresh grant stay in that agent's private file, the shared
credential ages out, and the rest of the fleet logs out every ~8h
(Discord report by Danathar, 2026-11-11).

Andy's direction: keep per-agent memory isolated, make an exception for
the login only. So no opt-in whole-directory sharing. Instead the hive
repairs the bridge in-process:

- repairClaudeCredentialBridge: when the agent's credential path is a
  regular file, read it (directly, or as its owner through the existing
  su-exec seam when the server cannot open an agent's 0600 file β€” the
  per-UID case), move it aside as .credentials.json.stale-<ts>, promote
  it into the shared login only if it is complete, usable and expires
  later than the shared token (a failed refresh's empty-token file never
  wins; ties keep shared), preserving the previous shared file, then
  restore the symlink. Legacy whole-directory ~/.claude symlinks and
  unreadable private files are left untouched, with a once-per-version
  log.
- Triggered at agent launch (setupPerAgentCLIHome), every 5s from the
  credential watchdog (HIVE_CLAUDE_CREDENTIAL_BRIDGE_INTERVAL) so a
  refresh is fleet-visible before siblings try the rotated grant, and
  right after a dashboard sign-in (ReloadClaudeToken).
- Audited as claude_credential_rebridged (no token material).
- claude.ReadTokens/ParseTokens/TokensUsable split out of LoadTokens so
  "cannot open" is distinguishable from "no token".

Tests reproduce the CLI's tmp+rename+chmod and show the shared file did
not update before the fix and does after; cover newer-shared, failed
refresh, su-exec owner read, unreadable fail-safe, legacy layout, stale
name collisions, watchdog cycle and sign-in trigger.

Refs hivecommons/hive#17039, hivecommons/hive#17253,
hivecommons/hive#17678, hivecommons/hive#17752, hivecommons/hive#17753

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 keep manager_tokens.go at its file-size ratchet baseline

Move the sign-in re-bridge body into claude_credential_bridge.go and
defer it from ReloadClaudeToken (LIFO: after the Unlock).

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› harden shared-home permission guard and credential promotion against non-regular files

Shared logins and CLI configs written by the hive (.claude/.credentials.json,
.copilot/config.json, the durable Copilot token) now go through a new
crux/sharedfile package: a randomly named temporary beside the target, mode
and owner set and verified on the open descriptor, rename, directory fsync,
and the write is refused unless the target is absent or a regular file in a
regular directory the server owns. Credential promotion writes a canonical
re-encoding of the parsed token block and checks the moved-aside source is the
same regular file before and after.

Parity with the v6 fail-safe behaviour: an unreadable private credential is
left in place except on a dashboard sign-in with a usable shared login, the
refusal is logged once per file version, WriteCredentials refuses a credential
without an access token, and ReadTokens treats malformed JSON as nothing
usable.

entrypoint.sh: the shared dot-dirs get the sticky bit (not under
HIVE_SHARED_AGENT_HOME=1); the single-file repairs act only on a regular file
in a regular directory and WARN once otherwise; the remaining root chmod/chown
sites under agent-writable directories gained the same checks. The Go
permissions watcher keeps setgid/sticky when widening a directory's group
bits. Shell tests drive the shipped guard functions with non-regular entries.

Refs hivecommons/hive#17039, hivecommons/hive#17253, hivecommons/hive#17678, hivecommons/hive#17748

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 wait for the watchdog re-bridge with testutil.Eventually instead of a polling sleep

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.224.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.224.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* ✨ hub: per-cluster host_aliases for hosted hives (#17805)

* ✨ hub: per-cluster host_aliases for hosted hives

Adds ClusterConfig.host_aliases ([{ip, hostnames[]}]) to /data/saas/clusters.json,
validated at load (IP parses, hostnames non-empty, lowercased; invalid blocks are
dropped with a warning, never the cluster). Rendered as Pod hostAliases in the
hosted-hive Deployment template next to the upstream_proxy env injection, and a
throttled, rate-limited reconcile sweep (3 patches / 15 min) converges existing
hives while preserving unrelated hand-applied aliases.

Needed for GHE hosts reachable only through a TLS reverse proxy on a fixed IP
(no CONNECT proxy), where upstream_proxy cannot apply.

Fixes #17801

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ“– api-reference: resync hub-server.go citations after host_aliases field

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: count PR author's own sign-off after a hive rebase; no decision gate on DCO asks (#17795)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* ✨ dashboard: render Substrate preflight warnings on the agent card (#17796)

* ✨ feat: render Substrate preflight warnings on the agent card

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› test: seed proc.Config so sandbox-warnings status test matches the builder's input

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› fix: dedupe repeated undelivered review kick warning (#17755)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› fix: cache app_keys_held key fingerprints so heartbeat collect stays in budget (#17806)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.225.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.225.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› fix(marketplace): restrict spoke-triggered ?repo= refresh to tracked repositories (#17800)

* πŸ› fix(marketplace): restrict spoke-triggered ?repo= refresh to tracked repositories

A registered spoke could pass any owner/name to POST /api/marketplace/refresh
and the hub would fetch it with its own GitHub identity and publish the
ingested model under the hub's name. Non-admin callers must now name a corpus
or HIVE_HUB_MARKETPLACE_EXTRA_REPOS entry; admins are unchanged.

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* 🌱 chore: refresh head to re-report required checks

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.226.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.226.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ”’ substratebridge: refuse tar entries that write through symlinks on Unpack (#17813)

* πŸ”’ substrate: unpack pulled workspaces through os.Root so tar symlinks cannot escape

Fixes #17769

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* chore: re-trigger required checks after base moved

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* ✨ dashboard: show substrate kicks and stream bridge output in the terminal pane (#17809)

* ✨ dashboard: show substrate kicks and stream bridge output in the terminal pane

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› substratebridge: create the workspace dir before the streaming run test

newBridgeServer intentionally leaves the workspace path uncreated so the
PUT/GET workspace tests can exercise the missing-directory paths, but
TestHandlerStreamsRunOutput ran a command in it and failed with
chdir: no such file or directory before any frame was streamed.

Hive-Fix-Attempt: 1/6
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› substrate kicks: stream only transitions after the viewer connected; cover new sandbox paths

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ› Mark hive's in-cluster k8s API dials so the egress redirect never MITMs them (#17818)

* πŸ› Mark hive's in-cluster k8s API dials so the egress redirect never MITMs them

On nodes without the xt_owner netfilter module (IBM Cloud IKS/ROKS) the
HIVE_PROXY chain has only the packet-mark self-exemption, so every :443
dial the hive makes for itself must carry SO_MARK. The proxy's upstream
dials did; the spoke's own Kubernetes API clients (self-upgrade, startup
probe, node metrics, ingress probes, kubejob) did not, so they were
REDIRECTed into the MITM proxy, hit the private-range forward guard and
got EOF β€” every wec2 hive failed self-upgrade with
'reading startup probe before upgrade … EOF'.

Move the SO_MARK dialer into a new crux/egressmark package (crux/proxy
imports crux/agent which imports kubejob, so kubejob cannot import
proxy) and use it for all five in-cluster API transports.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 Allow crux/egressmark in the dashboard import ratchet

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 Add crux/egressmark tests for the coverage gate

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› hub: stop flagging off-cadence and scheduled agents as idle-with-work (#17825)

classifyInactiveAgent ignored AgentSummary.ExpectedActive, so an agent with
no cadence in the governor's current mode (e.g. redteam while quiet) was
alerted as idle whenever the hive had queued work. Spokes also reported
KickIntervalSec=0 for times/cron cadences, dropping the hub to the 45m rule
between slots. Skip the idle rule when the spoke says the agent is not
expected active, and report the gap from the last fired slot to the next
as the kick interval for scheduled cadences.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: use unique temp files when persisting hive and reach state (#17740)

* fix: use unique temp files when persisting hive and reach state

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* 🌱 chore: re-run PR verifier after cancelled concurrent run

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* 🌱 chore: refresh head to clear a Copilot PR Automation run stuck in queued

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› hub heartbeat: back off and classify a permanent 403 rejection (#17823)

* πŸ› hub heartbeat: back off and classify a permanent 403 rejection instead of retrying every tick

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* 🌱 fleetreport: add threshold case for hub_heartbeat_rejected_unprovisioned

TestEveryRuntimeClassTriggerAndNonTriggerThresholds requires every class in
Classes to have a trigger/non-trigger case; the new class was missing one.

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.227.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.227.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* ✨ knowledge: Plaud voice notes as a knowledge source (#17821)

* ✨ knowledge: Plaud voice notes as a knowledge source

Add a plaud knowledge connector that reads Plaud Note recordings (Plaud
summary + transcript) through the free official Plaud MCP server over
stdio, with a minimal JSON-RPC MCP client, Connect/Disconnect sign-in on
the Knowledge Integrations card, docs and tests.

Fixes #17820

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 knowledge/plaud: port dashboard edits to static-src, document auth routes in openapi.json, drop unused field

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: hivecommons-hive[bot] <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› hive: do not gate the dashboard bind on the advisory-issue ensure (#17829)

* πŸ› hive: do not gate the dashboard bind on the advisory-issue ensure

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* πŸ› hive: add missing time import in boot_github_deps_test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ”– release: v5.227.1

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.227.1 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› substrate: garbage-collect older suspended kick actors when snapshot_on_idle is on (#17830)

* πŸ› substrate: garbage-collect older suspended kick actors when snapshot_on_idle is on

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› substrate: scope superseded-actor GC to the atespace it was created in and log via slog

Review follow-up for #17830:
- lastSuspended records {Name, Atespace}; the delete uses the stored
  atespace and is skipped (with a warning) when the agent's atespace
  changed between kicks, instead of targeting a name never created there.
- Replace stdlib log.Printf with an injected *slog.Logger on
  SubstrateOptions (slog.Default() fallback), wired from the agent
  manager, with agent/actor/atespace/error attrs.
- Document the one-snapshot-per-agent retention rule in
  sandbox-isolation.md, ADR 0026 and the dashboard snapshot_on_idle help.

Hive-Fix-Attempt: 1/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ› hub: converge startupProbe and init image onto existing hosted deployments (#17831)

* πŸ› hub: converge startupProbe and init image onto existing hosted deployments

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* docs: resync hub-server api-reference citations after server.go field insertion

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* πŸ”– release: v5.228.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› hub: negatively cache rejected bearer tokens; stop re-probing exhausted user quota (#17837)

validateGitHubToken re-hit GET /user with the caller's own token on every
request once the token was rejected or rate-limited, charging the owner's
exhausted core bucket and prolonging the 401 outage. Remember 401/403/429
for 60 s (or until X-RateLimit-Reset, capped at 15 min), record the probe in
REST accounting under the pass-through caller agent:hub-bearer-validate, and
route hub release-notes contents reads through the shared ETag/accounting
transport. Document the fleet-wide operator steps for getting personal
tokens out of cluster secrets (ARC runner scale set -> GitHub App auth).

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› crux serve: count auth-rejected requests in crux.serve.auth.rejections (#17839)

instrument sits inside authenticate, so requests refused with a 401 never reached crux.serve.requests or crux.serve.request.duration. Wrapping authenticate with instrument would label them with the unmatched route, so a separate counter, crux.serve.auth.rejections, is incremented in each 401 branch of authenticate. Its only attribute is a fixed-enum reason (missing_or_invalid_token, project_forbidden, no_token_configured); never a token, path or remote address.

Closes hivecommons/crux#146

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.228.1

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.1 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* fix(hub): bound bearer token validation cache (#17846)

Sweep expired entries at capacity and skip new entries when all slots remain occupied. Apply the same limit to accepted and rejected tokens.

Hive-Run: hivecommons/hive#17844
Hive-Plan: issue-17844-cap-and-expired-sweep
Hive-Spec: issue-17844#recommendation

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: Karibhou <Karibhou@users.noreply.github.com>

* πŸ”– release: v5.228.2

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.2 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ”’ knowledge: start the Plaud MCP server with an allowlisted env, not the hive process env (#17845)

stdioMCPDialer passed os.Environ() to a third-party MCP server fetched at
run time (npx -y @plaud-ai/mcp@latest), so every hub-injected credential
the hive process carries β€” DASHBOARD_AUTH_TOKEN (server-verified owner on
the dashboard API), HIVE_TERMINAL_KEY, HIVE_HEARTBEAT_KEY, HIVE_HUB_SECRET,
HIVE_UPSTREAM_PROXY_URL (proxy Basic credential), GitHub/Linear/provider
keys β€” reached code the hive does not control. crux/agent strips the same
set from agent panes (#17657); the MCP dialer bypassed that.

The child now gets an allowlist: PATH, HOME, locale, TMPDIR, TERM, the
proxy and CA-bundle variables Node needs to reach the registry through the
egress proxy, NODE_*/npm_config_*/NPM_CONFIG_* and PLAUD_*. Any name that
contains TOKEN, SECRET, PASSWORD or CREDENTIAL is dropped even when it
matches a prefix.

Fixes #17843

Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.228.3

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.3 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ”– release: v5.228.4

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.4 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* 🌱 images: bump omp 18.18.0 -> 18.19.0 (#17850)

Automated pin bump (#8419). Digests recomputed from the downloaded
release artifacts by src/scripts/cli-pin-bump.sh:

  VERSION=18.19.0
  SHA256_AMD64=b44fda2ae5c9cce2bbe124d1672cba532a136d9e1eef68599b16efcd3bc5c626
  SHA256_ARM64=61826bf89c2c0512417fee05fa2f486e60ee11b23ea95a17257a6c49edb6b8d5

Signed-off-by: hive-release[bot] <hive-release@hive.kubestellar.io>
Signed-off-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: hive-release[bot] <hive-release@hive.kubestellar.io>

* πŸ› army: stop attributing adopted PRs to a remote agent's rework queue and show why a fix is not dispatched (#17853)

The contribute fleet card listed every stage-store record by author in 072
or 074 as the relay's pending rework, so merged/closed PRs still carrying
stage/074-adopted-by-hive (the store prunes them only after 14 days) and
PRs the hive had taken over piled up as '33 PRs awaiting rework'. Only open,
not-adopted, not-dispatched PRs count now: the forge cache decides open vs
merged/closed when it knows the PR, otherwise a record the stage lane has
not re-observed for an hour is treated as gone. A queued fix whose PR is
gone leaves the fix queue instead of being handed to the relay.

The idle-with-queue stall: a fix dispatched to a socket that dropped stays
booked in flight with nobody holding it, so capacity 1 is busy forever and
the relay gets neither the fix nor new work (task_unavailable
fix_pr_in_flight, which the card hid behind 'idle Β· N awaiting rework').
The relay's next ready now releases such orphans (no attempt counted) and a
task_complete for the dispatch on a reconnected socket settles it. The card
prints the dispatcher's actual reason (pending_rework_blocked) instead of
'not yet dispatched', and links at most three PRs inline with the rest
behind a show-N-more disclosure.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: detect missing GitHub App repository coverage (#17733)

* fix: detect missing GitHub App repository coverage

Hive-Run: hivecommons/hive#17696
Hive-Plan: repository access and advisory recovery
Hive-Spec: issue-17696#expected
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

* fix: add App repository coverage changelog

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

* fix: bound boot coverage and probe large installations

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

* docs: clarify GitHub App coverage polling

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

---------

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>
Co-authored-by: hivecommons-hive[bot] <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.228.5

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.5 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* ✨ knowledge: Wispr Flow dictation history as a knowledge source (#17855)

* ✨ knowledge: Wispr Flow dictation history as a knowledge source

Adds knowledge.connectors[].type: wisprflow. Reads the desktop app's local
flow.sqlite read-only through the sqlite3 CLI (text columns only, never the
audio/screenshot BLOBs) or a folder of exported .json/.ndjson/.csv/.md
files, and emits one fact per finalised dictation (or per day) with the
dictation time, source app and URL. Incremental by timestamp with a 2h
overlap; min_words and apps filters; tags.

Dashboard: Knowledge -> Integrations card (path, status, last sync, count),
Add-source entry pointing out that new dictations need no connector at all
(dictate into + New Fact). Docs section next to Plaud's; changelog fragment.

Fixes #17854

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› knowledge: wisprflow tests run without the sqlite3 CLI

Sync tests now use a fake sqlite3 runner fed from testdata/wisprflow/history.json
so the SQLite path is exercised (and counted for coverage) on runners without
the CLI; the real-CLI test skips when sqlite3 is absent. TestWisprValidate
passes an explicit path so it no longer depends on a macOS default location.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix(agent): replace unopenable pluk session log so the supervisor can create it (#17889)

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ”– release: v5.228.6

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.228.6 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* ✨ workflows: owner toggle for model-based stage derivation from the dashboard (#17886)

* ✨ workflows: owner toggle for model-based stage derivation from the dashboard

workflow.enabled gates rules-based stage derivation and bound-model label
provisioning, but could only be set by editing hive.yaml, which hosted
hives cannot do. Add PUT /api/workflows/enabled {enabled} (owner-gated,
persists hive.yaml, audited workflow_enabled / workflow_disabled) and a
"Derive stages from bound models" switch in Settings β†’ Workflows β†’ Library
that confirms before turning on. The stage engine and the tracker already
read cfg.Workflow.Enabled live and the reloader mutates the same *Config
in place, so no reload hook is needed. Turning on also provisions every
bound repository's model labels at once through the sweep's memoised
ensureStageLabels path (new automerge.Engine.EnsureStageLabels, wired as
Dependencies.WorkflowProvisionLabels); failures come back as warnings and
the next sweep retries. The Repos binding row says "columns only β€” turn
on derivation in Workflows" when a binding exists while the flag is off.

Refs hivecommons/hive#17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* chore: move changelog fragment to root changelog.d/

Refs hivecommons/hive#17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.229.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.229.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* fix: classify plain HTTP proxy forwarding failures (#17864)

Hive-Run: hivecommons/hive#17859
Hive-Plan: classify repeated proxy forward failures
Hive-Spec: fleet-report#runtime-classes

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: hivecommons-hive[bot] <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* ✨ Inbox: voice recorders as first-class input sources (Plaud β†’ spek RFC / dibs / knowledge) (#17885)

* ✨ Inbox: voice recorders as first-class input sources

Add a source-agnostic Inbox (sidebar β†’ Intelligence) that lists recordings
synced by voice knowledge connectors (plaud now, wisprflow next), keyed by
(source, item_id), and routes each one inside the hive to a spektacular RFC
with child issues (spektacular voice --publish, or a spoke-agent fallback),
a dibs draft idea, or Knowledge only β€” single, bulk, or by auto-routing
rules applied after every sync.

- pkg/inbox: Item/RouteState model, JSON store, fact-backed source,
  rules, spek_rfc + dibs destinations, service with sweep.
- config: inbox.{sources,rules,spek,dibs} + validation.
- dashboard: GET /api/inbox, POST /api/inbox/{route,dismiss},
  GET|POST /api/inbox/settings, Inbox section + modals, sidebar badge.
- Plaud: POST /api/knowledge/connectors/{name}/auth/complete paste
  fallback for hosted hives (Plaud OAuth only redirects to localhost:8199);
  the Integrations card now shows connection state + Open Inbox only.
- docs: inbox-voice-sources.md, knowledge-connectors.md, api-reference,
  dashboard-sections, openapi.json.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* ✨ Inbox: default spek_rfc agent fallback to first enabled agent

When the hive image has no spektacular binary and inbox.spek.agent is
unset, hand the RFC job to the alphabetically first enabled, unpaused
agent so candidate hives can file RFCs without extra config.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 Inbox: allowlist pkg/inbox dashboard import; sidebar order test includes inbox-section

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 Inbox: cover service/store/source edge cases and inbox config validation

Raises pkg/inbox to 92% and pkg/config back over the 90% coverage floor.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.230.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.230.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› PR comments: overlap/mutation only on a finding, thank first-time contributors only, no self-/lgtm relay on Prow PRs (#17870)

* πŸ› PR comments: post overlap/mutation reports only on a finding, thank first-time contributors only, no self-/lgtm relay on Prow PRs

Fixes #17863.

- .github/scripts/pr-overlap.sh: create the <!-- hive-pr-overlap --> comment
  only when a sibling PR conflicts or shares a file; a later clean run edits
  the existing comment to a one-line Resolved note; never post for a PR that
  never had a finding. The step summary keeps the full report.
- src/scripts/mutation-report.sh post: same for <!-- hive-mutation --> on a
  pass verdict; the report is appended to the step summary first.
- src/scripts/feedback-gate.sh (+ test): decides whether the merge thank-you
  is posted β€” skips bot/App authors, scanner/* and hive/* branches, hive-origin
  bodies and returning contributors; FEEDBACK_* env knobs. Wired by the
  feedback.yml change in the next commit.
- pkg/github: a dashboard approval on a Prow-managed, hive-authored PR relays
  /approve only (LGTMSkipped); Prow rejects a self-/lgtm every time and no
  hive lane reads the relayed /lgtm.
- docs: notifications.md (new section + Prow config location), glossary,
  running-at-level-6; changelog fragment.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› workflows: gate the Feedback Wanted thank-you on src/scripts/feedback-gate.sh

Refs #17863. Workflow-only commit: adds a gate job (self-test + decision)
before the hivecommons/infra reusable-feedback call, which runs only when
the gate outputs post=true.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🌱 governance: re-pin TRUSTED_SCRIPTS_TREE for the feedback-gate and mutation-report scripts

Refs #17863. src/scripts gained feedback-gate.sh / test-feedback-gate.sh and
mutation-report.sh changed, so the pinned tree moves to 1f1eb9048.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ› fix: atomic runtime config save + fresh legacy fallback (#17862) (#17867)

* πŸ› fix: atomic runtime config save + fresh legacy fallback (#17862)

Config.Save wrote /etc/hive/hive.yaml and /data/hive.yaml.runtime in place
with O_TRUNC. A liveness SIGKILL between truncate and write left a 0-byte
runtime file; the entrypoint's -s check then fell back to /data/hive.yaml.bak,
frozen at the Aug 2026 rename on every hive, and vllmd-03 booted level 3
instead of 6.

- writeFileAtomic: temp file in the target's dir, fsync, rename onto the
  EvalSymlinks-resolved target so the #17468 link survives; falls back to
  the old in-place write when rename is refused (single-file bind mounts).
- Both Save writes use it; runtime stays 0600, source keeps its mode.
- entrypoint: hive_refresh_legacy_fallback copies a YAML-valid runtime
  config over hive.yaml.bak on clean boots (tmp+mv), only when the new name
  is the boot source; hive_runtime_config_read WARNs loudly on fallback and
  flags a fallback older than the damaged runtime file.
- Tests: writer unit tests incl. symlink + no-partial-read race; shell
  suite covers WARN, refresh, parse guard, legacy-boot no-op.

Fixes #17862

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* test(config): cover atomic writer failure paths for coverage floor

Adds tests for missing target dir, ENOTDIR resolve error, dangling
symlink, rename+fallback double failure (no temp leak), in-place
fallback, syncDir tolerance and sourceConfigMode, lifting pkg/config
back over its 90% coverage floor.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* πŸ”– release: v5.231.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.231.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ› fix: wait for marketplace refresh completion in auth test (#17868)

Wait for a new refresh result rather than interpreting an idle refresher as proof that the newly accepted asynchronous run has finished.

Hive-Run: hivecommons/hive#17865

Hive-Plan: marketplace-refresh-flake

Hive-Spec: marketplace-refresh-flake#async-completion

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
Signed-off-by: Karibhou <157208370+Karibhou@users.noreply.github.com>
Co-authored-by: Karibhou <Karibhou@users.noreply.github.com>

* πŸ”– release: v5.231.1

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.231.1 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* πŸ“– docs: Inbox voice sources page + quickstart link (#17899)

* πŸ“– docs: add Inbox voice sources page and quickstart links

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* πŸ“– docs: use .row utility for Inbox quickstart link group

TestStyleRatchet counts inline style= attributes in static/index.html;
the new link group pushed it over the 1403 baseline. The existing .row
utility (flex, gap var(--sp-4)) renders the same.

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ“– docs: link #17882 from the changelog fragment

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* ✨ dashboard: Settings β†’ Inbox section (default repo, dibs URL, rules) (#17901)

* ✨ dashboard: Settings β†’ Inbox section for default repo, dibs URL and rules

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

#17885 landed inbox.* config, validation, parity allowlist and /api/inbox/settings but no Settings tab section. Add Settings β†’ Inbox reusing the Inbox drawer form and endpoint. Part of #17883.

* πŸ› dashboard: scope the Inbox settings form to its active root

The Inbox settings form renders twice β€” in the Inbox page modal and inline under Settings β†’ Inbox β€” and both can be mounted at once: the settings tab stays in the DOM and the closed modal keeps its last body. inboxReadRules collected .inbox-rule rows from every #inbox-settings-rules, the repo/dibs inputs were read via getElementById (first in document) and the inline sniff kept the modal open on save, so saving from the modal after visiting Settings β†’ Inbox posted the inline form's values and left the dialog up.

Wrap the form in .inbox-settings-form[data-inbox-form=inline|modal], replace the duplicated ids with data-inbox-field attributes, and resolve one active root per operation (the modal while open, otherwise the inline section). Read, render, save and the close-modal decision all go through that root.

Hive-Fix-Attempt: 1/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

---------

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* πŸ”– release: v5.232.0

Automated release commit. Compiles changelog.d/ fragments and moves
the CHANGELOG.md Unreleased section into a dated v5.232.0 entry.
See src/docs/releases.md.

Signed-off-by: hive-release-bot <actions@github.com>

* ✨ inbox: Plaud source onto the Source interface (#17900)

* ✨ inbox: read Plaud recordings directly through the Source interface

The Inbox only saw recordings through the facts the Plaud connector writes. Add a PlaudSource that lists/fetches straight from the connector (item id stays attr_plaud_file_id), preferred while Plaud is signed in with the fact-backed source as fallback.

Refs #17877, part of #17875.

Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: clubanderson <407614+clubanderson@users.noreply.github.com>

* πŸ› inbox: cache the direct Plaud listing off the poll path and keep fact fields

PreferDirect.List spawned the Plaud MCP process on every Items/Find/Sweep call, so each dashboard poll paid for a process spawn plus list_files pagination and a slow spawn past the handler timeout silently flipped the UI back to facts. The direct listing is now cached for a TTL and refreshed by a single background goroutine with its own timeout; List serves the cache (or just facts while nothing is cached) and never blocks on Plaud. A failed refresh clears the cache and is not retried before the TTL.

Direct items also dropped FactSlug/FactPath/Layer/Excerpt, hiding the Fact button and excerpt for recordings that do have facts. The fact listing is now merged by id onto the direct items, and facts Plaud no longer lists stay visible.

Refs #17877.

Hive-Fix-Attempt: 1/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>

* πŸ› inbox: wait on the Plaud refresh with testutil.Eventually, not a sleep loop

The sleep ratchet (TestSleepRatchet) rejects any new fixed …
hivecommons-hive Bot pushed a commit that referenced this pull request Nov 13, 2026
…17867)

* πŸ› fix: atomic runtime config save + fresh legacy fallback (#17862)

Config.Save wrote /etc/hive/hive.yaml and /data/hive.yaml.runtime in place
with O_TRUNC. A liveness SIGKILL between truncate and write left a 0-byte
runtime file; the entrypoint's -s check then fell back to /data/hive.yaml.bak,
frozen at the Aug 2026 rename on every hive, and vllmd-03 booted level 3
instead of 6.

- writeFileAtomic: temp file in the target's dir, fsync, rename onto the
  EvalSymlinks-resolved target so the #17468 link survives; falls back to
  the old in-place write when rename is refused (single-file bind mounts).
- Both Save writes use it; runtime stays 0600, source keeps its mode.
- entrypoint: hive_refresh_legacy_fallback copies a YAML-valid runtime
  config over hive.yaml.bak on clean boots (tmp+mv), only when the new name
  is the boot source; hive_runtime_config_read WARNs loudly on fallback and
  flags a fallback older than the damaged runtime file.
- Tests: writer unit tests incl. symlink + no-partial-read race; shell
  suite covers WARN, refresh, parse guard, legacy-boot no-op.

Fixes #17862

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* test(config): cover atomic writer failure paths for coverage floor

Adds tests for missing target dir, ENOTDIR resolve error, dangling
symlink, rename+fallback double failure (no temp leak), in-place
fallback, syncDir tolerance and sourceConfigMode, lifting pkg/config
back over its 90% coverage floor.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has signed the DCO. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

πŸ› Config.Save O_TRUNC + stale hive.yaml.bak fallback boots a months-old config after a mid-save kill (vllmd-03 L6β†’L3)

1 participant