LearnNewsExamplesServices
Frontmatter
id17884
titleConsumer app, engine, and dist URLs lack explicit owners
stateOpen
labels
bugdeveloper-experienceaiarchitecturebuildcore
assignees[]
createdAtAug 30, 2026, 7:01 PM
updatedAtSep 2, 2026, 10:48 AM
githubUrlhttps://github.com/neomjs/neo/issues/17884
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Consumer app, engine, and dist URLs lack explicit owners

Open Backlog/active-chunk-19 bugdeveloper-experienceaiarchitecturebuildcore
neo-gpt
neo-gpt commented on Aug 30, 2026, 7:01 PM

[!IMPORTANT] [PROVISIONAL_UNGRADUATED: D#17886]

Implementation is paused. Continued physical-consumer measurement changed this ticket's root premise twice; Discussion #17886 now owns divergence and the public path contract. This ticket is the reserved implementation landing pad only after graduation.

Context

Neo applications need to find one public workspace/mount root, then resolve applications, Main, workers, themes, resources, and package assets beneath or relative to it. Current configuration exposes basePath, appPath, mainPath, and workerBasePath, but merged source uses five different anchors to interpret them.

The latest deployment evidence resolves an earlier false dichotomy in this body: the application mount and public workspace root coincide by construction in the measured deployment because the whole workspace is served at <mount>/. The direct /apps/<app>/ route is a development convenience where they diverge. The defect is therefore not yet proven to require a second configurable root. The open fork is whether to formalize and normalize existing basePath, add a distinct workspace URL, retain owner-relative paths, use explicit per-surface URLs, or generate a manifest.

Evidence was gathered on merged dev at df05ec3854 from source, build output, historical issues, and peer-trusted physical-consumer traces. The consumer identity is private; only redacted topology and URLs appear here.

Five Measured Resolution Families

Surface Current derivation Source coordinate Observed result
Source-mode theme CSS document URL after basePath.substring(6) plus ../../dist/<environment>/ src/main/addon/Stylesheet.mjs:72-89, :99-105 direct route requests /apps/dist/development/css/**; mounted route happens to request /dist/development/css/**
Worker entries workerBasePath + fileName from page config src/worker/Manager.mjs:236-243 works when config already carries the mount-aware worker URL
Application entry App-worker module URL plus hard-coded ../../${appPath} src/worker/App.mjs:378-388 external generators compensate with parent segments tied to package depth
Main entry MicroLoader.mjs module URL plus free-form mainPath src/MicroLoader.mjs:1-4; appworker generator :73-78 copied loader at <mount>/src/ plus baked ../main.js requests <mount>/main.js, losing dist/development/
Bundled theme CSS document URL after deleting six characters from basePath Stylesheet.mjs:52-55, :77-78, :103-105 absolute mount is truncated into a tail such as udio/; Font Awesome and five global sheets 404, leaving a live App worker behind a black page

The fifth specimen proves the severity boundary: bundled Main and all three core workers load from the dist tree, then stylesheet loading alone prevents the shell from mounting.

Source-of-Authority Conflict

  • learn/guides/advanced/BuildArchitecture.md defines basePath as the physical depth from an application folder back to project root and shows it changing after relocation into dist/<environment>/.
  • src/DefaultConfig.mjs documents basePath as the path to the Neo.mjs directory, which is not the same thing for an installed consumer.
  • Dist appworker configs generate basePath, mainPath, and workerBasePath independently from output depth.
  • src/MicroLoader.mjs imports mainPath relative to its own module URL; source and copied consumer loaders can therefore give the same config different meaning.

This conflict is why the previous body's proposed workspaceBasePath is withdrawn rather than implemented.

Provisional Contract Ledger

Target Surface Current Authority Unresolved Contract Fork Graduation Evidence
Neo.config.basePath DefaultConfig; app configs; build generators formalize as public workspace root vs. preserve legacy/package meaning behind a new workspace URL compatibility census + exact URL matrix
Neo.config.appPath App worker; app generators workspace-root logical identity vs. worker-relative path/absolute override native + bundled app load at shallow/nested depths
Neo.config.mainPath / MicroLoader loader module URL; build generators workspace-root logical identity vs. explicitly loader-relative path copied/source loader matrix; red control dropping environment segment
Neo.config.workerBasePath Main-thread Worker creation derived workspace path vs. explicit per-realm override worker URLs in dedicated/shared modes
theme/resource URLs Stylesheet; createStyleSheet; environment branches root + environment asset identity vs. generated manifest/per-surface URL global/class/Font Awesome 200s in all environments
app/dist generators app creation + appworker webpack configs emit invariant logical identities vs. environment-relative physical paths generated-config read-back tests
dist/esm config generation buildScripts/build/esmodules.mjs#minifyFile preserve absolute basePath without parent-prefixing; derive workerBasePath for the ESM output tree rather than the source mount physical generated-config read-back + worker URL requests (DC_kwDODSospM4BFeP2)
dist/esm installed-package import transform buildScripts/build/esmodules.mjs#regexImport; app scaffold output rewrite installed-package specifiers for every emitted quote form, or replace regex ownership with an AST transform; no compatibility dist/esm/node_modules tree scaffold single-quote control reaches /dist/esm/node_modules/neo.mjs/** 404 only after config isolation (DC_kwDODSospM4BFeP2)

Newly Measured ESM Prerequisites — Owned Here, Still Provisional

The physical four-environment fixture in D#17886 found two independent dist/esm build-output defects at dev@f1d3ed2840:

  1. An absolute source basePath: '/mount/' becomes ../..//mount/, while generated workerBasePath becomes /mount/src/worker/ and drops the dist/esm output segment. Main loads, but App/Data/VDom worker URLs 404.
  2. After a served-config-only isolation fixes those URLs, scaffolded single-quoted imports still point at ../../../node_modules/neo.mjs/src/**. The ESM build flattens Engine modules to dist/esm/src/**, but regexImport matches only double quotes/backticks, so the first View dependency 404s beneath dist/esm/node_modules/neo.mjs/**.

These are prerequisites of this ticket’s all-environment URL contract, not separate shadow work and not gate-6 path-derivation controls. They remain provisional until D#17886 graduates and the ADR + full Contract Ledger are rewritten together. The positive fixture must repair them; compatibility symlinks would invalidate the evidence.

Evidence: DC_kwDODSospM4BFeP2; peer separation/custody audit DC_kwDODSospM4BFeQF.

Code-Ready Gate

  • D#17886 receives a substantive non-author divergence cycle.
  • Every option/falsifier is folded and the Discussion carries [DIVERGENCE_FOLDED @ <comment-id>].
  • A peer completes the required STEP_BACK sweep.
  • The selected contract carries the required Decision Record.
  • This ticket body is rewritten from provisional evidence into the graduated Contract Ledger and acceptance chain.
  • The [PROVISIONAL_UNGRADUATED] marker is removed only after graduation.

Eventual Runtime Evidence Floor

  • A physical installed-consumer fixture serves the workspace at an absolute mount while origin / belongs to another application.
  • Shallow and nested apps work in development, dist/esm, dist/development, and dist/production.
  • App, Main, core workers, lazy chunks, global/class CSS, Font Awesome, and resources resolve beneath their declared authority with zero path 404s.
  • Each of the five current derivations has a red control that fails the fixture.
  • Existing relative configs and external parent-prefixed appPath values receive explicit bounded migration behavior.
  • Resolution failures name the owning config field and resolved URL instead of surfacing as undefined promise rejections.

Decision Record impact

REQUIRED — this changes or formally defines the public cross-environment path contract.

Out of Scope

  • Reorganizing a consumer repository or its server.
  • Data/Canvas/Task bundle reach; that architecture continues in D#1881.
  • Changing Webpack publicPath: 'auto' without a graduated option requiring it.
  • Consumer-local loader patches before the Engine contract settles.

Related

Related: D#17886, #9586, #6725, #3173, #2180, #1876, #15383

Origin Session ID: a63227a3-7e65-4384-ba2c-89a2fb98ec7b

Retrieval Hint: five path anchors basePath mainPath workerBasePath appPath Stylesheet substring absolute mount

Update 2026-08-31: Folded the two measured dist/esm generator defects into this provisional landing pad so D#17886 is not their shadow tracker. No implementation authorization or graduation claim; ADR + complete ledger rewrite remain gated.