From 93bb3a294fdb89435aab7fedb5cd08bd9c12b97f Mon Sep 17 00:00:00 2001 From: iris Date: Sat, 15 Aug 2026 11:46:51 +0200 Subject: [PATCH] docs(forge): reframe changelog-style asides as present-tense gotchas --- docs/forge.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/forge.md b/docs/forge.md index 6eaacbd7..0aa0f90c 100644 --- a/docs/forge.md +++ b/docs/forge.md @@ -51,9 +51,9 @@ read it without touching c0re's host-side credential store. Two things live in the `agent-configs` Forgejo organization: -- A config repo per agent (`agent-configs/`). As of #1787 the +- A config repo per agent (`agent-configs/`). The agent is a **write collaborator on its own** repo — it can push - config-change branches and (once #1838 P2 lands) open config PRs — but + config-change branches and (once config-PR support lands) open config PRs — 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 the agent can neither push `main` directly nor self-merge. `main` is @@ -118,19 +118,20 @@ 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**. (This -replaced an earlier design that left threads unread and leaned on a -persisted dedup cursor: a rebuild that lost the cursor re-delivered the -entire still-unread backlog as fresh wakes — the notification flood of -#2593 / #2106.) +what's been delivered; there is **no persisted cursor**. -**Read-before-comment coupling, dropped on purpose.** The old design -left threads unread so the hive-forge read-before-comment guard (which -keys off forge unread-state) would force the agent to view a thread -before commenting. That coupling is gone: the broker wake already -carries the notification body, so *delivery is the read*. An agent that -wants the full thread still runs `hive-forge comments` / `view`; the -guard no longer blocks a first comment on a freshly-delivered thread. +**Gotcha:** don't reintroduce a persisted dedup cursor here. A design +that leaves threads unread and tracks delivery via a separately-persisted +cursor is fragile — losing that cursor across a rebuild re-delivers the +agent's entire still-unread backlog as a flood of fresh wakes. Forge's +own read-state is the only durable record this design needs. + +**Read-before-comment guard doesn't block a fresh wake.** hive-forge's +read-before-comment guard (which keys off forge unread-state) does not +force the agent to view a thread before commenting on it: the broker +wake already carries the notification body, so *delivery is the read*. +An agent that wants the full thread still runs `hive-forge comments` / +`view`. **In-process dedupe (tiny, ephemeral).** A single-process map (thread id → last-delivered `updated_at`) guards the narrow window where a @@ -244,8 +245,8 @@ A notification carrying a `latest_comment_url` normally takes the comment path. But a merged/closed subject **keeps** its `latest_comment_url` set, so a just-merged PR that had any prior discussion would route to the comment path and render `[comment on PR]` (with a stale pre-merge comment -body) instead of `[PR merged]` — the agent never learns its PR merged -(#2495). So when the notification IS the merge/close transition — its +body) instead of `[PR merged]` — the agent never learns its PR merged. +So when the notification IS the merge/close transition — its event time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` of the subject's `closed_at` (set for both `merged` and `closed`) — the state-change path wins even with a comment url present @@ -280,7 +281,7 @@ A review submitted with **no body** carries no `latest_comment_url`, so it misses the comment path and lands on the state-change path with `state == "open"` — exactly like a freshly opened PR. Labeling that `new PR` is misleading: agents dismiss it as a duplicate of the -original open notification and miss the review (#1637). So the `open` +original open notification and miss the review. So the `open` state only earns the `new ` 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 `