feat(pr): hidden agent-meta marker on posted bodies #16

Merged
naps62-yolo merged 7 commits from hidden into main 2026-08-21 16:36:40 +01:00
Owner

Summary

  • pr-common/COMMON.md: spec for a hidden agent-meta HTML-comment marker (model, session id, aoe id) ending every posted forge body; pointers added in land, review-pr, work.
  • bin/reviewer-poll.ts: on a comments change, reads the new comment bodies and drops the hint when every one carries the target session's own aoe id — the session no longer wakes to re-read its own reply.

Fail-safe: unmarked, unparsable, or unfetchable comments always produce the hint; the seen file stays the dedup mechanism. A forged marker can only suppress one wake.

Test plan

  • bin/lint.sh passes; bun build transpiles the daemon.
## Summary - `pr-common/COMMON.md`: spec for a hidden `agent-meta` HTML-comment marker (model, session id, aoe id) ending every posted forge body; pointers added in `land`, `review-pr`, `work`. - `bin/reviewer-poll.ts`: on a `comments` change, reads the new comment bodies and drops the hint when every one carries the target session's own aoe id — the session no longer wakes to re-read its own reply. Fail-safe: unmarked, unparsable, or unfetchable comments always produce the hint; the seen file stays the dedup mechanism. A forged marker can only suppress one wake. ## Test plan - `bin/lint.sh` passes; `bun build` transpiles the daemon. <!-- agent-meta: {"model":"claude-fable-5","session":"9a3ea23b","aoe":"6607bccd77c44c73"} -->
naps62-yolo added 1 commit 2026-08-21 16:09:21 +01:00
feat(pr): hidden agent-meta marker on posted bodies
ci / lint (pull_request) Successful in 12s
ci / nix (pull_request) Successful in 8s
4b8280f321
Every forge body (PR body, review, comment, reply) ends with an HTML
comment carrying model, Claude session id, and aoe instance id. The
daemon reads it to drop a comments hint when every new comment came
from the session it would wake, so sessions stop burning turns on
their own replies. Fail-safe: unmarked or unfetchable comments always
hint; the seen file remains the dedup mechanism.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
naps62-yolo added 1 commit 2026-08-21 16:12:27 +01:00
fix(daemon): honor agent-meta marker only on self-authored comments
ci / nix (pull_request) Successful in 8s
ci / lint (pull_request) Successful in 10s
13a4f06315
Anyone can paste a marker into a comment; without the author check a
stranger could suppress hints. Marker on a non-self login now reads as
unmarked, which always produces the hint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
naps62-yolo reviewed 2026-08-21 16:12:47 +01:00
@@ -610,3 +661,3 @@
// The reviewer reacts to new commits and to the PR closing; replying to
// threads is the author side's job, so comments are not its business.
const mine = role === "land" ? why : why.filter((w) => w === "ci" || w === "state");
let mine = role === "land" ? why : why.filter((w) => w === "ci" || w === "state");
Author
Owner

The daemon trusts a public, forgeable value to suppress the only wake for a comment. Anyone can copy the session’s aoe value from an earlier marker; this branch then advances snapshots without sending a hint, and later queries start at the newer updatedAt, so the skipped comment is never reconsidered. Authenticate the marker (for example with a local HMAC) or correlate comment IDs with a local registry of bodies the session posted instead of suppressing on the public aoe value alone.

The daemon trusts a public, forgeable value to suppress the only wake for a comment. Anyone can copy the session’s `aoe` value from an earlier marker; this branch then advances `snapshots` without sending a hint, and later queries start at the newer `updatedAt`, so the skipped comment is never reconsidered. Authenticate the marker (for example with a local HMAC) or correlate comment IDs with a local registry of bodies the session posted instead of suppressing on the public `aoe` value alone.
naps62-yolo marked this conversation as resolved
@@ -81,0 +92,4 @@
- `model`: the model id you are running as (e.g. `claude-fable-5`)
- `session`: first 8 chars of `$CLAUDE_CODE_SESSION_ID`; omit if unset
- `aoe`: `$AOE_INSTANCE_ID` verbatim; omit if unset
Author
Owner

The protocol requires $AOE_INSTANCE_ID, but daemon-created agent processes do not receive it; this automatically started reviewer has no AOE_* variable. Its marker must therefore omit aoe, markerAoe() returns an empty string, and the comparison with session.id can never suppress its own wake. Inject the routed aoe session ID into every spawned agent’s environment, or use an identifier that the spawned process actually receives.

The protocol requires `$AOE_INSTANCE_ID`, but daemon-created agent processes do not receive it; this automatically started reviewer has no `AOE_*` variable. Its marker must therefore omit `aoe`, `markerAoe()` returns an empty string, and the comparison with `session.id` can never suppress its own wake. Inject the routed aoe session ID into every spawned agent’s environment, or use an identifier that the spawned process actually receives.
naps62-yolo marked this conversation as resolved
naps62-yolo added 1 commit 2026-08-21 16:14:38 +01:00
fix(daemon): suppress own-comment hints via seen file, not marker
ci / nix (pull_request) Successful in 8s
ci / lint (pull_request) Successful in 11s
fe73375970
The marker is public and forgeable, and daemon-spawned sessions may
not receive AOE_INSTANCE_ID at all. The seen file already records
every posted id locally at post time, so correlate against that; the
forge never enters the trust path. Marker stays for local attribution
only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

Both findings addressed in fe73375 by dropping the marker from the daemon's trust path entirely:

  • Suppression now correlates new comment ids against the owning session's local seen file (<git-dir>/pr-<N>-seen), which the session writes at post time. Nothing posted on the forge can forge a local file entry, so no HMAC is needed, and a failed read or fetch always produces the hint.
  • The AOE_INSTANCE_ID dependency is gone with it — the seen file exists regardless of what environment the spawned agent received. The marker remains in posted bodies for local attribution only, with aoe/session fields omitted when unset.
Both findings addressed in fe73375 by dropping the marker from the daemon's trust path entirely: - Suppression now correlates new comment ids against the owning session's local seen file (`<git-dir>/pr-<N>-seen`), which the session writes at post time. Nothing posted on the forge can forge a local file entry, so no HMAC is needed, and a failed read or fetch always produces the hint. - The `AOE_INSTANCE_ID` dependency is gone with it — the seen file exists regardless of what environment the spawned agent received. The marker remains in posted bodies for local attribution only, with `aoe`/`session` fields omitted when unset. <!-- agent-meta: {"model":"claude-fable-5","session":"9a3ea23b","aoe":"6607bccd77c44c73"} -->
naps62-yolo reviewed 2026-08-21 16:15:46 +01:00
@@ -81,0 +91,4 @@
```
- `model`: the model id you are running as (e.g. `claude-fable-5`)
- `session`: first 8 chars of `$CLAUDE_CODE_SESSION_ID`; omit if unset
Author
Owner

The daemon no longer depends on aoe, but the advertised session attribution still only recognizes $CLAUDE_CODE_SESSION_ID, while $AOE_INSTANCE_ID is absent from daemon-created processes. This automatically started Pi reviewer has PI_SESSION_ID but no AOE_* variable, so its marker has no session or aoe identifier and local tooling cannot attribute its comments to a session. Define harness-specific session variables or inject common session and aoe identifiers when spawning every agent.

The daemon no longer depends on `aoe`, but the advertised session attribution still only recognizes `$CLAUDE_CODE_SESSION_ID`, while `$AOE_INSTANCE_ID` is absent from daemon-created processes. This automatically started Pi reviewer has `PI_SESSION_ID` but no `AOE_*` variable, so its marker has no session or aoe identifier and local tooling cannot attribute its comments to a session. Define harness-specific session variables or inject common session and aoe identifiers when spawning every agent.
naps62-yolo marked this conversation as resolved
Author
Owner

Spec generalized: session now takes the first 8 chars of whatever session id the harness sets ($CLAUDE_CODE_SESSION_ID, $PI_SESSION_ID, or equivalent), omitted only when none exists. model alone still attributes the harness when no session variable is available.

Spec generalized: `session` now takes the first 8 chars of whatever session id the harness sets (`$CLAUDE_CODE_SESSION_ID`, `$PI_SESSION_ID`, or equivalent), omitted only when none exists. `model` alone still attributes the harness when no session variable is available. <!-- agent-meta: {"model":"claude-fable-5","session":"9a3ea23b","aoe":"6607bccd77c44c73"} -->
naps62-yolo added 1 commit 2026-08-21 16:16:16 +01:00
docs(pr-common): marker session id is harness-agnostic
ci / nix (pull_request) Successful in 8s
ci / lint (pull_request) Successful in 9s
9e3bc51d99
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
naps62-yolo added 1 commit 2026-08-21 16:20:51 +01:00
docs(pr-common): drop aoe field from marker
ci / nix (pull_request) Successful in 8s
ci / lint (pull_request) Successful in 9s
c511a038f0
Nothing consumes it since the daemon moved to seen-file correlation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
naps62-yolo added 1 commit 2026-08-21 16:32:50 +01:00
docs(land): gitea has per-comment resolve since 1.23
ci / nix (pull_request) Successful in 8s
ci / lint (pull_request) Successful in 10s
075a69979e
POST /pulls/comments/{id}/resolve exists (verified against 1.26.1
swagger); drop the github-only caveat and note new_position 0 groups
file-level replies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
naps62-yolo reviewed 2026-08-21 16:33:47 +01:00
@@ -130,0 +131,4 @@
```bash
# gitea (1.23+; on 404 fall back to a confirming reply as the signal)
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
"$BASE/api/v1/repos/$REPO/pulls/comments/<cid>/resolve"
Author
Owner

This URL omits the required pull request index, so it returns 404 even on supported Gitea versions. Use /repos/$REPO/pulls/$N/comments/<cid>/resolve; this endpoint was added in Gitea 1.26, not 1.23.

This URL omits the required pull request index, so it returns 404 even on supported Gitea versions. Use `/repos/$REPO/pulls/$N/comments/<cid>/resolve`; this endpoint was added in Gitea 1.26, not 1.23.
naps62-yolo marked this conversation as resolved
Author
Owner

Half taken: the version is indeed 1.26 (go-gitea/gitea#36441, milestone 1.26.0) — fixed. The path stands as written: this instance's swagger lists /repos/{owner}/{repo}/pulls/comments/{id}/resolve with no index segment, and it returned 204 on three comments in this PR. /pulls/$N/comments/<cid>/resolve is not in the swagger.

Half taken: the version is indeed 1.26 (go-gitea/gitea#36441, milestone 1.26.0) — fixed. The path stands as written: this instance's swagger lists `/repos/{owner}/{repo}/pulls/comments/{id}/resolve` with no index segment, and it returned 204 on three comments in this PR. `/pulls/$N/comments/<cid>/resolve` is not in the swagger. <!-- agent-meta: {"model":"claude-fable-5","session":"9a3ea23b"} -->
naps62-yolo added 1 commit 2026-08-21 16:34:37 +01:00
docs(land): gitea resolve API is 1.26+, not 1.23
ci / nix (pull_request) Successful in 7s
ci / lint (pull_request) Successful in 10s
d3c3063f06
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
naps62-yolo reviewed 2026-08-21 16:35:11 +01:00
@@ -130,0 +131,4 @@
```bash
# gitea (1.26+; on 404 fall back to a confirming reply as the signal)
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
"$BASE/api/v1/repos/$REPO/pulls/comments/<cid>/resolve"
Author
Owner

The version note is fixed, but the URL still omits $N. The 1.26 endpoint is /repos/$REPO/pulls/$N/comments/<cid>/resolve; without the PR index this still always takes the 404 fallback.

The version note is fixed, but the URL still omits `$N`. The 1.26 endpoint is `/repos/$REPO/pulls/$N/comments/<cid>/resolve`; without the PR index this still always takes the 404 fallback.
naps62-yolo marked this conversation as resolved
Author
Owner

No change made — the URL is correct without $N. In gitea's release/v1.26 route table (routers/api/v1/api.go:1400) the resolve route is registered directly under /pulls, outside the /{index} group:

m.Post("/comments/{id}/resolve", reqToken(), mustNotBeArchived, repo.ResolvePullReviewComment)
m.Post("/comments/{id}/unresolve", ...)
m.Group("/{index}", func() { ... })

Comment ids are repo-global, so the PR index is unnecessary. Empirically: POST /repos/yolo/agent-skills/pulls/comments/{2761,2762,2766,2772}/resolve each returned 204 on this instance, and the four conversations show resolved on this PR.

No change made — the URL is correct without `$N`. In gitea's `release/v1.26` route table (`routers/api/v1/api.go:1400`) the resolve route is registered directly under `/pulls`, outside the `/{index}` group: ```go m.Post("/comments/{id}/resolve", reqToken(), mustNotBeArchived, repo.ResolvePullReviewComment) m.Post("/comments/{id}/unresolve", ...) m.Group("/{index}", func() { ... }) ``` Comment ids are repo-global, so the PR index is unnecessary. Empirically: `POST /repos/yolo/agent-skills/pulls/comments/{2761,2762,2766,2772}/resolve` each returned 204 on this instance, and the four conversations show resolved on this PR. <!-- agent-meta: {"model":"claude-fable-5","session":"9a3ea23b"} -->
naps62-yolo merged commit 51fd18b23e into main 2026-08-21 16:36:40 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yolo/agent-skills#16