LearnNewsExamplesServices
Frontmatter
id16524
titleThe lifecycle audit finds graduated-open Discussions and tells nobody
stateOpen
labels
bugaiarchitecture
assignees[]
createdAtAug 4, 2026, 11:31 PM
updatedAtAug 4, 2026, 11:31 PM
githubUrlhttps://github.com/neomjs/neo/issues/16524
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

The lifecycle audit finds graduated-open Discussions and tells nobody

Open Backlog/active-chunk-13 bugaiarchitecture
neo-opus-ada
neo-opus-ada commented on Aug 4, 2026, 11:31 PM

Context

Split out of #16449, which bundled two independent defects at filing: "the audit reaches nobody AND reads a stale snapshot." The snapshot half is delivered (PR pending); this is the half that is not, and it is the one named first in that title.

Splitting rather than stretching the close target: a PR delivering four of six ACs cannot honestly say Resolves, and quietly narrowing the ticket to fit the diff would erase the undelivered work instead of tracking it.

The Problem

ai/scripts/diagnostics/audit-discussion-lifecycle.mjs finds real, actionable state — graduated-open Discussions that recorded [GRADUATED_TO_TICKET] and were never closed — and emits it to a terminal that nobody is looking at. It runs only when someone already suspicious enough to investigate types the command.

That inverts the point of a guard. A diagnostic that requires you to already suspect the problem tells you nothing you did not know. The measured population on dev was six graduated-open Discussions; none of them reached a maintainer through this tool.

There is also a timing defect. Recording [GRADUATED_TO_TICKET] while leaving the Discussion open is only ever caught by a later audit sweep — the graduation moment itself, when the author is present and the context is loaded, passes silently. By the time the audit sees it the cheap fix window has closed.

The Architectural Reality

The producer side is ready and needs no changes:

  • formatReport(result, {json: true}) already emits the full verdict as JSON (:411)
  • shouldFail is already exported, so a caller can distinguish advisory from mechanical
  • the freshness contract landed with #16449 means a consumer can tell a stale verdict from a current one — a prerequisite for automation, and the reason this is filed second. Automating a guard that could not state its own staleness would broadcast stale findings on a schedule, which is worse than the silence it replaces.

The sanctioned precedent is .github/workflows/data-sync-watchdog.yml (55 lines) plus buildScripts/dataSyncWatchdog.mjs: a scheduled cron, workflow_dispatch inputs for forceBreach / forceRecovery / dryRun, concurrency group, thresholds as env constants that fail loud when present-but-unparseable, and a single standing alarm issue maintained idempotently — opened on breach, updated while breached, closed on recovery.

That last property is what makes this safe to schedule: one issue that tracks state, not one issue per run.

The Fix

  1. A scheduled workflow modelled on data-sync-watchdog.yml, invoking the audit with --json.
  2. An evaluator that maintains one standing alarm issue for graduated-open findings — open/update/close idempotently.
  3. stale-open and resolved-only-review stay out of the alarm. They are advisory and require judgement; escalating them mechanically is how a useful signal becomes noise a maintainer learns to close unread.
  4. A graduation-time check so [GRADUATED_TO_TICKET] with an open Discussion is caught when it happens, not only by a later sweep.

The guard stays read-only toward Discussions throughout: it may write its own alarm issue, and must never close a Discussion, edit a body, or post a comment on one.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
new scheduled workflow this ticket runs the audit, maintains the standing alarm dryRun default true, as the watchdog precedent does workflow comments dry-run dispatch shows planned actions and writes nothing
standing alarm issue this ticket exactly one, idempotent open/update/close issue body states the snapshot age verbatim forceBreach then forceRecovery round-trip
formatReport --json unchanged already the machine surface freshness spec pins it verbatim
snapshot freshness #16449 alarm body must carry it UNKNOWN age ⇒ alarm says so rather than implying current delivered
graduation-time check this ticket detects [GRADUATED_TO_TICKET] + open Discussion at graduation ideation-sandbox-workflow.md spec

Decision Record impact

none. The Discussion lifecycle grammar is unchanged; this gives an existing read-only guard a destination and one earlier trigger point.

Acceptance Criteria

  • graduated-open findings reach a maintainer-visible surface without anyone invoking the command manually.
  • Exactly one standing alarm exists across repeated runs — a second scheduled run updates it rather than filing a duplicate. Red witness: two consecutive breach runs.
  • The alarm closes on recovery, and a forceBreachforceRecovery round-trip demonstrates both edges.
  • A dryRun dispatch prints planned actions and writes nothing — verified by the issue list being unchanged.
  • The alarm body states the snapshot's age, and an UNKNOWN age is carried as unknown rather than presented as current.
  • Recording [GRADUATED_TO_TICKET] while leaving the Discussion open is detectable at graduation time, not only by a later audit.
  • The guard remains read-only toward Discussions: it never closes one, edits a body, or posts a comment on one.
  • stale-open and resolved-only-review are not escalated into the alarm and remain advisory.

Out of Scope

  • The freshness reporting itself — delivered under #16449.
  • Auto-closing graduated Discussions. The guard proposes; a human disposes. Read-only is a property worth keeping, not a limitation to remove.
  • Widening the alarm to every candidate kind — see AC 8; that is the failure mode where the alarm becomes noise.

Avoided Traps

  • Automating a guard that cannot state its own staleness. Scheduling this before #16449 landed would have broadcast stale findings on a cron — the reason this ticket is second, not a sequencing preference.
  • One issue per run. The watchdog precedent maintains a single standing alarm specifically to avoid this; a duplicate-per-run alarm trains maintainers to ignore it.
  • A new notification surface. The repo already has a proven standing-alarm mechanism. Reusing it beats inventing a second one, and #16515's duplication census is measuring exactly this class of drift.

Related

  • #16449 — the freshness half, delivered; this is the other half of its title
  • #16515 — ai-folder duplication census; reusing the watchdog precedent rather than adding a surface is the point
  • .github/workflows/data-sync-watchdog.yml + buildScripts/dataSyncWatchdog.mjs — the pattern to model

Live latest-open sweep: checked latest 20 open issues at 2026-08-04T21:36Z; no equivalent found. A2A in-flight claim sweep: no overlapping [lane-claim].

Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c

Retrieval Hint: query_raw_memories("discussion lifecycle audit standing alarm destination") · audit-discussion-lifecycle graduated-open