LearnNewsExamplesServices
Frontmatter
id11554
titleSync `closed` + `closedAt` to Discussion frontmatter in DiscussionSyncer
stateClosed
labels
documentationenhancementaibuild
assigneesneo-opus-ada
createdAtMay 18, 2026, 1:48 AM
updatedAtJun 7, 2026, 7:12 PM
githubUrlhttps://github.com/neomjs/neo/issues/11554
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 18, 2026, 2:07 AM

Sync closed + closedAt to Discussion frontmatter in DiscussionSyncer

Closed v13.0.0/archive-v13-0-0-chunk-12 documentationenhancementaibuild
neo-opus-ada
neo-opus-ada commented on May 18, 2026, 1:48 AM

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

  • frontmatter object in DiscussionSyncer.mjs includes closed + closedAt keys.
  • Unit-test coverage at test/playwright/unit/ai/services/github-workflow/ asserts the two new fields appear in the serialized frontmatter (if existing DiscussionSyncer.spec.mjs tests frontmatter shape; otherwise add a focused assertion).
  • One sample synced file (resources/content/discussions/chunk-1/discussion-10520.md or similar) post-sync shows closed: true + closedAt: <timestamp> for a known-closed Discussion (e.g., #10520, #11168).
  • Issue-frontmatter parity check: confirm the field naming aligns with how Issue frontmatter exposes its state field (avoid divergence between state for Issues vs closed for Discussions). If divergent, document the rationale in the PR (Discussions don't have a state GraphQL field; closed is the canonical Discussion shape).

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"

tobiu referenced in commit c70f267 - "fix(github-workflow): sync closed + closedAt to Discussion frontmatter (#11554) (#11555) on May 18, 2026, 2:07 AM
tobiu closed this issue on May 18, 2026, 2:07 AM
tobiu referenced in commit ad2afdf - "fix(github-workflow): DiscussionSyncer + PullRequestSyncer metadata persistence + frontmatter integrity gate (#11573) (#11574) on May 18, 2026, 9:21 AM