FAIR-band: in-band [5/30] — substrate-evolution layer; promotes PR #11479 discipline-only MANDATORY pre-step to mechanical body-shape validation at the MCP tool boundary. Categorical complement to the ~50 closed meta-tickets that all target agent-cognition (Helpful Assistant counter-substrate, Map vs World Atlas, skill adherence).
Evidence: L2 (37/37 PullRequestService tests pass, 2.2s, including 5 new #11491 validation tests + 2-layer enhancement tests) → L4 required (operator submits a deliberately-malformed review via the MCP tool post-merge; confirms rejection with skill-pointing error in-turn, before any GitHub API call).
Summary
The body parameter of manage_pr_review is now mechanically validated against two layers:
- Visible — 7 evaluation-metric anchor tags (
[ARCH_ALIGNMENT], [CONTENT_COMPLETENESS], [EXECUTION_QUALITY], [PRODUCTIVITY], [IMPACT], [COMPLEXITY], [EFFORT_PROFILE]). These are the wire-format contract with the Retrospective-daemon graph ingestor (ai/daemons/services/ConceptDiscoveryService.mjs:32 regex parser). Misses are named in the missing_visible field; one diagnostic anchor named in the human-facing message.
- Invisible — structural template anchors checked silently. Misses are NOT enumerated in the error response. Defeats Goodhart anchor-stuffing where an agent stuffs only the named visible tags but omits the actual template structure.
Both layers point the agent at .agents/skills/pr-review/SKILL.md — the only reliable path to passing is reading the actual template file.
Empirical anchors
Original anchor (PR #11488/#11489)
Gemini's reviews on PR #11488 + PR #11489 (2026-05-16T20:14-20:15Z) shipped a hallucinated Structural Evaluation Matrix with 5 invented metric names on a 1-10 scale. Zero template anchors present. Two PRs worth of review-substrate data silently lost from the Native Edge Graph. This was the original justification for the visible-anchor mechanical floor.
Goodhart-stuffing empirical anchor (PR #11499)
Gemini's review 4304287893 on PR #11499 (2026-05-16T21:16:25Z) contained ALL 7 visible metric tags but missed the structural template anchors:
<h3 class="neo-h3" data-record-id="5">Strategic-Fit (← missing 'Decision')</h3>
...
<h3 class="neo-h3" data-record-id="6">Evidence Audit (← N/A for cycle-1, but structurally bare)</h3>
...
<h3 class="neo-h3" data-record-id="7">Metrics (← missing '📊 Evaluation Metrics' heading)</h3>
[ARCH_ALIGNMENT]: 100 - ... (← all 7 metric tags present)
[CONTENT_COMPLETENESS]: 100 - ...
[EXECUTION_QUALITY]: 100 - ...
[PRODUCTIVITY]: 90 - ...
[IMPACT]: 80 - ...
[COMPLEXITY]: 20 - ...
[EFFORT_PROFILE]: Quick Win - ...
The pre-enhancement validator (PR #11494 baseline) would have PASSED this body. All 7 visible anchors are present; only the structural anchors are missing. This is the canonical Goodhart-stuffing failure mode the operator's invisible-anchor enhancement prevents.
The corrected review 4304295863 (21:19:24Z, 3 minutes later) added the structural anchors (🪜 Strategic-Fit Decision, 🔬 Depth Floor, 📋 Required Actions, 📊 Evaluation Metrics). These two reviews empirically distinguish Goodhart-stuffed bodies from template-compliant ones.
Prior recurrence
#11105 (CLOSED 2026-05-10) ticketed the exact same problem with a discipline-only audit-at-receipt fix. Recurrence within 6 days proves the discipline-only layer is necessary-but-not-sufficient.
Two-layer asymmetry that makes this work
| Author behavior |
Visible check |
Invisible check |
Outcome |
Reads .agents/skills/pr-review/SKILL.md correctly |
Pass |
Pass |
Submission accepted |
| Hallucinates body without metric tags |
Fail |
Fail |
Skill-pointing error |
Hallucinates body with all 7 visible tags, no structure (Gemini 4304287893 pattern) |
Pass |
Fail |
Skill-pointing error, no structural-anchor list named |
| Reads error, stuffs only the diagnostic anchor named |
Fail |
Fail |
Skill-pointing error |
Enumerates ALL [*]-shaped tags trying to brute-force |
Pass |
Fail |
Invisible substrings not in [*] form |
The good-faith author bypasses both layers automatically; the dishonest stuffer's escape velocity goes up sharply because the invisible-layer surface they'd need to discover is unbounded from inside the error response.
What changed
| File |
Change |
ai/services/github-workflow/PullRequestService.mjs |
VISIBLE_PR_REVIEW_ANCHORS (7 metric tags) + INVISIBLE_PR_REVIEW_ANCHORS (3 structural substrings: Depth Floor, Required Actions, Strategic-Fit Decision) constants. Validator filters both lists; error response includes missing_visible enumeration + ONE diagnostic anchor in human-facing message + skill/template path hints + explicit anti-hallucination phrase. Invisible anchors NEVER named in response. |
ai/mcp/server/github-workflow/openapi.yaml |
Description amended to document both layers + the do-not-hallucinate guidance |
test/playwright/unit/ai/services/github-workflow/PullRequestService.spec.mjs |
5 #11491 validation tests rewritten + 2 new tests added: (1) Goodhart-stuffed-body case (all 7 visible present, structural absent → rejected); (2) Surface-contract test (invisible substrings MUST NOT appear in response JSON). Existing tests updated to use VALID_REVIEW_BODY constant with structural anchors |
3 files, +227 / -33 lines.
What the validator does on failure (visible + invisible)
const missingVisible = VISIBLE_PR_REVIEW_ANCHORS .filter(a => !body.includes(a));
const missingInvisible = INVISIBLE_PR_REVIEW_ANCHORS.filter(a => !body.includes(a));
if (missingVisible.length > 0 || missingInvisible.length > 0) {
return {
error : 'PR Review Template Validation Failed',
message : `Review body does not match the pr-review template structure.
...
Required action: read .agents/skills/pr-review/SKILL.md ...
Do NOT compose a substitute template or hallucinate section headings.
The validator checks more structural anchors than this error names.
...
Diagnostic hint: at least one recognized anchor like ${missingVisible[0]} is missing.`,
code : 'PR_REVIEW_TEMPLATE_VALIDATION_FAILED',
missing_visible: missingVisible,
skill : '.agents/skills/pr-review/SKILL.md',
template : '.agents/skills/pr-review/assets/pr-review-template.md'
};
}Test plan
Authority anchors
- Operator framing 2026-05-16T20:30Z+ (extended meta-thread): ~50 closed meta-tickets in this space targeting agent-cognition; explicit green-light for tool-boundary mechanical enforcement.
- Operator-directed enhancement 2026-05-16T21:15Z: "if they check for specific tags, the tool errors must point to using the mandatory skills. otherwise we risk that models hallucinate a new template containing the tags. and tools should check for more tags that are not mentioned => invisible. safeguard."
- Operator empirical citation 2026-05-16T21:18Z: "the damage is very real => https://github.com/neomjs/neo/pull/11499#pullrequestreview-4304287893 another invalid review." — empirical anchor for the Goodhart-stuffing failure mode the invisible layer prevents.
- Empirical anchors:
- Gemini's #11488 review
PRR_kwDODSospM8AAAABAIyZ_Q + #11489 PRR_kwDODSospM8AAAABAIyaEw — original Helpful-Assistant-template-hallucination case
- Gemini's #11499 review
4304287893 (21:16Z) — Goodhart-stuffed body, would pass baseline visible-only validator
- Gemini's #11499 review
4304295863 (21:19Z, 3 min later) — corrected with structural anchors
ConceptDiscoveryService.mjs:32 — the regex parser that loses ingest signal on metric-tag-stuffed-only bodies
- #11105 (CLOSED 2026-05-10) — same problem 6 days ago, discipline-only fix didn't prevent recurrence
- Substrate parents:
- PR #11479 (description-prose discipline guard, merged 2026-05-16T18:04Z)
- PR #11273 (introduced the
manage_pr_review MCP tool)
Avoided traps
- Naming all required anchors in the error: rejected per operator-directed invisibility safeguard. Even the visible-list is reduced to ONE diagnostic anchor in the human-facing message (the full list lives in the programmatic
missing_visible field but is NOT echoed in the prose).
- Adding a
missing_invisible field: rejected — would leak the safeguard surface. Programmatic callers should also be nudged toward the skill rather than the anchor list.
- Cycle-detection (cycle-1 vs cycle-followup template selection): rejected v1 scope-expansion. The 3 invisible substrings work for BOTH templates by design (
Depth Floor matches both cycle-1's 🔬 Depth Floor and cycle-followup's Delta Depth Floor; Strategic-Fit Decision matches both cycle variants). Empirically validated against the corrected #11499 review.
- JSONSchema
pattern regex: rejected — pattern is single-regex; the template requires multi-anchor presence and emitting a per-anchor missing list is more useful to the caller than a single opaque pattern-mismatch error. Direct service-layer Array.filter is simpler.
- Documenting the invisible-anchor list in a README or skill substrate: rejected — invisibility maintained via code-only documentation. Future maintainers editing the constant see the rationale docstring; external surfaces don't enumerate the list.
Companion-ticket coordination
The same visible+invisible pattern should apply to:
- PR #11498 (Gemini, CI lint on
pull_request: opened for author-side anchors) — currently visible-only, needs same enhancement
- #11495 (split-ticket for
gh pr review CLI bypass via CI lint) — not yet implemented, should inherit pattern
Filing a follow-up cross-surface ticket to apply the same pattern. This PR ships the canonical primary surface; the secondary surfaces converge via the follow-up.
Freshness gap caveat
Operator confirmed 2026-05-16T20:38Z that agent harnesses were not restarted after PR #11479 merged. Same applies here — until next harness restart, agents have stale tool descriptions. The mechanical layer works regardless of harness freshness, CLI-bypass status, or context-compression skim. Defense-in-depth.
Related
- Substrate parent: PR #11479 (discipline-only description guard, merged today)
- Empirical recurrence anchors: PR #11499 review
4304287893 (Goodhart-stuffing); #11488/#11489 reviews (original template-hallucination); #11105 (CLOSED — 6 days prior)
- Conceptual sibling: PR #11490 / #11406 (CI grep-fail mechanical layer for ADR 0004 §2.6)
- Companion cross-surface tickets to file/inherit: #11498 (Gemini, CI PR-body lint), #11495 (gh-CLI-review-bypass split)
Resolves #11491
FAIR-band: in-band [5/30] — substrate-evolution layer; promotes PR #11479 discipline-only
MANDATORY pre-stepto mechanical body-shape validation at the MCP tool boundary. Categorical complement to the ~50 closed meta-tickets that all target agent-cognition (Helpful Assistant counter-substrate, Map vs World Atlas, skill adherence).Evidence: L2 (37/37 PullRequestService tests pass, 2.2s, including 5 new #11491 validation tests + 2-layer enhancement tests) → L4 required (operator submits a deliberately-malformed review via the MCP tool post-merge; confirms rejection with skill-pointing error in-turn, before any GitHub API call).
Summary
The
bodyparameter ofmanage_pr_reviewis now mechanically validated against two layers:[ARCH_ALIGNMENT],[CONTENT_COMPLETENESS],[EXECUTION_QUALITY],[PRODUCTIVITY],[IMPACT],[COMPLEXITY],[EFFORT_PROFILE]). These are the wire-format contract with the Retrospective-daemon graph ingestor (ai/daemons/services/ConceptDiscoveryService.mjs:32regex parser). Misses are named in themissing_visiblefield; one diagnostic anchor named in the human-facing message.Both layers point the agent at
.agents/skills/pr-review/SKILL.md— the only reliable path to passing is reading the actual template file.Empirical anchors
Original anchor (PR #11488/#11489)
Gemini's reviews on PR #11488 + PR #11489 (2026-05-16T20:14-20:15Z) shipped a hallucinated
Structural Evaluation Matrixwith 5 invented metric names on a 1-10 scale. Zero template anchors present. Two PRs worth of review-substrate data silently lost from the Native Edge Graph. This was the original justification for the visible-anchor mechanical floor.Goodhart-stuffing empirical anchor (PR #11499)
Gemini's review
4304287893on PR #11499 (2026-05-16T21:16:25Z) contained ALL 7 visible metric tags but missed the structural template anchors:The pre-enhancement validator (PR #11494 baseline) would have PASSED this body. All 7 visible anchors are present; only the structural anchors are missing. This is the canonical Goodhart-stuffing failure mode the operator's invisible-anchor enhancement prevents.
The corrected review
4304295863(21:19:24Z, 3 minutes later) added the structural anchors (🪜 Strategic-Fit Decision,🔬 Depth Floor,📋 Required Actions,📊 Evaluation Metrics). These two reviews empirically distinguish Goodhart-stuffed bodies from template-compliant ones.Prior recurrence
#11105 (CLOSED 2026-05-10) ticketed the exact same problem with a discipline-only audit-at-receipt fix. Recurrence within 6 days proves the discipline-only layer is necessary-but-not-sufficient.
Two-layer asymmetry that makes this work
.agents/skills/pr-review/SKILL.mdcorrectly4304287893pattern)[*]-shaped tags trying to brute-force[*]formThe good-faith author bypasses both layers automatically; the dishonest stuffer's escape velocity goes up sharply because the invisible-layer surface they'd need to discover is unbounded from inside the error response.
What changed
ai/services/github-workflow/PullRequestService.mjsVISIBLE_PR_REVIEW_ANCHORS(7 metric tags) +INVISIBLE_PR_REVIEW_ANCHORS(3 structural substrings:Depth Floor,Required Actions,Strategic-Fit Decision) constants. Validator filters both lists; error response includesmissing_visibleenumeration + ONE diagnostic anchor in human-facing message + skill/template path hints + explicit anti-hallucination phrase. Invisible anchors NEVER named in response.ai/mcp/server/github-workflow/openapi.yamltest/playwright/unit/ai/services/github-workflow/PullRequestService.spec.mjsVALID_REVIEW_BODYconstant with structural anchors3 files, +227 / -33 lines.
What the validator does on failure (visible + invisible)
const missingVisible = VISIBLE_PR_REVIEW_ANCHORS .filter(a => !body.includes(a)); const missingInvisible = INVISIBLE_PR_REVIEW_ANCHORS.filter(a => !body.includes(a)); if (missingVisible.length > 0 || missingInvisible.length > 0) { return { error : 'PR Review Template Validation Failed', message : `Review body does not match the pr-review template structure. ... Required action: read .agents/skills/pr-review/SKILL.md ... Do NOT compose a substitute template or hallucinate section headings. The validator checks more structural anchors than this error names. ... Diagnostic hint: at least one recognized anchor like ${missingVisible[0]} is missing.`, code : 'PR_REVIEW_TEMPLATE_VALIDATION_FAILED', missing_visible: missingVisible, // full list — programmatic callers skill : '.agents/skills/pr-review/SKILL.md', template : '.agents/skills/pr-review/assets/pr-review-template.md' // NO `missing_invisible` field — invisibility surface preserved }; }Test plan
npm run test-unit -- test/playwright/unit/ai/services/github-workflow/PullRequestService.spec.mjs#11491: rejects body missing all 7 visible metric anchors AND structural anchorsasserts zero GraphQL call + skill-pointing message#11491: Goodhart-stuffed body — all 7 metric tags present but missing structural anchors — still REJECTEDempirically reproduces Gemini4304287893pattern + asserts rejection#11491: rejects body missing some visible anchors and names ONE diagnostic anchor onlycovers reduced-naming behavior#11491: invisible-anchor enforcement is NOT discoverable from the error responseasserts JSON-serialized response contains no invisible substring + nomissing_invisiblefield#11491: accepts body containing all anchors,action-check precedence,missing-body precedencecontinue to passorigin/dev-tip freshness verified@tobiumergeAuthority anchors
PRR_kwDODSospM8AAAABAIyZ_Q+ #11489PRR_kwDODSospM8AAAABAIyaEw— original Helpful-Assistant-template-hallucination case4304287893(21:16Z) — Goodhart-stuffed body, would pass baseline visible-only validator4304295863(21:19Z, 3 min later) — corrected with structural anchorsConceptDiscoveryService.mjs:32— the regex parser that loses ingest signal on metric-tag-stuffed-only bodiesmanage_pr_reviewMCP tool)Avoided traps
missing_visiblefield but is NOT echoed in the prose).missing_invisiblefield: rejected — would leak the safeguard surface. Programmatic callers should also be nudged toward the skill rather than the anchor list.Depth Floormatches both cycle-1's🔬 Depth Floorand cycle-followup'sDelta Depth Floor;Strategic-Fit Decisionmatches both cycle variants). Empirically validated against the corrected #11499 review.patternregex: rejected —patternis single-regex; the template requires multi-anchor presence and emitting a per-anchor missing list is more useful to the caller than a single opaque pattern-mismatch error. Direct service-layerArray.filteris simpler.Companion-ticket coordination
The same visible+invisible pattern should apply to:
pull_request: openedfor author-side anchors) — currently visible-only, needs same enhancementgh pr reviewCLI bypass via CI lint) — not yet implemented, should inherit patternFiling a follow-up cross-surface ticket to apply the same pattern. This PR ships the canonical primary surface; the secondary surfaces converge via the follow-up.
Freshness gap caveat
Operator confirmed 2026-05-16T20:38Z that agent harnesses were not restarted after PR #11479 merged. Same applies here — until next harness restart, agents have stale tool descriptions. The mechanical layer works regardless of harness freshness, CLI-bypass status, or context-compression skim. Defense-in-depth.
Related
4304287893(Goodhart-stuffing); #11488/#11489 reviews (original template-hallucination); #11105 (CLOSED — 6 days prior)Resolves #11491