Skip to content

feat(review): regenerate a public-safe summary when the narrative is withheld - #9956

Merged
JSONbored merged 2 commits into
mainfrom
feat/regenerate-public-safe-summary-9809
Jul 30, 2026
Merged

feat(review): regenerate a public-safe summary when the narrative is withheld#9956
JSONbored merged 2 commits into
mainfrom
feat/regenerate-public-safe-summary-9809

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Closes #9809.

When every sentence of a review's narrative trips the public-safety sanitizer — routine for a PR touching this project's own scoring/gate code, where an honest sentence says "score" or "ranking" — the reader got a fixed placeholder rather than a real summary. #9806 made that placeholder honest; this makes it a summary.

On the withheld branch only, the narrative is rewritten for a public audience in one small completion, then held to the identical sanitizer.

The prompt is not the boundary

The rewrite is not trusted because it was asked nicely. It goes through the same toPublicSafeBySentence gate, and anything that does not survive is discarded for the fixed sentence. So this can only improve the text a reader sees — it cannot widen what is publishable. A prompt-injected or careless rewrite fails closed, as does a provider error and the model's own CANNOT_SUMMARIZE opt-out.

Mutation-verified: replacing toPublicSafeBySentence(trimmed, options) with trimmed fails the suite.

One conflict in the issue, resolved toward safety

The issue asks for a real summary every time, and that a withheld blocker never read as clean. For a withheld blocker those pull against each other — the rewrite would be describing findings it cannot name.

Blockers keep their fixed sentence, structurally rather than by trusting the prompt. Prose reading clean over a blocker the model actually raised is the one outcome that could green-light a PR. Also mutation-verified: letting the regenerated text through on that branch fails the suite.

If you'd rather have B (regenerate for blockers too, with the prompt required to state that findings exist) or C (regenerate and append the fixed blocker sentence), both are small follow-ups — but only this version makes the never-green-light property independent of prompt compliance.

Where the IO lives

The provider call is at the single caller, not inside composeAdvisoryNotes. The issue's design put it in the composer, but that function is pure and synchronous; making it async would push await through its call site and cost the testability that makes the sanitizer's behaviour verifiable at all.

narrativeWasWithheld() lets the caller decide whether to spend the call before composing, and computes it the same way the composer does — reproducing the sanitizer's decision by other means would let the two drift, and a mis-fire would spend a completion on every review rather than the ~2-3/day this targets.

Telemetry

loopover_ai_review_summary_regenerated_total{outcome=published|fallback}, registered in DEFAULT_METRIC_META like its siblings so it renders with HELP/TYPE. Both outcomes are counted — an all-fallback result must not look identical to the feature never firing.

Validation

  • 7 new tests; both safety properties mutation-verified (the blocker guard and the sanitizer-is-authority rule each fail the suite when inverted).
  • Full sweep of all 36 check-* scripts: none failing. selfhost:validate-observability green.
  • Full suite: 25,771 passing with one failure I caused and fixed — my provider-path cases stubbed global fetch and never restored it, which leaked into selfhost-metrics.test.ts in a full run while passing in isolation. Second commit unstubs; re-run in flight and I'll post the result.

…withheld

Closes #9809.

When every sentence of a review's narrative trips the public-safety sanitizer --
routine for a PR touching this project's own scoring/gate code, where an honest
sentence says 'score' or 'ranking' -- the reader got a fixed placeholder rather
than a real summary. #9806 made that placeholder honest; this makes it a summary.

On the withheld branch only, the narrative is rewritten for a public audience in
one small completion and then held to the IDENTICAL sanitizer. The prompt is not
the boundary: a rewrite that still names forbidden vocabulary is discarded and
the fixed sentence stays the floor, so this can only improve the text a reader
sees and can never widen what is publishable. A prompt-injected or careless
rewrite fails closed, as does a provider error or the model's own
CANNOT_SUMMARIZE opt-out.

Scoped to the NO-BLOCKERS branch. The issue asks for a real summary every time
and that a withheld blocker never read as clean; for a withheld blocker those
conflict, and this resolves toward the safety property -- prose reading clean
over a blocker the model actually raised is the one outcome that could
green-light a PR. Blockers keep their fixed sentence, structurally, not by
trusting the prompt.

The provider call lives at the single caller rather than inside
composeAdvisoryNotes, which stays pure and synchronous: making the composer async
would push await through its call site and cost the testability that makes the
sanitizer's behaviour verifiable. narrativeWasWithheld() lets the caller decide
whether to spend the call before composing, computed the same way the composer
decides, so the two cannot drift.

The counter records both outcomes -- an all-fallback result must not look
identical to the feature never firing.
The provider-path cases stub global fetch and never restored it, so the stub
leaked into whatever file the worker ran next -- it took out
selfhost-metrics.test.ts in a full run while passing in isolation.
@loopover-orb

loopover-orb Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Caution

🛑 LoopOver review result - fixes required

Review updated: 2026-07-30 21:22:45 UTC

3 files · 1 AI reviewer · no blockers · CI failing · unstable

🛑 Suggested Action - Manual Review

Review summary
This PR adds a narrow, well-scoped feature: when a review's narrative is entirely withheld by the public-safety sanitizer, it asks the same provider for a public-audience rewrite and re-runs it through the identical `toPublicSafeBySentence` gate before ever publishing it, falling back to the existing fixed sentence otherwise. The design correctly keeps blockers on the fixed sentence (never letting regenerated prose read as clean over a real blocker), fails closed on provider error/empty output/CANNOT_SUMMARIZE, and adds a paired counter so an all-fallback outcome is distinguishable from the feature never firing. Issue #9809 is closed and the diff matches its description; tests cover the withheld/non-withheld/blocker/sanitizer-failure/provider-failure paths.

Nits — 6 non-blocking
  • src/services/ai-review.ts: `narrativeWasWithheld` only inspects `assessments[0]` to decide withholding but `regeneratePublicSafeSummary`'s caller also only takes the first non-empty assessment via `.find` — worth a one-line comment noting the two must stay in sync since only the first review's narrative is ever rewritten even in dual-reviewer mode.
  • src/services/ai-review.ts:2129: the 400-token ceiling (`REGENERATED_SUMMARY_MAX_TOKENS`) is well-commented but consider extracting the '2-3 sentences' constraint into the constant's doc for traceability.
  • test/unit/ai-review.test.ts: no test exercises the dual-reviewer path where `reviewsForNotes.length > 1` and only the first review's assessment is used for regeneration — worth a case confirming a second reviewer's narrative isn't silently dropped from the rewrite input.
  • src/services/ai-review.ts: `regeneratePublicSafeSummary`'s system prompt hardcodes the forbidden-term list separately from the sanitizer's own vocabulary (`toPublicSafeBySentence`) — a drift between the two lists over time would only be caught by the sanitizer re-filtering, worth a comment noting the prompt list is a hint, not the source of truth.
  • Consider a regression test with two reviews where only the second review's assessment is non-empty (first is empty string) to confirm `.find` behavior is intentional.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.

CI checks failing

  • codecov/patch — 65.21% of diff hit (target 99.00%)

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9809
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 9 registered-repo PR(s), 8 merged, 280 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 9 PR(s), 280 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Linked issue satisfaction

Addressed
The diff implements all four scope items: regeneratePublicSafeSummary with prompt/sanitize/fallback, wiring into the withheld branch that preserves the blockers-never-clean rule and the fixed-sentence floor, a new telemetry counter loopover_ai_review_summary_regenerated_total, and tests covering survival, sanitizer trip fallback, and the blockers-never-read-clean case.

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, Ruby, Go, MDX, Shell, Solidity, JavaScript
  • Official Gittensor activity: 9 PR(s), 280 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Decision record
  • action: hold · clause: success
  • config: 05f72f6a0be367b70c900735664ea0f9809da3f105a719210448949833a50343 · pack: oss-anti-slop · ci: failed
  • record: 88421a1ff26c3d7311b69c4208961b843eb1d7e38b81c056aa370af89d9ccd56 (schema v6, head a1720c5)

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored

Copy link
Copy Markdown
Owner Author

Full suite re-run after the fetch-unstub commit: 25,772 passing, 0 failures (1,326 files). The selfhost-metrics.test.ts cross-file failure is gone — it was my stub leaking, as described above.

Combined with the 36/36 checker sweep, this branch is green end to end.

@JSONbored JSONbored self-assigned this Jul 30, 2026
@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.21739% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.00%. Comparing base (9783944) to head (a1720c5).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/services/ai-review.ts 65.21% 5 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9956      +/-   ##
==========================================
- Coverage   91.88%   91.00%   -0.88%     
==========================================
  Files         928      928              
  Lines      113675   113697      +22     
  Branches    27412    27421       +9     
==========================================
- Hits       104445   103466     -979     
- Misses       7931     9125    +1194     
+ Partials     1299     1106     -193     
Flag Coverage Δ
backend 94.10% <65.21%> (-1.57%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/selfhost/metrics.ts 100.00% <ø> (ø)
src/services/ai-review.ts 96.69% <65.21%> (-0.71%) ⬇️

... and 3 files with indirect coverage changes

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 30, 2026
@JSONbored
JSONbored merged commit 81f28ca into main Jul 30, 2026
9 of 10 checks passed
@JSONbored
JSONbored deleted the feat/regenerate-public-safe-summary-9809 branch July 30, 2026 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(review): regenerate a public-safe summary when the narrative is withheld — a real summary on every review

1 participant