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.
This commit is contained in:
parent
c41c67c949
commit
060f325716
4 changed files with 62 additions and 62 deletions
|
|
@ -48,8 +48,8 @@ hive-c0re to re-mint with the new scopes.
|
|||
|
||||
Each agent gets its own Forgejo user + access token, provisioned at
|
||||
boot by `hive-c0re::forge`. The provisioning flow is idempotent:
|
||||
existing accounts + tokens are reused, so container destroy/recreate
|
||||
doesn't lose forge identity. The token is written to
|
||||
`hive-c0re::forge` reuses existing accounts + tokens, so container destroy/recreate
|
||||
doesn't lose forge identity. It writes the token to
|
||||
`<state>/forge-token` (one line, no trailing newline) inside the
|
||||
agent container so `hive-forge` CLI + `forge_notify` poller can
|
||||
read it without touching c0re's host-side credential store.
|
||||
|
|
@ -63,7 +63,7 @@ Two things live in the `agent-configs` Forgejo organization:
|
|||
`hive-c0re/src/forge/config_pr_poll.rs` re-scans every 5 minutes as a
|
||||
fault-tolerance backstop) — but
|
||||
`main` is branch-protected core-only: only hive-c0re's verify-and-ff-push
|
||||
merge handler lands on `main`, an operator-team approval is required, and
|
||||
merge handler lands on `main`, the operator team must approve first, and
|
||||
the agent can neither push `main` directly nor self-merge. `main` is
|
||||
fast-forward-only — hive-c0re never force-pushes (the merge handler's ff
|
||||
push lands fine; the `push_config` mirror pushes `main` + the add-only
|
||||
|
|
@ -126,7 +126,7 @@ That size property is the whole point. A container rebuild starts the
|
|||
poller with no memory of what it delivered, re-scans `?all=false`, and
|
||||
finds nothing stale — the delivered threads are already read on forge.
|
||||
Forge's own read-state is thus the durable, cross-rebuild record of
|
||||
what's been delivered; there is **no persisted cursor**.
|
||||
what `forge_notify` has delivered; there is **no persisted cursor**.
|
||||
|
||||
**Gotcha:** don't reintroduce a persisted dedup cursor here. A design
|
||||
that leaves threads unread and tracks delivery via a separately persisted
|
||||
|
|
@ -149,10 +149,10 @@ re-fire the wake. it's **not persisted** and resets on restart (forge
|
|||
read-state covers the durable case). Each poll prunes it to the ids in
|
||||
the single `limit=UNREAD_FETCH_LIMIT` (50) fetch page, so it can never
|
||||
exceed that many entries (a debug assertion pins the invariant; the
|
||||
fetch limit and the bound are the same constant). A failed _delivery_
|
||||
is left unread and out of the map, so it resurfaces next tick.
|
||||
fetch limit and the bound are the same constant). `forge_notify` leaves
|
||||
a failed _delivery_ unread and out of the map, so it resurfaces next tick.
|
||||
|
||||
Self-echo notifications (the agent's own writes, see below) are marked
|
||||
`forge_notify` marks self-echo notifications (the agent's own writes, see below)
|
||||
read directly without a delivery — same `mark_read` call, no wake.
|
||||
|
||||
### Activation gates (graceful no-ops)
|
||||
|
|
@ -176,17 +176,17 @@ best-effort: logged at debug/warn and retried next tick.
|
|||
|
||||
Forgejo fires notifications for the agent's own actions (it opened a
|
||||
PR, posted a comment, submitted a review). Surfacing those would
|
||||
loop claude on its own writes. The comment/review case is dropped
|
||||
loop claude on its own writes. `forge_notify` drops the comment/review case
|
||||
silently (mark-read without delivery):
|
||||
|
||||
- **Self-authored comments / reviews** — comment payload's
|
||||
`user.login` matches `own_login`.
|
||||
- **Self-authored creations** (an agent opening its own PR/issue) — the
|
||||
already-fetched subject payload's poster `user.login` matches
|
||||
`own_login`. Only _creations_ are dropped; a later state change on the
|
||||
agent's own subject is driven by someone else and still surfaces.
|
||||
`own_login`. `forge_notify` drops only _creations_ this way; a later state
|
||||
change on the agent's own subject comes from someone else, so it still surfaces.
|
||||
|
||||
`own_login` is fetched at startup via `GET /api/v1/user`. On fetch
|
||||
`forge_notify` fetches `own_login` at startup via `GET /api/v1/user`. On fetch
|
||||
failure the filter degrades open (no filtering) rather than crashing
|
||||
the task — a noisy inbox beats a silently stuck poller — but the fetch
|
||||
is **re-attempted on each poll tick** until it succeeds, so a boot-time
|
||||
|
|
@ -246,7 +246,7 @@ not context (mara, triage: "just the issue title should be enough").
|
|||
|
||||
Review labels come from the Forgejo `state` field: `APPROVED` →
|
||||
`approved`, `REQUEST_CHANGES` → `changes requested`, `COMMENT` →
|
||||
`review comment`. `PENDING` is dropped (review saved but not
|
||||
`review comment`. `forge_notify` drops `PENDING` (review saved but not
|
||||
submitted yet — no peer-visible event). Unknown states fall back to
|
||||
the generic comment wrapper.
|
||||
|
||||
|
|
@ -276,17 +276,17 @@ hidden behind a stale comment.
|
|||
The one gap the timestamp cut leaves: a genuine comment posted **within
|
||||
`NEW_ITEM_TOLERANCE_SECS` of the merge** bumps `updated_at` close enough
|
||||
to `closed_at` that `state_change_is_current` returns `true` — so it takes
|
||||
the state-change path and its body would be dropped. Best of both worlds:
|
||||
the state-change path, dropping its body. Best of both worlds:
|
||||
on the merge/close path we fetch the `latest_comment_url` comment and, when
|
||||
its `created_at` is strictly **after** the subject's `closed_at`
|
||||
(`comment_is_after_close`) — that is it raced the merge rather than being the
|
||||
pre-merge last comment the subject keeps — append it as a
|
||||
`comment by <author>: <excerpt>` block before the meta suffix
|
||||
(`fresh_post_close_comment_tail`). The wake carries **both** `[PR merged]`
|
||||
and the racing comment. The kept pre-merge comment (created before
|
||||
`closed_at`) is left off, a self-authored racing comment is dropped (don't
|
||||
echo the agent's own write), and a missing/unparseable `created_at`/
|
||||
`closed_at` appends nothing (conservative — only surface a comment we can
|
||||
and the racing comment. `forge_notify` leaves the kept pre-merge comment (created before
|
||||
`closed_at`) off, drops a self-authored racing comment (don't
|
||||
echo the agent's own write), and appends nothing for a missing/unparseable `created_at`/
|
||||
`closed_at` (conservative — only surface a comment we can
|
||||
positively place after the close). Cost: one extra comment fetch on
|
||||
merge/close notifications, acceptable given how rare they are.
|
||||
|
||||
|
|
@ -299,15 +299,15 @@ so it misses the comment path and lands on the state-change path with
|
|||
original open notification and miss the review. The `open`
|
||||
state only earns the `new <kind>` label when the notification's event
|
||||
time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` (120s) of the
|
||||
subject's `created_at`. Anything later is labeled `activity on <kind>`
|
||||
subject's `created_at`. `forge_notify` labels anything later `activity on <kind>`
|
||||
— neutral and non-misleading, since we can't cheaply say _what_ the
|
||||
activity was without an extra reviews fetch. Missing/unparseable
|
||||
timestamps default to `new` (preserve prior behavior rather than mask a
|
||||
genuine new item). Timestamps are parsed by a small dependency-free
|
||||
RFC 3339 helper (`parse_rfc3339`).
|
||||
genuine new item). A small dependency-free
|
||||
RFC 3339 helper (`parse_rfc3339`) parses timestamps.
|
||||
|
||||
Number is extracted from `subject.html_url`'s last path segment
|
||||
(strips `#anchor` first); repo slug from `repository.full_name`.
|
||||
`forge_notify` extracts the number from `subject.html_url`'s last path segment
|
||||
(strips `#anchor` first), and the repo slug from `repository.full_name`.
|
||||
Both degrade gracefully when absent (number → blank, repo → blank)
|
||||
so unexpected Forgejo shapes don't crash the formatter.
|
||||
|
||||
|
|
@ -326,15 +326,15 @@ For new PRs, the kind label flips to `[review requested #N
|
|||
owner/repo]` when `own_login` appears in `requested_reviewers`,
|
||||
regardless of the Forgejo `reason` field. Forgejo doesn't reliably
|
||||
set `reason == "review_requested"` (often null instead), so the
|
||||
fallback checks the subject payload directly. Detection is gated on
|
||||
`is_new` so the label only fires once on PR creation, not on every
|
||||
fallback checks the subject payload directly. `is_new` gates detection
|
||||
so the label only fires once on PR creation, not on every
|
||||
subsequent comment.
|
||||
|
||||
### Subscription management
|
||||
|
||||
The poller does **not** autounsubscribe from repo watches — it
|
||||
delivers every unread notification it's handed. Bounding the
|
||||
firehose (dropping broad repo watches an agent doesn't need) is done
|
||||
explicitly via a hive-forge CLI subscription verb, not by the poller
|
||||
guessing which watches to drop. See the `subscription` verb in
|
||||
delivers every unread notification it's handed. A hive-forge CLI
|
||||
subscription verb bounds the firehose explicitly (dropping broad repo
|
||||
watches an agent doesn't need) — the poller never guesses which
|
||||
watches to drop. See the `subscription` verb in
|
||||
[`docs/tools/forge.md`](../tools/forge.md).
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ HTTPS, both authenticated by an operator-supplied personal access token
|
|||
|
||||
Provisioning is UI-driven, mirroring the dashboard side of the
|
||||
[matrix account](matrix.md) flow: paste a PAT into the agent's credentials
|
||||
tab and it works. No per-agent nix declaration, no rebuild — the token is
|
||||
injected into the agent's state dir out of band.
|
||||
tab and it works. No per-agent nix declaration, no rebuild — hive-c0re
|
||||
injects the token into the agent's state dir out of band.
|
||||
|
||||
## Enabling
|
||||
|
||||
The integration is **on by default** for every agent (`hyperhive.github.enable
|
||||
= true`), inert until a PAT is provisioned. No per-agent declaration is
|
||||
= true`), inert until the operator provisions a PAT. No per-agent declaration is
|
||||
needed — an agent gains GitHub by having a PAT written to its token
|
||||
file.
|
||||
|
||||
|
|
@ -81,8 +81,8 @@ When enabled, the container gets:
|
|||
Both scripts read the token from `<state>/github-token` **at invocation
|
||||
time**, so a PAT written (or rotated) mid-session takes effect immediately
|
||||
— no container rebuild or restart. Until the file exists, `gh` / `git push`
|
||||
fail unauthenticated. The token path is baked into the scripts at
|
||||
build time (not read from an env var), because claude's Bash tool runs in a
|
||||
fail unauthenticated. The build bakes the token path into the scripts
|
||||
(rather than reading it from an env var), because claude's Bash tool runs in a
|
||||
minimal environment that wouldn't carry one.
|
||||
|
||||
### Notifications
|
||||
|
|
@ -108,13 +108,13 @@ logs why and exits 0, which is why it's `Restart = on-failure` and
|
|||
never `always` — a clean exit on a PAT-less agent must not become a
|
||||
restart loop.
|
||||
|
||||
Forgejo's notifications API is modelled on GitHub's, so one tolerant
|
||||
Forgejo models its notifications API on GitHub's, so one tolerant
|
||||
parse serves both: `id`, `repository.full_name`,
|
||||
`subject {title,url,latest_comment_url}` and `updated_at` line up field
|
||||
for field. The two real differences are absorbed by lenient
|
||||
deserializers — GitHub sends the thread id as a _string_ where Forgejo
|
||||
sends a number, and says `PullRequest` where Forgejo says `Pull`. Todo
|
||||
keys are prefixed `gh:<id>` so a github thread id can't collide with a
|
||||
for field. Lenient deserializers absorb the two real differences —
|
||||
GitHub sends the thread id as a _string_ where Forgejo
|
||||
sends a number, and says `PullRequest` where Forgejo says `Pull`.
|
||||
`hive-github-notify` prefixes todo keys `gh:<id>` so a github thread id can't collide with a
|
||||
forge one.
|
||||
|
||||
Two host differences worth knowing before touching this code:
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ repo `knowledge`). The repo is public, so every agent's forge account
|
|||
has read access without an explicit per-agent collaborator grant;
|
||||
only the `core` account has push access, for autoseeding.
|
||||
|
||||
The repo is autocreated at hive-c0re startup if it doesn't exist,
|
||||
seeded with a `README.md` containing a contribution guide and a
|
||||
hive-c0re autocreates the repo at startup if it doesn't exist,
|
||||
seeding it with a `README.md` containing a contribution guide and a
|
||||
blank table of contents. Add an entry to that ToC each time you
|
||||
create a new document.
|
||||
|
||||
|
|
@ -44,8 +44,8 @@ hive-c0re maintains the local clone at
|
|||
event to every hive over the swarm queue and each hive runs `git
|
||||
pull`, so agents see the new content on their next turn.
|
||||
|
||||
A hive that's offline when the event is sent doesn't get it on
|
||||
reconnect — the periodic pull below is what closes that gap. One
|
||||
A hive that's offline when the swarm controller sends the event
|
||||
doesn't get it on reconnect — the periodic pull below is what closes that gap. One
|
||||
hive briefly showing older `/knowledge` content than another is
|
||||
expected, and resolves by itself within the fallback interval.
|
||||
|
||||
|
|
@ -76,11 +76,11 @@ sender `system`, body `[system] /knowledge updated:` followed by a
|
|||
"see the repo" fallback if computing the diff itself fails). This is
|
||||
the same broadcast mechanism used for other hive-wide notices — inbox
|
||||
message only, no forced wake, and it carries the standard "this was a
|
||||
broadcast" hint. A diff or per-agent send failure is logged but never
|
||||
blocks the pull itself.
|
||||
broadcast" hint. hive-c0re logs a diff or per-agent send failure
|
||||
without blocking the pull itself.
|
||||
|
||||
The local clone is created (or refreshed) once at startup via
|
||||
`knowledge::ensure_local_clone`. If the repo is brand new and
|
||||
`knowledge::ensure_local_clone` creates (or refreshes) the local
|
||||
clone once at startup. If the repo is brand new and
|
||||
empty, `ensure_local_clone` seeds it with the default README
|
||||
before returning.
|
||||
|
||||
|
|
@ -105,8 +105,8 @@ write access, so they can't push a branch directly. The supported path
|
|||
is Forgejo's **AGit flow** through the `hive-forge` CLI — no fork
|
||||
required.
|
||||
|
||||
1. Clone the repo (credentials are injected automatically; the `-r`
|
||||
flag selects the repo, the clone lands in `./knowledge`):
|
||||
1. Clone the repo (`hive-forge` injects credentials automatically;
|
||||
the `-r` flag selects the repo, the clone lands in `./knowledge`):
|
||||
|
||||
```sh
|
||||
hive-forge -r internal/knowledge clone
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ homeserver.
|
|||
⚠️ **they're still not interchangeable, and the difference is the
|
||||
cost of changing one.** `gatewayHost` is a routing detail clients
|
||||
rediscover through `.well-known`, so it's safe to move on a running
|
||||
deployment. `serverName` is baked into every user and room id, so
|
||||
adopting a new one does **not** rename the existing users and rooms —
|
||||
deployment. The matrix id format bakes `serverName` into every user and
|
||||
room id, so adopting a new one does **not** rename the existing users and rooms —
|
||||
it strands them, because their ids still name a homeserver that no
|
||||
longer answers.
|
||||
|
||||
|
|
@ -125,8 +125,8 @@ see it. The agent only receives the resulting `access_token`.
|
|||
6. **hive-c0re restarts `hive-matrix-daemon`** for the agent
|
||||
immediately after writing the token so the daemon picks up the
|
||||
new credential without waiting for a full container restart. If
|
||||
the restart fails (for example daemon not yet running on first boot) the
|
||||
error is logged as a warning and the `.path`-trigger sibling
|
||||
the restart fails (for example daemon not yet running on first boot)
|
||||
hive-c0re logs the error as a warning and the `.path`-trigger sibling
|
||||
(`hive-matrix-daemon.path` watching for `matrix-token` appearance)
|
||||
brings the daemon up on the same boot cycle anyway.
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ Initial rollout settings:
|
|||
- `allow_federation = true` at the protocol level so swarms can be
|
||||
wired up later by extending `trustedServers` without a homeserver
|
||||
restart. `trusted_servers = []` keeps it effectively closed
|
||||
until peers are listed.
|
||||
until you list peers.
|
||||
- `allow_registration = true` (required for the token flow to
|
||||
engage). The absent
|
||||
`yes_i_am_very_very_sure_…_open_registration_…` flag keeps the
|
||||
|
|
@ -148,8 +148,8 @@ Initial rollout settings:
|
|||
homeserver admin. **The agent matrix client always supports decryption
|
||||
regardless of this flag** — it uses the `e2e-encryption` feature of
|
||||
`matrix-sdk` so it can read encrypted rooms it's invited to even when
|
||||
this homeserver doesn't permit room encryption. Crypto keys are stored
|
||||
in the per-agent sqlite store under the state dir and persist across
|
||||
this homeserver doesn't permit room encryption. matrix-sdk stores
|
||||
crypto keys in the per-agent sqlite store under the state dir; they persist across
|
||||
restarts (lost on `--purge`). `read_room` decrypts via
|
||||
`room.messages()` — UTD events surface as `event_type =
|
||||
"m.room.encrypted"` with `body = "[unable to decrypt]"`.
|
||||
|
|
@ -159,7 +159,7 @@ Initial rollout settings:
|
|||
|
||||
## Hive Matrix Space
|
||||
|
||||
On first boot (after all agent accounts are provisioned), hive-c0re
|
||||
On first boot, after hive-c0re provisions all agent accounts, it
|
||||
creates a private **Matrix Space** named `"hive"` using the admin
|
||||
account (`@hive:<server_name>`) and invites every provisioned agent
|
||||
into it. This gives the operator a single Space in FluffyChat or any
|
||||
|
|
@ -174,17 +174,17 @@ restricted (any Space member including the operator can join; agents
|
|||
are explicitly invited). Room version pinned to 10 for the restricted
|
||||
join floor.
|
||||
|
||||
**State**: both room IDs are persisted to `/var/lib/hyperhive/matrix/`
|
||||
**State**: hive-c0re persists both room IDs to `/var/lib/hyperhive/matrix/`
|
||||
(mode `0600`, owned by the hive-c0re service user):
|
||||
- `space-room-id` — the Space itself
|
||||
- `chat-room-id` — the `hive-chat` room
|
||||
|
||||
These paths are **outside** every agent state dir and are NOT deleted by
|
||||
`nixos-container destroy --purge` — both survive full agent purges and
|
||||
are reused on re-provision.
|
||||
`nixos-container destroy --purge` — both survive full agent purges, and
|
||||
hive-c0re reuses them on re-provision.
|
||||
|
||||
**Idempotent**: if the files exist and are non-empty, the Space and
|
||||
room are considered already created. Delete the files to force
|
||||
**Idempotent**: if the files exist and are non-empty, hive-c0re considers the Space and
|
||||
room already created. Delete the files to force
|
||||
re-creation (for example after a homeserver wipe).
|
||||
|
||||
## Configuration tuning
|
||||
|
|
@ -219,7 +219,7 @@ surprising behaviour:
|
|||
empty string renders `.<hive>`-shaped garbage in both nginx
|
||||
`server_name` (treated as wildcard catch-all, surprising) and
|
||||
`/etc/hosts` (invalid entry). `null` is the right opt-out shape;
|
||||
empty string is rejected explicitly.
|
||||
a config assertion rejects empty string explicitly.
|
||||
SSO is unconditional, so the three below are requirements of running a
|
||||
homeserver at all rather than of a setting:
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue