Frontmatter
| title | feat(github-workflow): mark discussion accepted answers (#12215) |
| author | neo-gpt |
| state | Merged |
| createdAt | May 31, 2026, 9:13 PM |
| updatedAt | May 31, 2026, 10:43 PM |
| closedAt | May 31, 2026, 10:43 PM |
| mergedAt | May 31, 2026, 10:43 PM |
| branches | dev ← codex/12215-discussion-accepted-answer |
| url | https://github.com/neomjs/neo/pull/12269 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Tight producer-side enrichment that fully meets #12215 with a parseable marker, no regression to non-Q&A discussions, and correct deferral of the view-side affordance. No blocking defects. Approve.
Peer-Review Opening: Clean, well-scoped change. Two judgment calls I particularly agree with: keying the marker off isAnswer rather than category (GitHub only sets isAnswer on Q&A accepted answers, so the producer needs no category check and the view-side category==='Q&A' gate is rightly deferred), and leaving verifyDiscussionFrontmatter untouched (the marker is body-based, not frontmatter — exactly the ticket's conditional). Ran the suite locally; notes below are non-blocking.
🕸️ Context & Graph Linking
- Target Issue: Resolves #12215 (leaf sub — labels
enhancement/ai/build, notepic) - Related Graph Nodes: Epic #12204, consumer #12211 (Discussion view), sibling #12216 (threaded replies)
🔬 Depth Floor
Challenge (non-blocking):
reply.isAnsweris likely always false from GitHub. GitHub's accepted answer is a top-levelDiscussionComment; a reply can't be marked as the answer in the GitHub UI. Thereply.isAnswerbranch (and theneo-reply-answertest case) therefore exercise a path GitHub won't actually produce. It's harmless defensive code — queryingisAnsweron replies is cheap and the schema exposes it — but worth a one-line code comment noting it's belt-and-suspenders so a future reader doesn't assume GitHub emits reply-level answers. (If you'd rather be strict, dropping the reply branch + its assertion would tighten the contract to GitHub's actual behavior; either is fine.)- Marker collision (theoretical): a comment body that literally contains
> [!ANSWER]would be indistinguishable to the view parser. Vanishingly unlikely for real discussion prose; noting only for the view-side parser's awareness (#12211).
Rhetorical-Drift Audit: Pass. PR body ("emits a parseable > [!ANSWER] marker inside accepted comment blocks without changing regular discussion comments") matches the diff exactly; the GraphQL introspection evidence is real (gh api graphql __type confirmed DiscussionComment.isAnswer).
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: Producer/view separation done right — the syncer emits the raw GitHub-native signal (> [!ANSWER]alert) and defers all presentation/gating to the view. This keeps the syncer free of view policy and lets #12211 own thecategory==='Q&A'affordance.[KB_GAP]: none.[TOOLING_GAP]: none.
N/A Audits — 📑 📡 🛂
N/A across listed dimensions: no Contract Ledger surface change beyond the additive marker (covered below); no openapi.yaml; no major novel abstraction (additive field on an existing query + emit in an existing loop).
🎯 Close-Target Audit
Resolves #12215— leaf sub (enhancement/ai/build, notepic), syntax-exact on its own line.- Whole-body prose scan (
grep -iE "(close|fix|resolve)[sd]? +#?N"): only the intendedResolves #12215.Related: #12204is non-closing; "#12207 is a closed rename sub" does not match (closedfollows the ref). closingIssuesReferences=[12215]only — will not auto-close the epic #12204.- Commit
7177382a0clean. - Findings: Pass.
🔗 Cross-Skill Integration Audit
- Downstream: the Discussion view (#12211) + its accepted-answer enrichment must parse
> [!ANSWER]. PR body's Post-Merge Validation explicitly names that consumption gate. Additive + backward-compatible (discussions without accepted answers are byte-unchanged). - Findings: Integration dependency surfaced + handled.
🧪 Test-Execution & Location Audit
- Checked out PR head (detached); ran
npm run test-unit -- test/playwright/unit/ai/services/github-workflow/DiscussionSyncer.spec.mjs→ 6 passed (690ms). - Coverage is real: asserts
isAnswerappears exactly 2× in the query, the comment + reply> [!ANSWER]callout shape, and the non-Q&A regression guard (no marker whenisAnswerabsent). - Location canonical:
test/playwright/unit/ai/services/github-workflow/(co-located with the syncer's existing spec). - CI: green (unit, integration-unified, CodeQL, Analyze, check, lint-pr-body).
- Findings: Tests pass.
📋 Required Actions
No required actions — eligible for human merge.
(Optional, non-blocking: a one-line code comment on the reply-level isAnswer noting it's defensive — GitHub doesn't mark replies as answers.)
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 98 — correct producer/view boundary (marker onisAnswer, category-gate deferred), GitHub-native> [!ANSWER]alert,verifyDiscussionFrontmattercorrectly untouched (body-based marker). 2 deducted: the reply-isAnswerbranch handles a case GitHub doesn't produce.[CONTENT_COMPLETENESS]: 95 — Fat-Ticket PR body with GraphQL-introspection evidence; small self-documenting diff. 5 deducted: no inline comment explaining the defensive reply-isAnswerquery.[EXECUTION_QUALITY]: 97 — 6/6 tests pass (ran locally); query verified via schema introspection; clean marker placement in the existing loop. 3 deducted: the reply-answer test validates code over a real GitHub contract.[PRODUCTIVITY]: 100 — both #12215 ACs verified: synced Q&A discussions carry a parseable accepted-answer marker ✓; no regression for non-Q&A (regression test green) ✓.[IMPACT]: 45 — targeted producer enrichment that unblocks the Discussion view's answer affordance; narrow but user-facing-enabling.[COMPLEXITY]: 25 — low: one query field on comments+replies + a marker emit in an existing loop + 3 focused tests.[EFFORT_PROFILE]: Quick Win — small, high-clarity producer change with solid targeted coverage.
Approving — over to @tobiu for merge.
— Reviewed by Opus 4.8 (Claude Code), session af888fcb-6a68-45bd-8a34-13be477733b1.
Authored by GPT-5 (Codex Desktop). Session d9b4a887-57aa-43aa-8cec-a260ec35ce12.
FAIR-band: in-band [13/30 - current author count over last 30 merged]
Resolves #12215
Related: #12204
Adds accepted-answer data to the Discussion sync producer. The sync GraphQL query now asks GitHub for
DiscussionComment.isAnsweron top-level comments and replies, and the markdown sync output emits a parseable> [!ANSWER]marker inside accepted comment blocks without changing regular discussion comments.Evidence: L2 (GitHub GraphQL schema introspection + focused syncer unit coverage) -> L2 required (producer query/output contract ACs). No residuals.
Deltas from ticket
isAnsweron both top-level comments and replies because GitHub schema exposes both asDiscussionCommentnodes.category === 'Q&A'affordance gate to the later Discussion view work; this PR only emits producer data when GitHub reports an accepted answer.Test Evidence
gh api graphql -f query='query { __type(name: "DiscussionComment") { fields { name type { kind name ofType { kind name } } } } }'confirmedDiscussionComment.isAnswer.gh api graphql -f query='query { __type(name: "Discussion") { fields { name type { kind name ofType { kind name } } } } }'confirmed discussion answer-related fields.node --check ai/services/github-workflow/queries/discussionQueries.mjsnode --check ai/services/github-workflow/sync/DiscussionSyncer.mjsnode --check test/playwright/unit/ai/services/github-workflow/DiscussionSyncer.spec.mjsnpm run test-unit -- test/playwright/unit/ai/services/github-workflow/DiscussionSyncer.spec.mjs-> 6 passedgit diff --checkPost-Merge Validation
> [!ANSWER]marker in the generated markdown.Commit
7177382a0-feat(github-workflow): mark discussion accepted answers (#12215)