LearnNewsExamplesServices
Frontmatter
id12184
titlegh-workflow syncer archives non-semver milestones as version folders
stateClosed
labels
bugaiarchitecture
assigneesneo-opus-ada
createdAtMay 30, 2026, 2:06 AM
updatedAtMay 30, 2026, 7:46 AM
githubUrlhttps://github.com/neomjs/neo/issues/12184
authorneo-opus-ada
commentsCount1
parentIssue12186
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 30, 2026, 7:46 AM

gh-workflow syncer archives non-semver milestones as version folders

Closed v13.0.0/archive-v13-0-0-chunk-14 bugaiarchitecture
neo-opus-ada
neo-opus-ada commented on May 30, 2026, 2:06 AM

Context

The portal tickets view (News → Tickets) shows two garbage "release" groups sorted above the real versions:

  • vneo.d.ts - Typescript definitions for all neo framework classes
  • vNeo-Material Component Library v0.1

…appearing before v12.1.0. Surfaced during operator review 2026-05-30.

The Problem

Closed issues are archived under resources/content/archive/issues/<version>/, where <version> is derived from the issue's GitHub milestone title with no semver validation. IssueSyncer.mjs:356-359 (and PullRequestSyncer.mjs:111-114):

version = milestone.title.startsWith(prefix) ? milestone.title : prefix + milestone.title;

So a descriptive milestone becomes a garbage version folder (the leading v is the versionDirectoryPrefix). Empirical: issues #3286 (milestone: neo.d.ts - Typescript definitions for all neo framework classes) and #3287 (milestone: Neo-Material Component Library v0.1), both closed 2024-09-15. gh release list confirms these are not releases — they are milestones. A milestone can map to a release, but only when its title is a valid version.

This is data-level corruption: the syncer writes garbage archive buckets. tickets.mjs's sort then floats the non-semver names to the top (a separate localeCompare quirk) — a symptom; a tickets-view filter is only defense-in-depth.

The Architectural Reality

  • ai/services/github-workflow/sync/IssueSyncer.mjs:356 — milestone→version, no semver guard, even though the oldVersion branch (:360) and the bucket-shift compare (:389-390) already guard with semver.valid(semver.clean(...)). The milestone branch was simply missed.
  • ai/services/github-workflow/sync/PullRequestSyncer.mjs:111 — identical pattern for PRs.
  • Closed-issue fallback chain: milestone → oldVersion (semver-guarded) → closedAt→release (IssueSyncer.mjs:367). If version stays null, the item goes to active/Backlog (:378).

The Fix

Add && semver.valid(semver.clean(<milestone>.title)) to the milestone-as-version condition in IssueSyncer.mjs:356 and PullRequestSyncer.mjs:111. A non-semver milestone is then not treated as a release; the closed item falls through to the closedAt→release branch and is bucketed into the real version that shipped after it closed — not a garbage folder, not stuck in Backlog. Re-sync regenerates the clean archive (ADR-0004 regeneratable-cache).

Verified: semver.valid(semver.clean(t))v12.1.0 / 12.1.0 / v10.0.0-beta.1 valid; neo.d.ts - … / Neo-Material Component Library v0.1null.

Decision Record impact

aligned-with ADR 0004 — the archive is a regeneratable cache; a re-sync produces the corrected buckets.

Acceptance Criteria

  • IssueSyncer + PullRequestSyncer treat a milestone as a version only when it is valid semver.
  • Non-semver-milestone closed items bucket into their closedAt→release version (or Backlog if none), not a title-named folder.
  • After re-sync, resources/content/archive/issues/ contains only semver version folders; the two garbage groups disappear from tickets.json.
  • (Optional defense-in-depth) tickets.mjs skips or sorts-last any residual non-semver groups.

Out of Scope

  • The release.tagName branches (IssueSyncer:371 / PullRequestSyncer:119 / DiscussionSyncer:114) — releases are reliably semver; a consistency guard there is a small follow-up, not this bug.
  • Re-tagging/removing the legacy GitHub milestones (GitHub-side; unnecessary once the syncer guards).

Related

  • #11365 (Restore milestone-aware routing capability in gh-workflow syncers) — adjacent milestone-handling, distinct concern.
  • Authority: learn/agentos/decisions/0004-github-content-architecture.md.

Retrieval Hint: grep milestone.title + versionDirectoryPrefix in ai/services/github-workflow/sync/.

tobiu closed this issue on May 30, 2026, 7:46 AM
tobiu referenced in commit b9e2d48 - "feat(github-workflow): one-time non-destructive archive re-bucket migration (#12194) (#12196) on May 30, 2026, 3:06 PM