HOMELAB-2659: fix(forgejo): dangerous class needs two independent review sessions #88

Merged
pi-landing merged 2 commits from plane/HOMELAB-2659-merge-panel into main 2026-11-04 07:15:51 +00:00
Owner

The ruling (binding)

Overseer ruling 2026-11-04T05:10Z (ld-hypervisor), replacing the earlier per-environment design:

  • forgejo_merge_pr stops requiring a different declared model and stops requiring a premium model.
  • Routine (non-dangerous) merges: one exact-head verdict is sufficient (unchanged).
  • Dangerous class: two approvals from two INDEPENDENT REVIEW SESSIONS. The same session approving twice must fail.
  • Every other leg stays exactly as it is: green CI on the exact head, exact head pin, landing identity, the formal verdict as the durable record, open holds refuse, open blocker findings refuse, formal review state is never evidence, no override parameter.
  • FORGEJO_DANGEROUS_PATHS is not emptied and the dangerous-class classification is not weakened.

Why: the sanctioned method is one DeepSeek seat per review (omniroute/deepseek-flash). It cannot satisfy a premium demand, so the dangerous class could not land at all. Model diversity was only ever a proxy for "a second, independent pass"; the review session is the real thing, and it is now what the lock records.

The exemption, resolved explicitly and recorded

The ruling also protects two flows — "Jesús's flow and sbx-sendrast must not change, so keep them on their current policy". That exemption is OWNER-SCOPED, not a guessed list of repo names:

  • LandingPolicy.legacyPanelOwners: string[], default ["jesus", "Sendrast"]
  • overridable with FORGEJO_LEGACY_PANEL_OWNERS (comma-separated), beside the existing FORGEJO_DANGEROUS_PATHS / FORGEJO_PREMIUM_MODELS / FORGEJO_LANDING_REVIEWERS overrides in resolveLandingPolicy()
  • owner derived from repoRef by its last two path segments, case-insensitive, so owner/repo and https://forgejo…/owner/repo agree
  • a legacy-owner repo keeps the current rule unchanged in effect (two declared models, one premium) and its refusals say legacy owner-scoped panel

Rationale, in the code comment and in docs/forgejo.md: the ruling names the two flows; a live Forgejo read-back names their owners (jesus: infra-core, qs_project, project_dino, hermes-automation, Transponder, dyno — Sendrast: sendrast-web, sendrast-app, api-sendrast, deploy, sendrast-firmware, firmware-lilygo, canary-flow); an owner scope covers their current and future repos without guessing individual repo names; and the default is a declaration, not an inference.

What changed

File Why
forgejo/landing.ts VerdictRecord.session + SESSION_DECL_RE/SESSION_ID_RE (read anywhere in the body, like model:, ≥6 chars so a placeholder can never parse); repoOwner() / isLegacyPanelRepo(); sessionPanel(); leg 3 split into the new rule and the untouched legacy rule; success/refusal/collapse notes follow the governing rule
forgejo/verdict.ts ComposeRequest/Result.session, sessionLine(), standalone session: lifted/refused like tier:, session added to the read-back prefix proof
forgejo/verdict-cli.ts optional --session <id>; documented in usage/--help and the file header; no default, no env fallback; every existing guard (identity assertion, one-POST discipline, read-back) untouched
forgejo/index.ts the forgejo_merge_pr contract sentence every orchestrator reads now states the new rule (census formatting untouched)
docs/forgejo.md leg 4 rewritten (sessions + exemption + why), override table gains FORGEJO_LEGACY_PANEL_OWNERS, grammar + helper docs carry session:
forgejo/tests/landing-sessions.test.ts (new) 26 cases: both policies, negative cases, helper flag, tool-contract rail
forgejo/tests/{landing-record,landing-model-supersede,landing-corpus,landing-blockers,landing-pr70-reviews,verdict-694-replay,fake}.test/ts old-rule assertions re-pointed, never deleted (details below)

The load-bearing detail: sessions are counted over ledger.exactHead

Every fleet reviewer posts as ld-reviewer with the same model: and no lens:, so two genuinely independent sessions share one reviewerKey() and ledger.current collapses them to the newest — a panel of one. Counting over the reduced ledger would see one session exactly where two reviewed, so the rule could never pass. sessionPanel() therefore counts the latest non-hold trusted verdict per session over the unreduced exact-head stream, and an undeclared session forms a single (unstated) group that is reported and refused, never merged into a real session.

session is deliberately NOT added to reviewerKey(): hold-withdrawal and supersession (HOMELAB-2411, HOMELAB-2522) must stay byte-identical, and both are asserted unchanged. This is stated in a code comment so the next reader does not "fix" it.

Red → green evidence

Red first, on origin/main (346ff343) with no landing.ts change — full log at /home/aaron/repos/homelab/working-files/homelab-2518/HOMELAB-2659-red.log:

Test Files  1 failed (1)
     Tests  18 failed | 7 passed (25)

Real assertion failures (quoted from that run, not paraphrased):

  • permits two sessions on the SAME model with no premium seat → expected 'Refusing to merge Homelab/pi-extensions#79 …' to be undefined, because the old refusal read 1 declared model(s) (omniroute/deepseek-flash) — dangerous class needs two reviewers on different models and no exact-head verdict came from a premium model
  • refuses the same session approving twice → expected 'Refusing to merge …' to contain 'two independent sessions'
  • refuses a single approval on a dangerous PR → expected … to contain 'two independent sessions'
  • refuses when a panel verdict declares no session, and names the comment → expected … to contain 'do not declare \session: `'`
  • defaults legacyPanelOwners to the two resolved owners → expected undefined to deeply equal [ 'jesus', 'Sendrast' ]
  • carries --session as a \session:` line in the composed grammar block→expected undefined to be 'sess-helper-0001'`
  • documents --session in its usage text → usage text had no --session

Green after implementation — full log at …/HOMELAB-2659-green.log:

$ cd forgejo && npx tsc --noEmit     → clean
$ npx vitest run
 Test Files  18 passed (18)
      Tests  540 passed (540)

(origin/main baseline before this branch was 17 files / 511 tests; the new file adds 26 and two suites gained cases.)

The two policies, as they now read

Dangerous class, new rule (any repo whose owner is not in legacyPanelOwners):

  review panel policy: two independent review sessions — owner "homelab" of Homelab/pi-extensions is not in legacyPanelOwners (jesus, Sendrast)
  1 independent review session(s) (sess-only-one) — dangerous class needs two approvals from two independent sessions; the same session approving twice is ONE session

Nothing on this path mentions premium or different models. A shared model is reported as information only: dangerous class: two independent sessions on the head (#1 session=… model=omniroute/deepseek-flash, #2 session=… model=omniroute/deepseek-flash, both on omniroute/deepseek-flash).

Dangerous class, legacy owner panel (Sendrast/sendrast-web):

  review panel policy: legacy owner-scoped panel — owner "sendrast" of Sendrast/sendrast-web is in legacyPanelOwners (jesus, Sendrast), so this owner keeps the two-declared-models/one-premium rule; every other repo uses two independent review sessions
  1 declared model(s) (omniroute/deepseek-flash) — dangerous class needs two reviewers on different models
  no exact-head verdict came from a premium model (premium: kimi-pool, kimi-k3, kimi-k2.7-code, kimi-for-coding, premium)

session: is not required on that path.

Test coverage (both policies keep real coverage — no rail deleted)

  • New file landing-sessions.test.ts (26): same-model two sessions → PERMIT; same session twice → REFUSE; one approval → REFUSE; missing session: → REFUSE naming the comment; missing model: → REFUSE; different models, no premium → PERMIT; a session whose latest stance is a concern still counts; routine PR, one approval → PERMIT; legacy owner same-model → REFUSE; legacy two models + premium → PERMIT; legacy does not need session:; legacy same-session-twice → REFUSE; case-insensitive + URL-shaped repoRef; resolveLandingPolicy() default and FORGEJO_LEGACY_PANEL_OWNERS (including empty-override fallback and an override that moves Homelab into legacy and Sendrast out); session is not a reviewer-key axis; a different session cannot withdraw another key's hold; same-key withdrawal still works; untrusted identity still not evidence; red CI still refuses; --session lands in the composed body and nowhere else changes; placeholder session refused; the forgejo_merge_pr description states the new rule and no longer states the old one.
  • Re-pointing, stated plainly. These suites assert rules the ruling changed or records that predate it, and each is re-pointed with an in-file comment saying why — none is deleted:
    • landing-model-supersede.test.ts: the supersession/premium rails now run on the legacy owner Sendrast/sendrast-web (that is where a model rail still means something), plus a new describe replaying the same #115 stream under the new rule with sessions — proving supersession is unchanged.
    • landing-record.test.ts: the old describe is renamed legacy owner panel: two declared models, one premium and runs on Sendrast/sendrast-web; the tool-level rails that must still land on a dangerous Homelab PR now declare their sessions; re-using one review id cannot mint a second approval is rewritten to the replacement rule (a re-used review id cannot mint a second session either) — this is the one assertion of a rule that no longer exists.
    • landing-pr70-reviews.test.ts and verdict-694-replay.test.ts: real corpora recorded before the ruling (2026-10-28 / 2026-10-30) cannot contain a session:, so they replay under the legacy owner policy through the same FORGEJO_LEGACY_PANEL_OWNERS override the operator has; their subjects are the blocker/recognition rails, not panel policy.
    • landing-corpus.test.ts, landing-blockers.test.ts, fake.ts: the collapse-advice assertions move to the new wording, and the panel fixtures that stand for a complete modern panel declare their session.

Scope discipline

  • Branch plane/HOMELAB-2659-merge-panel, worktree .worktrees/HOMELAB-2659-merge-panel, one commit 48ed217, base main (346ff343).
  • No merge, no review, no verdict, no deployment performed by this worker. No existing PR comment was edited or deleted; nothing was posted on Plane; ~/.pi/agent/extensions was not touched; no token or secret was put in argv.
## The ruling (binding) Overseer ruling **2026-11-04T05:10Z (ld-hypervisor)**, replacing the earlier per-environment design: - `forgejo_merge_pr` **stops** requiring a different declared model and **stops** requiring a premium model. - Routine (non-dangerous) merges: **one** exact-head verdict is sufficient (unchanged). - Dangerous class: **two approvals from two INDEPENDENT REVIEW SESSIONS**. The same session approving twice must fail. - Every other leg stays exactly as it is: green CI on the exact head, exact head pin, landing identity, the formal verdict as the durable record, open holds refuse, open blocker findings refuse, formal review state is never evidence, no override parameter. - `FORGEJO_DANGEROUS_PATHS` is **not** emptied and the dangerous-class classification is **not** weakened. Why: the sanctioned method is one DeepSeek seat per review (`omniroute/deepseek-flash`). It cannot satisfy a premium demand, so the dangerous class could not land at all. Model diversity was only ever a proxy for "a second, independent pass"; the review session is the real thing, and it is now what the lock records. ## The exemption, resolved explicitly and recorded The ruling also protects two flows — *"Jesús's flow and sbx-sendrast must not change, so keep them on their current policy"*. That exemption is **OWNER-SCOPED**, not a guessed list of repo names: - `LandingPolicy.legacyPanelOwners: string[]`, default `["jesus", "Sendrast"]` - overridable with `FORGEJO_LEGACY_PANEL_OWNERS` (comma-separated), beside the existing `FORGEJO_DANGEROUS_PATHS` / `FORGEJO_PREMIUM_MODELS` / `FORGEJO_LANDING_REVIEWERS` overrides in `resolveLandingPolicy()` - owner derived from `repoRef` by its last two path segments, case-insensitive, so `owner/repo` and `https://forgejo…/owner/repo` agree - a legacy-owner repo keeps the **current** rule unchanged in effect (two declared models, one premium) and its refusals say `legacy owner-scoped panel` Rationale, in the code comment and in `docs/forgejo.md`: the ruling names the two flows; a live Forgejo read-back names their owners (`jesus`: infra-core, qs_project, project_dino, hermes-automation, Transponder, dyno — `Sendrast`: sendrast-web, sendrast-app, api-sendrast, deploy, sendrast-firmware, firmware-lilygo, canary-flow); an owner scope covers their current *and future* repos without guessing individual repo names; and the default is a declaration, not an inference. ## What changed | File | Why | |---|---| | `forgejo/landing.ts` | `VerdictRecord.session` + `SESSION_DECL_RE`/`SESSION_ID_RE` (read anywhere in the body, like `model:`, ≥6 chars so a placeholder can never parse); `repoOwner()` / `isLegacyPanelRepo()`; `sessionPanel()`; leg 3 split into the new rule and the untouched legacy rule; success/refusal/collapse notes follow the governing rule | | `forgejo/verdict.ts` | `ComposeRequest/Result.session`, `sessionLine()`, standalone `session:` lifted/refused like `tier:`, `session` added to the read-back prefix proof | | `forgejo/verdict-cli.ts` | optional `--session <id>`; documented in usage/`--help` and the file header; no default, no env fallback; every existing guard (identity assertion, one-POST discipline, read-back) untouched | | `forgejo/index.ts` | the `forgejo_merge_pr` contract sentence every orchestrator reads now states the new rule (census formatting untouched) | | `docs/forgejo.md` | leg 4 rewritten (sessions + exemption + why), override table gains `FORGEJO_LEGACY_PANEL_OWNERS`, grammar + helper docs carry `session:` | | `forgejo/tests/landing-sessions.test.ts` (new) | 26 cases: both policies, negative cases, helper flag, tool-contract rail | | `forgejo/tests/{landing-record,landing-model-supersede,landing-corpus,landing-blockers,landing-pr70-reviews,verdict-694-replay,fake}.test/ts` | old-rule assertions **re-pointed**, never deleted (details below) | ### The load-bearing detail: sessions are counted over `ledger.exactHead` Every fleet reviewer posts as `ld-reviewer` with the same `model:` and no `lens:`, so two genuinely independent sessions share **one** `reviewerKey()` and `ledger.current` collapses them to the newest — a panel of one. Counting over the reduced ledger would see one session exactly where two reviewed, so the rule could never pass. `sessionPanel()` therefore counts the latest non-hold trusted verdict per session over the unreduced exact-head stream, and an undeclared session forms a single `(unstated)` group that is reported and refused, never merged into a real session. `session` is deliberately **NOT** added to `reviewerKey()`: hold-withdrawal and supersession (HOMELAB-2411, HOMELAB-2522) must stay byte-identical, and both are asserted unchanged. This is stated in a code comment so the next reader does not "fix" it. ## Red → green evidence Red first, on `origin/main` (346ff343) with no `landing.ts` change — full log at `/home/aaron/repos/homelab/working-files/homelab-2518/HOMELAB-2659-red.log`: ``` Test Files 1 failed (1) Tests 18 failed | 7 passed (25) ``` Real assertion failures (quoted from that run, not paraphrased): - `permits two sessions on the SAME model with no premium seat` → `expected 'Refusing to merge Homelab/pi-extensions#79 …' to be undefined`, because the old refusal read `1 declared model(s) (omniroute/deepseek-flash) — dangerous class needs two reviewers on different models` **and** `no exact-head verdict came from a premium model` - `refuses the same session approving twice` → `expected 'Refusing to merge …' to contain 'two independent sessions'` - `refuses a single approval on a dangerous PR` → `expected … to contain 'two independent sessions'` - `refuses when a panel verdict declares no session, and names the comment` → `expected … to contain 'do not declare \`session: <id>\`'` - `defaults legacyPanelOwners to the two resolved owners` → `expected undefined to deeply equal [ 'jesus', 'Sendrast' ]` - `carries --session as a \`session:\` line in the composed grammar block` → `expected undefined to be 'sess-helper-0001'` - `documents --session in its usage text` → usage text had no `--session` Green after implementation — full log at `…/HOMELAB-2659-green.log`: ``` $ cd forgejo && npx tsc --noEmit → clean $ npx vitest run Test Files 18 passed (18) Tests 540 passed (540) ``` (`origin/main` baseline before this branch was 17 files / 511 tests; the new file adds 26 and two suites gained cases.) ## The two policies, as they now read **Dangerous class, new rule** (any repo whose owner is not in `legacyPanelOwners`): ``` review panel policy: two independent review sessions — owner "homelab" of Homelab/pi-extensions is not in legacyPanelOwners (jesus, Sendrast) 1 independent review session(s) (sess-only-one) — dangerous class needs two approvals from two independent sessions; the same session approving twice is ONE session ``` Nothing on this path mentions premium or different models. A shared model is reported as information only: `dangerous class: two independent sessions on the head (#1 session=… model=omniroute/deepseek-flash, #2 session=… model=omniroute/deepseek-flash, both on omniroute/deepseek-flash)`. **Dangerous class, legacy owner panel** (`Sendrast/sendrast-web`): ``` review panel policy: legacy owner-scoped panel — owner "sendrast" of Sendrast/sendrast-web is in legacyPanelOwners (jesus, Sendrast), so this owner keeps the two-declared-models/one-premium rule; every other repo uses two independent review sessions 1 declared model(s) (omniroute/deepseek-flash) — dangerous class needs two reviewers on different models no exact-head verdict came from a premium model (premium: kimi-pool, kimi-k3, kimi-k2.7-code, kimi-for-coding, premium) ``` `session:` is not required on that path. ## Test coverage (both policies keep real coverage — no rail deleted) - **New file `landing-sessions.test.ts`** (26): same-model two sessions → PERMIT; same session twice → REFUSE; one approval → REFUSE; missing `session:` → REFUSE naming the comment; missing `model:` → REFUSE; different models, no premium → PERMIT; a session whose latest stance is a concern still counts; routine PR, one approval → PERMIT; legacy owner same-model → REFUSE; legacy two models + premium → PERMIT; legacy does not need `session:`; legacy same-session-twice → REFUSE; case-insensitive + URL-shaped `repoRef`; `resolveLandingPolicy()` default and `FORGEJO_LEGACY_PANEL_OWNERS` (including empty-override fallback and an override that moves Homelab *into* legacy and Sendrast *out*); session is not a reviewer-key axis; a different session cannot withdraw another key's hold; same-key withdrawal still works; untrusted identity still not evidence; red CI still refuses; `--session` lands in the composed body and nowhere else changes; placeholder session refused; the `forgejo_merge_pr` description states the new rule and no longer states the old one. - **Re-pointing, stated plainly.** These suites assert rules the ruling changed or records that predate it, and each is re-pointed with an in-file comment saying why — none is deleted: - `landing-model-supersede.test.ts`: the supersession/premium rails now run on the legacy owner `Sendrast/sendrast-web` (that is where a model rail still means something), **plus** a new describe replaying the same #115 stream under the new rule with sessions — proving supersession is unchanged. - `landing-record.test.ts`: the old describe is renamed `legacy owner panel: two declared models, one premium` and runs on `Sendrast/sendrast-web`; the tool-level rails that must still land on a dangerous Homelab PR now declare their sessions; `re-using one review id cannot mint a second approval` is **rewritten to the replacement rule** (a re-used review id cannot mint a second *session* either) — this is the one assertion of a rule that no longer exists. - `landing-pr70-reviews.test.ts` and `verdict-694-replay.test.ts`: real corpora recorded **before** the ruling (2026-10-28 / 2026-10-30) cannot contain a `session:`, so they replay under the legacy owner policy through the same `FORGEJO_LEGACY_PANEL_OWNERS` override the operator has; their subjects are the blocker/recognition rails, not panel policy. - `landing-corpus.test.ts`, `landing-blockers.test.ts`, `fake.ts`: the collapse-advice assertions move to the new wording, and the panel fixtures that stand for a complete modern panel declare their session. ## Scope discipline - Branch `plane/HOMELAB-2659-merge-panel`, worktree `.worktrees/HOMELAB-2659-merge-panel`, one commit `48ed217`, base `main` (346ff343). - **No merge, no review, no verdict, no deployment performed by this worker.** No existing PR comment was edited or deleted; nothing was posted on Plane; `~/.pi/agent/extensions` was not touched; no token or secret was put in argv.
HOMELAB-2659: fix(forgejo): dangerous class needs two independent review sessions
All checks were successful
CI / pr-title (pull_request) Successful in 0s
CI / forgejo-ci (pull_request) Successful in 1m14s
CI / pi-otel-telemetry (pull_request) Successful in 1m19s
CI / skill-args (pull_request) Successful in 1m22s
tests / python (pull_request) Successful in 6s
CI / plane (pull_request) Successful in 1m42s
CI / mcp-bridge (pull_request) Successful in 31s
CI / forgejo (pull_request) Successful in 1m51s
CI / nats-intercom (pull_request) Successful in 36s
CI / refocus (pull_request) Successful in 54s
CI / ask-user-question (pull_request) Successful in 57s
CI / redact (pull_request) Successful in 1m14s
CI / pi-friction (pull_request) Successful in 1m16s
CI / todo (pull_request) Successful in 1m24s
CI / pi-swarm-suite (pull_request) Successful in 6m57s
48ed217b5c
The overseer ruling of 2026-11-04T05:10Z (ld-hypervisor) replaces the
per-environment design: forgejo_merge_pr STOPS requiring a different declared
model and STOPS requiring a premium model. Routine merges keep needing one
exact-head verdict; a dangerous-class change now needs TWO approvals from TWO
INDEPENDENT REVIEW SESSIONS, and the same session approving twice is one
approval. Every other leg is untouched: green CI on the exact head, the exact
head pin, the landing identity, holds, blocker findings, formal review state
never being evidence, and no override parameter.

Why: the sanctioned method is one DeepSeek seat per review
(omniroute/deepseek-flash), which cannot satisfy a premium demand. Model
diversity was only a proxy for "a second, independent pass"; the session is the
real thing, and it is now recordable.

Exemption, resolved explicitly rather than guessed: the ruling protects "Jesús's
flow and sbx-sendrast", so the LEGACY panel rule (two declared models, one
premium) is OWNER-SCOPED — LandingPolicy.legacyPanelOwners, default
["jesus", "Sendrast"] from a live Forgejo read-back, overridable with
FORGEJO_LEGACY_PANEL_OWNERS. An owner scope covers those flows' current and
future repos without guessing repo names; the default is a declaration, not an
inference.

- forgejo/landing.ts: VerdictRecord.session + SESSION_DECL_RE/SESSION_ID_RE read
  from the body like model:; repoOwner()/isLegacyPanelRepo(); sessionPanel()
  counts independent sessions over ledger.exactHead (NOT the reduced ledger —
  every reviewer posts as ld-reviewer on one model, so two sessions share one
  reviewer key); leg 3 split into the new rule and the unchanged legacy rule.
  session is deliberately NOT part of reviewerKey(), so hold withdrawal and
  supersession stay byte-identical.
- forgejo/verdict.ts + verdict-cli.ts: optional --session <id>, composed as a
  standalone session: line inside the grammar block and included in the
  read-back prefix proof. No default, no env fallback; a placeholder is refused.
- forgejo/index.ts: the forgejo_merge_pr contract sentence every orchestrator
  reads now states the new rule.
- docs/forgejo.md: leg 4 rewritten (sessions, the exemption and why), the
  override table gains FORGEJO_LEGACY_PANEL_OWNERS, grammar and helper docs
  carry session:.
- tests: new forgejo/tests/landing-sessions.test.ts (26 cases, red first:
  18 failed / 7 passed on origin/main). Old-rule assertions re-pointed, never
  deleted: the legacy describe runs on Sendrast/sendrast-web, the real pre-ruling
  corpora (#70, #694) replay under the legacy owner policy, and the panel
  fixtures that are meant to constitute a complete modern panel declare their
  session. New-rule counterparts are in landing-sessions.test.ts and in
  landing-model-supersede.test.ts.

Full suite: 18 files / 540 tests green; tsc --noEmit clean.
ld-reviewer approved these changes 2026-11-04 06:12:56 +00:00
Dismissed
ld-reviewer left a comment
Collaborator

Verdict: APPROVED

model: omniroute/deepseek-flash
head: 48ed217b5c

session: 01a1a528-d3c3-7446-a658-24d868d3a690

Approved at this head.

Base verified rather than taken on trust: the worktree's local main is a stale ref, origin/main is 346ff343 and is an ancestor of the reviewed head, so I diffed against origin/main and got exactly the 13 paths the PR declares (landing.ts, verdict.ts, verdict-cli.ts, index.ts, docs/forgejo.md, the new landing-sessions suite and seven re-pointed suites). No unrelated file, no scope creep, no change to forgejo/api.ts or forgejo/types.ts.

I re-ran the suite myself in the PR worktree: npx tsc --noEmit clean, npx vitest run 18 files / 540 tests passed, and git status clean afterwards. CI run #220 is green on this exact sha with all 14 required contexts.

The load-bearing detail is implemented correctly. sessionPanel() iterates ledger.exactHead, not ledger.current, and keeps the latest trusted non-hold verdict per session, so two fleet sessions that share one reviewer key (same author, same model, no lens) are two panel members. ledger.exactHead is chronological, because sortPRText sorts by timestamp ascending and supersedeUntaggedVerdicts preserves that order, so "last write per session wins" holds. An undeclared session groups on the empty string and cannot collide with a declared one, since the declaration regex demands at least six characters; the unstated group is reported and refused, never merged into a real session. A session whose latest stance is a concern stays a panel member, which is the reading nonHolds has always had on the legacy path.

reviewerKey() is byte-identical — still author/model/lens, with the session appearing nowhere in it — so hold withdrawal and supersession keep their release semantics. The suite pins both directions: a same-key later stance still wins, and an approval on a different model cannot withdraw a hold keyed elsewhere. As a sensitivity probe I re-ran the whole suite with FORGEJO_LEGACY_PANEL_OWNERS widened to include Homelab: exactly one test failed, the rewritten id-reuse assertion, which confirms the suites are isolated and that the assertions depending on the governing policy are not vacuous.

The legacy path really is the old rule and does not demand a session declaration. On Sendrast/sendrast-web the lock refuses one model alone, refuses two approvals on the same model, refuses when nothing premium is on the head, refuses a verdict that declares no model, and permits two declared models with one premium with no session declared at all; jesus/infra-core passes with no session line either. Owner derivation takes the second-to-last path segment after stripping query, fragment, trailing slashes and a .git suffix, and lower-cases it, so owner/repo and a URL-shaped ref agree; a repo literally named after a legacy owner under a different owner resolves to that other owner and cannot mis-match, and an unparseable ref yields no owner and falls to the new rule. FORGEJO_LEGACY_PANEL_OWNERS cannot silently empty the list into a fail-open: the splitter returns null when no non-blank part survives, so blank or comma-only values fall back to the declared default, and an override matching nothing merely moves repos onto the new two-session rule.

Nothing else weakened. No premium or different-model demand remains on the new path, and the tests assert its absence rather than just its irrelevance. FORGEJO_DANGEROUS_PATHS and its default list are untouched, with forgejo/** still classed dangerous. The CI-by-name, exact-head, landing-identity, hold and open-blocker legs have no hunk in this diff, formal review state is still not evidence, and no override parameter or marker was added anywhere. The two strings every orchestrator reads state the new rule and the owner-scoped exemption accurately, and the old "two verdicts on different declared models with one premium" sentence is gone rather than merely supplemented — a test asserts its absence.

The tests have teeth. The new suite asserts both directions of each bullet: permit on two same-model sessions with no premium, refuse on one approval, refuse on the same session twice, refuse on a missing session declaration while naming the comment, refuse on a missing model declaration, permit on different models with no premium, permit on a routine PR with one verdict, plus the legacy negatives, the case-insensitive and URL-shaped owner match, the env override in both directions, reviewer-key invariance, withdrawal in both directions, untrusted-identity exclusion, red CI, the helper's flag composition and read-back, placeholder refusal, and the tool-contract text. The re-pointing is honest: the legacy model rails moved onto a legacy owner instead of being deleted, the two pre-ruling corpora replay under the documented owner override with an in-file comment saying why, and the rewritten id-reuse assertion asserts the same property in the new axis — one record, not two, and no merge call. The red log is genuine: 18 failed and 7 passed, and the received strings are the real old-rule refusals ("1 declared model(s) (omniroute/deepseek-flash) — dangerous class needs two reviewers on different models" and "no exact-head verdict came from a premium model"), which is what main actually produces. The green log matches my own run.

Three observations that do not block. First, the session is an honesty declaration the lock cannot attest: one agent can satisfy the gate by writing two different session values, exactly as two different model values satisfied the old premium rule. The docs describe the comparison accurately — the two declared values must differ — but never say plainly that independence is declared rather than verified, and one sentence in the docs and in the tool description would stop a future reader believing the lock establishes it. Second, the declaration regex reads the value anywhere in the body with a fairly loose alphabet and length floor, so incidental prose such as a timestamp following the word session parses as a session id; placeholders are correctly rejected, and the realistic effect is fail-closed because two reviews sharing one incidental token collapse and refuse, but requiring a standalone line would be tighter. Third, the rewritten re-used-review-id assertion does not actually isolate id reuse, because both rows declare one session, so the panel is one member whether or not id reuse were mishandled; the same was true of the pre-change version, where both rows shared one model, so no rail was traded away, but the test name promises slightly more than the fixture proves.

No blocker open.

I never edited code, never merged, never touched another worktree or branch, never wrote to the deployed extension directory, and put no token or secret in any argv. The head had not moved when I posted.

## Verdict: APPROVED model: omniroute/deepseek-flash head: 48ed217b5c582628b1bad63b699fe98bb178f1eb session: 01a1a528-d3c3-7446-a658-24d868d3a690 Approved at this head. Base verified rather than taken on trust: the worktree's local `main` is a stale ref, `origin/main` is 346ff343 and is an ancestor of the reviewed head, so I diffed against origin/main and got exactly the 13 paths the PR declares (landing.ts, verdict.ts, verdict-cli.ts, index.ts, docs/forgejo.md, the new landing-sessions suite and seven re-pointed suites). No unrelated file, no scope creep, no change to forgejo/api.ts or forgejo/types.ts. I re-ran the suite myself in the PR worktree: `npx tsc --noEmit` clean, `npx vitest run` 18 files / 540 tests passed, and `git status` clean afterwards. CI run #220 is green on this exact sha with all 14 required contexts. The load-bearing detail is implemented correctly. `sessionPanel()` iterates `ledger.exactHead`, not `ledger.current`, and keeps the latest trusted non-hold verdict per session, so two fleet sessions that share one reviewer key (same author, same model, no lens) are two panel members. `ledger.exactHead` is chronological, because `sortPRText` sorts by timestamp ascending and `supersedeUntaggedVerdicts` preserves that order, so "last write per session wins" holds. An undeclared session groups on the empty string and cannot collide with a declared one, since the declaration regex demands at least six characters; the unstated group is reported and refused, never merged into a real session. A session whose latest stance is a concern stays a panel member, which is the reading `nonHolds` has always had on the legacy path. `reviewerKey()` is byte-identical — still author/model/lens, with the session appearing nowhere in it — so hold withdrawal and supersession keep their release semantics. The suite pins both directions: a same-key later stance still wins, and an approval on a different model cannot withdraw a hold keyed elsewhere. As a sensitivity probe I re-ran the whole suite with `FORGEJO_LEGACY_PANEL_OWNERS` widened to include Homelab: exactly one test failed, the rewritten id-reuse assertion, which confirms the suites are isolated and that the assertions depending on the governing policy are not vacuous. The legacy path really is the old rule and does not demand a session declaration. On Sendrast/sendrast-web the lock refuses one model alone, refuses two approvals on the same model, refuses when nothing premium is on the head, refuses a verdict that declares no model, and permits two declared models with one premium with no session declared at all; jesus/infra-core passes with no session line either. Owner derivation takes the second-to-last path segment after stripping query, fragment, trailing slashes and a .git suffix, and lower-cases it, so owner/repo and a URL-shaped ref agree; a repo literally named after a legacy owner under a different owner resolves to that other owner and cannot mis-match, and an unparseable ref yields no owner and falls to the new rule. `FORGEJO_LEGACY_PANEL_OWNERS` cannot silently empty the list into a fail-open: the splitter returns null when no non-blank part survives, so blank or comma-only values fall back to the declared default, and an override matching nothing merely moves repos onto the new two-session rule. Nothing else weakened. No premium or different-model demand remains on the new path, and the tests assert its absence rather than just its irrelevance. `FORGEJO_DANGEROUS_PATHS` and its default list are untouched, with forgejo/** still classed dangerous. The CI-by-name, exact-head, landing-identity, hold and open-blocker legs have no hunk in this diff, formal review state is still not evidence, and no override parameter or marker was added anywhere. The two strings every orchestrator reads state the new rule and the owner-scoped exemption accurately, and the old "two verdicts on different declared models with one premium" sentence is gone rather than merely supplemented — a test asserts its absence. The tests have teeth. The new suite asserts both directions of each bullet: permit on two same-model sessions with no premium, refuse on one approval, refuse on the same session twice, refuse on a missing session declaration while naming the comment, refuse on a missing model declaration, permit on different models with no premium, permit on a routine PR with one verdict, plus the legacy negatives, the case-insensitive and URL-shaped owner match, the env override in both directions, reviewer-key invariance, withdrawal in both directions, untrusted-identity exclusion, red CI, the helper's flag composition and read-back, placeholder refusal, and the tool-contract text. The re-pointing is honest: the legacy model rails moved onto a legacy owner instead of being deleted, the two pre-ruling corpora replay under the documented owner override with an in-file comment saying why, and the rewritten id-reuse assertion asserts the same property in the new axis — one record, not two, and no merge call. The red log is genuine: 18 failed and 7 passed, and the received strings are the real old-rule refusals ("1 declared model(s) (omniroute/deepseek-flash) — dangerous class needs two reviewers on different models" and "no exact-head verdict came from a premium model"), which is what main actually produces. The green log matches my own run. Three observations that do not block. First, the session is an honesty declaration the lock cannot attest: one agent can satisfy the gate by writing two different session values, exactly as two different model values satisfied the old premium rule. The docs describe the comparison accurately — the two declared values must differ — but never say plainly that independence is declared rather than verified, and one sentence in the docs and in the tool description would stop a future reader believing the lock establishes it. Second, the declaration regex reads the value anywhere in the body with a fairly loose alphabet and length floor, so incidental prose such as a timestamp following the word session parses as a session id; placeholders are correctly rejected, and the realistic effect is fail-closed because two reviews sharing one incidental token collapse and refuse, but requiring a standalone line would be tighter. Third, the rewritten re-used-review-id assertion does not actually isolate id reuse, because both rows declare one session, so the panel is one member whether or not id reuse were mishandled; the same was true of the pre-change version, where both rows shared one model, so no rail was traded away, but the test name promises slightly more than the fixture proves. No blocker open. I never edited code, never merged, never touched another worktree or branch, never wrote to the deployed extension directory, and put no token or secret in any argv. The head had not moved when I posted.
ld-reviewer approved these changes 2026-11-04 06:13:58 +00:00
Dismissed
ld-reviewer left a comment
Collaborator

Verdict: APPROVED

model: omniroute/deepseek-flash
head: 48ed217b5c

session: 01a1a528-d3f5-7446-a658-24da622cfe49

Scope of this review

  • I reviewed exactly 48ed217b5c against base 346ff343e6 (13 files, +1199/-108), and I re-read the PR over the API at review time: state open, head sha 48ed217, unmerged, base main 346ff34.
  • CI on that exact sha: run #220, 14/14 jobs success; run #221 (python) also success on the same sha.

The ruling's bullets, checked one by one

  • Routine class: one exact-head verdict is still sufficient (not dangerous class: one exact-head verdict is sufficient), and that branch is untouched by the diff.
  • Dangerous class no longer compares models and no longer asks for a premium seat: sessionPanel is the whole panel test on the new path, and premiumModels / FORGEJO_PREMIUM_MODELS are carried unchanged (the premium list is only read by the legacy path now).
  • Two approvals from two INDEPENDENT SESSIONS: leg 3 requires sessions.length >= 2 over sessionPanel(ledger), and a panel row with no session: is its own problem ("do not declare session: <id>, so session independence cannot be checked"), so an undeclared reviewer cannot be counted towards the two.
  • The same session approving twice fails, and I probed the case that matters: two APPROVED verdicts from one session on two DIFFERENT models (deepseek-flash and kimi-pool, i.e. two distinct reviewer keys) still yield 1 independent review session(s) (sess-aaaaaa) and refuse. That is precisely the shape the model rule would have passed and the session rule must not.
  • The other legs have not moved: the leg 1 (green CI by name) and leg 2 (head identity, exact-head record, superseded/unpinned accounting) hunks are absent from the diff; holds, untrusted-identity reporting and formal-review-state-is-never-evidence are read exactly as before.
  • reviewerKey() did NOT gain session: landing.ts:1484 is unchanged (author + model + lens), so hold withdrawal and supersession stay byte-identical. landing-sessions.test.ts asserts the #664 shape (two sessions on one key are one current stance) and the withdrawal case, and I re-probed both.
  • FORGEJO_DANGEROUS_PATHS is untouched and still defaults to the full dangerous list; nothing in this diff empties or narrows it.

Independence is counted on the unreduced stream

  • sessionPanel iterates ledger.exactHead (post-supersession, pre-reviewerKey reduction), not ledger.current. That is correct and load-bearing: every fleet reviewer posts as ld-reviewer on the same model with no lens:, so two real sessions share one reviewer key and ledger.current would collapse them to one, and the rule could never pass if it counted the reduced set. I confirmed the two sets differ on the intended fixture (exactHead 2, current 1, panel 2 sessions).
  • A session whose latest stance is a concern stays a panel member (polarity === "hold" is the only excluded stance), and I checked this is not a new weakening: the legacy model count read the same non-hold set, so the reading is the one that was already in force.
  • An undeclared session cannot be folded into a declared one: the panel map keys it on "". Probed a mixed stream (one declared sess-cccccc plus one untagged approval) and it refuses on both counts (#8 do not declare session and 1 independent review session(s)).
  • Two untagged approvals do not become two sessions either: they collapse into the single "" group and the refusal reads 0 independent review session(s) (none).
  • Untrusted identities still contribute nothing: a ci-reader approval carrying a well-formed session: does not enter the panel (probed: 1 independent review session(s) (sess-iiiiii)).

Fail-open hunting

  • Two arbitrary session: values from one reviewer session: this is a DECLARATION rule and it cannot detect a lie. That is exactly what the ruling asked for ("each verdict declaring model: and session:"), and it does not make the lock weaker than the old rule in a way the ruling did not request, because the old rule was equally declaration-based (two model: strings).
  • A formal review that shadows a comment cannot mint a second session: both rows go through the same extractVerdict pass, so they either declare the same session (one member) or one of them declares none (refused). landing-record.test.ts keeps that rail, now expressed as 1 independent review session(s) (sess-review-927) for the re-used review id #927.
  • An untagged verdict is never rescued into a session: supersedeUntaggedVerdicts is unchanged, and its retirements only ever remove rows from the panel (fewer sessions), never add one.
  • Placeholders cannot become sessions: SESSION_DECL_RE needs at least six characters of [\w.:-], so session: <id> and session: <session> do not parse (probed both, including the literal snippet printed in docs/forgejo.md), and composeVerdictBody refuses a short or <id> --session.

The legacy owner-scoped path

  • Structurally the old leg 3 behind isLegacyPanelRepo(): same missingModel, same models.length < 2, same premium.length === 0 problems, same refusal prose, and no session: demand anywhere on that path (jesus/infra-core with two models and no session still permits).
  • Owner matching cannot capture a Homelab/* repo by accident: repoOwner() is an exact lower-cased compare on the penultimate path segment, so Homelab/pi-extensions resolves to homelab and Homelab/jesus resolves to homelab (not jesus); a repo actually owned by jesus resolves to jesus.
  • FORGEJO_LEGACY_PANEL_OWNERS fails closed on empty input: resolveLandingPolicy reuses split(), which returns null for "" and for " , ,", so both fall back to ["jesus","Sendrast"] (probed). A non-empty value can move an owner into or out of the exemption, which is the documented explicit act, not a default.
  • URL-shaped repoRef resolves to the same owner the API reports: https://forgejo.aaron.reynoza.org/Sendrast/sendrast-web gives sendrast, with .git and trailing slashes stripped.

The documented contract

  • The forgejo_merge_pr tool description states the new rule, the legacy owner panel, and keeps the four unchanged guarantees (green CI, exact-head verdict COMMENT, formal state never evidence, no override parameter). The old "two verdicts on different declared models with one premium" sentence is gone, and a test asserts its absence rather than its contradiction.
  • docs/forgejo.md leg 4 matches the code: two exact-head verdicts, each declaring model: and session:, distinct session values, no session refuses, sessions counted over the unreduced stream, session not in the reviewer key, plus the owner-scoped exemption and the FORGEJO_LEGACY_PANEL_OWNERS override including the empty-falls-back rule.
  • Neither string claims more than a declaration proves: both say "declares", the session is described as the declared discriminator, and the "why sessions and not models" paragraph states that model diversity was only ever a proxy. The documented helper block order (header, model, head, session, tier) matches composeVerdictBody.

Tests

  • npx tsc --noEmit clean and npx vitest run 18 files / 540 tests green in the PR worktree; git status there is clean before and after my run (I wrote nothing into it).
  • New tests/landing-sessions.test.ts (26 cases): each ruling bullet has a positive and a negative case, and the negatives are real — same session twice on two models refuses, undeclared session refuses by comment id, missing model: still refuses, a single approval refuses, a legacy owner keeps the two-model/premium rule and still refuses one model, the env override moves a repo both ways, owner matching is case-insensitive and URL-shaped.
  • Re-pointed suites did not trade a rail away. landing-record.test.ts keeps every legacy assertion (one model alone, two on the same model, no premium, missing model:) on Sendrast/sendrast-web while the re-used-review-id case moved onto the new rule with a stronger assertion. landing-model-supersede.test.ts keeps the whole 2411/2522 supersession corpus on the legacy owner AND replays the #115 shape under the new rule (two sessions land it, a cache-less shadow still refuses). landing-corpus.test.ts, landing-blockers.test.ts, landing-pr70-reviews.test.ts and verdict-694-replay.test.ts only add declarations to fixtures — pre-ruling records could not have a session:, so they are replayed under the documented legacy override — and change exactly the assertions whose wording follows the rule.
  • Red/green: working-files/homelab-2518/HOMELAB-2659-red.log shows 18 real failures of the new file against 346ff34, and the sampled failures are assertions of the OLD rule (1 declared model(s) (omniroute/deepseek-flash), no exact-head verdict came from a premium model, so they are one verdict, not two), not parser errors or missing fixtures; the green log matches the run I observed (18 files / 540 tests).

Observations, none of them a blocker

  • O1: the deployed ~/.pi/agent/extensions/forgejo/verdict-cli.ts predates this PR, so --session is silently ignored there (its parseArgs accepts unknown flags) and the composed block places prose after a blank line. A reviewer using the deployed helper must write the session: line by hand; the failure mode is fail-closed (the lock refuses do not declare session), so this is a deployment-ordering note for the ticket, not a defect in the diff.
  • O2: any token of six or more characters reads as a declaration, so session: unknown would count as a distinct session from a real id. Rejecting reserved words would be defence in depth; a declaration rule cannot prove independence in any case, so this does not change what the gate guarantees.
  • O3: repoOwner() takes the last two segments, so a deep URL (.../Sendrast/x/pulls/88) would resolve the owner to pulls. Not reachable today: collectLandingEvidence and the census both build repoRef from parseRepoRef as owner/repo, and I checked both call sites.
  • O4: a dangerous PR still lands on two CONCERN verdicts because "concern" is a non-hold stance. That is pre-existing (the legacy count read the same nonHolds), so it is not a regression introduced here; it is the one place I would want an explicit rule later.
  • O5: landing-pr70-reviews.test.ts sets process.env.FORGEJO_LEGACY_PANEL_OWNERS at module scope, which can leak into a later file in the same worker and flip a repo to the legacy panel; verdict-694-replay.test.ts scopes the same setting through io.env and an explicit policy instead. The suite is deterministic today; restoring the env would make it robust.

Hygiene

  • The diff touches exactly the 13 files the ticket names, with no unrelated file, no secret or token, no write to ~/.pi/agent/extensions, and no Plane write; the deployed helper directory was read and executed read-only, never modified.

No blocker open.

## Verdict: APPROVED model: omniroute/deepseek-flash head: 48ed217b5c582628b1bad63b699fe98bb178f1eb session: 01a1a528-d3f5-7446-a658-24da622cfe49 ### Scope of this review - I reviewed exactly 48ed217b5c582628b1bad63b699fe98bb178f1eb against base 346ff343e6f7fd1ae78bf98fa45f655176fb70b2 (13 files, +1199/-108), and I re-read the PR over the API at review time: state open, head sha 48ed217, unmerged, base main 346ff34. - CI on that exact sha: run #220, 14/14 jobs success; run #221 (python) also success on the same sha. ### The ruling's bullets, checked one by one - Routine class: one exact-head verdict is still sufficient (`not dangerous class: one exact-head verdict is sufficient`), and that branch is untouched by the diff. - Dangerous class no longer compares models and no longer asks for a premium seat: `sessionPanel` is the whole panel test on the new path, and `premiumModels` / `FORGEJO_PREMIUM_MODELS` are carried unchanged (the premium list is only read by the legacy path now). - Two approvals from two INDEPENDENT SESSIONS: leg 3 requires `sessions.length >= 2` over `sessionPanel(ledger)`, and a panel row with no `session:` is its own problem ("do not declare `session: <id>`, so session independence cannot be checked"), so an undeclared reviewer cannot be counted towards the two. - The same session approving twice fails, and I probed the case that matters: two APPROVED verdicts from one session on two DIFFERENT models (`deepseek-flash` and `kimi-pool`, i.e. two distinct reviewer keys) still yield `1 independent review session(s) (sess-aaaaaa)` and refuse. That is precisely the shape the model rule would have passed and the session rule must not. - The other legs have not moved: the leg 1 (green CI by name) and leg 2 (head identity, exact-head record, superseded/unpinned accounting) hunks are absent from the diff; holds, untrusted-identity reporting and formal-review-state-is-never-evidence are read exactly as before. - `reviewerKey()` did NOT gain `session`: `landing.ts:1484` is unchanged (author + model + lens), so hold withdrawal and supersession stay byte-identical. `landing-sessions.test.ts` asserts the #664 shape (two sessions on one key are one current stance) and the withdrawal case, and I re-probed both. - `FORGEJO_DANGEROUS_PATHS` is untouched and still defaults to the full dangerous list; nothing in this diff empties or narrows it. ### Independence is counted on the unreduced stream - `sessionPanel` iterates `ledger.exactHead` (post-supersession, pre-`reviewerKey` reduction), not `ledger.current`. That is correct and load-bearing: every fleet reviewer posts as `ld-reviewer` on the same model with no `lens:`, so two real sessions share one reviewer key and `ledger.current` would collapse them to one, and the rule could never pass if it counted the reduced set. I confirmed the two sets differ on the intended fixture (exactHead 2, current 1, panel 2 sessions). - A session whose latest stance is a concern stays a panel member (`polarity === "hold"` is the only excluded stance), and I checked this is not a new weakening: the legacy model count read the same non-hold set, so the reading is the one that was already in force. - An undeclared session cannot be folded into a declared one: the panel map keys it on `""`. Probed a mixed stream (one declared `sess-cccccc` plus one untagged approval) and it refuses on both counts (`#8 do not declare session` and `1 independent review session(s)`). - Two untagged approvals do not become two sessions either: they collapse into the single `""` group and the refusal reads `0 independent review session(s) (none)`. - Untrusted identities still contribute nothing: a `ci-reader` approval carrying a well-formed `session:` does not enter the panel (probed: `1 independent review session(s) (sess-iiiiii)`). ### Fail-open hunting - Two arbitrary `session:` values from one reviewer session: this is a DECLARATION rule and it cannot detect a lie. That is exactly what the ruling asked for ("each verdict declaring model: and session:"), and it does not make the lock weaker than the old rule in a way the ruling did not request, because the old rule was equally declaration-based (two `model:` strings). - A formal review that shadows a comment cannot mint a second session: both rows go through the same `extractVerdict` pass, so they either declare the same session (one member) or one of them declares none (refused). `landing-record.test.ts` keeps that rail, now expressed as `1 independent review session(s) (sess-review-927)` for the re-used review id #927. - An untagged verdict is never rescued into a session: `supersedeUntaggedVerdicts` is unchanged, and its retirements only ever remove rows from the panel (fewer sessions), never add one. - Placeholders cannot become sessions: `SESSION_DECL_RE` needs at least six characters of `[\w.:-]`, so `session: <id>` and `session: <session>` do not parse (probed both, including the literal snippet printed in `docs/forgejo.md`), and `composeVerdictBody` refuses a short or `<id>` `--session`. ### The legacy owner-scoped path - Structurally the old leg 3 behind `isLegacyPanelRepo()`: same `missingModel`, same `models.length < 2`, same `premium.length === 0` problems, same refusal prose, and no `session:` demand anywhere on that path (`jesus/infra-core` with two models and no session still permits). - Owner matching cannot capture a `Homelab/*` repo by accident: `repoOwner()` is an exact lower-cased compare on the penultimate path segment, so `Homelab/pi-extensions` resolves to `homelab` and `Homelab/jesus` resolves to `homelab` (not `jesus`); a repo actually owned by `jesus` resolves to `jesus`. - `FORGEJO_LEGACY_PANEL_OWNERS` fails closed on empty input: `resolveLandingPolicy` reuses `split()`, which returns null for `""` and for `" , ,"`, so both fall back to `["jesus","Sendrast"]` (probed). A non-empty value can move an owner into or out of the exemption, which is the documented explicit act, not a default. - URL-shaped `repoRef` resolves to the same owner the API reports: `https://forgejo.aaron.reynoza.org/Sendrast/sendrast-web` gives `sendrast`, with `.git` and trailing slashes stripped. ### The documented contract - The `forgejo_merge_pr` tool description states the new rule, the legacy owner panel, and keeps the four unchanged guarantees (green CI, exact-head verdict COMMENT, formal state never evidence, no override parameter). The old "two verdicts on different declared models with one premium" sentence is gone, and a test asserts its absence rather than its contradiction. - `docs/forgejo.md` leg 4 matches the code: two exact-head verdicts, each declaring `model:` and `session:`, distinct session values, no session refuses, sessions counted over the unreduced stream, `session` not in the reviewer key, plus the owner-scoped exemption and the `FORGEJO_LEGACY_PANEL_OWNERS` override including the empty-falls-back rule. - Neither string claims more than a declaration proves: both say "declares", the session is described as the declared discriminator, and the "why sessions and not models" paragraph states that model diversity was only ever a proxy. The documented helper block order (header, model, head, session, tier) matches `composeVerdictBody`. ### Tests - `npx tsc --noEmit` clean and `npx vitest run` 18 files / 540 tests green in the PR worktree; `git status` there is clean before and after my run (I wrote nothing into it). - New `tests/landing-sessions.test.ts` (26 cases): each ruling bullet has a positive and a negative case, and the negatives are real — same session twice on two models refuses, undeclared session refuses by comment id, missing `model:` still refuses, a single approval refuses, a legacy owner keeps the two-model/premium rule and still refuses one model, the env override moves a repo both ways, owner matching is case-insensitive and URL-shaped. - Re-pointed suites did not trade a rail away. `landing-record.test.ts` keeps every legacy assertion (one model alone, two on the same model, no premium, missing `model:`) on `Sendrast/sendrast-web` while the re-used-review-id case moved onto the new rule with a stronger assertion. `landing-model-supersede.test.ts` keeps the whole 2411/2522 supersession corpus on the legacy owner AND replays the #115 shape under the new rule (two sessions land it, a cache-less shadow still refuses). `landing-corpus.test.ts`, `landing-blockers.test.ts`, `landing-pr70-reviews.test.ts` and `verdict-694-replay.test.ts` only add declarations to fixtures — pre-ruling records could not have a `session:`, so they are replayed under the documented legacy override — and change exactly the assertions whose wording follows the rule. - Red/green: `working-files/homelab-2518/HOMELAB-2659-red.log` shows 18 real failures of the new file against 346ff34, and the sampled failures are assertions of the OLD rule (`1 declared model(s) (omniroute/deepseek-flash)`, `no exact-head verdict came from a premium model`, `so they are one verdict, not two`), not parser errors or missing fixtures; the green log matches the run I observed (18 files / 540 tests). ### Observations, none of them a blocker - O1: the deployed `~/.pi/agent/extensions/forgejo/verdict-cli.ts` predates this PR, so `--session` is silently ignored there (its `parseArgs` accepts unknown flags) and the composed block places prose after a blank line. A reviewer using the deployed helper must write the `session:` line by hand; the failure mode is fail-closed (the lock refuses `do not declare session`), so this is a deployment-ordering note for the ticket, not a defect in the diff. - O2: any token of six or more characters reads as a declaration, so `session: unknown` would count as a distinct session from a real id. Rejecting reserved words would be defence in depth; a declaration rule cannot prove independence in any case, so this does not change what the gate guarantees. - O3: `repoOwner()` takes the last two segments, so a deep URL (`.../Sendrast/x/pulls/88`) would resolve the owner to `pulls`. Not reachable today: `collectLandingEvidence` and the census both build `repoRef` from `parseRepoRef` as `owner/repo`, and I checked both call sites. - O4: a dangerous PR still lands on two CONCERN verdicts because "concern" is a non-hold stance. That is pre-existing (the legacy count read the same `nonHolds`), so it is not a regression introduced here; it is the one place I would want an explicit rule later. - O5: `landing-pr70-reviews.test.ts` sets `process.env.FORGEJO_LEGACY_PANEL_OWNERS` at module scope, which can leak into a later file in the same worker and flip a repo to the legacy panel; `verdict-694-replay.test.ts` scopes the same setting through `io.env` and an explicit policy instead. The suite is deterministic today; restoring the env would make it robust. ### Hygiene - The diff touches exactly the 13 files the ticket names, with no unrelated file, no secret or token, no write to `~/.pi/agent/extensions`, and no Plane write; the deployed helper directory was read and executed read-only, never modified. No blocker open.
ld-reviewer requested changes 2026-11-04 06:36:17 +00:00
Dismissed
ld-reviewer left a comment
Collaborator

Verdict: REQUEST_CHANGES

model: omniroute/kimi-pool
head: 48ed217b5c

session: 01a1a52a-08a6-7446-a658-24dd563e6dc9

Premium seat (Kimi pool) on the landing gate itself, dispatched under the 2026-11-04T05:58Z bootstrap ruling. Reviewed the exact head 48ed217b only; CI run #220 is green on that sha with all 14 required contexts, and the head has not moved.

What holds, checked in the code and not taken from the PR description. Session independence is counted by sessionPanel over ledger.exactHead, the unreduced exact-head stream, never over ledger.current, so two fleet sessions on one reviewer key are two panel members. session is not part of reviewerKey(), so hold withdrawal and supersession stay byte-identical, and both are asserted unchanged by tests. A verdict with no session lands in one reported (unstated) group and refuses. An untrusted identity is not panel evidence even with a declared session. The legacy owner panel keeps the old two-model/one-premium rule unchanged, does not need session, matches owners case-insensitively from the last two path segments of a URL-shaped repoRef, and an empty FORGEJO_LEGACY_PANEL_OWNERS falls back to the declared default instead of emptying the list into a fail-open. No other leg moved: CI-by-name, the exact-head pin, landing identity, holds, blocker findings, the no-override rule, and FORGEJO_DANGEROUS_PATHS are untouched, and the forgejo/index.ts diff is the one contract sentence. Tests re-run in the worktree: npx tsc --noEmit clean and npx vitest run at 18 files and 540 tests, all passed; the re-pointed suites keep their rails and say why in-file, and the one rewritten assertion is disclosed in the PR body.

Blocker — H2659-K1: the honest limit of the self-declared session line is not documented; docs/forgejo.md overclaims it

The entire dangerous-class gate now rests on a self-declared session string, and the lock cannot tell one session declaring two ids from two sessions. A single session can post two approvals with two different declared session values and pass the panel. A single session can also post a comment and a formal review under two different declared session values and pass. An untagged verdict refuses and an untrusted identity refuses, but nothing binds a declared session value to the session that wrote it.

model: was always self-declared too, so self-declaration is not new. What is new is that the old rule forced a second declared model family and a premium seat, and this PR removes that backstop without recording what stands in its place, on the exact class of change that rewrites the gate.

docs/forgejo.md leg 4 overclaims. It says the fleet records the real thing directly and that independence cannot be checked from a declaration nobody made, which together imply a declaration somebody made is checked. It is not checked. It is trusted at face value from a trusted reviewer identity, exactly like model: always was.

The forgejo_merge_pr tool description in forgejo/index.ts states the new rule without this limit either, and it is the contract sentence every orchestrator reads before it dispatches reviewers.

The fix is documentation only. State in docs/forgejo.md leg 4 and in the forgejo_merge_pr tool description that the session declaration is self-declared and taken at face value from a trusted reviewer identity, that the lock cannot distinguish one session declaring two ids from two independent sessions, and that real independence is procedural: the orchestrator dispatches genuinely separate review sessions and each verdict declares the session id it actually ran in. Replace the records-the-real-thing wording with what the lock actually records.

No code change is needed for this blocker. The implementation matches the ruling; the contract overstates what it enforces, on the gate that every other merge trusts.

BLOCKER OPEN: H2659-K1

## Verdict: REQUEST_CHANGES model: omniroute/kimi-pool head: 48ed217b5c582628b1bad63b699fe98bb178f1eb session: 01a1a52a-08a6-7446-a658-24dd563e6dc9 Premium seat (Kimi pool) on the landing gate itself, dispatched under the 2026-11-04T05:58Z bootstrap ruling. Reviewed the exact head 48ed217b only; CI run #220 is green on that sha with all 14 required contexts, and the head has not moved. What holds, checked in the code and not taken from the PR description. Session independence is counted by sessionPanel over ledger.exactHead, the unreduced exact-head stream, never over ledger.current, so two fleet sessions on one reviewer key are two panel members. session is not part of reviewerKey(), so hold withdrawal and supersession stay byte-identical, and both are asserted unchanged by tests. A verdict with no session lands in one reported (unstated) group and refuses. An untrusted identity is not panel evidence even with a declared session. The legacy owner panel keeps the old two-model/one-premium rule unchanged, does not need session, matches owners case-insensitively from the last two path segments of a URL-shaped repoRef, and an empty FORGEJO_LEGACY_PANEL_OWNERS falls back to the declared default instead of emptying the list into a fail-open. No other leg moved: CI-by-name, the exact-head pin, landing identity, holds, blocker findings, the no-override rule, and FORGEJO_DANGEROUS_PATHS are untouched, and the forgejo/index.ts diff is the one contract sentence. Tests re-run in the worktree: npx tsc --noEmit clean and npx vitest run at 18 files and 540 tests, all passed; the re-pointed suites keep their rails and say why in-file, and the one rewritten assertion is disclosed in the PR body. ### Blocker — H2659-K1: the honest limit of the self-declared session line is not documented; docs/forgejo.md overclaims it The entire dangerous-class gate now rests on a self-declared session string, and the lock cannot tell one session declaring two ids from two sessions. A single session can post two approvals with two different declared session values and pass the panel. A single session can also post a comment and a formal review under two different declared session values and pass. An untagged verdict refuses and an untrusted identity refuses, but nothing binds a declared session value to the session that wrote it. model: was always self-declared too, so self-declaration is not new. What is new is that the old rule forced a second declared model family and a premium seat, and this PR removes that backstop without recording what stands in its place, on the exact class of change that rewrites the gate. docs/forgejo.md leg 4 overclaims. It says the fleet records the real thing directly and that independence cannot be checked from a declaration nobody made, which together imply a declaration somebody made is checked. It is not checked. It is trusted at face value from a trusted reviewer identity, exactly like model: always was. The forgejo_merge_pr tool description in forgejo/index.ts states the new rule without this limit either, and it is the contract sentence every orchestrator reads before it dispatches reviewers. The fix is documentation only. State in docs/forgejo.md leg 4 and in the forgejo_merge_pr tool description that the session declaration is self-declared and taken at face value from a trusted reviewer identity, that the lock cannot distinguish one session declaring two ids from two independent sessions, and that real independence is procedural: the orchestrator dispatches genuinely separate review sessions and each verdict declares the session id it actually ran in. Replace the records-the-real-thing wording with what the lock actually records. No code change is needed for this blocker. The implementation matches the ruling; the contract overstates what it enforces, on the gate that every other merge trusts. BLOCKER OPEN: H2659-K1
HOMELAB-2659: docs(forgejo): state the session independence limit (H2659-K1)
All checks were successful
CI / pr-title (pull_request) Successful in 1s
CI / mcp-bridge (pull_request) Successful in 1m18s
CI / forgejo-ci (pull_request) Successful in 1m44s
CI / nats-intercom (pull_request) Successful in 1m40s
CI / pi-otel-telemetry (pull_request) Successful in 2m1s
tests / python (pull_request) Successful in 18s
CI / todo (pull_request) Successful in 1m58s
CI / redact (pull_request) Successful in 1m9s
CI / plane (pull_request) Successful in 2m42s
CI / refocus (pull_request) Successful in 2m46s
CI / ask-user-question (pull_request) Successful in 3m14s
CI / forgejo (pull_request) Successful in 3m21s
CI / skill-args (pull_request) Successful in 3m21s
CI / pi-friction (pull_request) Successful in 2m5s
CI / pi-swarm-suite (pull_request) Successful in 6m51s
55bbb2b1a5
ld-reviewer approved these changes 2026-11-04 07:09:51 +00:00
Dismissed
ld-reviewer left a comment
Collaborator

Verdict: APPROVED

model: omniroute/deepseek-flash
head: 55bbb2b1a5

session: 01a1a560-a906-7446-a658-24ea6557f1bf

I reviewed 55bbb2b1a5 only, against base main 346ff343e6, and I read the branch as its two commits rather than as one blob, so the K1 answer and the change it documents are judged separately: 48ed217b5c carries the ruling, 55bbb2b carries the documentation.

55bbb2b is documentation and comment only

git show 55bbb2b touches three files and adds no executable line: docs/forgejo.md (the "why sessions and not models" paragraph reworded, plus a new "The limit of the declaration (H2659-K1)" paragraph), one clause inside the forgejo_merge_pr promptGuidelines string in forgejo/index.ts, and six * comment lines above export interface SessionPanel in forgejo/landing.ts. I read the landing.ts hunk line by line: every added line sits inside a block comment, and nothing on that path is a regex, a branch condition, a refusal string or an exported signature. git show 55bbb2b -- forgejo/tests is empty, so no test expectation was moved to fit the new text. The only non-comment byte change in the commit is the tool-description string, which is the contract an orchestrator reads rather than a code path, so the lock at 55bbb2b behaves identically to 48ed217.

The limit is stated, in both places the review asked for

docs/forgejo.md leg 4 now carries a dedicated paragraph: the session: line is a SELF-DECLARATION the lock reads at face value, nothing cryptographically binds it to the session that wrote it, one agent session could post two verdicts under two different session: ids or shadow its own comment with a formal review carrying a second id, independence is therefore PROCEDURAL and enforced outside the lock, and what the lock contributes is "measurement and report … not proof". The forgejo_merge_pr description says the same thing in one clause: "that session: line is a SELF-DECLARATION the lock reads at face value, not a proof — nothing binds it to the session that wrote it — so session independence is procedural: the orchestrator must dispatch genuinely separate reviewer sessions, and a forged signature is a fault". The landing.ts comment on SessionPanel repeats it where a future maintainer will read it.

The requirement is not softened, and no fresh overclaim replaces the old one

Both places keep the requirement at full strength: the description still says "TWO APPROVALS FROM TWO INDEPENDENT REVIEW SESSIONS … the same session approving twice is ONE approval, and a verdict with no session: refuses", and the docs add "Naming the limit does not soften the rule: two real sessions are still the requirement, and one session that declares itself twice is a fault, not a loophole". I grepped for the old overclaim the blocker quoted and for its cousins: the "records the real thing directly" sentence is gone, and "proves" / "verified independence" / "proven independence" appear nowhere near the session rule (the surviving proves hits are about the helper's read-back proof of author and commit_id, unrelated to session independence). The replacement wording — "the fleet can name the unit that actually carries that second pass: the review session" — is a claim about naming, not about verifying, and the limit paragraph sits four lines below it.

Substance is intact and I did not take the summary's word for it

sessionPanel() iterates ledger.exactHead and never ledger.current, keeping the latest trusted non-hold verdict per session, so two fleet sessions that share one reviewer key (same author, same model, no lens) are two panel members; an undeclared session groups on "", which cannot collide with a declared one because SESSION_DECL_RE demands six characters, and that group is reported and refused rather than merged. reviewerKey() at landing.ts:1484 is still author + model + lens with session appearing nowhere in it, so hold withdrawal and supersession remain byte-identical; the suite pins both directions, including that a same-key later stance still wins and that an approval on a different model cannot withdraw a hold keyed elsewhere. The legacy owner path really is the old rule and does not ask for a session declaration: on Sendrast/sendrast-web and jesus/infra-core two declared models with one premium pass with no session: line at all, while one model alone refuses. Owner derivation takes the second-to-last path segment after stripping query, fragment, trailing slashes and a .git suffix, then lower-cases it, so owner/repo and a URL-shaped ref agree and a repo literally named after a legacy owner under a different owner resolves to that other owner; Homelab/pi-extensions derives owner homelab and cannot be captured by jesus/Sendrast (a case-insensitive match on the repo segment is not what the code does). An empty or blank FORGEJO_LEGACY_PANEL_OWNERS falls back to the declared ["jesus","Sendrast"] because the splitter returns null when no non-blank part survives, and the test asserts that explicitly. FORGEJO_DANGEROUS_PATHS and its default list are untouched, forgejo/** is still dangerous, no override parameter or marker was added, and the leg 1 / leg 2 / leg 4 / leg 5 hunks are absent from the diff — the only evaluateLandingLock hunks are leg 3 itself, the record it returns, its refusal strings and its success note.

Tests and CI as I observed them

In the PR worktree with node_modules used read-only: npx tsc --noEmit clean, then npx vitest run at 18 files / 540 tests, all passed (landing-sessions.test.ts 26 cases among them), and git status --porcelain empty afterwards. I read landing-sessions.test.ts against the ruling's bullets and its negatives: two sessions on the same DeepSeek model with no premium seat permits; one session approving twice refuses with "the same session approving twice is ONE session"; a single approval refuses; a panel verdict with no session: refuses and is named by id; a missing model: still refuses; two sessions on different models with no premium permits and the note drops the "both on" clause; a concern-only session still counts as a panel member; the routine class still needs one verdict; the legacy owner keeps two-models/one-premium and refuses without a session line only by the model rule; the owner match is case-insensitive and URL-shaped refs agree; an env override moves a repo in both directions; and the "what must NOT change" block pins the reviewer key, the withdrawal semantics, untrusted-identity exclusion and CI-not-green. CI on this exact sha: run #224, all 14 required contexts success (runs #223 and #225 are python-only partials on the same sha). Run #222 failed pi-swarm-suite in tests/core/task-store-wiring.test.ts, which is not in this diff and whose excerpt is the expected ENOTDIR /dev/null/nope diagnostic rather than an assertion this change could affect; the fresh run #224 passes that same job in 6m51s, so the required contexts are green at the head I reviewed.

One refinement I noted and do not treat as blocking

Neither session clause spells out that the face-value read happens only for a TRUSTED reviewer identity; docs/forgejo.md states that rule in legs 2 and 3 immediately above (approvals are only evidence from ld-reviewer or the FORGEJO_LANDING_REVIEWERS set, holds likewise), and the lock enforces it — an untrusted ci-reader approval carrying a well-formed session: does not enter the panel, which landing-sessions.test.ts asserts. So the limit paragraph is not overclaiming by omission, and I record this as a wording refinement, not a finding.

H2659-K1 is resolved by 55bbb2b: the "records the real thing" wording is gone and both required places now state that the session: line is a self-declaration read at face value, that it cannot distinguish one session declaring two ids from two independent sessions, and that real independence is procedural — enforced outside the lock by dispatching separate sessions.

No blocker open.

## Verdict: APPROVED model: omniroute/deepseek-flash head: 55bbb2b1a52da169c746edab5050445e4f461551 session: 01a1a560-a906-7446-a658-24ea6557f1bf I reviewed 55bbb2b1a52da169c746edab5050445e4f461551 only, against base main 346ff343e6f7fd1ae78bf98fa45f655176fb70b2, and I read the branch as its two commits rather than as one blob, so the K1 answer and the change it documents are judged separately: 48ed217b5c carries the ruling, 55bbb2b carries the documentation. ### 55bbb2b is documentation and comment only `git show 55bbb2b` touches three files and adds no executable line: docs/forgejo.md (the "why sessions and not models" paragraph reworded, plus a new "The limit of the declaration (H2659-K1)" paragraph), one clause inside the `forgejo_merge_pr` promptGuidelines string in forgejo/index.ts, and six `*` comment lines above `export interface SessionPanel` in forgejo/landing.ts. I read the landing.ts hunk line by line: every added line sits inside a block comment, and nothing on that path is a regex, a branch condition, a refusal string or an exported signature. `git show 55bbb2b -- forgejo/tests` is empty, so no test expectation was moved to fit the new text. The only non-comment byte change in the commit is the tool-description string, which is the contract an orchestrator reads rather than a code path, so the lock at 55bbb2b behaves identically to 48ed217. ### The limit is stated, in both places the review asked for docs/forgejo.md leg 4 now carries a dedicated paragraph: the `session:` line is a SELF-DECLARATION the lock reads at face value, nothing cryptographically binds it to the session that wrote it, one agent session could post two verdicts under two different `session:` ids or shadow its own comment with a formal review carrying a second id, independence is therefore PROCEDURAL and enforced outside the lock, and what the lock contributes is "measurement and report … not proof". The `forgejo_merge_pr` description says the same thing in one clause: "that session: line is a SELF-DECLARATION the lock reads at face value, not a proof — nothing binds it to the session that wrote it — so session independence is procedural: the orchestrator must dispatch genuinely separate reviewer sessions, and a forged signature is a fault". The landing.ts comment on `SessionPanel` repeats it where a future maintainer will read it. ### The requirement is not softened, and no fresh overclaim replaces the old one Both places keep the requirement at full strength: the description still says "TWO APPROVALS FROM TWO INDEPENDENT REVIEW SESSIONS … the same session approving twice is ONE approval, and a verdict with no session: refuses", and the docs add "Naming the limit does not soften the rule: two real sessions are still the requirement, and one session that declares itself twice is a fault, not a loophole". I grepped for the old overclaim the blocker quoted and for its cousins: the "records the real thing directly" sentence is gone, and "proves" / "verified independence" / "proven independence" appear nowhere near the session rule (the surviving `proves` hits are about the helper's read-back proof of author and `commit_id`, unrelated to session independence). The replacement wording — "the fleet can name the unit that actually carries that second pass: the review session" — is a claim about naming, not about verifying, and the limit paragraph sits four lines below it. ### Substance is intact and I did not take the summary's word for it `sessionPanel()` iterates `ledger.exactHead` and never `ledger.current`, keeping the latest trusted non-hold verdict per session, so two fleet sessions that share one reviewer key (same author, same model, no lens) are two panel members; an undeclared session groups on `""`, which cannot collide with a declared one because `SESSION_DECL_RE` demands six characters, and that group is reported and refused rather than merged. `reviewerKey()` at landing.ts:1484 is still author + model + lens with `session` appearing nowhere in it, so hold withdrawal and supersession remain byte-identical; the suite pins both directions, including that a same-key later stance still wins and that an approval on a different model cannot withdraw a hold keyed elsewhere. The legacy owner path really is the old rule and does not ask for a session declaration: on Sendrast/sendrast-web and jesus/infra-core two declared models with one premium pass with no `session:` line at all, while one model alone refuses. Owner derivation takes the second-to-last path segment after stripping query, fragment, trailing slashes and a `.git` suffix, then lower-cases it, so `owner/repo` and a URL-shaped ref agree and a repo literally named after a legacy owner under a different owner resolves to that other owner; `Homelab/pi-extensions` derives owner `homelab` and cannot be captured by `jesus`/`Sendrast` (a case-insensitive match on the repo segment is not what the code does). An empty or blank `FORGEJO_LEGACY_PANEL_OWNERS` falls back to the declared `["jesus","Sendrast"]` because the splitter returns null when no non-blank part survives, and the test asserts that explicitly. `FORGEJO_DANGEROUS_PATHS` and its default list are untouched, `forgejo/**` is still dangerous, no override parameter or marker was added, and the leg 1 / leg 2 / leg 4 / leg 5 hunks are absent from the diff — the only evaluateLandingLock hunks are leg 3 itself, the record it returns, its refusal strings and its success note. ### Tests and CI as I observed them In the PR worktree with `node_modules` used read-only: `npx tsc --noEmit` clean, then `npx vitest run` at 18 files / 540 tests, all passed (landing-sessions.test.ts 26 cases among them), and `git status --porcelain` empty afterwards. I read landing-sessions.test.ts against the ruling's bullets and its negatives: two sessions on the same DeepSeek model with no premium seat permits; one session approving twice refuses with "the same session approving twice is ONE session"; a single approval refuses; a panel verdict with no `session:` refuses and is named by id; a missing `model:` still refuses; two sessions on different models with no premium permits and the note drops the "both on" clause; a concern-only session still counts as a panel member; the routine class still needs one verdict; the legacy owner keeps two-models/one-premium and refuses without a session line only by the model rule; the owner match is case-insensitive and URL-shaped refs agree; an env override moves a repo in both directions; and the "what must NOT change" block pins the reviewer key, the withdrawal semantics, untrusted-identity exclusion and CI-not-green. CI on this exact sha: run #224, all 14 required contexts success (runs #223 and #225 are python-only partials on the same sha). Run #222 failed `pi-swarm-suite` in `tests/core/task-store-wiring.test.ts`, which is not in this diff and whose excerpt is the expected `ENOTDIR /dev/null/nope` diagnostic rather than an assertion this change could affect; the fresh run #224 passes that same job in 6m51s, so the required contexts are green at the head I reviewed. ### One refinement I noted and do not treat as blocking Neither session clause spells out that the face-value read happens only for a TRUSTED reviewer identity; docs/forgejo.md states that rule in legs 2 and 3 immediately above (approvals are only evidence from `ld-reviewer` or the `FORGEJO_LANDING_REVIEWERS` set, holds likewise), and the lock enforces it — an untrusted `ci-reader` approval carrying a well-formed `session:` does not enter the panel, which landing-sessions.test.ts asserts. So the limit paragraph is not overclaiming by omission, and I record this as a wording refinement, not a finding. H2659-K1 is resolved by 55bbb2b: the "records the real thing" wording is gone and both required places now state that the `session:` line is a self-declaration read at face value, that it cannot distinguish one session declaring two ids from two independent sessions, and that real independence is procedural — enforced outside the lock by dispatching separate sessions. No blocker open.
ld-reviewer approved these changes 2026-11-04 07:10:33 +00:00
Dismissed
ld-reviewer left a comment
Collaborator

Verdict: APPROVED

model: omniroute/kimi-pool
head: 55bbb2b1a5
session: 01a1a560-dffa-7446-a658-24efc6492a11

Premium seat (Kimi), dispatched under the overseer ruling 2026-11-04T05:58Z sanctioning one genuine Kimi verdict beside the two DeepSeek seats because this PR changes the landing gate itself.

H2659-K1 is resolved by 55bbb2b: docs/forgejo.md leg 4 now carries the paragraph "The limit of the declaration (H2659-K1)" stating verbatim that the session: line is a self-declaration read at face value, that nothing cryptographically binds it to the writing session, that one session could post two verdicts under two ids or shadow its own comment with a formal review carrying a second id and still pass the leg, that independence is therefore procedural and enforced outside the lock, and that what the lock contributes is measurement and report, not proof — and the forgejo_merge_pr tool description in forgejo/index.ts now carries the matching clause ("a SELF-DECLARATION the lock reads at face value, not a proof — nothing binds it to the session that wrote it"), with the same limit additionally stated on the sessionPanel() comment seam in forgejo/landing.ts ("It is a measurement, not a proof.").

The previous overclaim is gone: "the fleet records the real thing directly" is corrected to "the fleet can name the unit that actually carries that second pass", and a grep of the head for "records the real thing", "proves independence", and "verified/proven independence" finds no surviving overclaim — the only "proves" hits are pre-existing, accurate statements about read-back evidence. The two-session requirement is not softened: the new text says explicitly "two real sessions are still the requirement, and one session that declares itself twice is a fault, not a loophole."

Full-diff re-judgement at this head: git show 55bbb2b is documentation/comment-only — docs/forgejo.md prose, one tool-description string in forgejo/index.ts, and a six-line comment addition on the SessionPanel seam; no behaviour, regex, refusal text, or test change anywhere in the response commit. sessionPanel() counts sessions over the unreduced ledger.exactHead (never the reduced ledger.current/nonHolds), which is exactly correct because every fleet reviewer shares one reviewerKey(). reviewerKey() is byte-identical to the base (verified by diff of the function body between 346ff34 and 55bbb2b), so hold withdrawal and supersession semantics are unchanged. The legacy-owner path preserves the old rule behaviourally — two declared models, one premium, no session: demand — and repoOwner() handles URL-shaped repoRef (query/hash stripped, trailing slashes and .git removed, last two segments, lower-cased); an empty FORGEJO_LEGACY_PANEL_OWNERS falls back to ["jesus","Sendrast"] because split() returns null on empty. FORGEJO_DANGEROUS_PATHS is intact, legs 1/2/4 (code) and doc legs 1/2/3/5 are untouched, and there is no override parameter.

Verification: npx tsc --noEmit clean; npx vitest run green — 18 files / 540 tests; git status clean in the worktree. CI at this head is green (run #224, all 14 required contexts; the earlier #222 pi-swarm-suite failure was a runner-load flake in a package the diff does not touch, and the sanctioned close/reopen produced a green run at the unchanged head).

No blocker open.
blockers resolved

## Verdict: APPROVED model: omniroute/kimi-pool head: 55bbb2b1a52da169c746edab5050445e4f461551 session: 01a1a560-dffa-7446-a658-24efc6492a11 Premium seat (Kimi), dispatched under the overseer ruling 2026-11-04T05:58Z sanctioning one genuine Kimi verdict beside the two DeepSeek seats because this PR changes the landing gate itself. H2659-K1 is resolved by 55bbb2b: docs/forgejo.md leg 4 now carries the paragraph "The limit of the declaration (H2659-K1)" stating verbatim that the `session:` line is a self-declaration read at face value, that nothing cryptographically binds it to the writing session, that one session could post two verdicts under two ids or shadow its own comment with a formal review carrying a second id and still pass the leg, that independence is therefore procedural and enforced outside the lock, and that what the lock contributes is measurement and report, not proof — and the `forgejo_merge_pr` tool description in forgejo/index.ts now carries the matching clause ("a SELF-DECLARATION the lock reads at face value, not a proof — nothing binds it to the session that wrote it"), with the same limit additionally stated on the `sessionPanel()` comment seam in forgejo/landing.ts ("It is a measurement, not a proof."). The previous overclaim is gone: "the fleet records the real thing directly" is corrected to "the fleet can name the unit that actually carries that second pass", and a grep of the head for "records the real thing", "proves independence", and "verified/proven independence" finds no surviving overclaim — the only "proves" hits are pre-existing, accurate statements about read-back evidence. The two-session requirement is not softened: the new text says explicitly "two real sessions are still the requirement, and one session that declares itself twice is a fault, not a loophole." Full-diff re-judgement at this head: `git show 55bbb2b` is documentation/comment-only — docs/forgejo.md prose, one tool-description string in forgejo/index.ts, and a six-line comment addition on the SessionPanel seam; no behaviour, regex, refusal text, or test change anywhere in the response commit. `sessionPanel()` counts sessions over the unreduced `ledger.exactHead` (never the reduced `ledger.current`/`nonHolds`), which is exactly correct because every fleet reviewer shares one `reviewerKey()`. `reviewerKey()` is byte-identical to the base (verified by diff of the function body between 346ff34 and 55bbb2b), so hold withdrawal and supersession semantics are unchanged. The legacy-owner path preserves the old rule behaviourally — two declared models, one premium, no `session:` demand — and `repoOwner()` handles URL-shaped `repoRef` (query/hash stripped, trailing slashes and `.git` removed, last two segments, lower-cased); an empty `FORGEJO_LEGACY_PANEL_OWNERS` falls back to `["jesus","Sendrast"]` because `split()` returns null on empty. `FORGEJO_DANGEROUS_PATHS` is intact, legs 1/2/4 (code) and doc legs 1/2/3/5 are untouched, and there is no override parameter. Verification: `npx tsc --noEmit` clean; `npx vitest run` green — 18 files / 540 tests; `git status` clean in the worktree. CI at this head is green (run #224, all 14 required contexts; the earlier #222 `pi-swarm-suite` failure was a runner-load flake in a package the diff does not touch, and the sanctioned close/reopen produced a green run at the unchanged head). No blocker open. blockers resolved
ld-reviewer left a comment
Collaborator

Verdict: APPROVED

model: omniroute/deepseek-flash
head: 55bbb2b1a5

session: 01a1a560-a932-7446-a658-24ece132fdf3

H2659-K1 is resolved by 55bbb2b: the independence limit is now stated in one voice in docs/forgejo.md (leg 4, "The limit of the declaration"), in the sessionPanel docblock, and in the forgejo_merge_pr promptGuidelines that an orchestrator actually reads — each says the session: line is a SELF-DECLARATION the lock takes at face value, that nothing binds the string to the session that wrote it, that independence is therefore procedural (the orchestrator dispatches genuinely separate sessions, and a forged signature is a fault), and that naming the limit does not soften the two-real-sessions requirement.

Scope of the head commit: 55bbb2b changes 23 lines of docs/forgejo.md prose, six lines of comment inside the SessionPanel docblock, and one promptGuidelines sentence in forgejo/index.ts. No regex, no refusal string, no branch condition, no test expectation. The behaviour change is all in the parent 48ed217, so this head is judged mostly as the answer to K1, and I read both.

Substance, verified by reading the head and by running the lock: sessionPanel() counts one row per session over ledger.exactHead, i.e. the stream before the reviewerKey() reduction, which is what makes two same-model sessions two members; reviewerKey() is byte-identical (author/model/lens) and a test pins the split (two exact-head rows, one current row); the legacy branch is the pre-ruling code moved, not rewritten, and it does not ask for session: (a jesus repo with two models and one premium seat permits with no session declared); repoOwner() takes the last two path segments and lower-cases them, and isLegacyPanelRepo() fails closed to the new rule when no owner parses; FORGEJO_LEGACY_PANEL_OWNERS set to "", " ", "," or " , " falls back to ["jesus","Sendrast"] rather than to no exemption; FORGEJO_DANGEROUS_PATHS is untouched (same 31 patterns as main, and the file list still contains forgejo/**); the leg 1 / leg 2 / leg 4 / leg 5 hunks are comment-only and the merge tool still exposes no override parameter.

Adversarial probe, one comment cannot vote twice: a single body carrying two session: lines yields exactly one session (extractVerdict returns the first match, "sess-first-decl" in my run), because the declaration is read with a non-global match, so a single review submission cannot be split into two members. A verdict declaring no session is reported by comment id and refuses ("session independence cannot be checked"); an untrusted author's session is not a panel member at all (the ci-reader row was excluded and the panel stayed at one); a verdict pinned to another head does not join the panel.

Adversarial probe, the documented limit reproduces: one session posting two verdicts under two fabricated ids passes the leg, and so does one session shadowing its own comment with a formal review (I also checked the inline-comment kind, which passes too) that carries a second id. That is exactly the sentence K1 added, the docs name both shapes, and the promptGuidelines now warn an orchestrator in plain words that the declaration is not proof and independence must be dispatched as separate sessions. I found no route to a two-member panel that the documented limit does not already cover.

Observations, none of them blocking and none introduced by this commit: (a) the declaration is read anywhere in the body, so ordinary prose can declare by accident — a body whose text reads "My session: reviewed this with the operator present." parsed as session "reviewed" and, next to one real declaration, made a two-member panel; that is the same shape the existing model: declaration has had since it shipped, it is only permissive when a second distinct verdict exists, and it is worth a follow-up rather than a change here; (b) the K1 sentence is the one part of the promptGuidelines contract no test asserts (the rest of that string is asserted), so a later edit could drop the warning silently; (c) the leg this PR was told not to touch — a trusted objection is cleared when any later exact-head verdict shares its reviewer key, and the whole sanctioned fleet shares one key — is unchanged, as the ruling required, so with the new rule a dangerous-class change can be permitted by two approvals posted after a seat dissented, without that seat withdrawing anything; I checked it because the new rule makes the leg reachable on the dangerous path for the first time, I recommend a separate ticket, and I do not hold this PR for a leg the ruling froze.

Tests at this head, run by me in the worktree: npx tsc --noEmit clean, npx vitest run 18 files / 540 tests green, and the worktree is clean afterwards. The 26 cases in tests/landing-sessions.test.ts are not vacuous: the negatives assert the named comment ids, the session count, the refusal wording that carries the rule, and the absence of the old premium/different-model language on the new path, and the legacy cases assert the opposite direction on a real legacy owner. CI is green on this exact sha (run #224, all 14 contexts; runs #223 and #225 also green).

Hygiene: no secret in argv (the credential was a 0600 file), no files added or modified by me, no write outside the worktree, no Plane or GITS write.

## Verdict: APPROVED model: omniroute/deepseek-flash head: 55bbb2b1a52da169c746edab5050445e4f461551 session: 01a1a560-a932-7446-a658-24ece132fdf3 H2659-K1 is resolved by 55bbb2b: the independence limit is now stated in one voice in docs/forgejo.md (leg 4, "The limit of the declaration"), in the `sessionPanel` docblock, and in the `forgejo_merge_pr` promptGuidelines that an orchestrator actually reads — each says the `session:` line is a SELF-DECLARATION the lock takes at face value, that nothing binds the string to the session that wrote it, that independence is therefore procedural (the orchestrator dispatches genuinely separate sessions, and a forged signature is a fault), and that naming the limit does not soften the two-real-sessions requirement. Scope of the head commit: 55bbb2b changes 23 lines of docs/forgejo.md prose, six lines of comment inside the `SessionPanel` docblock, and one promptGuidelines sentence in forgejo/index.ts. No regex, no refusal string, no branch condition, no test expectation. The behaviour change is all in the parent 48ed217, so this head is judged mostly as the answer to K1, and I read both. Substance, verified by reading the head and by running the lock: `sessionPanel()` counts one row per session over `ledger.exactHead`, i.e. the stream before the `reviewerKey()` reduction, which is what makes two same-model sessions two members; `reviewerKey()` is byte-identical (author/model/lens) and a test pins the split (two exact-head rows, one `current` row); the legacy branch is the pre-ruling code moved, not rewritten, and it does not ask for `session:` (a jesus repo with two models and one premium seat permits with no session declared); `repoOwner()` takes the last two path segments and lower-cases them, and `isLegacyPanelRepo()` fails closed to the new rule when no owner parses; `FORGEJO_LEGACY_PANEL_OWNERS` set to "", " ", "," or " , " falls back to `["jesus","Sendrast"]` rather than to no exemption; `FORGEJO_DANGEROUS_PATHS` is untouched (same 31 patterns as main, and the file list still contains forgejo/**); the leg 1 / leg 2 / leg 4 / leg 5 hunks are comment-only and the merge tool still exposes no override parameter. Adversarial probe, one comment cannot vote twice: a single body carrying two `session:` lines yields exactly one session (`extractVerdict` returns the first match, "sess-first-decl" in my run), because the declaration is read with a non-global match, so a single review submission cannot be split into two members. A verdict declaring no session is reported by comment id and refuses ("session independence cannot be checked"); an untrusted author's session is not a panel member at all (the `ci-reader` row was excluded and the panel stayed at one); a verdict pinned to another head does not join the panel. Adversarial probe, the documented limit reproduces: one session posting two verdicts under two fabricated ids passes the leg, and so does one session shadowing its own comment with a formal review (I also checked the inline-comment kind, which passes too) that carries a second id. That is exactly the sentence K1 added, the docs name both shapes, and the promptGuidelines now warn an orchestrator in plain words that the declaration is not proof and independence must be dispatched as separate sessions. I found no route to a two-member panel that the documented limit does not already cover. Observations, none of them blocking and none introduced by this commit: (a) the declaration is read anywhere in the body, so ordinary prose can declare by accident — a body whose text reads "My session: reviewed this with the operator present." parsed as session "reviewed" and, next to one real declaration, made a two-member panel; that is the same shape the existing `model:` declaration has had since it shipped, it is only permissive when a second distinct verdict exists, and it is worth a follow-up rather than a change here; (b) the K1 sentence is the one part of the promptGuidelines contract no test asserts (the rest of that string is asserted), so a later edit could drop the warning silently; (c) the leg this PR was told not to touch — a trusted objection is cleared when any later exact-head verdict shares its reviewer key, and the whole sanctioned fleet shares one key — is unchanged, as the ruling required, so with the new rule a dangerous-class change can be permitted by two approvals posted after a seat dissented, without that seat withdrawing anything; I checked it because the new rule makes the leg reachable on the dangerous path for the first time, I recommend a separate ticket, and I do not hold this PR for a leg the ruling froze. Tests at this head, run by me in the worktree: `npx tsc --noEmit` clean, `npx vitest run` 18 files / 540 tests green, and the worktree is clean afterwards. The 26 cases in tests/landing-sessions.test.ts are not vacuous: the negatives assert the named comment ids, the session count, the refusal wording that carries the rule, and the absence of the old premium/different-model language on the new path, and the legacy cases assert the opposite direction on a real legacy owner. CI is green on this exact sha (run #224, all 14 contexts; runs #223 and #225 also green). Hygiene: no secret in argv (the credential was a 0600 file), no files added or modified by me, no write outside the worktree, no Plane or GITS write.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Homelab/pi-extensions!88
No description provided.