LearnNewsExamplesServices
Frontmatter
id19166
titleThe portal's content generators derive resources/content from the working directory
stateClosed
labels
enhancementaibuild
assigneesneo-opus-grace
createdAtSep 24, 2026, 4:10 PM
updatedAtOct 5, 2026, 3:11 PM
githubUrlhttps://github.com/neomjs/neo/issues/19166
authorneo-opus-grace
commentsCount2
parentIssue17416
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 5, 2026, 3:11 PM
milestonev13.2

The portal's content generators derive resources/content from the working directory

Closed v13.2.0/archive-v13-2-0-chunk-23 enhancementaibuild
neo-opus-grace
neo-opus-grace commented on Sep 24, 2026, 4:10 PM

Context

#19158's census (IC 5815306776) lists the engine's build readers that derive the frozen mirror, at filing:

  • From the working directory (ROOT_DIR = process.cwd()):
    • buildScripts/docs/index/tickets.mjs:31-33, pulls.mjs:30-32, discussions.mjs:22-24;
    • buildScripts/docs/seo/generate.mjs:10,634-695, its conversation globs.
  • From __dirname: buildScripts/release/analyzeClosedSinceRelease.mjs:29-32.

Vega's comment adds the release notes: buildScripts/docs/index/release.mjs and the SEO release-note glob read the served resources/content/release-notes from the working directory too, while the engine authors its notes in .github/RELEASE_NOTES since #19322.

The portal's runtime read, Neo.config.basePath + path in apps/portal/view/content/Component.mjs:188, fetches whatever path those indexes write.

The Problem

D#19051 adopted C1. <corpus-root> is a declared location (ADR 0004 §2.1.1), and deriving it from the working directory is what §5.8 forbids.

#19322 deleted the mirror on 2026-09-29. Since then:

  • a local run of any of these generators reads an absent tree;
  • the engine's dev portal fetches paths that are no longer there.

The Fix

  • Corpus: the conversation generators, the SEO routes and analyzeClosedSinceRelease take the corpus root as an input, with no working-directory default.
  • Release notes: release.mjs and the SEO release routes take the release-notes root as an input. The orchestrator defaults it to the engine's .github/RELEASE_NOTES, where the notes are authored.
  • Paths: the index paths they write are relative to their own root.
  • Portal: it fetches conversation bodies and release notes from two served bases, each a config value. A developer's server serves a local corpus checkout at the corpus base; the pages build sets the bases it serves from.
  • Baseline: these rows leave check-content-root-derivations-baseline.json in the same PR.

Decision Record impact: aligned with ADR 0004 §2.1.1 and §5.8, and D#19051 C1.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
--corpus-root <dir> (new) on docs/index/{tickets,pulls,discussions}.mjs, docs/seo/generate.mjs, docs/rebuildContentIndexesAndSeo.mjs, release/analyzeClosedSinceRelease.mjs. It replaces -i/--issues, -a/--archive and -i/--input ADR 0004 §2.1.1, §5.8; D#19051 C1 Reads issues/, pulls/, discussions/ and archive/ under the root Required, no working-directory default. A missing root, an absent one or a non-directory throws naming the flag, before any builder runs JSDoc, CLI help TicketIndex.spec, PortalContentIndexes.spec, seo/Generate.spec, RebuildContentIndexesAndSeo.spec
--release-notes <dir> (new) on docs/index/release.mjs, docs/seo/generate.mjs, docs/rebuildContentIndexesAndSeo.mjs Vega's comment; the intake Reads the notes, flat or under chunk-N/, below the root Required on the two generators; the orchestrator defaults it to <root>/.github/RELEASE_NOTES. Rejection as above JSDoc, CLI help RebuildContentIndexesAndSeo.spec, seo/Generate.spec
Exported options: corpusRoot on createTicketIndex, createPullRequestIndex, createDiscussionIndex, rebuildContentIndexesAndSeo and the SEO route functions; releaseNotesRoot on createReleaseIndex, the SEO route functions and the orchestrator as the flags As the flags As the flags. The issuesDir, archiveDir and inputDir options are removed JSDoc as the flags
Index paths: contentDir (tickets, pulls, discussions) and the release path the intake Relative to their own root, with forward slashes (issues/chunk-2, v13.1.0.md) none Pull, Ticket, Release model comments old-vs-new equivalence on one corpus; committed-index arms
corpusBasePath, releaseNotesBasePath (new) in apps/portal/neo-config.json the intake Fetch is Neo.config.basePath + base + path (Portal.view.content.Component, Portal.view.news.release.Component) Defaults: resources/content/ and .github/RELEASE_NOTES/. A deploy sets its own JSDoc on getContentPath dev-server browser render (L3)
buildScripts/release/prepare.mjs #19408 Rebuilds the release and label indexes only The conversation indexes, sitemap.xml and llms.txt are rebuilt where a corpus is declared @file unit suite
Dev-server static entry for /.github/RELEASE_NOTES #19408 Serves that one directory at its own path No entry without the directory (a workspace). Dotfiles stay unserved elsewhere JSDoc developmentThemeAssets.spec; browser 404 → 200
Adoption: the pages build neomjs/pages#13 Step 7 passes both flags; step 5 points releaseNotesBasePath at step 4.1's copy Engines before this change ignore both none neomjs/pages#14 dry run; post-deploy render
Adoption: the Brain's SyncService#rebuildContentIndexesAndSeo neomjs/neo-agent-brain#459 Passes corpusRoot once its engine pin moves past this change Until then it throws naming --corpus-root. Brain CI stubs that call none neomjs/neo-agent-brain#459

Acceptance Criteria

  • AC-1: Each generator run without its root input (--corpus-root, or --release-notes where it has no default) fails and names the input, as does a root that is absent or not a directory. With the inputs, it writes the same index as today from the same corpus commit.
  • AC-2: The portal renders an active and an archived ticket body and a release note from their served bases.
  • AC-3: The rows above leave the guard's baseline.

Out of Scope

  • Origin-qualified routes for non-neo origins (D#19051 acknowledgment AC 3), which is its own leaf.
  • The release notes' publishedAt stamp, which is #19409.
  • Adoption outside the engine: neomjs/pages#13 and neomjs/neo-agent-brain#459.

Related

#17416 (parent) · #19158 · #19159 · #19322 · #19408 · #19409 · neomjs/pages#10 · neomjs/pages#13 · neomjs/neo-agent-brain#459 · D#19051

Sweeps at 14:09Z:

  • Live latest-open: the latest 20 open issues; none equivalent.
  • Org search (content root generators, served content base, declared corpus root): only #19047 and #17416, neither equivalent.
  • MC: as in the #19158 sweep.
  • A2A: no claim.

Retrieval Hint: "portal generators corpus root input served content base C1"

Origin Session ID: 1f7129c9-c0f7-42e0-ba47-7a42e5ac57c2

Authored by Grace (Claude Opus 5.5, Claude Code) 🖖

tobiu referenced in commit 4658fbf - "feat(content): Portal content reads declared corpus and release-notes roots (#19166) (#19408) on Oct 5, 2026, 3:11 PM
tobiu closed this issue on Oct 5, 2026, 3:11 PM