refactor(frontend): establish source ownership and architecture gates (#6701)

This commit is contained in:
Alexandre Teixeira
2026-10-13 21:03:48 +01:00
committed by GitHub
parent b5b76fe46d
commit 1cc56cb9d7
18 changed files with 1955 additions and 3 deletions
@@ -0,0 +1,135 @@
# ADR 001: Feature-first native ESM with controlled deployment output
Date: 2026-10-13. Status: implemented F1 proof, proposed for maintainer adoption.
Baseline: official `upstream/dev` at `8feb50e89468235cd3e0986da80edbd3acaf2c40`.
Scope: source ownership and distribution foundation. Notes implementation is deferred.
## Decision
Maintain migrated JavaScript in `frontend/src/`. Publish native ES modules to
explicitly registered paths in `static/js/`, and commit source and output together.
Use `python scripts/build_frontend.py` to copy bytes without rewriting imports,
bundling, minification, source maps, timestamps or generated headers. A checked-in
`frontend/modules.json` maps source paths to deployment paths. All other static
files remain authored legacy modules or deployment assets until a reviewed slice
transfers their ownership. Do not claim that the whole static tree is generated.
The proof transfers maintenance of the stateless action-menu ordering helper to
`frontend/src/shared/actionMenuOrder.js`. Its output remains
`/static/js/actionMenuOrder.js`, with the same three exports and identical bytes.
All seven current consumers, HTML, service worker and runtime entry points retain
their existing URLs. There is no wrapper, extra request or second browser module
instance. The source copy is a development input, not a second runtime entry point.
Use `frontend/src/app/` for composition, `features/<feature>/index.js` for feature
public APIs, and responsibility directories only when implementation requires
them. The [handbook](handbook.md) defines dependency, ownership and lifecycle rules.
F1 creates only the shared proof, without empty application or feature scaffolding.
## Evidence and alternatives
Both options were executed against this helper, rather than assumed to work.
Measurements are local compatibility observations, not general performance claims.
| Property | Controlled native ESM copy, selected | esbuild 0.25.12 controlled ESM build |
| --- | --- | --- |
| Proof output | 1,688 bytes, identical SHA-256 to existing runtime | 1,554 bytes with `--bundle --format=esm --target=es2022 --platform=browser` |
| Repeatability | Clean temporary publication and idempotent rerun tested | Two independent compiler invocations emitted identical bytes |
| Runtime contract | Existing exports, imports, cache entry and module identity unchanged | Export names, icon and ordering matched in an executed Node import |
| Tooling | Python standard library; no production dependency | Compiler and platform binary needed for artifact production |
| Local cost | Copy check: 0.03 s, 15,596 KB maximum RSS | Cached `npx` ESM transform: 0.58 s, 114,196 KB maximum RSS |
| Incremental migration | Explicit file ownership, preserved module boundaries | Feasible for the stateless leaf; stateful facade/chunk identity still needs a separate graph spike |
| Release implications | Commit output; install/startup paths consume existing static assets | Would also need committed or release-produced output plus compiler locking and facade/chunk manifest policy |
The timing rows measure different tool paths, including `npx` overhead. The
compiler's single-leaf success does not establish stateful graph or lazy chunk
compatibility. The 134-byte bundle reduction does not justify that additional
contract in F1. A future compiler adoption requires a new ADR and graph-level
evidence. React, Vue, TypeScript, a global state library, hashed chunks and a
development server have no demonstrated necessity here.
Acorn 8.19.0 is added only as an exact, lockfile-pinned development dependency for
architecture checks. A real JavaScript parser is needed to enforce boundaries
across multiline imports, re-exports and lazy thunks without treating comments
and string literals as executable imports. It is MIT licensed, about 568 KB
unpacked, and contributes no deployed JavaScript or runtime dependency.
## Distribution contract
1. Source and generated output are reviewed and committed together. CI checks
byte parity; it does not silently rebuild and accept drift.
2. Native Python startup continues to use `STATIC_DIR` and `/static`, without
invoking the publisher. Native installers already obtain tracked static files.
3. Docker's current `COPY . .` includes the output. `.dockerignore` excludes
`node_modules/`. The Docker runtime and entrypoint require no frontend build.
4. `Odysseus.spec` and `build-windows-portable.ps1` already include `static/`.
The macOS wrapper uses the Python checkout and its static assets. Neither
needs the source tree or frontend tooling to serve the proof.
5. A release producer runs the checks before packaging and distributes the
entire matching static graph. The publisher is not a transactional deployment
mechanism. Partial copying of a new graph into a live deployment is unsupported.
6. New output paths must declare LF checkout behavior in `.gitattributes`, as
this proof does, so Windows checkout conversion cannot create false byte drift.
Before checking parity or writing output, the publisher uses the manifest to
require effective `text: set` and `eol: lf` on every source/output path. It asks
Git in a temporary repository containing only the applicable repository-owned
`.gitattributes` files, including nested rules. Global/local Git overrides
cannot supply missing declarations. No blanket legacy-JS rule is introduced.
Git is a development/publication prerequisite, not a startup dependency.
An isolated distribution probe executes the real FastAPI lifespan, shell and
static requests after omitting `frontend/` and `node_modules/` and removing Node
from the child process search path. It verifies bytes, MIME, CSP, revalidation,
source-tree 404 behavior and static path confinement. The same probe runs in an existing Docker image
with networking disabled. This is not a claim that a fresh full Docker image or
Windows/macOS executable was built; see [validation](validation.md).
## Imports, identity and offline operation
New maintained modules use one unversioned deployment URL. Existing legacy URLs,
including their version queries, stay unchanged. A copy mapping must preserve
the resolution of every literal relative import; the architecture checker rejects
a mapping that changes that resolution. Canonical absolute `/static/js/...`
imports are also supported. No bare packages or browser imports of `frontend/src`
are supported. Existing graph and identity guards continue to cover the runtime.
Each registered output joins the exact `PRECACHE` or `PANEL_PRECACHE` array. The
new checker parses those arrays, rather than accepting a URL mentioned in a
comment. Installation fetches do not evaluate lazy modules. Import-time DOM work,
timers, state creation and network I/O must be characterized before changing an
entry point's load timing. New source dependency cycles are rejected; existing
legacy cycles are inventoried and remain outside the new rules.
The proof already has its exact precache entry, so no SW cache-name or HTML edit
is needed. Its first evaluation in a fresh offline browser page passes, and
importing the panel registry does not evaluate the editor. The current worker's
`/static/` scope cannot control the SPA root. Open PR #6227 addresses that existing
limitation; F1 does not fix or mask it. Full root offline reload is not certified.
## Security and compatibility
Keep backend authorization and owner scope authoritative. No agent, database,
auth, CSP, static mount or API implementation changes belong to this decision.
The helper remains same-origin ESM without eval, compiler runtime or external
resources. No new source maps or vendored assets are emitted; existing SRI/CSP
policy and distribution notices remain unchanged. The publisher validates path
containment, symlinks, duplicate ownership and unmapped source before writing.
It never deletes unowned static assets. Deleting or retiring outputs is an
explicit reviewed migration operation, not automatic cleanup.
## Rollout, rollback and limits
Adopt F1 for controlled, reviewed slices after maintainer review. Leave Notes,
bootstrap, HTML and service worker unchanged here. Register new files explicitly;
update offline manifests only when new runtime dependencies are introduced.
F1 rollback can remove the source manifest, proof tooling, gates and documentation
without changing runtime bytes. Later rollback restores the complete prior
source/output/facade/precache set through an authorized follow-up change.
Boundary gates cannot prove semantic purity, owner isolation or correct teardown.
Domain platform/clock checks are intentionally conservative syntax guards;
behavior tests still establish those contracts. Worker URLs, styles and vendored
libraries are outside the maintained-JS import gate and need explicit review if
a future lane changes them. Legacy exceptions require an exact specifier and a
module-scoped reason; there are none in F1. New source is not permission to
grandfather new dependency cycles or to replicate broadly shared mutable state.
+127
View File
@@ -0,0 +1,127 @@
# F1 runtime and architecture assessment
Assessment date: 2026-10-13. Repository: `odysseus-dev/odysseus`.
Worktree: `worktrees/architecture/frontend-foundation`.
Branch: `refactor/frontend-architecture-foundation`.
HEAD, local `upstream/dev`, and the live GitHub official `dev` commit were all
`8feb50e89468235cd3e0986da80edbd3acaf2c40`. The initial worktree was clean.
That commit is the merged PR #6495, selected Notes/Todos agent context.
`origin` points to the maintainer fork; integration targets official `upstream/dev`.
Inputs read completely: the 2026-10-13 frontend architecture RFC and coding brief
from Downloads, `specs/frontend.md`, repository instructions and existing tests.
The current user brief supersedes the coding brief's `origin/dev` wording and
defines the Notes state/workflow slice before reminders. The design is self-contained
in this directory and does not require access to Downloads.
Input provenance:
- RFC SHA-256: `11dedcbb9971f4d787f2721b9c27aa43299baba39ca91066d01507d2d69ac4f8`.
- Coding brief SHA-256: `e4882867617b9c732ce4f445e3bc2da0b4ec1315119f8226c1fd8c1acbacf29d`.
## Module graph and bootstrap
`node scripts/check_frontend.mjs --inventory` reproduces the parser-based inventory.
It excludes vendored `static/lib/` and committed editor build output. At this base
it scans 230 JS files, finds 707 static import/re-export edges and 108 literal
dynamic import edges, and finds no missing targets. No computed dynamic imports
were found in the original parser audit. The earlier existing-regex scan counted
814 edges; executable syntax parsing counts 815. Use parser evidence for design
and retain the existing regression guard unchanged.
| Area | Current boundary and implication |
| --- | --- |
| HTML shell | `static/index.html`: nonce-bearing boot scripts, CSS/preloads, direct ESM script tags, then app/init/a11y. Multiple entry roots mean module identity matters. |
| App composition | `static/app.js`: feature imports, globals, route/sidebar composition, session hydration and auth-fetch wiring. It eagerly imports Notes, Calendar, Chat, Documents and Settings. |
| Startup | `static/js/startupShell.js`, `sessions.js`, `init.js`: first-paint scheduling, deferred hydration, user-switch storage cleanup and cosmetic permission gates. Preserve boot ordering. |
| Lazy loading | `static/js/panels.js`: only `editor` is registered. A memoized import thunk shares successful/in-flight promises and retries failed imports. Do not label all panels lazy. |
| Optional libraries | `markdown.js` lazily loads vendored KaTeX/Mermaid. KaTeX and fonts are precached; Mermaid becomes cached after first use. Pyodide has a CDN dependency. |
| Feature packages | Settings registry/navigation/finder/lifecycle, Calendar utilities/reminders, Email wrapper/package and editor tools already demonstrate partial decomposition. |
Highest import fan-in: `ui.js` 77, `spinner.js` 55, `editor/state.js` 39,
`escMenuStack.js` 23, and storage/modal/z-order helpers 17 each. The measured
static strongly connected components are:
- Theme and UI.
- Email Inbox, compatibility wrapper, package index, AI reply, attachments,
body rendering, menus, reader, settings page and unsubscribe.
- Cookbook, diagnosis, HW Fit, download, running and serve modules.
These are existing compatibility constraints, not approved patterns for new
source. Dynamic edges connect more features but do not themselves establish
initialization cycles. No global legacy dependency rewrite is attempted in F1.
## State, side effects and ownership
| Owner | Evidence and architectural treatment |
| --- | --- |
| Shared configuration | `appConfig.js` owns invalidatable settings/tool request promises and consumes a sessionStorage prefetch once. Retain single identity and writer invalidation. |
| Browser persistence | `storage.js` centralizes keys and safe access; feature files also use direct localStorage. `init.js` cleans storage on user changes. Preferences are not an authorization store. |
| Feature state | Notes has module variables, selection sets, undo/drafts and reminder state. Email exports a broadly mutable package state object; editor state is imported by 39 modules. Keep ownership explicit; do not expand these into a global store. |
| Import-time effects | Notes reads storage/window and schedules reminders. Email state reads localStorage. Theme/UI initialize from DOM readiness; modal manager installs global listeners. Chip/mobile helpers bind at startup. Moving an import can change behavior before any exported function is called. |
| Compatibility globals | App exposes controllers on `window`; Notes exposes `window.notesModule`. They are compatibility bridges, not the new inter-feature API convention. |
| Async ownership | Session hydration, config invalidation, draft serialized saves and request-generation guards already prevent stale completion. New slices must preserve these rather than only move declarations. |
Good patterns to retain: narrow zero-state leaves, explicit wrapper export lists,
public API tests, retryable memoized lazy imports, shared config invalidation,
request-generation checks and source/package-aware regression helpers.
Patterns to avoid: blanket barrels exposing internals, feature imports from
generic helpers, cycles through coordinators, direct writes to another feature's
state or DOM, anonymous global listener ownership, import-triggered network/timer
initialization in new APIs, and helpers grouped by convenience instead of purpose.
## Runtime, assets and distribution
| Contract | Verified source |
| --- | --- |
| Static URL and revalidation | `app.py`: `register_static_mime_types`, `_RevalidatingStatic`, `/static` mount. JS MIME is `text/javascript`; JS/CSS/HTML have `Cache-Control: no-cache`. |
| App paths | `src/constants.py` and `src/runtime_paths.py`: checkout root in native mode, PyInstaller `_MEIPASS` in frozen mode; static assets follow app root, persisted data follows data-directory policy. |
| CSP | `core/middleware.py`: same-origin scripts, nonce and jsDelivr allowance; `src/app_helpers.py` injects HTML nonces. No CSP relaxation is needed for the proof. |
| Integrity | No `integrity=` attributes were found in the shell. Proof output is byte-identical and emits no source maps; no existing SRI or vendored artifacts change. |
| Runtime-generated assets | `/api/generated-image/...` uses a confined data path and owner handling; report/render endpoints, Blob manifests/icons and uploads are runtime concerns outside static source publication. |
| Docker | `Dockerfile` copies app code and existing static output; `.dockerignore` excludes node_modules. Node/npm in the image support Browser MCP, not frontend startup. |
| Native installers | `setup.py`, `start-macos.sh`, `launch-windows.ps1`, systemd service: Python dependencies/startup consume the checkout. No frontend build is invoked. |
| Windows desktop | `Odysseus.spec` and `build-windows-portable.ps1` include the whole static directory. Maintain output here rather than require a new bundle path or compiler. |
| macOS | `build-macos-app.sh` creates a launcher around the Python checkout. Static serving follows the existing checkout path. Installer execution was not performed on Linux. |
## PWA limitations and test coverage
`static/sw.js` matches exact URLs, including queries. Install fetches each
precache entry separately and tolerates failed fetches. It does not discover
transitive dependencies. HTML root and JS/CSS handlers are network-first;
other static assets are cache-first with background refresh. API and non-GET
requests bypass caching. Cache updates need explicit version/manifest review.
The older frontend spec's root stale-while-revalidate description is stale.
Registration in `static/index.html` currently requests no wider scope, so the
worker controls `/static/`, not `/`. Root offline reload is therefore a known
pre-existing gap. PR #6227 proposes the scope/header correction. F1 tests actual
offline ESM loading under the current scope and leaves that separate fix alone.
Offline APIs, first-use Mermaid and optional CDN runtimes are not certified.
Existing coverage includes runtime graph resolution and version parity; editor
offline graph coverage and lazy-loader retries; Email API/export/evaluation
coverage; Notes autosave, XSS, selection/listener and agent-context regressions;
MIME, nonce and runtime paths. Tests use pytest, Node's built-in runner and existing
Playwright Chromium. `tests/TESTING_STANDARD.md` requires behavior-first, isolated
tests. F1 adds real publication failures, architecture violations and an offline
browser proof. It does not replace existing source-sensitive tests.
## Open PR overlap, checked live on 2026-10-13
The open-PR listing was fetched through all pages; selected PR file lists were
inspected. Refresh this inventory before any later slice.
| PR | Overlap and disposition |
| --- | --- |
| [#6470](https://github.com/odysseus-dev/odysseus/pull/6470) | Theme/sidebar redesign touches HTML, SW, shared UI and CSS. F1 changes none of those assets. Future manifest/UI slices need collision review. |
| [#6695](https://github.com/odysseus-dev/odysseus/pull/6695) | OXS touches bootstrap, HTML, platform, middleware and other backend boundaries. F1 does not establish an extension API or modify that platform. |
| [#6227](https://github.com/odysseus-dev/odysseus/pull/6227) | Root SW scope/header fix. Independent follow-up dependency for broader offline certification. |
| [#6394](https://github.com/odysseus-dev/odysseus/pull/6394) | Older CSS extraction touches legacy style/SW assets; requires separate supersession review. |
| [#6627](https://github.com/odysseus-dev/odysseus/pull/6627) | npm development-dependency update overlaps package/lock files; reconcile the exact Acorn development entry on integration. |
| [#6585](https://github.com/odysseus-dev/odysseus/pull/6585), [#6647](https://github.com/odysseus-dev/odysseus/pull/6647), [#6652](https://github.com/odysseus-dev/odysseus/pull/6652) | Notes optional-field persistence and agent routing/dispatch. Future characterization must reflect their merged state; F1 changes no Notes or backend behavior. |
| [#6594](https://github.com/odysseus-dev/odysseus/pull/6594), [#6152](https://github.com/odysseus-dev/odysseus/pull/6152) | Desktop launcher/build and frozen-runtime packaging work. Existing paths stay stable; packaging still needs platform-owner validation. |
PR titles alone do not establish compatibility. This report records the checked
file overlap and relevant dependencies, not approval of those implementations.
+223
View File
@@ -0,0 +1,223 @@
# Odysseus frontend architecture handbook
Status: F1 foundation and continuation guide, pending review. The final Notes
reference case study belongs to the comprehensive Notes draft PR; the recipient
handoff follows its integration. This document must evolve with reviewed internal
checkpoints and distinguish executed work from proposals.
Read the [ADR](ADR-001-source-and-deployment.md), [assessment](assessment.md),
[validation report](validation.md) and [Notes roadmap](notes-roadmap.md).
The frontend program ends with the Notes reference decomposition and handoff.
Calendar, Documents, Email, Chat, Skills, Settings and other later decompositions
belong to the designated frontend maintainer; do not start them automatically.
## Source, ownership and public APIs
`frontend/src/app/` composes public feature APIs and platform ports. Each
`features/<feature>/index.js` is an intentional API, with explicit named exports
and a compatibility default only when existing consumers require one. Internals
live under `domain/`, `application/`, `infrastructure/` or `ui/` as needed. Do not
create empty directories, boilerplate controllers or a framework by convention.
Feature names `app` and `shared` are reserved.
Keep pure business rules in domain; transitions and workflows in application;
HTTP/storage/browser services in infrastructure; DOM rendering and lifecycle in
UI. The index composes adapters, state and views. Application receives adapters
as explicit arguments or constructor ports, rather than importing concrete
infrastructure. Views call application operations; infrastructure does not own
business decisions. A public API returns data/operations, not mutable stores or
private DOM nodes. Importing an API should define capabilities; explicit lifecycle
methods activate them. Legacy side effects remain characterized until a reviewed
slice safely changes their placement.
Shared code has a named, proven cross-feature responsibility. `shared/domain/`
holds pure primitives, `shared/platform/` holds narrowly scoped platform adapters,
and `shared/ui/` holds common DOM capabilities. A named stateless root leaf such
as `shared/actionMenuOrder.js` is also allowed and receives domain restrictions.
Do not create a central `utils.js` collection or move feature business rules into
shared simply to satisfy an import rule.
## Mechanically checked dependency contract
| Importer | Permitted maintained-source targets |
| --- | --- |
| App composition | Other app code, shared code, feature `index.js` APIs |
| Feature index | Its own responsibility modules and shared code; other feature public indexes |
| Feature domain | Its own domain and shared domain/stateless leaves |
| Feature application | Its own application/domain, shared domain, other feature public indexes |
| Feature infrastructure | Its own infrastructure/domain, shared domain/platform |
| Feature UI | Its own UI/application/domain, shared domain/platform/UI |
| Shared domain leaf | Shared domain leaves only |
| Shared platform | Shared platform/domain only |
| Shared UI | Shared UI/platform/domain only |
No feature imports app composition. No shared code imports a feature. No external
feature imports private responsibility modules. UI and infrastructure communicate
with another feature through a supplied application operation/port. Maintained
source dependency cycles, including literal lazy imports, fail the architecture
gate. If features need bidirectional coordination, app composition injects a
callback or adapter instead of creating mutual imports. Prefer this even when a
one-way public index import would pass the gate.
Cross-feature events must document event name, payload, acknowledgement, owner,
subscription disposal and delivery timing. Keep them local and specific; F1 adds
no global event bus. Preserve `odysseus:notes-context-selected` and its synchronous
`accepted` acknowledgement in future Notes work. The server remains authoritative
for owner filtering and untrusted selected IDs. Frontend APIs cannot grant access.
The checker parses executable ESM, forbids computed imports in new source,
requires exact precache entries, rejects unresolved/remapped relative imports,
and permits only canonical static JS URLs with one unversioned identity for
maintained modules. Domain code cannot mention known browser/HTTP/timer globals
or an ambient clock; use `now`/clock inputs. This is a conservative vocabulary
check, including shadowed names, not a semantic purity proof. Randomness, hidden
side effects, authorization and lifecycle behavior require behavioral tests.
Legacy imports from new source default to forbidden. If an incremental bridge is
necessary, declare `legacyImports` on that source's manifest entry as a list of
`{ "specifier": "/static/js/example.js?v=existing", "reason": "contract, owner and removal slice" }`.
Only feature composition, infrastructure or UI boundaries may use it. Exact
specifier/reason and target existence are required; unused exceptions fail.
Keep the existing query identity. New domain/application modules receive no
legacy exception. F1 has zero exceptions. Do not use exceptions to invent new
shared platform dependencies on feature internals.
## State and lifecycle
Each feature has one authoritative mutable application-state owner. A transition
operation owns writes; UI receives a view/snapshot or subscription. Preserve
existing objects and initialization timing when extracting a stateful module.
Do not instantiate a new store in both the legacy facade and the feature API.
Compatibility facades only forward the same live implementation.
Document lifecycle scopes: page, authenticated user, session, mounted panel and
editor/draft. Name owners of subscriptions, DOM listeners, timers, observers,
pending requests and caches. Mounting must be idempotent; disposal releases the
resources it owns. Background reminders may outlive an open panel. Closing the
panel must not silently stop those reminders during extraction.
Keep request-generation and selected-session checks around asynchronous
completions. Serialize writes where the current contract requires it. Dispose
subscriptions explicitly and prevent late updates to a replaced view. Feature
state changes on user/session switch must follow current cleanup policy. Browser
storage is for preferences/drafts/cache state, never a substitute for backend
authorization, credential storage or owner filtering. Define every storage key's
owner, retention/reset behavior and compatibility format before moving it.
## Creating a module or introducing a feature
1. Identify responsibility, owner, public consumers, side effects and behavioral
evidence before editing. Start with a pure leaf when introducing a feature.
2. Place only the required source file(s), for example
`features/example/domain/calculate.js`. Add an index only when there is a
real supported feature API to compose.
3. Register each source in `frontend/modules.json`. A typical new mapping is
`features/example/domain/calculate.js` to
`js/features/example/domain/calculate.js`. Preserve the source/deployment
relative graph. The proof's legacy URL mapping is an explicit special case.
4. For an extracted leaf, the legacy feature coordinator may import its output
as a temporary composition bridge. New maintained features use public indexes;
the legacy tree is not globally forced into the new boundary rules in F1.
5. Add the exact output URL to the appropriate explicit SW array, retain lazy
evaluation timing and bump the cache name when the graph changes. Declare
`text eol=lf` for both mapped paths in repository-owned `.gitattributes`
files; source-tree or scoped output patterns are allowed. The publisher checks
effective Git rules, including nested overrides, and rejects a missing LF rule
even if output bytes already match. Do not broaden unrelated legacy checkout
rules or automatically rewrite all existing query identities.
6. Run the publisher, inspect the source/output diff, then run architecture,
identity, graph, offline and focused behavior checks. Include both sides of
the mapping in review. Retiring output requires a separate reviewed deletion;
the publisher does not clean the static tree.
```sh
python scripts/build_frontend.py
python scripts/build_frontend.py --check
npm ci --ignore-scripts --no-audit --no-fund
node scripts/check_frontend.mjs
node scripts/check_frontend.mjs --inventory
node --test tests/frontendArchitecture.test.mjs
./venv/bin/python -m pytest -q -rs tests/test_frontend_build.py tests/test_frontend_foundation_offline_js.py tests/test_frontend_module_graph.py tests/test_frontend_module_version_parity.py tests/test_panel_loader_js.py
./venv/bin/python scripts/verify_frontend_runtime.py
```
Use your actual repository virtualenv path; the F1 environment is recorded in
validation. Node 20+ follows current CI. Existing Playwright/browser prerequisites
are documented in `tests/README.md` and `tests/KNOWN_FAILURES.md`. Missing Node can
skip Python browser wrappers, which is a coverage gap, not a pass. The standalone
Node gates require locked development dependencies and fail when missing.
The publisher validates all inputs before writing and only overwrites owned
outputs whose bytes differ. It is idempotent and working-directory independent.
Git must be available for publication and `--check`; neither runs at application
startup. Attribute evaluation uses a private temporary Git repository and ignores
global/local overrides, so checkout consistency is reproducible in CI. It adds
no runtime configuration or backend imports. CI checks LF attributes, parity and source
syntax in existing jobs; the Node job also runs architecture self-tests. Existing
runtime identity tests remain authoritative across legacy imports, HTML and SW.
The isolated runtime probe is included in the new pytest publication tests.
## Review, release and rollback
Review the source/output mapping and exact exported API, then the owner/dependency
contract and behavior tests. Check manifests, URL identities, MIME/CSP, load timing,
storage compatibility and disposal. Reject unexplained feature behavior changes.
Do not repair unrelated defects under an extraction title.
Release from a validated revision with committed output. Native startup and
packagers consume static files without a compiler. Run parity before producing a
Docker or desktop artifact. Test the actual packaged application on its platform;
Linux contract probes cannot certify macOS/Windows executables. Deploy matching
shell, facades, graph and manifests as a unit. Keep an authorized path to the last
complete artifact. Rollback restores the whole matching set, not just an entry
file or cache version. Do not delete a facade until its public consumers have
migrated and compatibility tests make that decision reviewable.
## Proposal and PR templates
For a decomposition proposal, record: baseline; responsibility map; current public
API and consumers; state owner; I/O and lifecycle boundaries; planned slice;
allowed dependencies/temporary exceptions; preserved behavior; deterministic
characterization tests; deployment/offline changes; overlapping PRs; rollback;
explicit non-goals and remaining uncertainties.
For each migration PR, lead with the responsibility moved and resulting boundary.
Record source/output paths, API compatibility, state/lifecycle ownership,
characterization results, exact validation commands, platform gaps, exception
removal plan, upstream dependencies and rollback. Attach a concrete before/after
example when needed to establish behavioral equivalence. Preserve the repository
PR template and never claim unexecuted checks.
F1 is an entirely separate foundation-only PR. Deliver the complete Notes
decomposition, regression tests, compatibility validation and final reference
case study through one draft PR targeting official `dev`, using one Notes branch
and worktree after F1 integration. N0 through N6 and the N5 substages are internal
engineering checkpoints, not independent PRs or merges. Each checkpoint records
focused tests, review evidence, its validated revision or reproducible snapshot,
and rollback to the prior matching source/output/facade/precache set. Keep the
draft open while those checkpoints progress; mark it ready only after the entire
decomposition passes the [roadmap's acceptance gates](notes-roadmap.md).
Structure the Notes PR description around the final integrated design and link
each checkpoint's evidence. The receiving maintainer may use smaller independent
PRs for future frontend features at their discretion; the one-PR Notes decision
does not impose that delivery size on later work.
## Program backlog and final handoff
Priority order: review/integrate F1; resolve root offline certification with the
owner of PR #6227; complete the Notes checkpoints and final reference case study
within one draft PR following the [roadmap](notes-roadmap.md); integrate the
complete Notes PR after its acceptance gates; finalize the recipient handoff.
Only then does the receiving maintainer prioritize Calendar, Documents, Email,
Chat, Skills and Settings against current behavior and risk. Existing partial
packages and high fan-in helpers need assessment, not automatic replacement.
Final H acceptance requires a designated maintainer, the integrated Notes PR
revision and its internal checkpoint receipts, the actual public API/state/lifecycle
map and final reference case study included in that PR, validated deployment/offline
commands, known exceptions/debt,
outstanding PR dependencies, rollback evidence and a reproducible independent
setup by the recipient. F1 provides the handbook structure and contracts; it does
not claim Notes or the completed handoff have occurred. The initiating architect
then returns to separately authorized non-frontend architecture work.
+173
View File
@@ -0,0 +1,173 @@
# Notes reference decomposition roadmap
Status: planning only. F1 changes no Notes implementation. Begin after the
foundation has been reviewed and integrated into official `upstream/dev`.
The reference program ends after Notes integration and the completed H handoff.
F1 remains an entirely separate foundation-only PR. The complete Notes
decomposition, regression tests, compatibility validation and final reference
case study belong to one comprehensive draft PR targeting official `dev`.
## Verified current responsibility and compatibility map
Baseline: `8feb50e89468235cd3e0986da80edbd3acaf2c40`.
The module is `static/js/notes.js`, about 5,470 lines, with browser identity
`/static/js/notes.js?v=20260911notesselectioncancel1`. Preserve that identity across
the app import, HTML, SW and lazy consumers until a separately reviewed migration
changes the contract everywhere.
| Responsibility | Current evidence | Intended owner after its reviewed slice |
| --- | --- | --- |
| State and CRUD | `_notes`, selection/filter/editing/loading variables, `_undoStack`, `_fetchNotes`, `_saveNote`, `_patchNote`, `_archiveNoteById` | One application state owner and workflow operations; no second reminder/UI copy |
| Dates and recurrence | `_normalizeRepeat`, `_advanceRecurring`, `_nthWeekdayOfMonth`, `_lastWeekdayOfMonth`, `_toLocalDatetimeStr`, repeat formatting helpers | Pure domain calculations with explicit clock inputs |
| Network and persistence | Notes CRUD/reorder, upload, reminder firing, draft and reminder localStorage keys | Infrastructure adapters preserving request payloads, credentials and error contracts |
| Reminders | `_checkReminders`, `_startReminderLoop`, `_initReminders`, notification permission/display and due/fired badge calculations | Application reminder workflow plus injected clock/storage/notification/API ports |
| Draft autosave | `_draftKey`, `_collectFormDraft`, `_wireDraftAutosave`, `_applyDraftToNote`, `_commitOpenInPlaceEditor` | Draft workflow with editor view/persistence ports and serialized saves |
| UI | `openPanel`, `closePanel`, `_renderNotes`, forms/cards/checklists, masonry, color/photo/drawing, mobile swipe/reorder | Scoped UI modules with explicit mount/dispose and application operations |
| Agent context | `#notes-send-agent` handler, selected IDs, `odysseus:notes-context-selected` | Preserve event payload and synchronous acknowledgement; Chat owns attachment/session state |
| Agent solve action | `_agentSolveNote`: default-chat lookup, session creation and chat stream | Separate existing workflow; do not conflate with selected-note attachment |
| Compatibility coordinator | Default controller, named aliases, `window.notesModule`, delayed reminder startup | Stable legacy facade/composition bridge until consumers safely migrate |
The default API currently includes `openPanel`, `closePanel`, `togglePanel`,
`isPanelOpen`, `openNote`, `newNote`, `openNotes`, `closeNotes`, `isNotesOpen` and
`refreshDueBadge`. Named exports also include `dismissFiredReminderDot` and the
panel/open aliases. Characterization must pin the complete actual namespace and
default object separately, including identity of aliases and the window bridge.
Consumers include `static/app.js` sidebar/routes/badge polling,
`chatStream.js` note tool updates, `chatRenderer.js` note links and
`document.js` coordination. `static/index.html` and `static/sw.js` are entry/cache
contracts. The shared UI, confetti, modal, color picker, drag, tiling, docking,
z-order and Escape helpers are direct dependencies. Those legacy dependencies
remain in the coordinator until their ownership is reviewed, rather than moving
all UI coupling into domain modules.
Import-time behavior includes window origin/storage reads and
`setTimeout(_initReminders, 3000)`. The successful bootstrap loads Notes and starts
a 30-second reminder interval. App composition separately refreshes the badge
every five minutes. Panel/editing listeners, draft timers, highlight animations,
ResizeObserver and drawing/touch handlers have shorter scopes. Record them before
extraction; do not stop background reminders merely because a panel closes.
Listener cleanup concerns require characterization, not an unrelated fix hidden
inside an extraction.
Persistence includes `odysseus-notes-view`, label collapse and hint preferences,
fired/glowed/pending/active reminder markers, dismissed timestamp, and
`odysseus-note-draft-<id>` including `__new__`. Current direct APIs include Notes
CRUD/reorder/fire-reminder and upload, plus default-chat/session/chat-stream for
the existing solve action. Backend owner isolation and permission handling stay
authoritative and are outside this frontend decomposition's implementation scope.
## PR #6495 contract that every relevant slice preserves
Notes sends `odysseus:notes-context-selected` with `{ noteIds, accepted: false }`.
Chat's synchronous listener accepts only a valid active or pending chat, updates
session/pending selection, sets `accepted`, and updates the context indicator.
Notes clears select mode only after acceptance. Session lifecycle events
`odysseus:session-changed` carry session/materialization information. Chat appends
selected IDs as `selected_note_ids` when sending; server parsing validates IDs,
filters by owner and treats the resulting prompt context as untrusted.
Keep selection transfer to a materialized chat, reset on a new chat, rejection
when no chat/model can accept it, aliases and indicator behavior. Do not broaden
frontend selections into permissions or bypass the backend. See
`tests/selectedNotesContext.test.mjs`, `tests/test_selected_notes_context.py`,
`static/js/chat.js` selected-context listeners and send payload, and
`static/js/sessions.js` lifecycle event producers.
## One Notes branch, worktree and draft PR
After F1 integration and authorization to begin Notes, create one worktree and
branch, suggested name `refactor/notes-reference-decomposition`, from the latest
official `upstream/dev`. N0 through N6 are sequential internal engineering
checkpoints on that same branch. No independent GitHub PR is required for a
checkpoint or a UI substage. Maintain one draft PR targeting official `dev`, with
checkpoint evidence linked from its description. Keep it a draft until the
complete decomposition passes all acceptance gates below.
| Checkpoint | Responsibility | Deliverable and exit evidence |
| --- | --- | --- |
| N0 | Characterization | Public API/consumer, responsibility, state, lifecycle and network map; deterministic behavior tests; PR-overlap refresh; no extraction |
| N1 | Recurrence domain | Only pure date/recurrence rules to `features/notes/domain/recurrence.js`; preserve old wrapper call contracts, time-read position and returned strings; new offline dependency registered |
| N2 | Infrastructure | HTTP and storage adapters with exact request/error/key contracts; retain state ownership and backend authorization |
| N3 | Application | Single state owner and CRUD/optimistic/undo workflows; UI/reminders call operations; no competing arrays or stores |
| N4 | Reminders | Timer/notification/reminder orchestration through explicit ports; characterize startup, catch-up, deduplication, badge and background behavior |
| N5 | UI substages | Bounded card/panel, editor/draft, checklist, drawing and mobile reviews within the same PR; explicit listener/observer disposal and the same application operations |
| N6 | Compatibility and reference case study | Reduce coordinator; full API/consumer/lifecycle/offline comparison; retain legacy facade unless removal is proven safe; complete the final reference case study in the same PR |
Do not create the Notes worktree, branch or PR during F1. N5 remains several
bounded internal reviews rather than a second monolith move. Every checkpoint
and UI substage must have its own focused tests, review evidence, lessons,
exceptions and a rollback plan. Record the validated revision or reproducible
patch/snapshot, prior known-good checkpoint, exact test commands/results and
how to restore its matching source/output/facade/precache set while preserving
unrelated work. Checkpoint commits require the applicable maintainer authority;
no checkpoint or commit is created by this planning document.
Advance only after the current checkpoint's required checks and review findings
are resolved. A failed checkpoint returns to its prior validated state or receives
a bounded correction on the same branch; it does not require a separate PR.
If upstream changes require updating the baseline, update the same Notes branch,
record that revision and rerun affected checkpoint and compatibility gates.
## Complete Notes PR acceptance gates
Before marking the single draft PR ready for review:
- Complete N0 through N6, including all N5 substages, with their tests, review
receipts and reproducible checkpoint/rollback evidence.
- Pass the required full Notes regression and compatibility checks on the final
combined revision, including PR #6495 selection/context behavior, public APIs,
module identities, single state ownership, storage formats, timers and teardown.
- Pass source/output parity, LF attribute, architecture, import graph and exact
offline manifest gates; verify current bootstrap/lazy behavior and relevant
native/packaging contracts. Record platform limits and distinguish preserved
offline module behavior from separately certified root offline reload.
- Resolve required gate failures and review findings; document unrelated existing
failures or coverage gaps explicitly rather than bypassing required checks.
- Include the final reference case study in this PR: actual before/after APIs,
dependency/state/lifecycle maps, stage decisions and lessons, validation recipes,
temporary exceptions, known debt and tested rollback/compatibility procedures.
These are readiness gates for the complete PR, not separate stage merges.
The receiving maintainer may choose smaller independent PRs for future frontend
features; this one-PR delivery decision applies to the Notes reference program.
## N0 and N1 characterization requirements
Pin recurrence normalization for legacy bare repeat values and explicit values;
none/unknown/malformed input; daily/weekly/monthly/yearly advancement; month ends,
leap years, nth/last weekdays including absent fifth occurrences; overdue catch-up
and the 5,000-step guard; future anchors; local serialization and preserved hours;
DST gaps/overlaps under explicit time zones. `_advanceRecurring` currently reads
`Date.now()` after its first step. Inject a clock without changing that ordering.
Record the existing behavior of invalid dates rather than inventing improvements.
Characterize request error handling, optimistic rollback/undo, archive/delete,
selection/filter/reset, ordering, uploads and notification permission errors.
For drafts, preserve synchronous local backup before the 500 ms persistence
timer, intentional empty text, checklist type fallback, serialized writes,
failure recovery and refusal to clear a newer draft when an older save finishes.
Use stub clocks/ports and temporary storage for unit behavior; browser tests for
public namespace evaluation, DOM lifecycle, selected context and actual offline
loading. Run real modules through their output paths. Do not rewrite source-range
tests wholesale; migrate each only with its responsible slice and equivalent
behavioral assertions. Existing source-sensitive Notes autosave tests currently
read `notes.js`, so extraction has an explicit test-maintenance dependency.
Before a slice, refresh #6585/#6647/#6652 backend behavior and #6470/#6695 shared
UI/platform overlaps from the [assessment](assessment.md). Existing fixes may
change characterization inputs, but this program does not merge or silently
reimplement those PRs. The independent root-SW fix #6227 is required before a
claim of full SPA offline reload readiness.
## H completion and stopping rule
The final reference case study is completed inside the Notes PR before it becomes
ready for review. After that PR is integrated, finalize H with the integrated
revision and checkpoint-to-integration receipts, outstanding dependencies and
the recipient's independent reproduction. Have the receiving maintainer
reproduce setup and validation independently and record that receipt. Transfer
future feature ownership explicitly. Do not automatically begin Calendar,
Documents, Email, Chat, Skills or Settings; the initiating architect continues
with separately approved non-frontend monolith work.
+453
View File
@@ -0,0 +1,453 @@
# F1 implementation and review report
Date: 2026-10-13. Recommendation: ready for controlled incremental adoption after
maintainer review, with the platform/offline limits below. The foundation proof
is implemented and tested; Notes decomposition and the completed H handoff are
planned only. No commit, push, merge, PR or issue operation was performed.
## Notes delivery documentation correction
The maintainer's final delivery decision is one comprehensive Notes draft PR
targeting official `dev`, after the separate foundation-only F1 PR is integrated.
N0 through N6 are internal engineering checkpoints in one Notes worktree and
branch. The complete decomposition, regression tests, compatibility validation
and final reference case study belong to that single PR; it stays a draft until
the complete acceptance gates pass. Each stage retains its own test results,
review receipts and reproducible rollback/checkpoint strategy, without requiring
an independent GitHub PR. The receiving maintainer may choose smaller independent
PRs for future frontend features.
This correction updates only `notes-roadmap.md`, `handbook.md` and this report.
Implementation and test files retain their pre-correction bytes. All five
foundation documents passed link, fence, whitespace, no-em-dash and delivery
wording checks. `git diff --check` and the preserved-runtime diff check passed.
A before/after snapshot confirmed exactly these three changed documents and
15 unchanged existing F1 files. The implementation test results below remain
the previous executed evidence; implementation tests were not rerun in this
documentation-only pass.
## Independent review correction
The maintainer reports that the independent F1 review found no critical
architectural defects. This pass preserves the reviewed native ESM architecture
and scope. Its correction is to turn the documented LF checkout convention into
a manifest-driven publisher/CI requirement. The full independent review text
was not supplied; this report records the supplied finding and executed evidence.
The correction changes only `scripts/build_frontend.py`,
`tests/test_frontend_build.py`, the ADR, the handbook and this report. The
`.gitattributes` rules, architecture checker, CI workflow, manifest, dependencies
and stateless source remain as reviewed. The full F1 inventory below still has
18 files, including `.gitattributes`.
The publisher now evaluates both paths of every manifest entry through Git's
actual attribute engine before parity evaluation or any deployment write. It
requires `text: set` and `eol: lf` from applicable repository-owned attribute files,
including nested overrides. A private temporary repository and isolated Git
configuration prevent global attributes, local `.git/info/attributes`, templates
or alternate Git directory settings from masking an omitted declaration. Paths
are sent through NUL-delimited stdin, preserving spaces and using no shell/path
quoting assumptions. The working repository's attributes, index and output are
not changed by this check. Git is a publication/development prerequisite; the
application does not invoke it to build or check frontend assets at startup.
The production integration diff is the new `check_lf_attributes(root, paths)`
helper, four standard-library imports, this call, and updated CLI help:
```diff
@@ synchronize
if unlisted:
raise ValueError(f"source modules missing from manifest: {sorted(map(str, unlisted))}")
+ check_lf_attributes(root, sources | outputs)
stale = []
```
The tests add fixture LF declarations and 14 cases. Ten parameterized cases
reject missing LF, CRLF, auto-text and binary-text rules for source/output paths
in both publish and check modes, even when bytes already match. The other four
cover a future manifest pair failing before any output is published and passing
after its LF declaration; nested rules and a spaced path; global/local settings
failing to mask an omitted repository rule; and clear failure without development
Git. Existing publication, ownership, confinement, parity and runtime tests remain.
## Exact attributes diff and checkout evidence
`git diff -- .gitattributes` is unchanged from the reviewed F1 diff:
```diff
diff --git a/.gitattributes b/.gitattributes
index 8681aee1..dd49c4a0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -10,6 +10,10 @@
entrypoint.sh text eol=lf
docker/entrypoint.sh text eol=lf
+# Maintained frontend modules and this lane's byte-identical deployment output.
+frontend/src/** text eol=lf
+static/js/actionMenuOrder.js text eol=lf
+
# Windows-native scripts stay CRLF.
*.ps1 text eol=crlf
*.cmd text eol=crlf
```
Exact command and results:
```text
$ git check-attr text eol -- frontend/src/shared/actionMenuOrder.js static/js/actionMenuOrder.js
frontend/src/shared/actionMenuOrder.js: text: set
frontend/src/shared/actionMenuOrder.js: eol: lf
static/js/actionMenuOrder.js: text: set
static/js/actionMenuOrder.js: eol: lf
```
The sampled legacy contracts were also inspected:
| Path | text | eol |
| --- | --- | --- |
| `static/js/notes.js` | auto | unspecified |
| `static/index.html` | auto | unspecified |
| `launch-windows.ps1` | set | crlf |
| `start-macos.sh` | set | lf |
Beyond samples, all 2,479 unrelated tracked paths were compared against HEAD for
both attributes and matched. The current Git 2.53.0 supports the diagnostic
`--source=HEAD` option; the publisher uses only the longstanding plain
`check-attr --stdin` interface, so it does not depend on that newer option.
The read-only whole-tree comparison command was:
```sh
python3 - <<'PY'
import subprocess
paths = subprocess.check_output(['git', 'ls-files', '-z'])
def attributes(source):
result = subprocess.run(
['git', 'check-attr', *source, '-z', '--stdin', 'text', 'eol'],
input=paths, capture_output=True, check=True,
)
fields = result.stdout.decode().split('\0')[:-1]
return {(fields[i], fields[i + 1]): fields[i + 2]
for i in range(0, len(fields), 3)}
before, after = attributes(['--source=HEAD']), attributes([])
owned = lambda p: p.startswith('frontend/src/') or p == 'static/js/actionMenuOrder.js'
changes = [(p, a, before[(p, a)], value) for (p, a), value in after.items()
if not owned(p) and before[(p, a)] != value]
assert not changes, changes
print(len({p for p, a in after if not owned(p)}), 'unrelated tracked paths unchanged')
PY
```
## Ownership enforcement after correction
| Contract | Enforcement and CI receipt |
| --- | --- |
| Canonical, contained source/output paths and unique ownership | Publisher validates the whole manifest before writing; `python-syntax` runs its `--check` |
| Every maintained JS source is registered | Publisher rejects unlisted source; duplicate sources/outputs also fail |
| Reproducible LF checkout of both mapped paths | Publisher queries isolated repository-owned attributes, even for already matching bytes; the same CI `--check` now enforces it |
| Committed output equals maintained source | Publisher checks byte parity without rebuilding in CI; normal publication writes only stale owned outputs |
| Feature API/layer direction, no new cycles, stable relative resolution and maintained identities | Existing architecture checker and Node self-tests in `node-syntax`; no checker changes needed |
| Explicit exact-URL offline coverage | Architecture checker parses the existing SW arrays; real offline browser proof remains green |
| Legacy runtime graph/URL identity and behavior | Existing focused pytest guards and the full CI pytest jobs remain in place |
These gates enforce the documented maintained-JS contract together. They do not
retroactively impose source ownership on all legacy static files, clean retired
outputs or prove semantic state/lifecycle correctness. Those existing limits and
the independent packaging/offline limitations below remain unchanged.
## Final correction validation
The full focused pytest command printed below was rerun with the same interpreter
and all listed files: **193 passed, 1 skipped, 1 existing SQLAlchemy warning in
12.33 s**. It includes all 27 publisher tests and the isolated native startup,
static traversal and no-Node distribution probe. The new 14 tests account for
the increase from the initial F1 run's 179 passes. No test failed in this pass.
| Correction check | Actual result |
| --- | --- |
| Combined focused pytest command below | 193 passed, 1 unavailable-image skip |
| `node --test tests/frontendArchitecture.test.mjs` | 10 passed |
| Notes oracle/autosave/selected-context command below | 11 passed |
| `python3 scripts/build_frontend.py` | Passed, zero output changes, attributes enforced |
| `python3 scripts/build_frontend.py --check` | Passed, attributes and byte parity enforced |
| `node scripts/check_frontend.mjs` | Passed, one maintained module |
| Helper SHA-256 and `git check-attr` | Identical source/output hash; both text=set, eol=lf |
| All unrelated tracked attributes versus HEAD | 2,479 paths unchanged |
| `git diff --check` and preserved-runtime command below | Passed, no runtime/backend/packaging diff |
| `python -m compileall -q scripts/build_frontend.py tests/test_frontend_build.py` | Passed using the recorded virtualenv interpreter |
| Isolated Docker runtime command below | Passed, networking disabled; no source tree, node_modules or Node on PATH |
| Complete inventory, whitespace and documentation links | All 18 changed files matched the inventory; checks passed |
The one skip remains the existing test requiring `odysseus-odysseus:latest`.
No new skip or waiver was introduced. Windows/macOS executable builds, a fresh
full Dockerfile build, the full pytest suite, Firefox/WebKit and CI's exact
Python 3.11/Node 20 versions remain unexecuted locally. Native Git semantics,
nested overrides, paths with spaces and ignored `core.autocrlf`/global attribute
settings are covered by executed Linux tests; actual Windows execution remains a
platform-owner check. The existing `/static/` worker scope still prevents a claim
of full root offline reload readiness. No service-worker scope change is included.
The earlier local ChromaDB probe incident remains recorded under failures below.
All runtime checks in this correction pass used the isolated distribution probe
with reserved non-listening Chroma port and disabled warmups; Docker additionally
had networking disabled. This pass performed no new unisolated ChromaDB probe and
no attempt to restore or alter the existing local service's tool index.
## Reviewer-ready PR summary
Suggested title: `Establish frontend source ownership and native ESM deployment contracts`.
Maintain a stateless helper under `frontend/src/` while publishing identical bytes
at its existing static URL. Add manifest-owned publication, repository-owned LF
attribute and byte-parity checks, parsed dependency/API/identity/offline gates,
and a self-contained architecture handbook with the Notes decomposition roadmap.
Startup and existing feature behavior retain their current runtime contracts.
Validation: 193 focused Python tests passed with one existing Docker-image skip;
10 architecture and 11 Notes compatibility tests passed; source/output parity,
LF gates, preserved-runtime diff and isolated native/Docker runtime proofs passed.
Desktop executable builds and full root offline reload remain explicit limits.
All 18 files are inventoried below. The working diff is ready for review and is
uncommitted; commit, push and PR creation require separate authorization.
## Verified repository state
Repository: `odysseus-dev/odysseus`. Branch:
`refactor/frontend-architecture-foundation`. Worktree:
`/home/alteixeira20/Projects/Odysseus/worktrees/architecture/frontend-foundation`.
Initial worktree was clean. HEAD and local official `upstream/dev` are
`8feb50e89468235cd3e0986da80edbd3acaf2c40`. The live official GitHub `dev` SHA
matched at both initial and final verification. This is PR #6495's merge commit.
The `origin` remote is the maintainer fork, not the integration target.
Final changes are uncommitted F1 files only. Runtime JavaScript, Notes, HTML,
SW, backend, Dockerfile and desktop startup/packaging implementations have no
diff. The stateless source and existing deployed helper are both 1,688 bytes,
SHA-256 `e17baec5d6ac381ce6234ab6f028a8208487280dd07d6950a3469f8488809684`.
The final publisher run reported zero changed output modules.
## Decisions and scope
The [ADR](ADR-001-source-and-deployment.md) compares two executed approaches:
controlled native ESM copying and esbuild 0.25.12 ESM compilation/bundling.
Native byte copying wins on existing URL/module/cache compatibility and absence
of runtime tooling. Commit explicit output beside maintained source, publish
only manifest-owned modules and enforce parity in CI. Python startup never builds.
The [handbook](handbook.md) defines feature public indexes, responsibility layers,
state/lifecycle ownership, cross-feature ports/events, shared-code limits, legacy
coexistence, module creation, validation, rollback and handoff contracts.
The [assessment](assessment.md) records graph/bootstrap/state/storage/security,
packaging assumptions and current open-PR overlaps. The
[Notes roadmap](notes-roadmap.md) defines N0 through N6 as internal checkpoints
for one comprehensive Notes draft PR, with the final reference case study in
that PR and H recipient handoff after integration. None is implemented in F1.
There is one maintained stateless leaf and zero legacy-import
exceptions, with no empty feature scaffolding.
Acorn 8.19.0 is the sole new, exact-pinned development dependency, used to parse
architecture edges accurately. It is not loaded or distributed as runtime JS.
esbuild was a pinned one-off spike and is not a project dependency. Existing
frameworks, application entry points and backend contracts are unchanged.
## Exact changed files
| File | Change |
| --- | --- |
| `.gitattributes` | LF checkout for maintained source and the registered deployed proof |
| `.github/workflows/ci.yml` | Parity check; locked parser install; source syntax and architecture self-tests |
| `package.json` | Exact Acorn development dependency |
| `package-lock.json` | Acorn lock/integrity entry |
| `frontend/modules.json` | Explicit source/deployment ownership mapping |
| `frontend/src/package.json` | Native ESM interpretation for source tests, no dependencies |
| `frontend/src/shared/actionMenuOrder.js` | Authoritative stateless source, copied verbatim from the existing helper |
| `scripts/build_frontend.py` | Validated source publisher with manifest-driven, repository-owned LF enforcement and non-mutating parity check |
| `scripts/check_frontend.mjs` | Parsed import/layer/API/cycle/identity/precache guards and graph inventory |
| `scripts/verify_frontend_runtime.py` | Isolated current-code distribution/runtime compatibility probe |
| `tests/frontendArchitecture.test.mjs` | Behavioral API equivalence and positive/negative architecture gates |
| `tests/test_frontend_build.py` | Publication/ownership/drift/runtime coverage plus positive and negative Git attribute enforcement tests |
| `tests/test_frontend_foundation_offline_js.py` | Real SW precache, offline first evaluation, canonical namespace identity and lazy registry proof |
| `docs/frontend-architecture/ADR-001-source-and-deployment.md` | Decision, alternatives and distribution/rollback contracts |
| `docs/frontend-architecture/assessment.md` | Evidence-backed architecture and PR-overlap inventory |
| `docs/frontend-architecture/handbook.md` | Permanent foundation and maintainer continuation methodology |
| `docs/frontend-architecture/notes-roadmap.md` | Reference decomposition and final handoff plan |
| `docs/frontend-architecture/validation.md` | This implementation/review/validation report |
## Initial F1 validation before the review correction
Environment: Linux; native Python 3.14.4; pytest 9.1.1; FastAPI 0.143.0;
Starlette 1.8.0; Node 22.23.1; Playwright 1.62.1 with installed Chromium.
CI remains Python 3.11 and Node 20; that exact interpreter pair was not executed
locally. Use the repository virtualenv, not the system Python missing dependencies.
The interpreter used here was
`/home/alteixeira20/.venvs/odysseus-pr-6495/bin/python`.
The historical counts below are coordinator-executed checks in this worktree, excluding
delegated claims and earlier repeated subsets. Delegation was discontinued on
user instruction; no delegated implementation was integrated.
| Check | Actual result |
| --- | --- |
| Read-only baseline: graph, identity, helper, loader, MIME, nonce, runtime paths | 54 passed, one existing SQLAlchemy deprecation warning |
| Final combined focused pytest command below | 179 passed, 1 skipped, one existing SQLAlchemy warning, 12.62 s |
| Final `pytest -q -rs tests/test_frontend_build.py` review rerun | 13 passed, including native startup and encoded static traversal rejection |
| `node --test tests/frontendArchitecture.test.mjs` | 10 passed, no skips or failures; rerun after final gate review |
| Notes oracle/autosave/selected-context Node command below | 11 passed, no skips or failures |
| `python3 scripts/build_frontend.py` and `--check` | Zero output changes; byte parity passed |
| `node scripts/check_frontend.mjs` | Passed, one maintained module |
| `node scripts/check_frontend.mjs --inventory` | 230 files, 707 static edges, 108 lazy literal edges, no missing targets, three existing static cyclic components |
| `npm ci --ignore-scripts --no-audit --no-fund` | Locked development dependency installation succeeded |
| Native `scripts/verify_frontend_runtime.py` | Real FastAPI lifespan and shell/static assertions passed without maintained source, node_modules or Node on PATH |
| Docker runtime probe below | Same current-code assertions passed with networking disabled and source mount read-only |
| esbuild spike | Two identical 1,554-byte bundle outputs; executed exports/icon/ordering equivalence passed |
| JS syntax checks and Python compileall below | Passed |
| `git diff --check`, preserved-scope diff check, hash/LF checks | Passed; no runtime/backend/packaging source diff |
| Foundation documentation links, trailing whitespace and em-dash checks | All five files passed |
The skipped test is
`tests/test_docker_devops_hardening.py:187`, which requires the unavailable
`odysseus-odysseus:latest` image. It was not relabeled or bypassed. F1's separate
Docker runtime probe used the available `odysseus-maintainer-preview-odysseus:latest`,
image ID `sha256:cc2d47e2327d573af01c6b027f23d2ab0f2ee9b85d658e9eb8065bd02b9c3515`,
with Python 3.14.7, FastAPI 0.141.1 and Starlette 1.6.0. This proves compatibility
of current candidate code/assets with that image's runtime dependencies, not a
fresh build of the full repository Dockerfile or a deployment.
Reproduce the final focused command with the actual tested interpreter:
```sh
/home/alteixeira20/.venvs/odysseus-pr-6495/bin/python -m pytest -q -rs \
tests/test_frontend_build.py tests/test_frontend_foundation_offline_js.py \
tests/test_frontend_module_graph.py tests/test_frontend_module_version_parity.py \
tests/test_action_menu_order.py tests/test_panel_loader_js.py \
tests/test_app_static_mime.py tests/test_serve_html_with_nonce.py \
tests/test_runtime_paths.py tests/test_ci_authoritative_validation.py \
tests/test_selected_notes_context.py tests/test_email_library_module_graph_js.py \
tests/test_settings_module_import_paths.py tests/test_app_config_shared_fetch_js.py \
tests/test_markdown_lazy_lib_loading_js.py tests/test_startup_shell_js.py \
tests/test_static_stylesheet_manifest.py tests/test_notes_dom_xss_helpers.py \
tests/test_notes_reminder_temporal_redos.py tests/test_notes_sidebar_new_note.py \
tests/test_notes_mobile_checklist_toggle_static.py \
tests/test_notes_document_mutual_minimize.py tests/test_docker_devops_hardening.py
node --test tests/frontendArchitecture.test.mjs
ENDPOINT_URL=http://127.0.0.1:1 node --test \
tests/noteTestOracle.test.mjs tests/notesDraftAutosave.test.mjs \
tests/selectedNotesContext.test.mjs
python3 scripts/build_frontend.py --check
node scripts/check_frontend.mjs
/home/alteixeira20/.venvs/odysseus-pr-6495/bin/python scripts/verify_frontend_runtime.py
docker run --rm --network none \
--mount type=bind,src=/home/alteixeira20/Projects/Odysseus/worktrees/architecture/frontend-foundation,dst=/candidate,readonly \
--workdir /candidate --entrypoint python \
odysseus-maintainer-preview-odysseus:latest scripts/verify_frontend_runtime.py
```
`ENDPOINT_URL` satisfies the existing oracle's import-time configuration requirement.
Its unit-test imports exercise pure exported functions; its network-driving main
is guarded by the executable-module check and does not run. The closed loopback
address is not a live model endpoint. No oracle implementation was changed.
Syntax commands executed:
```sh
bash -c 'shopt -s globstar nullglob; for f in static/app.js static/js/**/*.js frontend/src/**/*.js scripts/check_frontend.mjs tests/frontendArchitecture.test.mjs; do node --check "$f" || exit; done'
/home/alteixeira20/.venvs/odysseus-pr-6495/bin/python -m compileall -q app.py core routes src services scripts tests
git diff --check
git diff --exit-code -- static app.py Dockerfile .dockerignore Odysseus.spec build-macos-app.sh build-windows-portable.ps1 launcher.py core routes src services
git check-attr text eol -- frontend/src/shared/actionMenuOrder.js static/js/actionMenuOrder.js
sha256sum frontend/src/shared/actionMenuOrder.js static/js/actionMenuOrder.js
```
Spike compiler invocation, executed twice with equality and Node import assertions:
```sh
npx --yes esbuild@0.25.12 frontend/src/shared/actionMenuOrder.js \
--bundle --format=esm --target=es2022 --platform=browser --log-level=error
```
The clean publication proof uses temporary roots in `test_frontend_build.py`,
including an initially absent output tree. Real-checkout publication is idempotent.
No emitted JS bytes, URLs, boot scripts or cache manifests changed in this lane.
## Failures encountered and their resolution
- An early architecture self-test expected an identity diagnostic but got an
unresolved source path for a query-bearing relative import. The checker now
rejects alternate identity before resolving that source path. All final tests
pass; no test requirement was weakened.
- The initial Notes oracle command failed during import because `ENDPOINT_URL`
was missing. Inspection established that the unit-test path never runs its
endpoint-driving main. The explicit closed-address configuration above passed
all 11 tests. This prerequisite is recorded for N0 rather than hidden.
- The first isolated distribution fixture omitted the existing `companion/`
package, so both probes failed with `ModuleNotFoundError`. The fixture now
includes that required runtime directory; neither application nor packaging
implementation was modified to make the tests pass.
- The first exploratory native startup check connected to the existing local
ChromaDB and refreshed the built-in tool index. That was unintended. Subsequent
probes use a private distribution/data directory, a reserved non-listening
Chroma port, disabled existing warmup options and no Node on PATH. Docker also
disables networking. No attempt was made to overwrite or restore that service's
data; the earlier unisolated probe is not the reproducible validation recipe.
## Remaining risks and limits
Root offline reload is not certified: existing SW scope is `/static/`, and open
[#6227](https://github.com/odysseus-dev/odysseus/pull/6227) proposes the independent
scope/header correction. F1 preserves and tests actual offline module behavior
at the current scope. No first-use offline guarantee is claimed for optional
CDN libraries, Mermaid or server APIs.
Windows/macOS packaged executables, a fresh full Dockerfile build, the full pytest
suite, Firefox/WebKit offline behavior and CI's exact Python 3.11/Node 20 versions
were not executed here. Existing packaging paths are inspected and unchanged;
platform owners should run release validation before broader certification.
The unavailable-image test remains a coverage gap. Legacy source still has
cross-feature coupling, global side effects and static cycles. New boundaries
are enforced on maintained source, not retrospectively on the entire legacy tree.
The publisher intentionally does not clean retired outputs or provide atomic
multi-file deployment. Review full source/output/facade/manifest changes and
ship a coherent artifact. Domain syntax checks cannot prove all semantic purity
or disposal; behavioral characterization remains required. Reconcile the package
lock with #6627 and refresh all PR overlaps before integration.
## Reviewer checklist
- Verify the base contains #6495 and the official integration target is dev.
- Confirm the deployed helper hash and runtime-scope diff are unchanged.
- Review the layer matrix, public API rules, single-state ownership and zero
current legacy exceptions; verify negative tests actually reject violations.
- Review the manifest mapping, byte-parity/LF policy, exact offline precache and
source/output relative-resolution guard; verify missing or overridden LF
declarations fail publication and CI even when output bytes already match.
- Reproduce the focused tests and inspect the skipped-image prerequisite.
- Confirm no frontend compiler/runtime dependency is required at startup and
inspect the isolated Python/Docker probe's limits.
- Review overlap with #6470/#6695/#6227/#6627 and the Notes backend follow-ups.
- Treat the ADR as awaiting adoption review, and the Notes/H documents as plans.
- Authorize any commit/push/PR action separately. Do not begin Notes on this branch.
## Required next reviews and continuation
1. Review this F1 working diff. On explicit authorization, commit and submit only
the foundation-only F1 PR to official dev using the normal maintainer workflow.
2. Coordinate the existing #6227 offline fix and platform release validation as
separate work. Do not bundle that behavioral change into F1.
3. After F1 integration and Notes implementation authorization, create one Notes
worktree and branch from the latest official dev, preserving any subsequently
merged Notes changes. Use one comprehensive draft PR targeting official dev.
4. Complete N0 through N6 from the [Notes roadmap](notes-roadmap.md) on that same
branch, recording tests, review and rollback evidence at each checkpoint. Include
the final reference case study in the same PR and keep it a draft until the full
decomposition and regression/compatibility acceptance gates pass. Recurrence
extraction follows characterization as an internal stage, not another PR.
5. Complete H only after Notes integration and recipient reproduction. Transfer
future frontend work to the designated maintainer and stop this program.
Another maintainer can start at the handbook, run the documented source/parity,
architecture, runtime and behavior checks, inspect this report and select the
next authorized slice without relying on unwritten architecture decisions.