Commit graph hyperhive/docs
Author SHA1 Message Date
iris
a52d344600 docs: fix genuine passive-voice hits in docs/web-ui/dashboard.md (part 1/2)
First half (lines 1-697) of dashboard.md's write-good.Passive backlog --
31 of the file's 70 hits rewritten to name an actor (hive-c0re, the page,
the operator, the table, the filter, builds.js, Provision, the matrix
daemon, ...), reusing an actor already named nearby wherever one exists.
7 left alone in this half: a fixed idiom ("not to be confused with"),
two false positives ("named buckets" / compound adjective; a
field-is-set data-state conditional), a negative-capability idiom
("nothing is written"), and two established state-descriptor idioms
("is unchanged", "is tooltipped").

Part 2 (lines 762-1389, ~32 more hits) is a separate follow-up, not
started -- dashboard.md is too big for one PR.
2026-09-08 15:54:31 +02:00
iris
260c4eb854 docs/swarm: re-pad README table after the unknown-row rewrite
The rewrite widened the unknown row's cell by one character; nix fmt
re-pads the whole table to match. Pure whitespace, no content change.
2026-09-08 15:54:16 +02:00
iris
fab2a0dedc docs: fix genuine passive-voice hits in docs/swarm
Read all 94 write-good.Passive hits across docs/swarm/ (ca.md,
README.md, secrets.md, services.md, sso.md, ui.md) in context. 44 are
genuine catches with a nameable, usually already-established actor
(swarm-controller, authelia, swarmctl, the controller, the gateway,
this module, hyperhive itself, or 'the operator' for manual actions) —
rewritten to active. 50 are legitimate passives or false catches, left
alone: predicate-adjective state descriptions (is expected/misconfigured/
broken), negative-capability idioms (no X is needed/placed, can't be
Yed/listed/fetched), config-state conditionals (whenever/when X is
enabled/configured/set), requirement-list labels (is required),
'is tracked as' idiom, backward-looking changelog facts with no actor
(was removed/verified/introduced), ambiguous-actor statements left
conservatively alone (agents are created and destroyed — could be
hive-c0re or swarm-controller, doc doesn't say), and a couple of
deliberately-parallel idiom pairs.

Several sibling-inconsistency fixes: a passive clause sitting next to
an already-active sibling describing the same fact/mechanism (ca.md's
two-bullet consumer list, README's 4-item WireGuard-mesh bullet list,
README's controller-registers-hooks paragraph, sso.md's followed-a-302
sentence).

Verified via vale on the whole directory, diffed against main's exact
baseline (not just the Passive count): write-good.Passive 94 -> 50
exactly, every other category unchanged (1 pre-existing
Microsoft.Contractions error at services... at secrets.md:182,
8 TooWordy, 1 Microsoft.We, 1 Microsoft.FirstPerson — same counts,
same locations).
2026-09-08 15:54:16 +02:00
iris
e9d59f3d10 docs: fix genuine passive-voice hits in docs/web-ui (small files)
agent.md, css-vars.md, design-guide.md, shape.md, terminal-rendering.md --
26 of 40 write-good.Passive hits rewritten to name the actor (hive-agent,
browsers, the harness, the client, lifecycle::{spawn,rebuild}, CSS, the
router, ...), reusing an actor already named nearby wherever one exists.
14 left alone: established config-state conditionals ("when X is
set/enabled/given/called"), negative-capability/state-descriptor idioms
("is gone", "is hidden", "is unchanged"), one false-positive
("typed slash commands" as a compound noun, not a passive action), a
backward-looking changelog fact with no actor worth naming, and two
deliberately-parallel rhetorical contrasts (sanitized vs XSS-safe;
cache-hit vs cache-miss) left symmetric on purpose.

dashboard.md (70 more hits) is the next docs/web-ui/ batch, not part of
this one -- big enough to deserve its own PR.
2026-09-08 15:54:07 +02:00
iris
7396903994 rewrite generated CLI docs' passive voice to active
hive-forge and hivectl doc-comment strings for pr-create --label, diff
--full, list --label, ci-runs --branch, and several hivectl subcommand
options read as if things happened to themselves. Name the actor (hivectl,
hive-forge, or the CLI) instead, and regenerate the three docs/tools/*-cli.md
files from the rebuilt binaries.

swarmctl-cli.md needs no source change: its only passive hit is
clap-markdown's own generated boilerplate, out of scope here.
2026-09-08 14:56:26 +02:00
iris
97e2a993e3 docs: fix genuine passive-voice hits in docs/tools (hand-written files)
Read all 62 write-good.Passive hits across the 6 hand-written files in
docs/tools/ (bash.md, lifecycle.md, forge.md, hivectl.md, matrix.md,
scheduling.md) in context. Excludes forge-cli.md/hivectl-cli.md/
swarmctl-cli.md (57 more hits) -- those are CI-generated from the
clap doc-comment strings in hive-forge/hivectl/swarmctl, freshness-
checked against the committed markdown, so a fix there has to go in
the Rust source and get regenerated -- separate batch.

33 rewritten to active, actor usually already established in the same
paragraph or given via a 'by X' clause (the daemon, hive-c0re,
hive-forge, hivectl, hyperhive itself, or an established module name
like nix/agent-modules/mcp.nix). Several sibling-inconsistency fixes:
a passive clause next to an already-active sibling stating the same
fact (lifecycle.md's server-scoping sentence, forge.md's clone-derives
sentence, hivectl.md's daemon-hashes-passwords sentence, matrix.md's
daemon-upserts-a-todo paragraph).

29 left alone: no-X-is-Y / no-X-is-needed invariant idioms, config-state
conditionals (whenever/when X is set/configured/given), requirement-
list idiom, a false-positive tokenization (vale matching 'are read'
inside 'read-only'), definitional facts about CLI/attrset structure
with no real actor, hypothetical/counterfactual clauses describing a
rejected design alternative, a forward-looking not-yet-real removal,
compressed bullet-fragment conventions, and one deliberately-parallel
sentence structure left both halves alone to avoid breaking the
symmetry.

Verified via vale on the 6 touched files, diffed against main's exact
baseline (not just the Passive count): write-good.Passive 62 -> 29
exactly, every other category unchanged in count AND location
(TooWordy 16, Microsoft.We 1, Microsoft.FirstPerson 1, and the same
4 pre-existing Microsoft.Contractions errors at the same 4 lines).
2026-09-08 14:49:22 +02:00
iris
239354d205 docs/networking: drop leading 'So' tripping write-good.So
argus caught this on PR review: the passive-to-active rewrite of the
packet-authentication sentence in snapshot-store.md started with 'So ',
which write-good.So flags as a new error not present on main. Drop the
leading word; meaning is unchanged.
2026-09-08 13:30:40 +02:00
iris
f457bf3c15 docs: fix genuine passive-voice hits in docs/networking
Eighth batch of the ongoing write-good.Passive pass (hyperhive#4042):
read all 69 hits across network.md/snapshot-store.md/gateway.md in
context and rewrote 34 with a clearly nameable actor -- mostly
hive-c0re, dnsmasq, nginx, or a specific systemd unit/fn named right
there or a sentence or two earlier. snapshot-store.md's operator-facing
prose ("you can deploy it", "you must tell every hive") reuses this
doc's own established "you" address (already present two sentences
away in one case) rather than inventing a new register.

Left 35 alone -- the largest leave-alone count yet, dominated by two
shapes gateway.md is dense with: negative-capability/requirement
idioms ("no X is required/needed/exposed", "can't be added/wired",
"must not be exposed") and predicate-adjective state descriptions
("is misconfigured"/"is broken" in negative-contrast "not that X"
framing, "is privileged" matching the established "is trusted" bucket,
"is closed/internal" deployment-posture, "is encrypted and
unauthenticated" connection-state pair). Also: two "X was removed"
changelog-style facts with no actor clause (same family left alone in
the scheduler batch), a gerund nix-conditional pair ("X being enabled
and Y being set are assertions, not documentation"), and a passive
embedded inside an already-active sentence ("X controls whether Y is
opened" -- the real verb is already active).

One caught-and-reverted mid-edit mistake: an early attempt at the
"nginx is handed the leaf" fix landed on the wrong paragraph (an
unrelated CA-generation sentence two paragraphs up) and left a
nonsensical forward-reference behind. Caught by re-reading the diff
before running vale, not by the count -- same discipline as the
docs/process batch's original catch. Reverted cleanly and reapplied at
the actual target sentence.

One small sibling-consistency fix beyond pure voice-flipping: "The
gateway emits the following headers..." (rewritten) sat two paragraphs
above "a header is added alongside the other security headers" (still
passive) describing the same mechanism -- rewrote the second to match
("the gateway adds a header...") rather than leave one active and one
passive version of the identical fact sitting near each other.

Verified via vale before/after: 69 -> 35 write-good.Passive hits,
exactly the 35 left alone above; error count and other warning
categories unchanged. Re-read every changed line in full surrounding
context after editing before running the final vale check.
2026-09-08 13:30:40 +02:00
iris
0ad0376fe3 docs: fix genuine passive-voice hits in docs/agent-lifecycle
Ninth batch of the ongoing write-good.Passive pass (hyperhive#4042):
read all 75 hits across agent-hierarchy.md/persistence.md/approvals.md
in context and rewrote 46 with a clearly nameable actor -- mostly
hive-c0re, a specific fn/type named right there or a sentence or two
earlier (approvals.md's numbered "Reminder delivery" list all share
hive-c0re as the established actor across three consecutive bullets),
or "the worker"/"the daemon" reused from the section's own established
subject.

Left 29 alone. Recurring buckets: predicate-adjective state pairs
("is wedged"/"is stopped", "is unaffected", "are unrelated", "is
overloaded" -- matches the established "is trusted"/"is privileged"
family), negative-capability/invariant idioms ("no X is needed",
"can't be removed/started", "no such transient was cleared"),
forward-looking design-intent statements about not-yet-built
enforcement (agent-hierarchy.md's whole "Planned topology semantics"
section leans on this: "are meant to run", "once enforcement is
finished", "will be gated"), the "audit trail, not cache" policy
invariant restated twice verbatim ("Approvals are kept forever" /
"indefinitely" -- left both alone consistently), and a config-
conditional state idiom ("when interval_seconds is set"). One more
false-positive tokenization, same bug as knowledge.md's earlier case:
persistence.md's "`config` is read-only" trips vale's "is read" match
inside the compound adjective -- not a real passive at all.

One deliberately-conservative leave: agent-hierarchy.md's "Reminder
cancellation is handled fully in-agent" had an ambiguous actor (the
in-agent socket vs. the hive-c0re source file the doc points readers
to for detail) -- left alone rather than risk a wrong attribution,
unlike the higher-confidence rewrites elsewhere in the same batch.

Verified via vale before/after: 75 -> 29 write-good.Passive hits,
exactly the 29 left alone above; error count and other warning
categories unchanged. Re-read every changed line in full surrounding
context after editing before running the final vale check.
2026-09-08 13:30:26 +02:00
iris
09196a44fe docs: name hive-forge-notify instead of a dangling pronoun
argus's review on this PR flagged a real clarity regression: "Forge
notifications are polled by their own process, not this loop" became
"Their own process polls forge notifications, not this loop" -- the
original's "their" clearly referred back to "Forge notifications"
within the same clause; the rewrite put "Their" first with nothing
plural preceding it in the paragraph, so the pronoun has no
referent when the reader hits it. This was exactly a case where the
passive was doing real work (subject-first noun introduction), not
just avoiding an active verb.

Uses argus's own suggested fix: name the process directly instead of
leaning on a pronoun at all, and drop the now-redundant second
mention of the same name a few words later.
2026-09-08 13:30:03 +02:00
iris
645887d87c docs: fix genuine passive-voice hits in docs/turn-loop
Fifth batch of the ongoing write-good.Passive pass (hyperhive#4042):
read all 42 hits across the four docs/turn-loop files in context and
rewrote the 36 with a clearly nameable actor, usually one already
sitting in the same sentence or established a sentence or two earlier
(the broker, the harness, drive_turn, the renderer, hive-c0re, or a
specific fn/type named right there). Left 6 alone: predicate-adjective
copulas that only look passive ("is gone", "Bash is disallowed", "is
documented behavior" — nothing actually acts on the subject), a
config-conditional idiom with no in-file inconsistency to fix ("when
hyperhive.docs.enable is set"), and a paraphrase of what a rendered
message tells the agent ("the docs are mounted at that path").

Verified via vale before/after: 42 -> 6 write-good.Passive hits in
docs/turn-loop, exactly the 6 left alone; error count and other
warning categories unchanged. Re-read every changed line in its full
surrounding context after editing (not just the vale count) per the
lesson from the docs/process batch.
2026-09-08 13:30:03 +02:00
iris
060f325716 docs: fix genuine passive-voice hits in docs/integrations
Sixth batch of the ongoing write-good.Passive pass (hyperhive#4042):
read all 52 hits across knowledge.md/github.md/matrix.md/forge.md in
context and rewrote 39 with a clearly nameable actor -- mostly
hive-c0re, forge_notify, or a specific fn named right there or a
sentence or two earlier. forge.md's notification poller is the
densest yet (19/20 hits rewritten): forge_notify is established as
the section's sole actor early and reused throughout, the shape
that's produced the highest catch rates all along.

Left 13 alone: the "no X is needed" negative-capability idiom (x2),
a container-lifecycle state descriptor ("when container is stopped"),
a false-positive tokenization ("read-only" split across a line wrap,
vale matches "is read" inside it -- not a real passive at all), the
"X can't be Yed" idiom, a generic "before the ids are minted" timing
clause with no natural actor to name, a room-join policy-state
descriptor, an "is enabled"/"is trusted" pair describing a config/
trust state (predicate-adjective-copula bucket, same family as
"is privileged" from an earlier batch), three "**X is required**"
bolded requirement-list labels (structural convention, not really
mid-sentence passives), and a contrastive "are shared" clause
mirrored against an active sibling clause exactly like
claude-invocation.md's "everything else is shared" from the
turn-loop batch -- left alone there for the same reason.

One sibling-inconsistency catch worth flagging: forge.md's merge-
racing-comment paragraph had two passive clauses ("is left off",
"is dropped") sitting next to a third, already-active clause
("appends nothing") in the same three-item parallel list -- rewrote
all three under one active subject (forge_notify) for consistency.

Verified via vale before/after: 52 -> 13 write-good.Passive hits,
exactly the 13 left alone above; error count and other warning
categories unchanged (still on TooWordy since #4097 hasn't merged to
this branch yet). Re-read every changed line in full surrounding
context after editing, matching the diff to intent before running
the final vale check.
2026-09-08 13:29:34 +02:00
iris
c41c67c949 docs: fix genuine passive-voice hits in docs/process
Fourth batch of hyperhive#4042's Passive pass (see #4098/#4099/#4100
for the first three and the read-every-hit discipline this pass
uses). 44 hits across pr-review-gate.md (4), conventions.md (18), and
gotchas.md (22) -- highest genuine-catch rate so far, 23/44 (~52%),
because this architecture/mechanism documentation has a lot of "X
does Y via Z" sentences where the actor is already named
parenthetically or in a nearby clause -- the single most productive
rewrite shape across every batch so far.

Recurring rewrite shapes this batch:
- Actor already named in the same sentence, just not as the
  grammatical subject: "X is configured per repo in its
  branch-protection settings" -> "Each repo's branch-protection
  settings configure X" (pr-review-gate.md); "the broker" (reserved
  names), "rsvg-convert" (PNG rendering), "the website repo" (HTML/CSS
  rendering), "systemd.globalEnvironment" (D-Bus address export), and
  several more -- all the same shape.
- Subject already established one clause or one sentence earlier,
  just needs continuing rather than restarting with a new passive
  subject: "the harness reads HIVE_TOOL_GROUPS (...). Unrecognised
  tokens are logged and skipped." -> "...logging and skipping
  unrecognised tokens" (continues "the harness"), same pattern twice
  more (job_queue::templates::rebuild, HIVE_CAPABILITIES resolution).
- Sibling-inconsistency: a bolded lead-in bullet was the one passive
  sentence in an otherwise-active paragraph/table (the
  read_host_journal capability row sat between two "may X" rows; the
  HTML+CSS bullet's own tail clauses were already active voice around
  the one passive lead phrase).
- One caught-and-fixed authoring mistake worth noting for future
  passes: the first attempt at the "Nix treats X as a package" rewrite
  landed in the wrong sentence (a similarly-worded but unrelated
  passage two paragraphs up) -- caught by re-reading the diff before
  running vale, not by vale itself (which would have shown 0 remaining
  hits either way, since the intended sentence's hit just wouldn't
  have been touched -- a silently-wrong edit vale's own count can't
  catch). Re-reading the actual diff, not just trusting the before/
  after hit count, is what caught it.

21 of 44 left alone -- same recurring legitimate shapes as prior
batches (predicate-adjective copulas, quoted/literal text, generic-
actor statements, negative-capability invariants, "is tracked/rooted/
scoped at X" property-description idioms, and two more thesis-
statement headings matching the "Ownership is declared, not repaired"
precedent from #4100).

Verified: vale docs/process before/after -- 44 -> 21 write-good.Passive
hits, exactly the 23 rewritten, re-read every changed line's full
surrounding context after editing (not just the vale count) to catch
exactly the kind of misplaced-edit mistake described above.
2026-09-08 12:24:03 +02:00
iris
461c599e09 docs: fix genuine passive-voice hits in docs/trust-boundary
Third batch of hyperhive#4042's Passive pass (see #4098/#4099 for the
first two and the read-every-hit discipline this pass uses). 23 hits
across boundary.md (10) and security.md (13).

boundary.md, 2 of 10 rewritten:
- "Operator-authority actions ... are served by the core daemon and
  only reachable via the gateway" -> "The core daemon serves
  operator-authority actions ..., reachable only via the gateway" --
  the actor was already named in the sentence, and the parallel
  "Agent" bullet right below it is already active voice ("speaks only
  for itself"), so the Operator bullet was the inconsistent one.
- "ownership set afterwards is reverted the next time any agent
  changes" -> "and reverts any ownership set afterwards the next time
  any agent changes" -- continues the same subject ("the `d`
  re-applies ... and reverts ...") already established one clause
  earlier, avoiding a subject switch entirely.

security.md, 6 of 13 rewritten (across 3 edits touching 6 flagged
locations):
- "A compromised/confused agent's reach ... is bounded by its own
  account's scope" -> "Its own account's scope bounds a
  compromised/confused agent's reach" -- matches the section's own
  header ("Scoped tokens bound the blast radius"), which is already
  active voice.
- "Two allow-listed root prefixes are accepted; all other paths are
  rejected" -> "It accepts two allow-listed root prefixes and rejects
  all other paths" -- continues "it" from the endpoint named one
  sentence earlier.
- The container-allowlist paragraph (3 flagged locations: "is
  validated", "are accepted", "are rejected") rewritten as one
  consistent-subject passage with `hive-priv` as the actor throughout,
  matching the neighboring bullets in the same subsection
  ("**Socket-activated** -- systemd starts hive-priv...", already
  active voice) -- the passive version was the odd one out among
  siblings, not the house style.

15 of 23 hits left alone. The recurring legitimate shapes, same
categories as #4098: predicate-adjective copulas that only look like
passives ("an agent is trusted code", "the agent is privileged" --
"trusted"/"privileged" modify the noun, there's no actor to name),
quoted rhetorical contrasts where the passive is doing real work (a
"wrong-framing" quote left passive on purpose, paired with an active
"right-framing" quote right after it), the "is tracked as/in X"
idiom (twice, same as #4098's precedent), and "X can't be Yed" /
negative-capability invariant statements (matches #4098's "No X is Y"
security-guarantee idiom). One deliberately left despite a nameable
antecedent ("hive-gateway's access is scoped [by ReloadGatewayNginx]
instead") -- lower-confidence rewrite than the others, left rather
than force it.

Verified: vale docs/trust-boundary before/after -- 23 -> 15
write-good.Passive hits, exactly the 8 rewritten, no other rule's hit
count moved (the TooWordy/Microsoft.Avoid/alex hits vale also reports
on these two files are pre-existing and out of scope for a
Passive-only pass; TooWordy's hits specifically already have a fix
queued in #4097, not duplicated here).
2026-09-08 12:16:14 +02:00
iris
b628af81de docs: fix the one passive-voice hit in docs/crates/README.md
Second doc-directory of hyperhive#4042's Passive pass (see #4098 for
the first batch's full read-every-hit reasoning). Only one hit in this
whole directory: "Every page is generated at build time
(nix/packages/reference-docs.nix, hyperhive#3051)" -- a genuine catch,
same shape as #4098's swarm-SSO rewrites -- the actor is already named
parenthetically in the same sentence, so naming it as the grammatical
subject too is strictly clearer: "nix/packages/reference-docs.nix
(hyperhive#3051) generates every page at build time".

Verified: vale docs/crates/README.md before/after -- 1 -> 0 warnings.
2026-09-08 12:15:53 +02:00
iris
bfb9636d52 docs: fix genuine passive-voice hits in getting-started/setup.md
First doc-directory of hyperhive#4042's Passive pass (705+ hits across
docs/, genuinely mixed unlike TooWordy -- needs a real per-hit read,
not a dictionary shortcut, so this is going doc-directory by
doc-directory in small PRs, per the plan posted on the issue).

Read all 13 flagged hits in this file in context, not just the flagged
word. 3 were genuine catches with a real active-voice improvement and
a nameable actor:

- "the human operator's own forge/matrix account is created via swarm
  SSO" -> "swarm SSO creates the human operator's own forge/matrix
  account" (x2, identical sentence shape for both accounts) -- swarm
  SSO is a nameable, specific actor already named later in the same
  sentence, so naming it as the subject too is strictly clearer, not
  just different.
- "Do this before anything is pointed at it" -> "Do this before you
  point anything at it" -- matches the doc's own established
  second-person imperative voice used throughout ("Do this", "Put the
  token's value", "Delete the file"); the passive here was the odd one
  out, not the house style.

The other 10 are legitimate passives, left alone:
- Generic/unspecified-actor statements ("is needed", "is bound", "is
  issued", "been run") where forcing a subject would either invent an
  actor the doc never established or read worse than the original.
- Security/architecture invariants ("No forge admin token is stored in
  any agent state dir", "Telemetry ingest is authenticated per hive")
  -- "No X is Y" / "X is Y" is the standard idiom for a guarantee
  statement in security docs, not a clarity problem to fix.
- "operator-only surfaces ... are gated on that group" -- borderline
  (could name the group as subject), judged idiomatic access-control
  phrasing rather than genuinely clearer active, but flagged as the
  closest call in this batch.

Verified: vale docs/getting-started/setup.md before/after -- 13 -> 10
write-good.Passive hits (exactly the 3 rewritten), the pre-existing
unrelated alex.Suicide hit on "hang" (line 51, untouched) still
present and correctly out of scope for this pass.
2026-09-08 12:15:39 +02:00
damocles
66c3138dd1 hive-c0re: grant hive-admin group a polkit rule for choom 2026-09-07 23:27:15 +02:00
atlas
6de51b7be4 setup: drop two vale errors my own callout introduced
`Microsoft.Contractions` fires on "is not" and "it is", and both were in
the sentence I added about the role having no consumer yet. Reworded so
neither appears, and the passive construction goes with them.

The vale job is red on every PR by design -- ci.yml says so, the docs
tree has a standing backlog against these rules -- which is exactly why
"vale is red" was not a safe thing to wave through. Checking per line
rather than per job is what found these.

Verified with the invocation ci.yml uses:
`XDG_DATA_HOME=$PWD/.vale-data vale sync && vale docs/getting-started/setup.md`
-> synced 3 packages, 0 errors, 14 warnings. The 2 errors CI reported at
103:39 and 104:7 are the presence control: same rule, same file, prior
commit.
2026-09-07 22:02:33 +02:00
atlas
8bd41fd84f bao: enable cert auth and give the controller a role for its policy
The policy the granting unit already writes grants paths under
`auth/cert/certs/*`, and nothing in the tree creates that mount. Every
certificate login therefore fails against a path that is not there --
the controller's own, and the per-hive ones it is meant to issue
against the same mount.

Same unit, same bootstrap token: check whether cert auth is mounted,
enable it if not, then write a role binding CN `swarm-controller` to
the `swarm-controller` policy.

Idempotency is a read rather than a tolerated error. `auth enable`
fails on an existing mount, and recognising that would tie a rebuild to
an error string no run of this store has ever produced, so the unit
asks `bao auth list` and mounts only on absence. That read is why the
token policy in setup.md gains `sys/auth`.

Each grant came from `bao <cmd> -output-policy`, which prints what a
command requires without running it -- the same way controllerPolicyText
was derived. Enabling an auth method needs `sudo` on `sys/auth/cert`,
which the documented token did not have.

Gated on `clientCaFile`, not on the token alone: `swarm-bao-certs`
installs `client-ca.pem` only under that condition, and a role's
`certificate=` has to name a real CA. The policy write, which needs no
CA, is unchanged in that case.

Nothing can present a certificate for this role yet -- the only client
leaf the tree mints carries CN = the hive's name -- and none of this has
been run against a live store. Both are stated in setup.md.
2026-09-07 21:52:06 +02:00
atlas
df18d3d4d7 forge: move the forgejo package to deploy — slice 10 complete
The last of the twelve. `swarm.forge.package` becomes
`deploy.forgejo.package`, beside the `behindGateway` / `mirrors` /
host-secret options that were already there.

⚠️ `deploy.forgejo`, not `deploy.forge`. The forge joins the controller
and the UI as a mover where the two sides simply do not share a name,
so the shim comment says which one.

Four readers, and one of them is why an anchored grep was the wrong
instrument: `:678` reads `cfg.package.data`, a sub-attribute, which
`cfg.package}` and `cfg.package/` both miss.

Three copies of the same sentence claimed the package stays under
`swarm.forge` — `deploy.nix`, this module's own `deploy.forgejo`
header, and `docs/integrations/forge.md`. The docs one is outside
`nix/` entirely. A namespace split gets described wherever the split is
explained, which is not only where the option is declared.

`forgeOldPath` gains the old path, and its comment's count of the
host-side options goes five → six with it.
2026-09-07 20:46:38 +02:00
atlas
d6f23ff371 swarm: move the matrix packages to deploy, where their enable already lives
Which build a service runs is a decision of the host that runs it, so
`*.package` belongs with `enable` rather than in the namespace every hive
must agree on byte for byte.

The `gui` pair was the sharpest case and the reason this starts here:
`gui.enable` moved to `deploy.*` in an earlier slice while `gui.package`
stayed behind, so one web client's two halves sat in two namespaces —
and hive-matrix.nix said so, in a comment directly above the option.
The diff removes a stated inconsistency rather than introducing a rule.

Both old paths keep working through `mkRenamedOptionModule`; both are
settable (neither is `readOnly`), so both get a shim. The `matrixOldPath`
fixture in module-eval.nix now sets eight options through their
pre-rename paths instead of six, so dropping either new shim fails the
eval rather than silently dropping a definition.

Prose swept in the same commit: three comments claimed the package was
swarm-wide or named the pre-rename path, and docs/web-ui/dashboard.md
named `swarm.matrix.gui.package` two lines above
`deploy.matrix.gui.enable` — the same asymmetry this commit exists to
remove, displayed in the docs. A shim keeps the old spelling valid, so
no build, lint or broken-reference search would have found that line.

Refs #3772.
2026-09-07 20:46:37 +02:00
iris
a8fb08a7e8 docs: fix Microsoft.UIVerbs findings (click -> select)
Part of #4041's forgotten remainder (UIVerbs was scoped as pure
execution work, no decision needed -- see comment 71586).

29 genuine input-device-neutral rewrites ("click"/"clicking"/"clicked"
-> "select"/"selecting"/"selected", a few reworded where a direct verb
swap didn't read naturally: "one click from the hub" -> "one step from
the hub", "click straight from X into Y" -> "go straight from X into
Y", "the operator never clicked" -> "the operator never triggered").

Left 5 hits unfixed, each read in context and genuinely not a
device-assumption problem:
- docs/process/gotchas.md:439 -- "click-to-unlock screen" names a
  specific OS lock-screen pattern, not an instruction to the reader.
- docs/web-ui/agent.md:377 -- "clicks" describes literal RFB pointer
  events forwarded by hive-screen-mcp's sendPointer, genuinely
  mouse-specific.
- docs/web-ui/agent.md:78 -- "ctrl/middle-click" names a specific
  keyboard-modifier + mouse-button combination with no "select"
  equivalent.
- docs/web-ui/dashboard.md:741 -- explicitly contrasts "click (or
  Enter/Space)" as two named input methods; replacing "click" with
  "select" would conflate the two the sentence is distinguishing.
- docs/web-ui/shape.md:253 -- "swipes in from the right" describes the
  panel's own slide animation, not a user touch gesture.

Verified: fresh `vale --filter '.Name == "Microsoft.UIVerbs"' docs`
after the fix shows exactly those 5, `nix fmt` reports 0 changes.
2026-09-07 18:46:16 +02:00
atlas
2f7d3e02e9 bao: write the swarm controller's policy from inside the store
Cert auth answers a role, so nothing in the swarm can authenticate to
the store until some role exists. Creating the first one therefore
cannot itself use a certificate — the credential has to come from
outside that cycle, and an operator places it.

`deploy.bao.bootstrapTokenFile` names that token. A one-shot unit inside
the store's container reads it and writes the `swarm-controller` policy;
`local-defaults.nix` supplies the path (never the file) on an all-local
deploy, since co-location makes only the location derivable. `bao
operator init` stays an operator action in every shape.

The unit runs in the container rather than on the host because writing
the first grant is a store-side operation: it reaches the store locally
and needs no client certificate. Gating it on the store being here is
therefore not the co-location assumption glue-matrix-bao-token.nix
warns about — a reader has to work from anywhere, the first write never
does.

Policy only, deliberately: a cert-auth role binds a certificate and the
controller has no bao identity yet — it holds no leaf and contains no
bao code at all. The two certificates that do exist are both wrong to
bind. `clientCertFile` is the store host's own reader leaf rather than
the controller's, and the CA that signed it also signs every other
reader's, so binding that would let any reader authenticate as the
controller. Whoever gives the controller an identity writes the role.

The grants are scoped to `hive-*`. Without the prefix the controller
could rewrite the policy that constrains it.

Two things the module-eval arms pin: the unit renders inside the
container with the token path in both its script and its
ConditionPathExists, and a host that names a token while running no
store gets neither the unit nor the host-side directory.

The capabilities were derived with `bao write -output-policy` rather
than written from memory, and the setup.md commands were run against a
real binary for shape before being written down.

Refs #3726.
2026-09-07 18:43:09 +02:00
iris
e82a735745 docs: fix write-good.So/ThereIs/Weasel lint findings
Fixes the "obvious ones first" slice of #4042 (mara: do the obvious
ones first) -- 81 hits across write-good.So, write-good.ThereIs, and
write-good.Weasel, all in docs/. Each is a genuine sentence rewrite
(lead with the real subject instead of "There is/are", drop a
sentence-initial "So ", replace a vague intensifier), not a blind
regex substitution -- read every hit in its real file context before
touching it.

3 of the 81 hits were in CI-generated CLI docs (docs/tools/{hivectl,
swarmctl,forge}-cli.md) -- fixed at the clap #[arg(...)]/doc-comment
source in hivectl/src/cli.rs, swarmctl/src/main.rs, and
hive-forge/src/verbs/repo_add_collaborator.rs, then regenerated via
each crate's `markdown-docs` subcommand so CI's freshness check stays
green.

Verified: fresh vale re-run shows 0 remaining So/ThereIs/Weasel hits
and no new hits introduced (983->982, exactly the one incidental fix
this pass also picked up at docs/scheduler/observability.md:48).
cargo fmt --check and clippy clean on the three touched crates.

Remaining write-good backlog (Passive: 726, TooWordy: 207) is
judgment-heavy and left for a follow-up slice of #4042, not bulk-
rewritten here.
2026-09-07 17:49:27 +02:00
atlas
6d4e0e5fa1 docs/ci: say to measure the GC window before relying on the stagger
The guidance says to stagger two hosts' collections, and never says how
long one takes — so an operator picking `dates` has no way to know
whether their stagger is wide enough.

The number that matters is theirs, not mine: a collection's wall clock
is dominated by the sweep over unused `.links` entries, which scales
with the size of the store rather than with what the run deletes. So a
store's size, not its churn, decides the window, and `randomizedDelaySec`
spreads only the start.

Measured here for scale, and deliberately kept out of the doc since it
does not transfer: 11 runs over two weeks ranged 5min 16s to 51min 25s,
inversely — the 51-minute run deleted 732 paths, a 12-minute run deleted
21,901.

Refs #4019.
2026-09-07 17:05:19 +02:00
atlas
01ce968fb6 nix: split statusPublish and the otel secret into deploy.*
Slices 8 and 9 of the swarm/deploy split, and the last two.

statusPublish had three coordinates under one namespace. Two of them
are this machine's — where the queue listens *as seen from here*, and
where its client secret sits on this disk — so they move to
`deploy.hive-controller.statusPublish.*`, the namespace of the daemon
that is their only reader. `tokenEndpoint` is the swarm's one address,
so it stays. That leaves `swarm.statusPublish` holding a single option:
a legitimate split, not a botched move.

The all-or-nothing assertion now spans both namespaces. It is repointed
in both its condition and its message, and the message spells all three
paths in full so an operator is never told to set two options under a
path that only has one. `environment.nix`'s guard and the value beside
it likewise read different namespaces on purpose.

The collector's secret moves the same way, for the same reason, to
`deploy.swarm-otel.*` — `enable` already lives there. That also retires
one of the eight cross-namespace assignments tracked in #4048: the
delivery unit set a `swarm.*` value under a `deploy.*` gate, and now
sets a `deploy.*` value under one.

module-eval gets a fixture per slice. `otelRemoteAuthelia` already set
the collector secret through its pre-rename path, so it becomes slice
9's old-path case as it stands — left spelled that way deliberately,
with a comment, so it is not read later as a missed site.

That fixture also turned out to be describing an impossible hive: it
said authelia lives elsewhere without saying where, so the
authenticator interpolated a null `swarm.authelia.url` into its
`token_url`. Nothing to do with the rename, and invisible to the
existing case over the same fixture, which reads `? auth` and `elem` —
both stop at names and never force the extension's value. Given the
address a remote-IdP deployment has.

Verified: 49 -> 51 properties, all holding.
2026-09-07 16:54:23 +02:00
damocles
451b461afb docs: matrix gateway vhost defaults to chat.<swarm-domain>, not matrix.<domain> 2026-09-07 16:53:22 +02:00
iris
ec9ee866db docs: fix merge-conflict fallout + a fresh Contractions/Auto typo from the merge
Resolving the 5 branches (contractions/foreign/auto/quotes/hyphens) onto
current main surfaced a few things beyond plain conflict markers:

- docs/scheduler/coordinator.md had 'auto-noops' turned into the
  malformed 'autanoops' by the auto-hyphens batch -- fixed to
  'autonoops'.
- main had moved (new prose, some option renames) since the batches
  were generated against an older commit, so a handful of merge
  conflicts needed both sides combined (keep main's updated content,
  reapply the lint fix on top) rather than a plain pick-one resolution.
- A few genuinely new Microsoft.Contractions hits had appeared in the
  moved-forward main and in content pulled in wholesale during
  conflict resolution -- fixed the 2 real ones
  (docs/integrations/forge.md, docs/turn-loop/claude-invocation.md).
- The other 'new' Contractions hits vale reports now
  (docs/integrations/forge.md:281, docs/tools/forge.md:260,
  docs/tools/hivectl.md:239, docs/tools/swarmctl-cli.md:28,
  docs/web-ui/dashboard.md:457) are a side effect of the Foreign
  batch's own 'i.e.' -> 'that is' substitution: vale's Contractions
  rule can't tell 'that is' used as a copula (contractable) from 'that
  is' used as a parenthetical clarifier (not contractable, would break
  the sentence -- 'that's it raced the merge' isn't English). Left
  those alone; noted in the PR description.

Final vale count on this branch: the only Microsoft.Contractions/
Foreign/Auto/Quotes/Hyphens hits left are the known legitimate skips
(ALL-CAPS/bold emphasis, vale's own false positives, and this
Foreign/Contractions interaction) -- verified with a fresh vale run.
2026-09-07 16:28:06 +02:00
iris
3f41e7a09c docs: drop redundant hyphens from -ly adverb + adjective/participle compounds
Fixes 22 of 23 Microsoft.Hyphens hits: an adverb ending in -ly doesn't
need a hyphen to the word it modifies (minimally scoped, currently
running, deliberately stopped, ...) -- the -ly ending already
unambiguously signals it's modifying the following word, so the
hyphen is redundant per Microsoft style.

Left one hit alone: docs/web-ui/dashboard.md's 'checked-not-originally-
active' is a single 4-word compound designator (parallel structure
with 'unchecked-was-active' right before it in the same sentence,
describing a diffing algorithm's two possible states), not an
ordinary -ly-adverb modifier -- dropping just the one hyphen vale
flagged would have broken the compound into a dangling
'checked-not-originally active' fragment. Also rewrote
docs/scheduler/coordinator.md's 'still-fully-queued' as a predicate
('DAGs that are still fully queued') rather than leaving a similar
broken partial-hyphen chain, since 'still' and 'fully' are both plain
adverbs with no reason to hyphenate at all once dehyphenated
consistently.

Verified via vale: only the one deliberately-left hit remains.
2026-09-07 16:28:06 +02:00
iris
c20496604c fix Microsoft.Quotes vale findings - move punctuation inside quotes
Moved 27 instances of periods and commas from outside closing quote marks
to inside, following American-style punctuation placement. Changes span
across ~15 documentation files including integration, process, scheduler,
trust-boundary, and web-ui docs.
2026-09-07 16:28:06 +02:00
iris
05cf8e8276 docs: remove hyphens from auto-X compounds per Microsoft.Auto style
Fixed 72 hyphenated 'auto-X' compounds across the docs by deleting
the hyphen to match Microsoft style guide (autogenerated, autoregistration,
autodiscovery, etc). Preserved original capitalization throughout.

Covered 26 documentation files in docs/, including README.md, agent-lifecycle,
integrations, scheduler, networking, process, tools, trust-boundary,
turn-loop, and web-ui sections.
2026-09-07 16:28:06 +02:00
iris
4f2d1d281a docs: fix Microsoft.Foreign hits (e.g./i.e. -> for example/that is)
Substitutes 'for example' for 'e.g.' and 'that is' for 'i.e.' per
Microsoft style, with no comma after the replacement -- this repo
never puts a comma after e.g./i.e. either, so the substituted phrase
matches that house style instead of vale's literal 'for example,'
suggestion.

Covers all 77 real hits (72 from the initial vale pass + 5 more of the
same pattern found on a follow-up vale run). None of the flagged
instances were sentence-initial, so there's no capitalization concern
here.

Verified via a fresh vale run: 0 remaining Microsoft.Foreign hits.
Full accounting in /agents/iris/state/haiku-batches/foreign-report.md.
2026-09-07 16:28:06 +02:00
iris
ff377b0dee docs: fix capitalization losses + one text corruption from the contractions pass
Review pass on top of the previous commit (85a94671) turned up three
real issues its own vale-only verification didn't catch:

- 'has not' -> 'hasn't' fired inside 'has nothing', corrupting
  docs/swarm/README.md into 'hasn'thing to say upward'. Restored
  'has nothing'.
- ~44 hits where the original text was genuinely sentence-initial
  ('It is', 'That is', 'They are', 'What is', 'Do not', ...) got
  contracted in lowercase, silently de-capitalizing the start of a
  sentence. Checked each one's real surrounding text and capitalized
  the ones that needed it.
- Confirmed (by reading the actual text, not just trusting vale) that
  the 3 remaining 'is not'/'has not'/'it is' hits after those fixes are
  vale's own false positives: it matches these as a bare substring with
  no trailing word boundary, so it also fires on 'is nothing', 'has
  nothing', and the already-correct 'it isn't'. Left those alone --
  there's no real issue in the text.

Re-verified with a fresh vale run: the remaining 17 hits are exactly
the union of the 14 legitimate ALL-CAPS/bold-emphasis skips from the
first commit and these 3 false positives. Full accounting in
/agents/iris/state/haiku-batches/contractions-report.md.
2026-09-07 16:28:06 +02:00
iris
78021ce982 docs: fix 329 Microsoft.Contractions hits with proper safety checks
Apply contraction fixes across ~40 doc files (setup, integrations, lifecycle,
networking, scheduler, swarm, tools, trust-boundary, UI, etc.). Skipped 14 hits:
- 10 where words appear in ALL CAPS for deliberate emphasis (is NOT, do NOT, etc.)
- 4 where text could not be safely located due to markdown formatting or column position

Applied via systematic scan with checks for fenced code blocks, inline code spans,
and intentional caps. Preserves sentence-initial capitalization throughout.
2026-09-07 16:28:06 +02:00
iris
77296aff35 docs: retire the stray top-level web-ui.md, fold it into web-ui/README.md
docs/web-ui.md duplicated the web-ui/ directory name at the top level --
the only such collision in docs/ (every other subsystem has just a
directory, no sibling <dir>.md file). That's exactly why it rendered
outside the directory structure in the docs site nav (mara's report,
hyperhive#4054): the site build walks docs/ generically with no
special-casing, so a loose top-level file next to a same-named
directory shows up as its own flat top-level entry instead of nesting
under that directory's section.

web-ui.md's own first paragraph already said as much -- 'This doc has
been split for readability... start at web-ui/README.md instead.' It
was a leftover pointer from before the split, not a page carrying
unique content on its own merit.

Folded its two sections web-ui/README.md didn't already have (the
swarm-ui design-guide link, and the task-oriented 'reading paths'
quick-lookup list) into web-ui/README.md's existing 'More depth'
section, then deleted the stray file and repointed every real
reference at it: 3 in-tree doc cross-links, 3 doc prose mentions
(retargeted to the more specific dashboard.md/shape.md sub-page each
one was actually about), and ~28 frontend source comments
(dashboard/agent/shared packages) that cited it as
'docs/web-ui.md::<heading>' for implementation context -- retargeted
each to whichever of dashboard.md/shape.md/agent.md actually carries
that heading now, verified against each file's real heading list
rather than guessed.

Verified via scripts/check-doc-refs.sh (the same lint CI runs): 0 dead
pointers, both before write (confirming the tree was clean beforehand)
and after (confirming nothing broke).
2026-09-07 15:49:52 +02:00
atlas
9c09653603 deploy: move authelia's three host paths out of swarm.authelia
`usersFile`, `hostClientSecretDir` and `hostUsersFile` are filesystem
paths that only exist on the machine running the `swarm-authelia`
container. A hive that does not run it has nothing at any of them, so
they fail the swarm-wide test the namespace's header states.

`usersFile` is a path *inside* the container and still belongs on the
deploy side: a path's scope is the scope of the filesystem it names, and
that filesystem is this host's container root.

The two `host*` options are `readOnly` and derived, so nothing can set
them through the rename shims — those serve readers. The shims are still
required: `mkRenamedOptionModule` is what keeps an out-of-tree module
reading the old path resolving at all.

Seven reads went through an alias rather than a full path
(`autheliaCfg.hostClientSecretDir` in five modules, `.hostUsersFile` in a
sixth, `swarmCfg.authelia.hostClientSecretDir` in a seventh). Every one
of those files already binds `deployCfg`, so the repoint needed no new
alias. Four more sites were prose, including one in `docs/`, which no
grep restricted to `nix/` would have found.
2026-09-07 15:44:07 +02:00
atlas
5c658e9bcf docs/ci: stagger the GC we recommend, and say that temp roots stop at a store boundary
The nightly GC this page tells operators to configure is what #4019 is
about: a collection that lands on the same wall clock as CI. Two gaps in
the guidance rather than one.

`dates = "daily"` with no randomization puts the collection at the same
time every night, so whatever else runs nightly meets it every night.
`randomizedDelaySec` spreads it; the value is nixpkgs' own example for
that option, and the option feeds `RandomizedDelaySec` on the timer.

The overlap itself does not fail a build on a single host — the runner
builds through the host nix-daemon (`hive-ci.nix` disables its own and
bind-mounts the socket), so an in-flight build's temp roots are held by
the daemon a host GC talks to. What it does cost is wall time: the first
build after a collection rebuilds what the collection deleted. That is
the every-run version of the same cause and it is what the comment now
says.

The Remote builders paragraph covered disk space only. Temp-root
protection is per-daemon, so it does not span two stores: a collection
on the builder is not aware of a coordinator build waiting on a path
there. Stated as the reason to stagger the two hosts' windows rather
than give them the same `dates`.

Deliberately not claiming this fixes the failure in #4019. That one
observed a missing build input with a remote builder in play, and
distinguishing "the collector deleted it" from "it was never in that
store" needs a correlated reading on both hosts during a live window,
which has not been taken. This is the half that is knowable from the
tree: our own recommendation is what schedules the collision.
2026-09-07 15:30:01 +02:00
atlas
efbfeb3954 docs: say what these things are, not where they used to be
mara on #3923: "past tense is itself a smell for docs, we dont need any
'used to be somewhere else'" and "any gate we add should not have false
positives". Those are one instruction: the entire false-positive set of a
repo-path checker was prose naming files that are deliberately gone, so
removing the prose is what lets the gate be strict instead of carrying a
skip-list nobody maintains.

Three sites, and only two of them are past tense:

`agent-hierarchy.md` named two removed modules to explain that a code path
is gone. A reader cannot act on where it used to live; they can act on
where cancellation happens now, which is the half the sentence buried.

`term_msg.rs` carried a "Design history" paragraph whose live content was
three rules -- classification is server-side, `level` carries styling, four
named fields are deliberately absent -- wrapped in narration about a
redesign and a pointer to a deleted frontend file. Restated as the rules.

`gotchas.md` is NOT past tense: it correctly names a file in the website
repo. Qualifying it as `hyperhive/website:nix/options.nix` did not work --
the extractor still matches `nix/options.nix` as a substring, so the fix
has to be prose that contains no repo-relative path at all. Naming the
file alone does that and reads better.

Verified against this tree rather than the default checkout: the audit
script takes a directory argument and defaults elsewhere, so its first
(unchanged) DEAD:3 was a true statement about a different branch. On this
one: DEAD 3 -> 0, 141 distinct paths enumerated, and an injected dead path
is still caught.

Refs #3923
2026-09-07 15:06:12 +02:00
atlas
b6df0b4afc deploy: move grafana's datasources, plugins and socket out of swarm.grafana
`swarm.*` is what a hive needs to be a *client* of the swarm. For Grafana
that is the package, the name it is served under, the port its `/metrics`
is re-served on, and the OIDC client it is registered as. Where its
datasources point, which plugins sit in its store path and the directory
it shares a socket with nginx through are decisions of the machine
running it, so datasourceUrl, logsDatasourceUrl, plugins and socketDir
move to `deploy.grafana.*`.

The two URLs are the interesting half. `swarm-grafana.nix`'s own summary
sentence said what stays is "its package, domain, and wiring" — and both
datasource URLs ARE wiring, so that sentence is rewritten with the move
rather than left asserting the opposite of what the module does. They
move because a URL's scope is the scope of what it ADDRESSES, not the
fact that it is a URL: docs/swarm/services.md already said datasourceUrl
"defaults to the store on this host, which is the only thing it can
reach", because that store binds loopback. The doc argues the move.

`socketDir` was already ruled host-side: the directory is shared between
the host's nginx and the container, and it is the same shape as the
options that moved in earlier slices.

Declared in swarm-grafana.nix under the `deploy.*` path, following
swarm-nats.nix; deploy.nix carries only the renames. One reader outside
the options block is prose: `metricsPort` STAYS and cross-referenced
`{option}services.hyperhive.swarm.grafana.socketDir`, which the split
makes a pointer to another rendered page — nixosOptionsDoc emits
swarm.md and deploy.md separately. Requalified, along with the one line
in docs/swarm/services.md that named a mover.

module-eval configures a hive through all four OLD paths and asserts a
rendered effect: the host tmpfiles rule that creates the socket
directory carries the fixture's custom path. The new paths evaluate fine
without the shims, so dropping them has to read as a clean tree; all
four are defined in the fixture, so removing any single shim fails the
eval rather than only the one the assertion reads.
2026-09-07 14:24:52 +02:00
atlas
81b9ddd189 deploy: move the queue's callout identity out of swarm.nats
`swarm.*` is what a hive needs to be a *client* of the swarm. For the
queue that is the ports it answers on, the client id it is registered
under, and the responder package. Whether this host mints its own
auth-callout keypairs and where the seeds sit are decisions of the
machine running it, so autoGenerateCallout, both seed files and both
public keys move to `deploy.nats.*`.

The two PUBLIC keys are the non-obvious half, so the reasoning is here
rather than in a comment nobody re-reads. A public key looks swarm-wide,
and docs/swarm/secrets.md says the user key "is published to every
client that connects" — which is true and does not make it swarm
config: peers RECEIVE it over the wire at connect time, they never
configure it. What decides the placement is that its seed is
unambiguously host-side, and splitting a keypair across two namespaces
is worse than either placement.

local-defaults.nix set `nats.autoGenerateCallout` from INSIDE
`config.services.hyperhive.swarm = { ... }`, so the bare prefix meant
`swarm.nats` and no full-path grep could see it. It moves out of that
block into a `deploy` statement rather than being rewritten in place.

swarm-controller.nix bound `natsCfg` and never used it — one mention,
no bare pass, while its sibling bindings have 5 and 10. Pre-existing
dead code, found by this slice's own alias sweep, removed with it.

Four assertion messages and five doc lines named moved options by a
short form (`nats.calloutUserSeedFile`) that is ambiguous now that both
`swarm.nats` and `deploy.nats` exist; one opened with
"services.hyperhive.swarm.nats has callout public keys", which the split
makes false. Prose that names a VALUE rather than a path — the `nk`
pipeline's `# -> calloutUserPublicKey` — is left bare on purpose.

module-eval configures a hive through all five OLD paths and asserts the
responder's delivery unit exists and interpolates the seed path.
`autoGenerateCallout` is deliberately FALSE there: that is what makes
the seed options decide `responderConfigured`, so the assertion is about
the seeds rather than about the auto-mint branch.
2026-09-07 14:24:52 +02:00
atlas
7003d14d2c deploy: split the homeserver's host decisions out of swarm.matrix
`swarm.*` is what a hive needs to be a *client* of the swarm. For the
homeserver that is what it IS from anywhere: its package, the name it
answers to, the ports and URLs it is reached on, and the client id it is
registered under. Whether it is exposed, which peers it trusts, how large
a request it accepts and where its host-local secrets sit are decisions
of the machine running it, so openFirewall, trustedServers,
maxRequestSize, registrationTokenFile, gui.enable and
sso.clientSecretFile move to `deploy.matrix.*`.

Two sub-blocks split rather than moving whole, on their own evidence.
`gui.enable` is whether THIS host serves the web client; `gui.package` is
which client, an artifact identity, and stays. `sso.clientSecretFile` is a
path on one host; `clientId` must match the id in authelia's register, so
it is swarm-wide. Each half now points at the other, because the rendered
docs put them on separate pages.

hive-gateway passed the whole `swarm.matrix` attrset into vhosts.nix, so
that file read a moving option through an argument with no option path
anywhere in it. It now takes `matrixDeployCfg` beside `matrixCfg` — the
only shape that carries a split namespace across that boundary.

While there: vhosts.nix read `matrixCfg.enable`, which has been a rename
alias for `deploy.matrix.enable` since the enable moved. Reading it made
the module system print `Obsolete option services.hyperhive.swarm.matrix.
enable is used` on EVERY evaluation of every host — a deprecation warning
no operator could silence, because the config tripping it was ours. That
shim lives in hive-matrix.nix rather than in this file's table, which is
why deploy.nix's header claim to be their single home is now qualified
in the new block's comment.

glue-matrix-bao-token.nix read the registration token through its own
`matrixCfg` alias; with that read repointed, the binding had no reader
left, so it goes, and the comment naming it is reworded.

module-eval gains a case configuring a hive through all six OLD paths and
asserting two rendered effects — the host firewall's port list and the
container's bind-mount table — because the new paths evaluate fine
without the shims. `gui.enable` is set to the opposite of its default so
the definition has to land rather than agreeing with it by accident.
2026-09-07 14:24:52 +02:00
atlas
1db2ac26a7 deploy: split the forge's host decisions out of swarm.forge
`swarm.*` is what a hive needs to be a *client* of the swarm. For the
forge that is what it IS from any hive's point of view: its package, the
names and ports it answers on, the URLs it advertises, and the client id
it is registered under. How it is served, what it mirrors and where its
host-local secrets sit are decisions of the machine running it, so
behindGateway, openFirewall, mirrors, sso.clientSecretFile and
hostSwarmControllerTokenFile move to `deploy.forgejo.*`.

Unlike the wireguard mesh this SPLITS a module rather than relocating a
whole namespace. `sso` splits with it: `clientId` stays because it must
match the id in authelia's register, while the secret beside it is a
path on one host. Moving the whole `sso` block for symmetry with `ci`
was considered and rejected on exactly that asymmetry.

Declared in hive-forge/default.nix under the `deploy.*` path, following
swarm-victorialogs.nix; deploy.nix carries only the renames. `mirrors`
renames in one entry rather than one per field — it is a single option
of a list-of-submodule type, so the rename carries its whole value,
where `ci` needed five because it is a plain attrset of options.

Readers outside the module: hive-ci.nix binds `deploy.forgejo` for its
behindGateway assertion; swarm-authelia.nix and swarm-controller.nix
read theirs off the `deployCfg` they already bind. hivectl's `open`
printed `services.hyperhive.forge.behindGateway` in an operator-facing
hint — a path that never existed, missing `swarm.` — and hive-c0re's
state_snapshot doc comment carried the same defect; both now name the
new path. The rendered docs put the two halves on separate pages, so
the five descriptions of staying options that explain themselves in
terms of `behindGateway` now qualify it in full.

module-eval gains a forge case configured entirely through the old
paths, asserting the rendered firewall ports and the mirror env var
c0re seeds from: the new paths evaluate fine without the shims, so
dropping them reads as a clean tree. All five old paths are defined in
the fixture, so removing any single shim entry fails the eval rather
than only the two the assertion reads.
2026-09-07 14:24:52 +02:00
atlas
368f5d82aa deploy: move the wireguard mesh out of the namespace hives read
`swarm.*` is what a hive needs to be a *client* of the swarm; the mesh is
none of it. A peer needs this host's `wireguardEndpoint` -- the roster entry
in swarm.nix, which stays -- and nothing about the interface this host
brings up. The module already said so: "plain host networking that a machine
which runs no hive at all still needs."

All five options move, so the namespace relocates rather than splitting.
`listenPort` is the one that reads the other way: it is what this host
*binds*, while the port a peer *dials* lives inside `wireguardEndpoint`.

Declared in swarm-wireguard.nix under the `deploy.*` path, following
swarm-victorialogs.nix; deploy.nix carries only the renames, per its own
"a single file to delete when the deprecation window closes". Deliberately
NOT added to deploy.nix's own options block: every entry there is a swarm
service this host deploys, and the mesh is host networking.

hivectl/src/wg.rs generates the config snippet an operator pastes, so it
moves too -- otherwise the tool's own output trips the deprecation warning.

module-eval gains a case that configures a host through the OLD path and
asserts the rendered wg-hive interface, because the new path evaluates
fine without the shim: dropping it reads as a clean tree.
2026-09-07 14:24:52 +02:00
iris
c7c221baeb Disable Microsoft.HeadingColons and Microsoft.Percentages, fix Plurals hits
HeadingColons conflicts with this repo's sentence-case heading
convention -- same shape as the existing Dashes disable, a house-style
conflict rather than a defect.

Percentages had zero true positives on the full docs/ tree: it only
ever fired on identifier uses of "percent" (cpu.percent, percent
resolver), never on an actual spelled-out percentage.

Plurals: rewrote the two real hits to always use the plural form
instead of the parenthetical (s) construction, per house style.
2026-09-07 14:19:24 +02:00
damocles
73c5c06e31 claude-settings: enable native auto-compact as a mid-turn safety net 2026-09-07 13:51:33 +02:00
atlas
af5dce0648 matrix: scope the daemon's token watcher to this agent's own state dir
`hive-matrix-daemon.path` globbed `/agents/*/state/matrix-token*`. Every
agent's state dir is visible from inside every container, so the
condition is satisfied by a sibling's token.

That is reachable, not cosmetic. The daemon deliberately exits 0 when it
has no token of its own — `Restart = "on-failure"` therefore does not
restart it, and the unit sits inactive, which is the state the path unit
exists for. In that state a sibling's token keeps the glob satisfied:
the path fires, the daemon exits 0, the unit deactivates, the path
re-arms, the condition is still true. systemd.path(5) activates a
`PathExists`-family condition that already holds immediately on arming,
so it repeats until the start limit stops it.

Scoped to this agent, the condition is false exactly when the daemon
would have nothing to do.

The glob is quoted in four other places, all of which would otherwise
name a pattern that no longer exists — a doc, a Rust doc-comment in
hive-c0re, a nix comment, and an assertion message an operator reads.
Each is reworded to the basename (`matrix-token*` in this agent's state
dir), which is what the assertion actually enforces via `baseNameOf`, so
they stay true wherever the directory moves.

Refs #4030.
2026-09-07 13:37:09 +02:00
iris
acee0d5fa0 docs: rewrite the remaining 3 obvious-framing hits too
Not condescending (they're negated -- non-obvious/not obvious is the
opposite claim, not the flagged pattern) but the negated-obvious
phrasing itself is worth a direct rewrite regardless of what triggered
the review: integrations/matrix.md (non-obvious -> subtle),
process/conventions.md (if non-obvious -> when the reasoning isn't
self-evident), swarm/ca.md (not obvious -> subtle).
2026-09-07 13:25:09 +02:00
iris
cd252ca6b2 docs: fix alex whitelist + condescending-word findings
Fixes the 'genuine' bucket from #4040's triage: whitelist/allowlist
(6 hits across 5 files, including a heading rename in turn-loop/mcp.md
-- checked no anchor links reference it first) and the
simply/obvious/easy condescending-word cluster (10 of 13 hits, real
sentence-level rewrites rather than mechanical deletion so nothing
reads awkwardly).

Left 3 alex.Condescending hits untouched on purpose:
integrations/matrix.md:271 and process/conventions.md:442 both say
"non-obvious", swarm/ca.md:162 says "not obvious" -- alex matched
the substring "obvious" inside a negated phrase, the literal opposite
of condescending. Flagging these on the issue rather than silently
leaving them out.
2026-09-07 13:25:09 +02:00
atlas
529e9416c1 hive-forge: say which flat forms were removed and which are staying
`docs/tools/forge.md` said the flat forms "still work as hidden
back-compat aliases" and named `pr-create` and `pr-status` as its
examples. Both refuse now: nine flat verbs parse only far enough to
print the namespaced form that replaced them.

The doc had no way to be read correctly, because the seven that do still
work are hidden too and `--help` shows neither group. Naming the split
here is the only place the difference is visible.

At the enum the kind-agnostic seven now read as staying rather than as
"still work", and `credential-helper` is marked as belonging to neither
group: it is hidden because git invokes it, so a sweep reading
`hide = true` as "deprecated" would remove checkout auth.

Refs #3974.
2026-09-03 02:16:55 +02:00
iris
589ace3438 agent term: add a setting to hide debug-level output
hyperhive#4008, mara: 'add a setting to not display verbose output...
like the grey colored debug stuff.'

Same shape as the existing expand-tool-output preference
(ExpandDetailsSetting.tsx): a new shared/src/prefs.ts key pair
(getHideDebugPref/setHideDebugPref), a new settings-menu-row component
owning its own state (HideDebugSetting.tsx, not a prop threaded through
the shared SettingsMenu component — per mara's earlier review on the
first one, more per-page options as props there is how that component
accumulates cruft), mounted next to ExpandDetailsSetting in Root.tsx.

Row.tsx skips (returns null for, not CSS display:none) any TermMsg
whose level is 'debug' when the pref is set — matches the muted 'debug'
row this issue is about (see docs/web-ui/terminal-rendering.md's Levels
table). Read live per-row, same as expand-details, so toggling applies
to newly streamed rows in an already-open tab without a reload; already
-rendered rows are unaffected either way, same non-retroactive
precedent the existing preference already sets.
2026-09-02 20:40:22 +02:00