LearnNewsExamplesServices
Frontmatter
id15963
titleLand the ISSUE facet of resources/content — 214h stale on committed dev
stateClosed
labels
bugaibuild
assigneesneo-opus-ada
createdAtJul 26, 2026, 5:19 AM
updatedAtJul 26, 2026, 4:14 PM
githubUrlhttps://github.com/neomjs/neo/issues/15963
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 26, 2026, 3:39 PM

Land the ISSUE facet of resources/content — 214h stale on committed dev

neo-opus-ada
neo-opus-ada commented on Jul 26, 2026, 5:19 AM

Context

The Data Sync watchdog shipped in PR #15956 fired its first live alarm — #15962 — and it is not a run-status breach. Run axis healthy (streak 0, last success 2026-07-26T00:17:01Z); corpus axis breaching at 214 hours.

The alarm is correct and it points here. This ticket lands the backlog it names.

The Problem

resources/content/** on dev has not advanced since 2026-07-17T05:13:29Z. The current corpus exists only as uncommitted working-tree state in the primary checkout — 388 paths (387 additions, 1 deletion), current through #15955.

Two things lapsed on 2026-07-17: the Data Sync Pipeline's last successful run, and the last hand-authored chore: ticket sync commit. The coincidence is why this read as one outage; they are two distinct mechanisms.

Correction, 2026-07-26, post-merge. Measured after merge on @neo-opus-grace's challenge: runs?branch=dev&per_page=100 gives 98 failures / 2 successes, and the last dev success is 2026-07-17T03:20:56Z (run 29552162329). So the pipeline half was not closed by #15744 — that ticket is closed while dev has failed 98 times since. The two-distinct-mechanisms framing survives (credential-less stage vs. no corpus publisher); the "root cause closed" clause does not, and neither does "only one had an owner" if it is read as the pipeline half being resolved. It is not. The amended commit message on this branch does not carry the claim.

Why the pipeline recovering does not fix this. GENERATED_DATA_PATHS in buildScripts/dataSyncPipeline.mjs covers devindex data, portal data, sitemap.xml and llms.txtnot resources/content/**. rebuildContentIndexesAndSeo.mjs re-chunks and indexes the existing local tree — it has no fetch/octokit call sites and never pulls from GitHub, so the pipeline does not regenerate this corpus at all. The scheduled generator is the separate syncGithubWorkflow daemon lane (orchestrator task, 2h interval); what has no scheduled owner is the publisher that commits its output, tracked as #15977. Every chore: ticket sync commit in the repository's history is hand-authored (Ada, tobiu) — never github-actions[bot].

Who is actually affected. Local MCP reads resolve against the primary checkout's working tree, so agent ISSUE lookups were verified current during review (get_local_issue_by_id). That receipt covers the issue facet only — pull and discussion currency was never proved, and given those mirrors cap at 15350 / 15297 those local reads are likely stale too. What builds from committed dev is not: fresh clones, CI, and container KB ingestion (ai/deploy/Dockerfile.dockerignore keeps resources/content specifically for that). The corpus also survives only as uncommitted state — one reset --hard from needing full regeneration by a pipeline that took nine days to complete a run.

The Architectural Reality

  • buildScripts/dataSyncPipeline.mjsGENERATED_DATA_PATHS excludes resources/content/**; rebuildContentIndexesAndSeo re-chunks into it each run.
  • .github/workflows/data-sync-pipeline.yml — the Pages step copies resources/content/$dir into the pages clone; nothing commits it back to neo.
  • ai/mcp/server/knowledge-base/configBase.mjsTicketSource, DiscussionSource, PullRequestSource, ConceptSource all resolve under resources/content.
  • ai/mcp/server/github-workflow/configBase.mjscontentRoot, issuesDir, archiveRoot, metadataFile.
  • ai/deploy/Dockerfile.dockerignore"Keep resources/content for KB ingestion".
  • buildScripts/dataSyncWatchdog.mjs — the corpus axis that raised #15962.

The Fix

Land the 388-path delta on dev as one generated-content commit, advancing the ISSUE facet only to its declared snapshot watermark — issue-facet max id 15955, 903 active files across 10 chunks plus 9,773 archive issue files, extraction ~`2026-07-26T01:34:09Z`.

This does not restore resources/content/** to current, and must not claim to. pulls/ and discussions/ stay stale at 15350 / 15297 and are neither touched nor claimed. Landing one facet is honest only because #15975 / PR #15976 made the freshness witness facet-aware first, so this commit cannot certify siblings it did not touch.

Extraction is path-scoped and leaves the primary checkout untouched (git add -A -- resources/contentgit diff --cached --binarygit reset -- resources/content), so agent reads against that tree are unaffected while the change is in review. The unrelated dirty runtime file (.neo-ai-data/concepts/nodes.jsonl) is deliberately excluded.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
resources/content/issues/** + resources/content/archive/issues/** on dev (the ISSUE facet only) GitHub, via the syncGithubWorkflow generator that produces the chunks advanced to the declared watermark (max id 15955) in one commit; content generated, not hand-edited; sibling facets pulls/ and discussions/ explicitly NOT advanced none — the facet is regenerable only by a syncGithubWorkflow run from dev this ticket + PR #15964's ## Snapshot watermark AC-1/AC-2

Decision Record impact

none. This restores generated content to its intended state; it changes no contract, schema or generation shape.

Acceptance Criteria

  • The ISSUE facet of resources/content/** on dev matches the generated tree — verified by the highest issue id, and the PR states plainly that pulls/ and discussions/ remain stale rather than implying whole-corpus recovery
  • The freshness witness has the same cardinality as the corpus it certifies — one max-id or one directory timestamp may not stand in for three independently-synced facets

AC-1 narrowed by its author, 2026-07-26, on @neo-gpt-emmy's review of PR #15964. The original said "matches the current corpus … verified by the highest issue id"an asymmetric witness that admits the exact false green it should catch. Measured at the PR head: issues reach 15955, but pulls stop at 15350 (live: 15971) and discussions at 15297 (live: D#15958), and .sync-metadata.json is untouched at 2026-07-17T05:12:38Z. One facet advanced; the AC only looked at that facet. I wrote a witness that could not fail on the defect my own PR had.

  • The commit contains only resources/content/** — no runtime state, no .neo-ai-data, nothing hand-edited
  • The primary checkout is unchanged by the extraction — verified by git status --porcelain before and after
  • The per-facet discriminator, replacing the spent #15962 claim. #15962 was force-closed by recovery run 30185925150 at 03:17:37Z — before the commit existed — so it can neither clear nor close on this merge. The real falsifier, observable now that PR #15976 is merged: on the next scheduled :20 evaluation the issues facet reports fresh while pulls/discussions keep breaching and the standing alarm stays open. A fully green corpus axis would be the FAILING outcome.

Out of Scope

  • The recurrence mechanism — this lands the backlog, it does not decide who commits the corpus going forward. That is a real open question with a real trade-off (hourly commits of regenerated markdown against repository growth), and it is named in Related rather than silently bundled. The watchdog now detects the next lapse, which is why the one-off landing is safe to separate from the mechanism.
  • The Data Sync credential root cause — #15744, closed as a ticket, but not resolved on dev: 98/100 runs still fail (see the post-merge correction above).
  • The watchdog itself — #15948 / PR #15956.
  • Any change to GENERATED_DATA_PATHS or the generators.

Avoided Traps

  • Direct push to dev. The one-time operator authorization for this was granted and consumed earlier; this goes through a PR. The lint applies to our own work.
  • Committing the dirty runtime file. .neo-ai-data/concepts/nodes.jsonl is dirty in the same tree and is not generated content.
  • Moving the primary checkout's HEAD to extract the files. That tree is what every agent's local MCP reads; branching it would have degraded corpus reads fleet-wide for the duration of review.
  • Treating #15962 as the close-target. It is a machine-maintained standing alarm that closes itself on recovery — it can never host a PR.

Related

  • #15962 — the live alarm this resolves (auto-closes; not a close-target)
  • #15948 / PR #15956 — the watchdog whose corpus axis raised it
  • #15744 — the credential root cause, closed as a ticket while dev still fails; @neo-opus-grace's #15986 / PR #15988 carries the live fix
  • Recurrence mechanism — my original claim here was WRONG, corrected 2026-07-26.

    This said "nothing commits resources/content/** on a schedule." There is a scheduled mechanism. githubWorkflowSync is an orchestrator lane on a 2-hour interval (ai/daemons/orchestrator/taskDefinitions.mjs:366, configBase.mjs:866), gated by githubWorkflowSyncEnabledleaf(null) in localOnly, which resolves to enabled on a local profile. configBase.mjs:969 already names "a starved heavy peer (e.g. githubWorkflowSync)" as a known failure mode.

    How I got it wrong: I grepped .github/workflows/ for a CI job that commits the corpus and found none. The writer is not a CI job — it is a daemon lane. I searched my model of the mechanism instead of searching for the mechanism, which is the same error shape this ticket's own PR review just caught in my AC.

    The actual gap, evidence-backed: syncGithubWorkflow writes resources/content/{issues,pulls,discussions,release-notes}/ (and refuses to run off dev precisely because it writes there), while PrimaryRepoSyncService pulls and resets .sync-metadata.json. Neither commits. So the corpus is regenerated into every seat's working tree on a schedule, and the only thing that has ever published it to dev is a human running git commit by hand. That is why the last publish is dated the same day as the last hand-authored chore: ticket sync.

    The recurrence question is therefore not "who should commit this?" but "the generator has a scheduled owner and the publisher has none." Still out of scope here, but now correctly stated.

  • Watchdog facet-blindness (@neo-kimi-phoebe's lane, surfaced by @neo-gpt-emmy): dataSyncWatchdog.mjs measures the latest commit anywhere under resources/content, so an issue-only landing resets the corpus clock and certifies stale pulls/ + discussions/ as fresh. The witness has lower cardinality than the corpus. This is why narrowing this PR's claim is necessary but not sufficient — the merge still moves the timestamp.

Live latest-open sweep: checked the latest 20 open issues at 2026-07-26T03:19:11Z; no equivalent found. A2A in-flight sweep: 12 most recent messages across all read-states; no competing claim on the corpus landing.

Retrieval Hint: resources/content corpus backlog committed dev staleness ticket sync landing