From ab1b07acce0a6335f2c9085ade688cd1436d54cf Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 10 Jun 2026 01:40:57 +0200 Subject: [PATCH] ci(#1555): prose-ify legacy tracker tags; add lint:allow escape hatch Clean the legacy backlog so the tracker-tag lint can become a required gate (mara's warn-during-cleanup -> full-tree-enforcement path). Rewrite the ~33 real `closes/see #NNN` provenance refs in doc-comments to prose across hive-forge, hive-c0re, hive-ag3nt, hive-matrix-mcp, hive-sh4re, and add a `lint:allow` line marker to check-issue-refs.sh for genuine non-tracker `#` (a hash-digit heading-detection test input). Tree is now lint-clean; tracker-tag lint ready to promote to required. --- hive-ag3nt/src/forge_notify.rs | 16 ++++++++-------- hive-c0re/src/agent_server.rs | 2 +- hive-c0re/src/broker.rs | 4 ++-- hive-c0re/src/dashboard.rs | 4 ++-- hive-c0re/src/lifecycle.rs | 2 +- hive-c0re/src/rebuild_queue.rs | 2 +- hive-forge/src/body.rs | 2 +- hive-forge/src/client.rs | 8 ++++---- hive-forge/src/main.rs | 8 ++++---- hive-forge/src/verbs/assign.rs | 4 ++-- hive-forge/src/verbs/comments.rs | 6 +++--- hive-forge/src/verbs/diff.rs | 2 +- hive-forge/src/verbs/lint.rs | 6 +++--- hive-forge/src/verbs/list.rs | 2 +- hive-forge/src/verbs/pr_create.rs | 2 +- hive-forge/src/verbs/timeline.rs | 8 ++++---- hive-matrix-mcp/src/handlers.rs | 2 +- hive-matrix-mcp/src/lib.rs | 2 +- hive-matrix-mcp/src/paths.rs | 2 +- hive-matrix-mcp/src/timeline.rs | 2 +- hive-matrix-mcp/src/wake.rs | 2 +- hive-sh4re/src/assets.rs | 2 +- scripts/check-issue-refs.sh | 12 ++++++++++-- 23 files changed, 55 insertions(+), 47 deletions(-) diff --git a/hive-ag3nt/src/forge_notify.rs b/hive-ag3nt/src/forge_notify.rs index 607b79a3..c5e0f4b4 100644 --- a/hive-ag3nt/src/forge_notify.rs +++ b/hive-ag3nt/src/forge_notify.rs @@ -46,7 +46,7 @@ pub async fn run(socket: PathBuf) { let token_path = format!("{state_dir}/forge-token"); // Retry reading the token to handle races where hive-priv provisions the // token after the harness starts, or where a parent-container chown briefly - // makes the file unreadable (see #1304 / #1309). We wait up to + // makes the file unreadable. We wait up to // TOKEN_RETRY_MAX * TOKEN_RETRY_SECS before giving up. let token = { let mut attempts = 0u32; @@ -205,14 +205,14 @@ fn escape_md_headings(body: &str) -> String { /// Strict `CommonMark` ATX-heading detector: 1-6 leading `#`s followed /// by either a space, tab, or end-of-line. Anything tighter (`#tag`, -/// `#123`) is a non-heading line that the renderer will not promote. +/// `#9`) is a non-heading line that the renderer will not promote. fn is_atx_heading(line: &str) -> bool { let hashes = line.bytes().take_while(|&b| b == b'#').count(); if !(1..=6).contains(&hashes) { return false; } // Bare `#` / `##` / ... on its own line, or proper ATX with a - // space/tab after the run of `#`s; anything else (`#tag` / `#123`) + // space/tab after the run of `#`s; anything else (`#tag` / `#9`) // is not a heading. matches!(line.as_bytes().get(hashes), None | Some(b' ' | b'\t')) } @@ -843,11 +843,11 @@ mod tests { #[test] fn escape_md_headings_skips_non_atx_hash_lines() { - // ATX requires a space after the `#`s. Lines like `#tag`, - // `#123`, `#!/bin/bash` are NOT headings — escaping them - // would just add cosmetic noise where the renderer - // wouldn't promote the line in the first place. - let body = "#tag\n#123\n#!/bin/bash\n####### too many hashes\nbody"; + // ATX requires a space after the `#`s. Lines like `#tag`, a + // hash-then-digits run, or `#!/bin/bash` are NOT headings — + // escaping them would just add cosmetic noise where the + // renderer wouldn't promote the line in the first place. + let body = "#tag\n#123\n#!/bin/bash\n####### too many hashes\nbody"; // lint:allow: hash-digit heading test input, not a tracker tag let escaped = escape_md_headings(body); // All four leading `#` lines pass through untouched: too few // (still need space), seven `#`s (over the cap), shebang diff --git a/hive-c0re/src/agent_server.rs b/hive-c0re/src/agent_server.rs index 13ae10a1..c8842d06 100644 --- a/hive-c0re/src/agent_server.rs +++ b/hive-c0re/src/agent_server.rs @@ -841,7 +841,7 @@ pub(crate) fn handle_send( let resolved = crate::topology::resolve_recipient(agent, to); // Validate that the resolved recipient is a known local agent or the // special "operator" recipient. Without this check a typo in `to` - // silently queues a message nobody will ever read (issue #1165). + // silently queues a message nobody will ever read. // // Cross-hive messaging (`name@hive` qualified names) is not routed // through the broker — use the Matrix MCP tools for that instead. diff --git a/hive-c0re/src/broker.rs b/hive-c0re/src/broker.rs index 1ad74403..52d67e2c 100644 --- a/hive-c0re/src/broker.rs +++ b/hive-c0re/src/broker.rs @@ -347,7 +347,7 @@ impl Broker { } /// Unacknowledged messages addressed to `recipient`, newest-first. - /// Backs the dashboard's operator inbox (#1469): the operator never + /// Backs the dashboard's operator inbox: the operator never /// `recv`s over an agent socket, so messages to `"operator"` sit in /// the broker with `acked_at IS NULL` until the operator hits "mark /// all read" (which calls [`Broker::mark_all_read`]). This read @@ -1352,7 +1352,7 @@ mod tests { assert_eq!(broker.ack_turn("b").unwrap(), 5); } - /// The #1462 fix: a transient `ping` fired while no `recv` is parked + /// Transient-wake regression guard: a `ping` fired while no `recv` is parked /// must NOT be lost — it's buffered and drained by the next collect. #[test] fn transient_ping_buffered_when_no_receiver_parked() { diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index d5e20fc3..077d4b53 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -1221,8 +1221,8 @@ pub(crate) fn emit_meta_inputs_snapshot(coord: &Coordinator) { }); } -/// Unread operator-directed messages for the dashboard's Y3R C4LL inbox -/// (#1469). Returns messages addressed to `"operator"` that haven't been +/// Unread operator-directed messages for the dashboard's Y3R C4LL inbox. +/// Returns messages addressed to `"operator"` that haven't been /// acked yet (the operator clears them via the existing /// `POST /api/agent/operator/mark-all-read`). Newest-first; path-shaped /// tokens are validated so the client renders file links like the diff --git a/hive-c0re/src/lifecycle.rs b/hive-c0re/src/lifecycle.rs index 51d7af3a..011e363b 100644 --- a/hive-c0re/src/lifecycle.rs +++ b/hive-c0re/src/lifecycle.rs @@ -1145,7 +1145,7 @@ async fn set_nspawn_flags( // Make /shared writable by every agent. Containers share host uids (no // PrivateUsers), but each agent is a distinct unix user, so a root-owned // 0755 dir leaves them unable to write — the documented "read/write for - // all agents" contract was broken (#1374). A setgid group would need a + // all agents" contract was broken. A setgid group would need a // pinned GID declared in every container plus all agent users joined to // it (cross-container coordination + a rebuild cascade); instead we use // the /tmp model — sticky world-writable (1777). The sticky bit lets any diff --git a/hive-c0re/src/rebuild_queue.rs b/hive-c0re/src/rebuild_queue.rs index 6d8d6735..394d515f 100644 --- a/hive-c0re/src/rebuild_queue.rs +++ b/hive-c0re/src/rebuild_queue.rs @@ -1224,7 +1224,7 @@ mod tests { kind: QueueKind::Rebuild, agent: "agent-a".to_owned(), source: QueueSource::Approval, - reason: "approval #42 apply commit".to_owned(), + reason: "approval 42 apply commit".to_owned(), parent_id: None, inputs: Vec::new(), approval_id: Some(42), diff --git a/hive-forge/src/body.rs b/hive-forge/src/body.rs index 0025c572..05e39c88 100644 --- a/hive-forge/src/body.rs +++ b/hive-forge/src/body.rs @@ -1,5 +1,5 @@ //! Body-input resolution shared by every verb that posts a body. -//! Matches the bash `resolve_body` helper (#382): exactly one source +//! Matches the bash `resolve_body` helper: exactly one source //! between `--body`, `--body-file`, and piped stdin. Passing both //! `--body` and `--body-file` is a clear error. diff --git a/hive-forge/src/client.rs b/hive-forge/src/client.rs index 3308fc9e..ace4a3b3 100644 --- a/hive-forge/src/client.rs +++ b/hive-forge/src/client.rs @@ -32,7 +32,7 @@ pub struct Client { pub default_repo: String, /// Global `--json` flag — verbs that have a human-readable /// default path branch on `client.json_mode()` to pick the - /// JSON output shape instead. Closes #421. + /// JSON output shape instead. json_mode: bool, } @@ -131,7 +131,7 @@ impl Client { /// params (`?limit=N&state=open&...`) are preserved. Pages drain /// while the response carries a `Link: rel="next"` header, up to /// `max_pages` (the runaway-loop safety cap). Returns the merged - /// array. Used by `lint` for repo-wide queries (closes #505). + /// array. Used by `lint` for repo-wide queries. pub fn get_json_all(&self, path: &str, max_pages: u32) -> Result> { let sep = if path.contains('?') { '&' } else { '?' }; let mut merged = Vec::new(); @@ -271,8 +271,8 @@ fn read_token() -> Result { } /// Surface non-2xx HTTP responses as anyhow errors with the response -/// body included (matches `curl --fail-with-body`). Closes #353's -/// "silent failures with no clue what went wrong" case. +/// body included (matches `curl --fail-with-body`) — turns +/// silent failures into errors with a clear message. fn check_status(resp: Response, op: &str) -> Result { let status = resp.status(); if status.is_success() { diff --git a/hive-forge/src/main.rs b/hive-forge/src/main.rs index d086a556..4f99bfb1 100644 --- a/hive-forge/src/main.rs +++ b/hive-forge/src/main.rs @@ -8,7 +8,7 @@ //! Single binary with verb subcommands. Replaces the prior bash //! script (`hive-forge-tools.nix`) so that agents and operators get //! the same error handling, exit codes, and JSON shapes regardless -//! of how the bash mood was that day (closes #280). +//! of how the bash mood was that day. #![warn(missing_docs)] // Clap-derived `Args` structs are intentionally consumed by their @@ -26,7 +26,7 @@ use clap::{Parser, Subcommand}; #[derive(Parser)] #[command( name = "hive-forge", - about = "Forgejo CLI wrapper for hyperhive (closes #280)", + about = "Forgejo CLI wrapper for hyperhive", disable_help_subcommand = true )] struct Cli { @@ -36,7 +36,7 @@ struct Cli { #[arg(short = 'r', long, global = true)] repo: Option, /// Emit JSON output instead of the verb's default human-readable - /// shape, for verbs that support both (closes #421). Verbs whose + /// shape, for verbs that support both. Verbs whose /// only output is already JSON (`issue`, `pr`, etc.) ignore this /// flag — they always print JSON regardless. #[arg(long, global = true)] @@ -100,7 +100,7 @@ enum Verb { Subscription(verbs::subscription::Args), /// List timeline events on an issue or PR (closes, label adds, /// assignments, commit refs, pushes, etc.) — the audit trail - /// `view` + `comments` don't surface (closes #783). + /// `view` + `comments` don't surface. Timeline(verbs::timeline::Args), /// Upload a file as an attachment to an issue. AttachIssue(verbs::attach::IssueArgs), diff --git a/hive-forge/src/verbs/assign.rs b/hive-forge/src/verbs/assign.rs index 5c9ed713..d257e239 100644 --- a/hive-forge/src/verbs/assign.rs +++ b/hive-forge/src/verbs/assign.rs @@ -1,7 +1,7 @@ //! `assign [--remove]` — add or remove a user from an //! issue/PR's assignee list. Forgejo has no dedicated POST endpoint — -//! we read the current list, mutate, and PATCH the issue back (closes -//! #353's "no such endpoint" trap; matches the bash helper's logic). +//! we read the current list, mutate, and PATCH the issue back (Forgejo +//! has no such endpoint; matches the bash helper's logic). use anyhow::Result; use clap::Args as ClapArgs; diff --git a/hive-forge/src/verbs/comments.rs b/hive-forge/src/verbs/comments.rs index 026bbc1a..6c5bcb6a 100644 --- a/hive-forge/src/verbs/comments.rs +++ b/hive-forge/src/verbs/comments.rs @@ -1,6 +1,6 @@ //! `comments [--limit N | --tail N]` — list comments on an -//! issue or PR. Closes the curl-fallback gap (#418); `--tail` -//! closes the third of the four #694 gaps (paging-for-long-threads +//! issue or PR. Replaces the curl fallback; `--tail` +//! handles the paging-for-long-threads //! awkwardness). //! //! - `--limit N` (default 50, Forgejo's cap) returns the first N @@ -14,7 +14,7 @@ //! on this long thread?" without scrolling through the whole //! history. //! -//! Use the global `--json` flag for JSON output (#421). +//! Use the global `--json` flag for JSON output. use anyhow::Result; use clap::Args as ClapArgs; diff --git a/hive-forge/src/verbs/diff.rs b/hive-forge/src/verbs/diff.rs index a0eb6e82..57f27bbf 100644 --- a/hive-forge/src/verbs/diff.rs +++ b/hive-forge/src/verbs/diff.rs @@ -5,7 +5,7 @@ //! `package-lock.json`, …) is collapsed to a single //! `[: contents changed (+N -M, --full for content)]` //! line so a `flake.lock` rev bump doesn't drown the human- -//! authored changes in 5 000 lines of lock churn (#222). The +//! authored changes in 5 000 lines of lock churn. The //! per-file git headers (`diff --git`, `index`, `---`, `+++`, //! and any rename / mode metadata) are suppressed alongside the //! hunks since the placeholder already carries the file path and diff --git a/hive-forge/src/verbs/lint.rs b/hive-forge/src/verbs/lint.rs index af24ffac..ff4dd516 100644 --- a/hive-forge/src/verbs/lint.rs +++ b/hive-forge/src/verbs/lint.rs @@ -1,5 +1,5 @@ //! `lint ` — issue/PR/branch lint queries for triage -//! workflows (closes #505). Replaces ad-hoc curl + jq filtering with +//! workflows. Replaces ad-hoc curl + jq filtering with //! typed commands that always emit JSON via the global `--json` //! (default is a compact one-line-per-item human shape). //! @@ -97,7 +97,7 @@ struct NoReviewerArgs { /// Reviewer login to look for (matches `@` in PR body or /// any comment). Required — defaulting to a specific name would /// bake one deployment's reviewer convention into the binary - /// (mara's nit on #507). + /// (flagged in review). #[arg(long)] reviewer: String, } @@ -177,7 +177,7 @@ fn run_no_reviewer(client: &Client, args: NoReviewerArgs) -> Result<()> { continue; } // Paginate so PRs with >50 comments don't yield false positives - // (argus nit on #507). Same 1000-comment ceiling as elsewhere. + // (flagged in review). Same 1000-comment ceiling as elsewhere. let comments = client.get_json_all( &format!("/repos/{repo}/issues/{number}/comments?limit={PAGE_LIMIT}"), MAX_PAGES, diff --git a/hive-forge/src/verbs/list.rs b/hive-forge/src/verbs/list.rs index 49a78bc9..c8764a10 100644 --- a/hive-forge/src/verbs/list.rs +++ b/hive-forge/src/verbs/list.rs @@ -5,7 +5,7 @@ //! //! Mirrors Forgejo's `GET /repos/{owner}/{repo}/issues` query-string //! filters one-for-one so the mental model carries over. Closes the -//! second of the four #694 gaps (read-side; no boundary concerns — +//! read-side curl-fallback gap (no boundary concerns — //! every agent + the operator queries the issue tracker constantly). use std::fmt::Write as _; diff --git a/hive-forge/src/verbs/pr_create.rs b/hive-forge/src/verbs/pr_create.rs index 97a2b23d..c6cd21eb 100644 --- a/hive-forge/src/verbs/pr_create.rs +++ b/hive-forge/src/verbs/pr_create.rs @@ -8,7 +8,7 @@ //! hint block is filtered out of git's stderr (we print the canonical //! URL ourselves once the API returns). Other git stderr passes //! through. Default behaviour is unchanged: no push unless asked. -//! Closes the auto-push half of #222 per operator decision (opt-in +//! Adds the auto-push path per operator decision (opt-in //! flag). //! //! With `--agit` the PR is opened via Forgejo's `AGit` flow instead of diff --git a/hive-forge/src/verbs/timeline.rs b/hive-forge/src/verbs/timeline.rs index 8677cb8d..bd662e72 100644 --- a/hive-forge/src/verbs/timeline.rs +++ b/hive-forge/src/verbs/timeline.rs @@ -1,7 +1,7 @@ //! `timeline [--limit N]` — list timeline events on an -//! issue or PR. Closes #783 (last piece of the #694 epic: agents kept +//! issue or PR. Fills the gap where agents kept //! falling back to curl for "who closed this?" / "when was this -//! labelled?" archaeology). Composes naturally with `view ` / +//! labelled?" archaeology. Composes naturally with `view ` / //! `comments ` — separate verb keeps the existing shapes stable. //! //! Forgejo's `/issues/{n}/timeline` endpoint returns BOTH the actual @@ -12,7 +12,7 @@ //! //! `--tail N` is a follow-up (the timeline endpoint doesn't expose a //! total-count field so we can't use the count-then-page trick that -//! `comments --tail` lands in #770; future shape probably mirrors +//! `comments --tail` uses; future shape probably mirrors //! `comments --tail` once Forgejo grows a `count` query or we accept //! the trailing-slice cost). @@ -211,7 +211,7 @@ mod tests { //! Tests call `format_event` directly so any new event-type arm //! added in `print_event`'s dispatch is automatically covered by //! the rendering path (no parallel test-side dispatch to keep in - //! sync). Argus on PR #798 🟡: "extract a `format_event(ev) -> + //! sync). A review flagged: "extract a `format_event(ev) -> //! String` helper and test that function directly instead of //! duplicating the logic" — addressed. use super::*; diff --git a/hive-matrix-mcp/src/handlers.rs b/hive-matrix-mcp/src/handlers.rs index 1be0d4a2..23125db7 100644 --- a/hive-matrix-mcp/src/handlers.rs +++ b/hive-matrix-mcp/src/handlers.rs @@ -4,7 +4,7 @@ //! the payload to claude; `Error { message }` becomes the tool-call //! error message claude sees). //! -//! Tool surface mirrors damocles-daemon's v0 set per mara on #548: +//! Tool surface mirrors damocles-daemon's v0 set per the operator's call: //! `send_message`, `send_dm`, `send_reaction`, `send_reply`, `mark_read`, //! `list_rooms`, `list_room_members`, `read_room`. Plus a `ping` for the //! MCP bridge's liveness probe. diff --git a/hive-matrix-mcp/src/lib.rs b/hive-matrix-mcp/src/lib.rs index 8db22f9a..ae57bcd6 100644 --- a/hive-matrix-mcp/src/lib.rs +++ b/hive-matrix-mcp/src/lib.rs @@ -5,7 +5,7 @@ //! The wire protocol between the two binaries lives in [`protocol`]. //! Path helpers (token file, daemon socket) live in [`paths`]. //! -//! Phase 3 of #548. Architecture rationale + tool surface mirror the +//! Phase 3 of the matrix-MCP work. Architecture rationale + tool surface mirror the //! existing `damocles-daemon` (see issue thread for details). pub mod client; diff --git a/hive-matrix-mcp/src/paths.rs b/hive-matrix-mcp/src/paths.rs index 9838aff5..92918dd9 100644 --- a/hive-matrix-mcp/src/paths.rs +++ b/hive-matrix-mcp/src/paths.rs @@ -18,7 +18,7 @@ pub const DEFAULT_HOMESERVER: &str = "http://localhost:8008"; /// Lives under systemd's `RuntimeDirectory=hive-matrix` (a tmpfs path /// that disappears on container restart — fine, because the daemon /// recreates the socket on its own boot) so the agent unix user -/// (post-#658) can bind a socket inside it without root in `/run`. +/// can bind a socket inside it without root in `/run`. pub const DEFAULT_DAEMON_SOCKET: &str = "/run/hive-matrix/socket"; /// Resolve the matrix access-token file path. Override via diff --git a/hive-matrix-mcp/src/timeline.rs b/hive-matrix-mcp/src/timeline.rs index fcb7e425..4a81669f 100644 --- a/hive-matrix-mcp/src/timeline.rs +++ b/hive-matrix-mcp/src/timeline.rs @@ -1,7 +1,7 @@ //! Matrix event handlers: incoming room messages fire a hyperhive //! wake signal so the agent's harness drives a new claude turn. //! -//! Per mara on #548: wake body is a SHORT TEASER, not the full message +//! Per the operator's call: wake body is a SHORT TEASER, not the full message //! (msg stays unread server-side; agent fetches via `read_room`). The //! `wake::format_wake_body` truncates to ~100 chars. //! diff --git a/hive-matrix-mcp/src/wake.rs b/hive-matrix-mcp/src/wake.rs index 2fef492e..48d4ed53 100644 --- a/hive-matrix-mcp/src/wake.rs +++ b/hive-matrix-mcp/src/wake.rs @@ -7,7 +7,7 @@ //! The agent harness's `agent_server` parses it and treats it as a //! `Wake` from the matrix subsystem. //! -//! Per mara's call on #548 phase 3: the body is a SHORT TEASER, not +//! Per the operator's call (phase 3): the body is a SHORT TEASER, not //! the full message — the agent then reads the unmarked event via //! the `read_room` MCP tool. Truncation to ~100 chars keeps the wake //! prompt focused (`forge_notify` embeds longer excerpts because the diff --git a/hive-sh4re/src/assets.rs b/hive-sh4re/src/assets.rs index b5fcc0e1..ca6e239f 100644 --- a/hive-sh4re/src/assets.rs +++ b/hive-sh4re/src/assets.rs @@ -77,7 +77,7 @@ pub fn core_avatar_png() -> PathBuf { /// `$HIVE_ASSETS_DIR/branding/agent-configs.png` — secondary org /// mark for the `agent-configs/` mirror org. Rendered from /// `agent-configs.svg` at asset-build time (was rendered in -/// `hive-c0re/build.rs` before #555). +/// `hive-c0re/build.rs`). #[must_use] pub fn config_org_avatar_png() -> PathBuf { dir().join("branding/agent-configs.png") diff --git a/scripts/check-issue-refs.sh b/scripts/check-issue-refs.sh index 5cec3d3e..8053bed2 100755 --- a/scripts/check-issue-refs.sh +++ b/scripts/check-issue-refs.sh @@ -19,16 +19,24 @@ # overruns) and digit-runs followed by a letter — e.g. hash-route # fragments like #24h. Residual: a pure-numeric short hex (e.g. three # identical digits) trips it — write the six-digit form to dodge. +# +# Escape hatch: a line containing the marker `lint:allow` is exempt. +# Reserve it for genuine `#` that aren't tracker tags — e.g. a +# `#123` markdown-heading example or hash-prefixed test-input data — +# and keep a short reason next to the marker. Don't use it to keep a +# real tracker tag; rewrite those to prose. set -eu pattern='#[0-9]{2,5}([^0-9a-zA-Z]|$)' # `/dev/null` forces grep to always print a filename prefix, even when # xargs hands it a single file. `-r`/`-0` keep it robust to odd paths -# and an empty file list. +# and an empty file list. Lines carrying the `lint:allow` marker are +# dropped (legitimate non-tracker `#`; see the header). hits="$( git ls-files -z '*.rs' '*.nix' '*.js' '*.ts' '*.css' '*.html' \ - | xargs -0 -r grep -nE "$pattern" /dev/null 2>/dev/null || true + | xargs -0 -r grep -nE "$pattern" /dev/null 2>/dev/null \ + | grep -v 'lint:allow' || true )" if [ -n "$hits" ]; then