Context
Surfaced 2026-05-18 during post-#11553 review when reading synced Discussion #10520 (Anti-Rubber-Stamp PR Defense) — the synced markdown's frontmatter lacks any indicator of open/closed state, while issue frontmatter does carry it. This breaks symmetry between synced Issue and Discussion artifacts and forces a live GraphQL re-query to determine whether a Discussion is closed.
The Problem
The agent-facing synced Discussion files in resources/content/discussions/**/discussion-*.md are an A2A memory bridge — future agents read them via view_file or grep to assess substrate state without paying GraphQL round-trip cost. Today, an agent reading the frontmatter cannot answer "is this Discussion still open?" without an additional API call.
Concrete observation (synced resources/content/discussions/chunk-1/discussion-10520.md):
---
number: 10520
title: >-
Architecture: Codify Anti-Rubber-Stamp PR Defense Protocol (Extract Review Response)
author: neo-gemini-pro
category: Ideas
createdAt: '2026-04-30T09:17:14Z'
updatedAt: '2026-04-30T19:51:34Z'
---
No closed or closedAt field — yet #10520 was CLOSED 2026-04-30 (resolved-graduated).
The Architectural Reality
The fields are already fetched by the GraphQL query — ai/services/github-workflow/queries/discussionQueries.mjs:82-83 requests both closed and closedAt, and other paths within DiscussionSyncer use them (release-mapping at lines 91, 99, 109, 322-323).
The gap is purely at the frontmatter-object construction step. ai/services/github-workflow/sync/DiscussionSyncer.mjs:241-248:
const frontmatter = {
number : discussion.number,
title : discussion.title,
author : discussion.author?.login || 'unknown',
category : discussion.category?.name || 'Uncategorized',
createdAt : discussion.createdAt,
updatedAt : discussion.updatedAt
};closed + closedAt are dropped at this serialization step despite being available on discussion.
The Fix
Two-line addition at DiscussionSyncer.mjs:241-248:
const frontmatter = {
number : discussion.number,
title : discussion.title,
author : discussion.author?.login || 'unknown',
category : discussion.category?.name || 'Uncategorized',
createdAt : discussion.createdAt,
updatedAt : discussion.updatedAt,
closed : discussion.closed,
closedAt : discussion.closedAt
};The next sync-pipeline run will regenerate all synced Discussion .md files with the new fields populated (the content-hash-comparison loop at line ~280 will detect the frontmatter delta and rewrite every file). No backfill script required; the existing pipeline does the work.
Acceptance Criteria
Out of Scope
- Manual backfill of synced files. The hourly sync pipeline will repopulate all Discussion
.md files on its next run.
- State-reason / lock-reason fields (e.g.,
stateReason: 'RESOLVED'). GraphQL exposes these for Discussions, but the immediate gap is binary open/closed.
- Issue-side frontmatter changes. Issue frontmatter already carries
state per recent sync runs; no symmetric change needed there.
Avoided Traps
state: open|closed instead of closed: bool. Rejected — Discussions' GraphQL type does not expose a state enum field (verified empirically; gh CLI errors with "Field 'state' doesn't exist on type 'Discussion'"). The canonical shape is the closed boolean. Adopting closed matches the GraphQL contract and avoids re-deriving an enum that doesn't exist upstream.
- Adding the fields to the GraphQL query. Not needed — both fields are already fetched at
discussionQueries.mjs:82-83. The fix is at the frontmatter-construction step only.
Related
ai/services/github-workflow/sync/DiscussionSyncer.mjs (the fix surface)
ai/services/github-workflow/queries/discussionQueries.mjs (already fetches the fields)
- Issue-side frontmatter sync (parity reference; serves as the discipline anchor for "synced markdown should carry state")
Origin Session ID: 39eee906-3fd4-424f-9348-828b46ece38c
Handoff Retrieval Hint: "DiscussionSyncer frontmatter closed closedAt sync field gap"
Context
Surfaced 2026-05-18 during post-#11553 review when reading synced Discussion
#10520(Anti-Rubber-Stamp PR Defense) — the synced markdown's frontmatter lacks any indicator of open/closed state, while issue frontmatter does carry it. This breaks symmetry between synced Issue and Discussion artifacts and forces a live GraphQL re-query to determine whether a Discussion is closed.The Problem
The agent-facing synced Discussion files in
resources/content/discussions/**/discussion-*.mdare an A2A memory bridge — future agents read them viaview_fileor grep to assess substrate state without paying GraphQL round-trip cost. Today, an agent reading the frontmatter cannot answer "is this Discussion still open?" without an additional API call.Concrete observation (synced
resources/content/discussions/chunk-1/discussion-10520.md):--- number: 10520 title: >- Architecture: Codify Anti-Rubber-Stamp PR Defense Protocol (Extract Review Response) author: neo-gemini-pro category: Ideas createdAt: '2026-04-30T09:17:14Z' updatedAt: '2026-04-30T19:51:34Z' ---No
closedorclosedAtfield — yet#10520was CLOSED 2026-04-30 (resolved-graduated).The Architectural Reality
The fields are already fetched by the GraphQL query —
ai/services/github-workflow/queries/discussionQueries.mjs:82-83requests bothclosedandclosedAt, and other paths withinDiscussionSynceruse them (release-mapping at lines 91, 99, 109, 322-323).The gap is purely at the frontmatter-object construction step.
ai/services/github-workflow/sync/DiscussionSyncer.mjs:241-248:const frontmatter = { number : discussion.number, title : discussion.title, author : discussion.author?.login || 'unknown', category : discussion.category?.name || 'Uncategorized', createdAt : discussion.createdAt, updatedAt : discussion.updatedAt };closed+closedAtare dropped at this serialization step despite being available ondiscussion.The Fix
Two-line addition at
DiscussionSyncer.mjs:241-248:const frontmatter = { number : discussion.number, title : discussion.title, author : discussion.author?.login || 'unknown', category : discussion.category?.name || 'Uncategorized', createdAt : discussion.createdAt, updatedAt : discussion.updatedAt, closed : discussion.closed, closedAt : discussion.closedAt };The next sync-pipeline run will regenerate all synced Discussion
.mdfiles with the new fields populated (the content-hash-comparison loop at line ~280 will detect the frontmatter delta and rewrite every file). No backfill script required; the existing pipeline does the work.Acceptance Criteria
frontmatterobject inDiscussionSyncer.mjsincludesclosed+closedAtkeys.test/playwright/unit/ai/services/github-workflow/asserts the two new fields appear in the serialized frontmatter (if existingDiscussionSyncer.spec.mjstests frontmatter shape; otherwise add a focused assertion).resources/content/discussions/chunk-1/discussion-10520.mdor similar) post-sync showsclosed: true+closedAt: <timestamp>for a known-closed Discussion (e.g.,#10520,#11168).statefor Issues vsclosedfor Discussions). If divergent, document the rationale in the PR (Discussions don't have astateGraphQL field;closedis the canonical Discussion shape).Out of Scope
.mdfiles on its next run.stateReason: 'RESOLVED'). GraphQL exposes these for Discussions, but the immediate gap is binary open/closed.stateper recent sync runs; no symmetric change needed there.Avoided Traps
state: open|closedinstead ofclosed: bool. Rejected — Discussions' GraphQL type does not expose astateenum field (verified empirically; gh CLI errors with "Field 'state' doesn't exist on type 'Discussion'"). The canonical shape is theclosedboolean. Adoptingclosedmatches the GraphQL contract and avoids re-deriving an enum that doesn't exist upstream.discussionQueries.mjs:82-83. The fix is at the frontmatter-construction step only.Related
ai/services/github-workflow/sync/DiscussionSyncer.mjs(the fix surface)ai/services/github-workflow/queries/discussionQueries.mjs(already fetches the fields)Origin Session ID: 39eee906-3fd4-424f-9348-828b46ece38c Handoff Retrieval Hint: "DiscussionSyncer frontmatter closed closedAt sync field gap"