docs: the handbook follows the three layers: swarm, hives, agents
The handbook's sections were agent-lifecycle, integrations, swarm, tools, trust-boundary and web-ui, which do not follow how the system is built, so a reader could not tell which layer a page is about. Its pages now sit in swarm/, hives/ and agents/, with the accounts an agent can hold outside the system in external-accounts/. - swarm/: forge, matrix and the knowledge repo join the swarm pages, and so does the swarmctl reference. - hives/: the dashboard, hivectl and its reference, and the operator/agent boundary, with a README that takes over the old web-ui and tools pages. - agents/: the roster, persistence, config changes, the agent's own web page (was web-ui/agent) and the security model, with a README. - external-accounts/: GitHub and GitLab. Pages are moved whole; none is split yet. Every reference follows: relative links in the docs, `docs/2-handbook/...` paths in code comments, the nix checks that regenerate the CLI references and the ignore list, and docs/README.md, whose handbook section is now by layer.
This commit is contained in:
parent
263b21021a
commit
0e84fe8bc4
1 changed files with 393 additions and 363 deletions
|
|
@ -18,12 +18,12 @@
|
|||
//! **Multi-source**: always the internal Forgejo, plus github.com when the
|
||||
//! agent has a PAT. Each source polls independently behind
|
||||
//! [`Source`]; everything below is shared. Rationale
|
||||
//! + host differences: `docs/2-handbook/integrations/github.md::Notifications`.
|
||||
//! + host differences: `docs/2-handbook/external-accounts/github.md::Notifications`.
|
||||
//!
|
||||
//! Activation gates, self-notification filtering, body excerpt +
|
||||
//! truncation + heading escape, wrapper formats (comment / review /
|
||||
//! new-item / state-change), meta suffix, and review-request override
|
||||
//! all live in `docs/2-handbook/integrations/forge.md::Notification poller`.
|
||||
//! all live in `docs/2-handbook/swarm/forge.md::Notification poller`.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Write as _;
|
||||
|
|
@ -85,7 +85,7 @@ where
|
|||
/// a `new <kind>` label. Later activity that lands on the state-change
|
||||
/// path because it carries no `latest_comment_url` (e.g. a bodiless
|
||||
/// review submission) fires well outside this window, so we must not
|
||||
/// claim it's "new" — see docs/2-handbook/integrations/forge.md, "new vs activity on".
|
||||
/// claim it's "new" — see docs/2-handbook/swarm/forge.md, "new vs activity on".
|
||||
const NEW_ITEM_TOLERANCE_SECS: i64 = 120;
|
||||
|
||||
/// Fetch the account's own login for self-notification filtering.
|
||||
|
|
@ -133,7 +133,7 @@ fn notif_type_label(t: Option<NotifySubjectType>) -> &'static str {
|
|||
/// Escape strict-ATX markdown headings in a body before embedding it
|
||||
/// inside the forge-notify wrapper, so a leading `## title` line
|
||||
/// doesn't blow into an h2 in the dashboard render. See
|
||||
/// `docs/2-handbook/integrations/forge.md::Body excerpt + truncation + heading escape` for
|
||||
/// `docs/2-handbook/swarm/forge.md::Body excerpt + truncation + heading escape` for
|
||||
/// the strict-ATX-vs-`#tag` rationale and the `split_inclusive`
|
||||
/// trailing-newline contract.
|
||||
fn escape_md_headings(body: &str) -> String {
|
||||
|
|
@ -218,7 +218,7 @@ fn is_username_byte(b: u8) -> bool {
|
|||
/// `@username` mention AND aren't already present (as a substring) in
|
||||
/// `included_excerpt`. Surfaces tags that fell outside the truncation
|
||||
/// window so addressed agents never silently miss a mention on a long
|
||||
/// body. See `docs/2-handbook/integrations/forge.md::Body excerpt + truncation + heading
|
||||
/// body. See `docs/2-handbook/swarm/forge.md::Body excerpt + truncation + heading
|
||||
/// escape` for the truncate-before-escape ordering rule.
|
||||
fn extract_truncated_mention_lines<'a>(full_body: &'a str, included_excerpt: &str) -> Vec<&'a str> {
|
||||
full_body
|
||||
|
|
@ -275,7 +275,7 @@ fn review_state_label(state: &str) -> Option<&str> {
|
|||
/// Build a human-readable wake message for one Forgejo notification,
|
||||
/// or `None` for a self-echo the caller should mark-read without
|
||||
/// delivery. Wrapper format table + meta-suffix shape + number/repo
|
||||
/// extraction live in `docs/2-handbook/integrations/forge.md::Wrapper format` +
|
||||
/// extraction live in `docs/2-handbook/swarm/forge.md::Wrapper format` +
|
||||
/// `::Meta suffix`.
|
||||
async fn format_notification<S: Source>(
|
||||
client: &reqwest::Client,
|
||||
|
|
@ -327,7 +327,7 @@ async fn format_notification<S: Source>(
|
|||
|
||||
// Always fetch subject detail for assignee/reviewer metadata so
|
||||
// the meta suffix can show current ownership without a follow-up
|
||||
// fetch (see `docs/2-handbook/integrations/forge.md::Meta suffix`).
|
||||
// fetch (see `docs/2-handbook/swarm/forge.md::Meta suffix`).
|
||||
let subject = if subject_api_url.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -417,7 +417,7 @@ async fn format_notification<S: Source>(
|
|||
/// that comment was genuinely posted AFTER the close - a comment
|
||||
/// racing the merge inside the state-change tolerance window - it is
|
||||
/// appended so it isn't lost (best of both worlds; see
|
||||
/// `docs/2-handbook/integrations/forge.md::Merge racing a comment`). The ordinary
|
||||
/// `docs/2-handbook/swarm/forge.md::Merge racing a comment`). The ordinary
|
||||
/// pre-merge last comment (created before `closed_at`) is left off.
|
||||
/// The earlier-updates count is the same widened marker as the comment
|
||||
/// path; both are fetched here because `format_state_change_notification`
|
||||
|
|
@ -463,7 +463,7 @@ struct NotifMeta<'a> {
|
|||
|
||||
/// Build the `\nassignee: ...` (and optionally `\nreviewer: ...`)
|
||||
/// suffix appended to every wrapper. Shape + presence rules live in
|
||||
/// `docs/2-handbook/integrations/forge.md::Meta suffix`.
|
||||
/// `docs/2-handbook/swarm/forge.md::Meta suffix`.
|
||||
fn build_meta_suffix(subject: Option<&serde_json::Value>, is_pr: bool) -> String {
|
||||
let assignees: Vec<&str> = subject
|
||||
.and_then(|s| s["assignees"].as_array())
|
||||
|
|
@ -725,7 +725,7 @@ fn format_state_change_notification(
|
|||
// time. A review submitted with no body carries no
|
||||
// `latest_comment_url`, so it lands here instead of on the comment
|
||||
// path — and its event time is well after `created_at`. Labeling
|
||||
// that "new PR" is misleading (see docs/2-handbook/integrations/forge.md, "new vs activity
|
||||
// that "new PR" is misleading (see docs/2-handbook/swarm/forge.md, "new vs activity
|
||||
// on"): agents dismiss it as a
|
||||
// duplicate of the original open notification. When we can't confirm
|
||||
// creation, fall back to a neutral "activity on" label.
|
||||
|
|
@ -757,7 +757,7 @@ fn format_state_change_notification(
|
|||
// Review-request override: Forgejo doesn't reliably set
|
||||
// `reason == "review_requested"` (often null), so we check the
|
||||
// subject payload's `requested_reviewers` list directly. See
|
||||
// `docs/2-handbook/integrations/forge.md::Review-request override`.
|
||||
// `docs/2-handbook/swarm/forge.md::Review-request override`.
|
||||
let is_review_request = is_new
|
||||
&& *is_pr
|
||||
&& !own_login.is_empty()
|
||||
|
|
@ -801,7 +801,7 @@ fn format_state_change_notification(
|
|||
/// `created_at`: within `NEW_ITEM_TOLERANCE_SECS` ⇒ creation. When
|
||||
/// either timestamp is missing or unparseable we default to `true`,
|
||||
/// preserving the prior "new" behavior rather than masking a genuine
|
||||
/// new item behind the neutral fallback. See docs/2-handbook/integrations/forge.md, "new vs
|
||||
/// new item behind the neutral fallback. See docs/2-handbook/swarm/forge.md, "new vs
|
||||
/// activity on".
|
||||
fn notification_is_creation(
|
||||
event: Option<OffsetDateTime>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue