refactor(notes): decompose Notes into feature-owned modules (#6713)

* refactor(notes): decompose Notes into feature-owned modules

* fix(notes): avoid HTML interpolation in reminder picker

* docs(notes): drop checkpoint archives, session logs and publication drafts

The N0-N6 tarballs and raw pytest/node logs are not read by any script or
test, and the logs carry absolute home-directory paths. Once squashed into
dev they would stay in history for good. notes-pr.md and notes-issue.md
were the drafts used to open #6712 and #6713, not documentation.

notes-validation.md keeps the counts, the checkpoint hashes and
implementation.sha256 as the record; local interpreter paths and the local
Docker image name are replaced with portable placeholders.

* fix(notes): keep the state store raw and stop masonry re-observing after close

- state: writes built from views (filter, spread, splice) stored nested
  read-only views in the store, so structuredClone on a stored note threw
  DataCloneError. Store writes now unwrap nested views.
- layout: a save that resolves after closePanel re-renders the leaving pane
  and re-created the masonry ResizeObserver that teardown had disposed.
  Skip creating it once Notes is closed.
- index.js: drop 30 imported or declared names that were never used and
  group the imports at the top. Import order is unchanged.
- Revert unrelated punctuation edits in .gitattributes and keep the header
  comment and docstring first in compare/vote.js and
  test_notes_dom_xss_helpers.py.

Both fixes have a node test that fails on the previous head.

* test(notes): make script rejection assertion case-insensitive

Address CodeQL js/bad-tag-filter alert #1150 in test-only assertion and refresh Notes evidence manifest checksum.

---------

Co-authored-by: Léo <leograndcontact@gmail.com>
This commit is contained in:
Alexandre Teixeira
2026-10-15 09:29:41 +01:00
committed by GitHub
co-authored by Léo
parent 9f8eae72fd
commit f1a47f3812
121 changed files with 15318 additions and 5544 deletions
@@ -0,0 +1,112 @@
53dfddd71f02a9a60df42e3fb045bd1a80919d50870c0b77afe18a853f485bea .gitattributes
867c6a4b025fefecb6ac4a91a38563a36107d9653599f64685e16a5be4fe7727 frontend/modules.json
76de35ac93d72edb5dcd295e85c704af9b3f08c2fe9fdc56e0bcbebd4b322fd8 frontend/src/features/notes/application/agentActions.js
37021f9643f6a3299d36b6021783a559e60ad84848ac2b12f0980b5034b6b9c4 frontend/src/features/notes/application/cardOperations.js
fe3033db6ef316c6b15c6b01c0f202ee76a61ea2c5b6952b20144660ff17eb54 frontend/src/features/notes/application/checklistOperations.js
85fc592724524bb5ff37d7e5e58a68fda9d7e645810b6c87a2d4dd3cb41f9dd5 frontend/src/features/notes/application/deletion.js
ce38e7976ee315b198baebffe45f9a1f4e63d87affd9898ed5cf4bde37b903e7 frontend/src/features/notes/application/drafts.js
6210915184c30f73911f144fe0d12a9935d6f2ce42cb2aaf6aeaa75614ca51a2 frontend/src/features/notes/application/queries.js
0da923790f4036d5fe354df4e529feb4b96e178495cb951f269093c0a8347e4e frontend/src/features/notes/application/reminders.js
8f95d086fc2e60beae32b9e9debba41b35448e1c5b4ac78e4989652ef2d44e35 frontend/src/features/notes/application/selection.js
4ceb424b3b6e30a5f507804f42051be02390d4ce1f03ef284094e48825cde5c3 frontend/src/features/notes/application/state.js
1c5d5af833aab91f71a34c11535885eb7bf9f751de5aa89f79defe0f696f20fb frontend/src/features/notes/application/submission.js
1d58ea2d495c8d301c550eb9ba7f627c515a8784bcd105558d941522965e77b0 frontend/src/features/notes/application/workflows.js
9ddbfb20e0a5564dd83365ad498286d68ef03fd60d632619ad4d9656e2f73c95 frontend/src/features/notes/domain/noteRules.js
946ef7a98b0a2dbe329e8259250af11a11702c74fd997e17fb3aff007fbe40c9 frontend/src/features/notes/domain/recurrence.js
25cf3a1cef5dbac0840aef513ffa010a9b08d206de96ce2527e6159a5454b555 frontend/src/features/notes/index.js
8b186bbfbb003b8d738c80713e1cbb5c6924ee14119929c5ea65707b46076b0c frontend/src/features/notes/infrastructure/http.js
c7552d1f4600ae721437892fc0a1e5a0332abe30082bd943cd13a379c9514fec frontend/src/features/notes/infrastructure/notifications.js
ff91a55c55fbdd876c69d9ec10e4e992104c95b6d12d752d9745193fccbc646a frontend/src/features/notes/infrastructure/storage.js
4ff6d69d04547a0325041f774c3bf0707d8b67982d59a221127e07dc9b01383c frontend/src/features/notes/ui/cardChecklist.js
dd87f1929ef8a5d57fdf1ddb12a0a6be53d95be07946be54f946c6fe31ef80bc frontend/src/features/notes/ui/cardInteractions.js
4ff661a65d88e810f346260a354070dba325d6beaa57d1c4e82e8bf09b894538 frontend/src/features/notes/ui/cardRendering.js
113d4457f25291d15afb7fb6775ea063f24c9dac51bb0d262787c9e40705235b frontend/src/features/notes/ui/cardReorder.js
eecafbd92076209fa41826820125fbe9ffa757d6d1bff018e9acc2ede2a18e72 frontend/src/features/notes/ui/checklist.js
155ea3d70e653f38a62ff50fc5be6aee019d42e86d28753da9edb258ac7c93e1 frontend/src/features/notes/ui/checklistReorder.js
5462fea63a2aa9d1fc07dca91b462f6ff24f352b05d99ea6feb437469c0e1f3e frontend/src/features/notes/ui/clipboard.js
2619e5f8b15eb3057330ef220abe6bc354a496effc0df3d89385cdfef9242c86 frontend/src/features/notes/ui/colors.js
11cf24e7e8e8954d764cb58d786fa6f6d71f84f1c6c5ba644d761e28cfdbaedf frontend/src/features/notes/ui/draftEditor.js
f55d4eff7f550e183ecf240a45002936063dbb4eae153e11834da4cb6bd13d35 frontend/src/features/notes/ui/drawing.js
f85c86e70b3a380449fe3c7ecc7a4e68946de320523f5edc8e33f8eab3f0a02a frontend/src/features/notes/ui/editor.js
3946b8324ad8f0212621e526479668308e48b799bf0dbb608e5cd4626f6c1f03 frontend/src/features/notes/ui/filters.js
a893d09e96fb98d13db8ae5c3fb8c5e7a040c62031dd961396a18b7ae2a82a38 frontend/src/features/notes/ui/layout.js
8d8c62a87d4df134d3b9dd97eaf59d02c52f1a38f6bc2e109e8426081d453a8d frontend/src/features/notes/ui/lifecycle.js
62ad9408cb139143168f2c5834ec56b83264c4daed3e84fa9b4b0a281712144c frontend/src/features/notes/ui/mobileEditor.js
2a3ccbad103dcc880e4baf2b3aa3ece4df7b73752bd30078185f03227585148f frontend/src/features/notes/ui/mobileReorder.js
856fe945cf697278f795b8753704119839fa12bc5e0e69e9dab57c4b4dd6d24e frontend/src/features/notes/ui/panel.js
8aa081cb7341d9a67ef46c74da49aebd729e9b53a73d618b81c89566c874b507 frontend/src/features/notes/ui/quickAdd.js
8a4f23a013f06876894e6544b49540b288007d0d7e4cddca955978588f47a64a frontend/src/features/notes/ui/reminderDisplay.js
cb043be801ab40cf8141af6ff1ad788e69fef57a87960ca992071ad9ad29ad90 frontend/src/features/notes/ui/reminderPicker.js
38f12ba7771589aa427f054f48919569dcee9abe0246e02b9767202adb854cdc frontend/src/features/notes/ui/selection.js
2634f252b2899e2e70fc9fec37144b0cc9b0ed00724da81bfef74b1acf7c05b6 frontend/src/features/notes/ui/text.js
d2655ef163825ce6c40d8ef46bc9dfc63a2aa19fb1a7e10b28002f79cb0db409 frontend/src/features/notes/ui/toolbar.js
a0aa203e87814d0eebebb6b049776c3efd576f4a5c6e17e486c8a2893c9c21df frontend/src/features/notes/ui/windowLayout.js
68f0cbe07c7b61fcaf92920cd47042c576ab27fd9553c971cff45c595c25d80a frontend/src/shared/ui/confetti.js
c16fcde607dce2f10a1afd1f8845403a7d0b97a9d5a67ec7aa329c367ba585e8 scripts/verify_frontend_runtime.py
c2e5de2b8ce168220bad4fcefed710e60eeb0dd3e992e76cd980f2c0ca52815a scripts/verify_notes_browser_runtime.py
fa31d673c40f973490e9fac52130b4a915b944a7abdd217a9bd8d57e66d57ca6 static/js/compare/vote.js
76de35ac93d72edb5dcd295e85c704af9b3f08c2fe9fdc56e0bcbebd4b322fd8 static/js/features/notes/application/agentActions.js
37021f9643f6a3299d36b6021783a559e60ad84848ac2b12f0980b5034b6b9c4 static/js/features/notes/application/cardOperations.js
fe3033db6ef316c6b15c6b01c0f202ee76a61ea2c5b6952b20144660ff17eb54 static/js/features/notes/application/checklistOperations.js
85fc592724524bb5ff37d7e5e58a68fda9d7e645810b6c87a2d4dd3cb41f9dd5 static/js/features/notes/application/deletion.js
ce38e7976ee315b198baebffe45f9a1f4e63d87affd9898ed5cf4bde37b903e7 static/js/features/notes/application/drafts.js
6210915184c30f73911f144fe0d12a9935d6f2ce42cb2aaf6aeaa75614ca51a2 static/js/features/notes/application/queries.js
0da923790f4036d5fe354df4e529feb4b96e178495cb951f269093c0a8347e4e static/js/features/notes/application/reminders.js
8f95d086fc2e60beae32b9e9debba41b35448e1c5b4ac78e4989652ef2d44e35 static/js/features/notes/application/selection.js
4ceb424b3b6e30a5f507804f42051be02390d4ce1f03ef284094e48825cde5c3 static/js/features/notes/application/state.js
1c5d5af833aab91f71a34c11535885eb7bf9f751de5aa89f79defe0f696f20fb static/js/features/notes/application/submission.js
1d58ea2d495c8d301c550eb9ba7f627c515a8784bcd105558d941522965e77b0 static/js/features/notes/application/workflows.js
9ddbfb20e0a5564dd83365ad498286d68ef03fd60d632619ad4d9656e2f73c95 static/js/features/notes/domain/noteRules.js
946ef7a98b0a2dbe329e8259250af11a11702c74fd997e17fb3aff007fbe40c9 static/js/features/notes/domain/recurrence.js
25cf3a1cef5dbac0840aef513ffa010a9b08d206de96ce2527e6159a5454b555 static/js/features/notes/index.js
8b186bbfbb003b8d738c80713e1cbb5c6924ee14119929c5ea65707b46076b0c static/js/features/notes/infrastructure/http.js
c7552d1f4600ae721437892fc0a1e5a0332abe30082bd943cd13a379c9514fec static/js/features/notes/infrastructure/notifications.js
ff91a55c55fbdd876c69d9ec10e4e992104c95b6d12d752d9745193fccbc646a static/js/features/notes/infrastructure/storage.js
4ff6d69d04547a0325041f774c3bf0707d8b67982d59a221127e07dc9b01383c static/js/features/notes/ui/cardChecklist.js
dd87f1929ef8a5d57fdf1ddb12a0a6be53d95be07946be54f946c6fe31ef80bc static/js/features/notes/ui/cardInteractions.js
4ff661a65d88e810f346260a354070dba325d6beaa57d1c4e82e8bf09b894538 static/js/features/notes/ui/cardRendering.js
113d4457f25291d15afb7fb6775ea063f24c9dac51bb0d262787c9e40705235b static/js/features/notes/ui/cardReorder.js
eecafbd92076209fa41826820125fbe9ffa757d6d1bff018e9acc2ede2a18e72 static/js/features/notes/ui/checklist.js
155ea3d70e653f38a62ff50fc5be6aee019d42e86d28753da9edb258ac7c93e1 static/js/features/notes/ui/checklistReorder.js
5462fea63a2aa9d1fc07dca91b462f6ff24f352b05d99ea6feb437469c0e1f3e static/js/features/notes/ui/clipboard.js
2619e5f8b15eb3057330ef220abe6bc354a496effc0df3d89385cdfef9242c86 static/js/features/notes/ui/colors.js
11cf24e7e8e8954d764cb58d786fa6f6d71f84f1c6c5ba644d761e28cfdbaedf static/js/features/notes/ui/draftEditor.js
f55d4eff7f550e183ecf240a45002936063dbb4eae153e11834da4cb6bd13d35 static/js/features/notes/ui/drawing.js
f85c86e70b3a380449fe3c7ecc7a4e68946de320523f5edc8e33f8eab3f0a02a static/js/features/notes/ui/editor.js
3946b8324ad8f0212621e526479668308e48b799bf0dbb608e5cd4626f6c1f03 static/js/features/notes/ui/filters.js
a893d09e96fb98d13db8ae5c3fb8c5e7a040c62031dd961396a18b7ae2a82a38 static/js/features/notes/ui/layout.js
8d8c62a87d4df134d3b9dd97eaf59d02c52f1a38f6bc2e109e8426081d453a8d static/js/features/notes/ui/lifecycle.js
62ad9408cb139143168f2c5834ec56b83264c4daed3e84fa9b4b0a281712144c static/js/features/notes/ui/mobileEditor.js
2a3ccbad103dcc880e4baf2b3aa3ece4df7b73752bd30078185f03227585148f static/js/features/notes/ui/mobileReorder.js
856fe945cf697278f795b8753704119839fa12bc5e0e69e9dab57c4b4dd6d24e static/js/features/notes/ui/panel.js
8aa081cb7341d9a67ef46c74da49aebd729e9b53a73d618b81c89566c874b507 static/js/features/notes/ui/quickAdd.js
8a4f23a013f06876894e6544b49540b288007d0d7e4cddca955978588f47a64a static/js/features/notes/ui/reminderDisplay.js
cb043be801ab40cf8141af6ff1ad788e69fef57a87960ca992071ad9ad29ad90 static/js/features/notes/ui/reminderPicker.js
38f12ba7771589aa427f054f48919569dcee9abe0246e02b9767202adb854cdc static/js/features/notes/ui/selection.js
2634f252b2899e2e70fc9fec37144b0cc9b0ed00724da81bfef74b1acf7c05b6 static/js/features/notes/ui/text.js
d2655ef163825ce6c40d8ef46bc9dfc63a2aa19fb1a7e10b28002f79cb0db409 static/js/features/notes/ui/toolbar.js
a0aa203e87814d0eebebb6b049776c3efd576f4a5c6e17e486c8a2893c9c21df static/js/features/notes/ui/windowLayout.js
7f1a7961d586767298672fad71a0d627389e185f0fb28792a04d0b987e1d5c5c static/js/notes.js
68f0cbe07c7b61fcaf92920cd47042c576ab27fd9553c971cff45c595c25d80a static/js/shared/ui/confetti.js
97fd43c17f3cb1928947e24159a3b59dc81ef7dca95862e8a47154e9cb732f88 static/sw.js
910cd5777564e9f6338b8a864a772beffea197dc476464171c180e7be96e572d tests/browser/notesNative.mjs
ef4bc40a710919c81c582d3b9fe80376d0d5ae66fb9be3c293871a83ee360f97 tests/browser/notesOffline.mjs
437ec2e9865b3e066bbec8d7595a0d947f1e503b96e7b4d819ec1bed86c9bb1e tests/browser/notesReference.mjs
9af973a5d2fb08a5eed550be0198bef501af56802c3844b5d0bb8e718cc685cf tests/fixtures/notesRecurrenceBaseline.mjs
859a5f442dc717f59993891c338cd6ae9d6e5e009392a2ee69ab0e3b063bb572 tests/helpers/notes_source.py
739ffc3f20de8e2bdf1d513b17bf485f85821e9906109cdf8ffea04b81b0f1dd tests/notesApplication.test.mjs
1946369be43177b19a44cd9ae4f90d64da6558d1a470f8e347b490a2188782be tests/notesCharacterization.test.mjs
99f0cd92a30cf01c0ee6814f6684edeb80ebdb9a5ab96583244092865bfb6212 tests/notesDraftAutosave.test.mjs
0417205809de16c7c7338ea80ba19ca844603b3e06655987226f5f83d962d18b tests/notesInfrastructure.test.mjs
dcf7c2d33de9bb457f96af070354ecc3ab69669002bdc9c7c943bef1b3d2e9bc tests/notesRecurrence.test.mjs
dd9a703465dde8f92625a3226ab52d20a02aa5d3415fbcc70609f69a372ccc12 tests/notesReminders.test.mjs
5a9112569f226525faaf6603a9867620b47902534f9cd882c08f71f2f96614b2 tests/notesWorkflows.test.mjs
35fb2bd1f02757b73188342ec00f1e97253b8ba8dfb577861675872a517bd949 tests/selectedNotesContext.test.mjs
c6c64c2b695b84d00f8a80a980be00766175c482548c86484f991ef671ecfbe9 tests/test_notes_document_mutual_minimize.py
02c2aeb856798d982e5debcb9c605172933b0216a039cf0d96cdd415b1e2ef83 tests/test_notes_dom_xss_helpers.py
5a511c7a2ef5d28463df34f3462db9ce59575d624cf8181577bab2cc371b4f9f tests/test_notes_mobile_checklist_toggle_static.py
1db10a0d344818459d64279da807ddefe9b866dfe560ac4b154b486dd1a5efc0 tests/test_notes_reference_browser.py
49f466b1a717098aa25439b6ff9ef3ae3d027d9dd976212b165b0b99a582b952 tests/test_notes_reference_contracts_js.py
c2d877a11eafcb99b24a1c89111e5df4453a62bc8cf7d26ae1118b25c4a5ace4 tests/test_notes_search_reset_on_reopen_js.py
6d95f073421427b5e149ef1e85c8b5749095d00337f05960134552985f73e372 tests/test_notes_select_esc_listener_js.py
b3118a4b40fcfcbb26ad9ccbcb332fc6ced873d8cad1160be38a286ad1e664a4 tests/test_notes_sidebar_new_note.py
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

+12 -4
View File
@@ -1,9 +1,10 @@
# 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.
Status: F1 merged in PR #6701. The complete Notes reference is implemented as an
unpublished review diff on `refactor/notes-reference-decomposition`. Read its
[case study](notes-case-study.md), [module inventory](notes-module-inventory.md)
and [validation receipts](notes-validation.md). Publication and the recipient's
post-integration reproduction remain pending.
Read the [ADR](ADR-001-source-and-deployment.md), [assessment](assessment.md),
[validation report](validation.md) and [Notes roadmap](notes-roadmap.md).
@@ -90,6 +91,13 @@ 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.
The Notes reference uses internal read-only live views to preserve captured note
identity across existing asynchronous completions. Its state owner supplies all
mutations; the public index exposes operations without the store. Notes deliberately
retains last-completion fetch ordering. Do not infer request-generation guards from
its decomposition. Its page-owned reminders survive panel closure, and panel/editor
listeners and observers have separate disposal scopes.
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
@@ -0,0 +1,260 @@
# Notes reference decomposition
Implementation started: 2026-10-13. Final validation: 2026-10-14.
Foundation: [PR #6701](https://github.com/odysseus-dev/odysseus/pull/6701),
`1cc56cb9d78f3e2399d1d61155b71d771a17b85b`. Branch was safely updated via
non-destructive fast-forward to latest official `upstream/dev` at
`86b886c611338282af6d4a717246ea4e256f58df`, incorporating PRs #6704 and #6693.
Neither changed a file in the Notes diff. The independent AGY audit returned
CONDITIONAL PASS; all pre-publication corrective actions (issue template conformance,
PR template conformance, visual evidence paths, and em-dash removal) have been completed.
The entire implementation belongs to one Notes PR. N0 through N6 are internal
checkpoints, not separate branches, commits or PRs. The [validation receipts](notes-validation.md),
[module inventory](notes-module-inventory.md) and [changed-file inventory](notes-changed-files.md)
accompany this case study.
## Responsibility map and size
The original `static/js/notes.js` contained 5,471 lines. It mixed mutable Notes
data, view preferences, selection, undo, CRUD, date calculations, reminders,
browser persistence, editor autosave, rendering, canvas tools and window management.
Its import evaluated storage preferences and scheduled reminder initialization.
| Original responsibility | Maintained owner |
| --- | --- |
| Completion, tags, recurrence, date labels and serialization | `domain/recurrence.js`, `domain/noteRules.js` |
| Notes data and selection/filter/editing state | `application/state.js`, instantiated once in `index.js` |
| CRUD, undo and fetch completion ordering | `application/workflows.js`, `cardOperations.js`, `deletion.js` |
| Checklist mutations and rollback | `application/checklistOperations.js` |
| Editor optimistic submission and recovery | `application/submission.js` |
| Serialized autosave and synchronous backup | `application/drafts.js`; form collection in `ui/draftEditor.js` |
| Filtering, searching and ordering | `application/queries.js`; controls in `ui/filters.js` and `toolbar.js` |
| Selected-note attachment and bulk requests | `application/selection.js`; controls in `ui/selection.js` |
| Existing agent-solve workflow | `application/agentActions.js`, through supplied HTTP/session ports |
| HTTP, persistence and browser notifications | `infrastructure/http.js`, `storage.js`, `notifications.js` |
| Background scheduler and delivery decisions | `application/reminders.js` |
| Panel mounting, closing, keyboard handling and navigation | `ui/panel.js` |
| Docking, fullscreen bounds and stacking | `ui/windowLayout.js`, using existing shared capabilities |
| Cards, card gestures, quick add and masonry | `ui/cardRendering.js`, `cardInteractions.js`, `cardChecklist.js`, `cardReorder.js`, `quickAdd.js`, `layout.js` |
| Editor types, colors, reminders and checklists | `ui/editor.js`, `colors.js`, `reminderPicker.js`, `checklist.js` |
| Drawing, local drawing undo and PNG upload | `ui/drawing.js` |
| Mobile reader/editor and reorder gestures | `ui/mobileEditor.js`, `mobileReorder.js`, `checklistReorder.js` |
| Clipboard, safe rendering and reminder display | `ui/clipboard.js`, `text.js`, `reminderDisplay.js` |
| Owned document/window listener disposal | `ui/lifecycle.js`, with panel and observer disposal callbacks |
| Composition and supported exports | `index.js`; five-line legacy facade at `static/js/notes.js` |
The feature has 41 maintained modules: two domain, eleven application, three
infrastructure, twenty-four UI and one index. Source is counted once, excluding
byte-identical deployment copies. Domain has 356 lines, application 826,
infrastructure 112, UI 4,628, and composition 518: 6,440 maintained feature lines.
The module inventory records final per-file counts. The legacy entry shrank
from 5,471 to five lines. Interfaces and explicit composition increase the total
source size; reducing total lines was not the architectural objective.
One 28-line stateless confetti renderer also moved to `shared/ui/confetti.js`.
Notes and Compare both use it. Compare retains its existing `spawnConfetti` export,
which is the same function as the shared export. This removes Notes' import of
Compare's private voting implementation without migrating Compare itself.
## Dependency graph and composition
```mermaid
flowchart TD
Legacy[Legacy Notes URL and window bridge] --> Index[Notes index: one composition]
Index --> UI[Notes UI factories]
Index --> App[Application workflows and state owner]
Index --> Ports[HTTP, persistence and notification adapters]
UI --> Rules[Pure domain rules]
App --> Rules
Index --> Shared[Existing shared UI capabilities and maintained confetti]
```
Application modules receive adapters and feedback operations as arguments. They
do not import browser infrastructure or DOM views. Pure domain modules receive
the clock at the original read sites. UI consumes a read-only state view and
application operations. Composition supplies callbacks between views and workflows;
modules do not import the coordinator back. F1 rejects maintained-source cycles.
There are nine exact legacy-import exceptions, all on the Notes index. They bridge
the existing UI, modal, color-picker, drag, tile, dock, stacking, Escape and tour-hint
capabilities at their existing URLs. Their removal condition is migration of the
corresponding shared responsibility to maintained source with its identity and
lifecycle contract verified. No new feature imports another feature's private internals.
## State and asynchronous ownership
`createNotesState` is called once. It owns the Notes collection, selection IDs,
editing ID, search, filters, archive/view flags, loading and dismissed-reminder time.
Neither the facade nor reminders create a second collection. The public feature API
does not expose this store.
Internal views are read-only live views rather than detached snapshots. This choice
preserves captured note identity: an existing asynchronous rollback still acts on
the same captured note. Array/object writes and selection mutations must use the
state owner's operations. Workflow-local undo queues and serialized draft promises,
and UI-local canvas undo/gesture state, are not competing Notes stores.
Fetches retain the original last-completion behavior. An earlier request that
finishes later can overwrite a later request's result. Tests pin this behavior;
generation guards would be a separate behavioral change. Submission also retains
the original first-temporary-note replacement rule. Multiple simultaneous creations
need a separately reviewed correlation fix, not a silent change during extraction.
Archive, unarchive, pin, item edit/remove/add, delete and undo preserve their original
optimistic ordering and rollback. Animation callbacks remain view-owned. Drawing
save keeps the existing button guard around its asynchronous PNG upload. Draft writes
are serialized per editor; a completed older save cannot clear a newer local draft.
Reminder scans still use the originally loaded Notes collection, not a second
all-active cache. An archived-only collection or a failure-cleared list can defer
active reminders until an active refresh/reopen. The timer remains alive when the
panel closes, but extraction does not redesign this inherited data-availability
limitation. Review such a change separately from scheduler/state ownership.
## Public compatibility
The import URL remains `/static/js/notes.js?v=20260911notesselectioncancel1`.
App, Chat stream updates, Chat note links and Documents continue using their existing
imports. Calendar and Documents retain their window bridge calls. HTML and app
bootstrap code are unchanged. The service worker retains the exact legacy entry.
The legacy namespace still exports `openPanel`, `closePanel`, `togglePanel`,
`isPanelOpen`, `openNote`, `newNote`, `refreshDueBadge`, `dismissFiredReminderDot`,
`openNotes`, `closeNotes`, `isNotesOpen`, and `default`.
The default object's ten methods are unchanged; `dismissFiredReminderDot` remains
named-only. Aliases refer to the same functions, including those on the default
object. `openNote` and `refreshDueBadge` remain async; function arities are pinned.
`window.notesModule` is exactly the default controller. The canonical unversioned
feature index and both tested legacy entry URLs share the same controller and
operations. The default object retains its existing mutability. A different legacy
URL has its own ESM namespace, as native ESM requires, but creates no second store
or scheduler because it forwards to the same canonical index.
The canonical index additionally exports `startNotesBackground` for explicit
activation. The compatibility facade invokes it once through an idempotent start
operation. Existing consumers need no change. Remove the facade only after every
consumer, global bridge, bootstrap and offline reference has migrated together.
## Protected selected Notes/Todos integration
The sender dispatches `odysseus:notes-context-selected` on `window` with
`{ noteIds, accepted: false }`. Dispatch and acknowledgement remain synchronous.
Chat owns acceptance, its session/pending selection, the context indicator and
the `selected_note_ids` send payload. Notes exits selection only after acceptance.
Rejected selections stay selected and display the existing feedback.
No Chat internals were imported or changed. Existing session lifecycle events still
transfer a pending selection on materialization, restore selections on chat switches,
and reset them for a new chat. Backend ID validation and owner filtering remain
authoritative. Unit/backend tests cover these contracts; the real authenticated
browser probe verifies rejection, pending-chat acceptance, the actual indicator and
new-chat reset. Live model execution is not part of that probe.
## Persistence and HTTP contracts
| Key | Preserved format |
| --- | --- |
| `odysseus-notes-view` | `list` or `grid` |
| `odysseus-notes-labels-collapsed` | `1` or `0` |
| `odysseus-notes-first-open-hint-v1` | Existing first-open marker |
| `odysseus-notes-reminder-fired` | JSON array of IDs |
| `odysseus-notes-reminder-glowed` | JSON array of IDs |
| `odysseus-notes-reminder-pending-highlight` | JSON array of IDs |
| `odysseus-notes-reminder-active-highlight` | JSON array of IDs |
| `odysseus-notes-reminder-dismissed-at` | Decimal millisecond timestamp |
| `odysseus-note-draft-<id>` and `odysseus-note-draft-__new__` | Existing JSON draft fields and `_ts` |
CRUD, reorder, upload, reminder firing, default-chat lookup, session creation and
chat-stream requests keep their URLs, methods, credentials, payloads and error
interpretation. Uploads retain `files` and optional `gallery_id` multipart fields.
The agent-solve stream still disables `manage_notes` and drains without switching
the user's chat. No backend implementation changed.
Storage backup failures still warn once; failed network saves keep recoverable
drafts, including intentionally empty text. Draft JSON does not serialize a canvas
bitmap. A drawing remains ephemeral until its explicit PNG upload, as before.
## Lifecycle scopes and explicit corrections
| Scope | Owner and disposal |
| --- | --- |
| Browser page | One reminder startup timeout and interval; page termination ends them. The reminder factory also has explicit disposal. |
| Open panel | Capture/bubble keyboard handlers, reorder listeners and masonry observer; both close paths dispose them. |
| Editor/canvas | Local DOM handlers and draft workflow; drawing's window listener is released when its canvas detaches or the panel closes. |
| Mobile editor/gesture | Overlay and document touch handlers; owner replacement and panel disposal remove handlers and reset registration flags. |
| Menus/hints | Notes portals close with the panel; shared dismiss functions unregister their Escape/tour lifetimes. |
| File picker | Existing completion or 30-second cleanup owns the temporary input. |
Two implementation corrections follow the user's explicit lifecycle requirements.
The original close paths canceled the supposedly background reminder interval.
The new page-owned interval survives closing/minimizing Notes, while retaining the
3-second startup delay, 30-second period and immediate first check. Closing a panel
also releases owned observers/listeners, pending hold timers, active Notes gestures
and portals; late panel fetch callbacks do
not recreate UI on a detached panel. Network completion/state ordering is preserved.
Shared drag/window utilities continue owning their own active-gesture handlers.
They were not moved into Notes. Idle open/close and editor replacement are verified;
every possible interleaving with an active shared window drag was not tested.
## Source and deployment
Every maintained source is registered in `frontend/modules.json` and copied without
rewriting imports. Notes sources map to `static/js/features/notes/` at the same
relative paths. Shared confetti maps to `static/js/shared/ui/confetti.js`.
Repository-owned LF attributes and byte parity cover all 43 maintained modules,
including the unchanged F1 proof. The legacy facade is an authored compatibility
entry, not a second implementation.
All new outputs join exact service-worker precache arrays. A real offline test also
found eight previously missing exact legacy URLs in the Notes graph; those entries
are now included. The eager Notes graph has 57 exact runtime URLs. Cache naming
follows the repository's `odysseus-v659-notes-reference` convention. Root offline
reload remains outside certification because the existing worker scope is `/static/`.
Python startup and Docker serve the committed static graph without maintained
source or frontend tooling. The publisher is a development check/copy operation,
not a runtime compiler or an atomic deployment system.
## Checkpoints, rollback and lessons
[Checkpoint receipts](notes-validation.md#internal-checkpoints) record the actual
checks and local snapshot hashes. These intermediate snapshots are engineering
evidence, not standalone release artifacts. N5 includes bounded editor, cards,
checklist, drawing, mobile, toolbar and window reviews on the same branch.
The N3 mechanical state rewrite initially confused canvas-local and feature undo
bindings. The N5 drawing test found it; the canvas binding was restored and the
same drawing/undo interaction was replayed against the original implementation.
Use scope-aware transformations and test each stateful UI responsibility before
accepting a broad rewrite. Source parsing alone does not prove semantic equivalence.
For rollback, reconstruct the prior complete artifact from its revision/snapshot
in a fresh temporary directory, run parity/graph/behavior checks there, then publish
the matching static graph and service worker as a unit through the authorized
release process. Restore the whole facade/source/output/manifest set, not just
`notes.js`. No schema or storage-key migration needs reversal. Do not reset a dirty
worktree or delete unrelated outputs. The validation report gives a non-destructive
snapshot reproduction recipe.
For the next maintainer:
1. Start with the module inventory and the state/workflow contracts, then reproduce
the focused tests, offline test and isolated native browser probe.
2. Review the explicit timer/lifecycle corrections separately from unchanged
business rules. Inspect both maintained source and byte-identical deployment.
3. Resolve or track the reproduced upstream CI failures and the open PR overlaps
before authorizing publication. Create/link the dedicated issue, then publish
exactly one Notes PR targeting official `dev`.
4. Record the integrated revision and independent recipient reproduction to complete
H. The designated maintainer can then apply this method to future features.
For Calendar, Documents, Email and later monoliths, characterize actual exports,
URL identities, state writers, clock reads, I/O and lifecycle first. Extract pure
rules, then adapters, then a single state owner/workflows. Use explicit callback
ports to break coordinator cycles. Split UI by interactions and resource lifetime,
not by arbitrary line ranges. Transfer source/output ownership and offline URLs
with each boundary, and preserve source-sensitive assertions at their new owner.
Those feature migrations are not part of this implementation.
@@ -0,0 +1,165 @@
# Notes complete changed-file inventory
Changed files in PR #6713 (`refactor/notes-reference-decomposition`) against
`dev`. 121 files: 16 modified and 105 added. No files are deleted. Deployment copies
are byte-identical to their registered maintained sources. Image files are review
evidence, not production dependencies.
The unchanged foundation worktree, backend, app bootstrap, HTML, CSS, dependency
lockfiles, Dockerfile and CI workflow are outside this diff. The only Compare
change delegates its existing stateless confetti export to maintained shared UI.
## Maintained source (42)
| Status | File |
| --- | --- |
| A | [frontend/src/features/notes/application/agentActions.js](../../frontend/src/features/notes/application/agentActions.js) |
| A | [frontend/src/features/notes/application/cardOperations.js](../../frontend/src/features/notes/application/cardOperations.js) |
| A | [frontend/src/features/notes/application/checklistOperations.js](../../frontend/src/features/notes/application/checklistOperations.js) |
| A | [frontend/src/features/notes/application/deletion.js](../../frontend/src/features/notes/application/deletion.js) |
| A | [frontend/src/features/notes/application/drafts.js](../../frontend/src/features/notes/application/drafts.js) |
| A | [frontend/src/features/notes/application/queries.js](../../frontend/src/features/notes/application/queries.js) |
| A | [frontend/src/features/notes/application/reminders.js](../../frontend/src/features/notes/application/reminders.js) |
| A | [frontend/src/features/notes/application/selection.js](../../frontend/src/features/notes/application/selection.js) |
| A | [frontend/src/features/notes/application/state.js](../../frontend/src/features/notes/application/state.js) |
| A | [frontend/src/features/notes/application/submission.js](../../frontend/src/features/notes/application/submission.js) |
| A | [frontend/src/features/notes/application/workflows.js](../../frontend/src/features/notes/application/workflows.js) |
| A | [frontend/src/features/notes/domain/noteRules.js](../../frontend/src/features/notes/domain/noteRules.js) |
| A | [frontend/src/features/notes/domain/recurrence.js](../../frontend/src/features/notes/domain/recurrence.js) |
| A | [frontend/src/features/notes/index.js](../../frontend/src/features/notes/index.js) |
| A | [frontend/src/features/notes/infrastructure/http.js](../../frontend/src/features/notes/infrastructure/http.js) |
| A | [frontend/src/features/notes/infrastructure/notifications.js](../../frontend/src/features/notes/infrastructure/notifications.js) |
| A | [frontend/src/features/notes/infrastructure/storage.js](../../frontend/src/features/notes/infrastructure/storage.js) |
| A | [frontend/src/features/notes/ui/cardChecklist.js](../../frontend/src/features/notes/ui/cardChecklist.js) |
| A | [frontend/src/features/notes/ui/cardInteractions.js](../../frontend/src/features/notes/ui/cardInteractions.js) |
| A | [frontend/src/features/notes/ui/cardRendering.js](../../frontend/src/features/notes/ui/cardRendering.js) |
| A | [frontend/src/features/notes/ui/cardReorder.js](../../frontend/src/features/notes/ui/cardReorder.js) |
| A | [frontend/src/features/notes/ui/checklist.js](../../frontend/src/features/notes/ui/checklist.js) |
| A | [frontend/src/features/notes/ui/checklistReorder.js](../../frontend/src/features/notes/ui/checklistReorder.js) |
| A | [frontend/src/features/notes/ui/clipboard.js](../../frontend/src/features/notes/ui/clipboard.js) |
| A | [frontend/src/features/notes/ui/colors.js](../../frontend/src/features/notes/ui/colors.js) |
| A | [frontend/src/features/notes/ui/draftEditor.js](../../frontend/src/features/notes/ui/draftEditor.js) |
| A | [frontend/src/features/notes/ui/drawing.js](../../frontend/src/features/notes/ui/drawing.js) |
| A | [frontend/src/features/notes/ui/editor.js](../../frontend/src/features/notes/ui/editor.js) |
| A | [frontend/src/features/notes/ui/filters.js](../../frontend/src/features/notes/ui/filters.js) |
| A | [frontend/src/features/notes/ui/layout.js](../../frontend/src/features/notes/ui/layout.js) |
| A | [frontend/src/features/notes/ui/lifecycle.js](../../frontend/src/features/notes/ui/lifecycle.js) |
| A | [frontend/src/features/notes/ui/mobileEditor.js](../../frontend/src/features/notes/ui/mobileEditor.js) |
| A | [frontend/src/features/notes/ui/mobileReorder.js](../../frontend/src/features/notes/ui/mobileReorder.js) |
| A | [frontend/src/features/notes/ui/panel.js](../../frontend/src/features/notes/ui/panel.js) |
| A | [frontend/src/features/notes/ui/quickAdd.js](../../frontend/src/features/notes/ui/quickAdd.js) |
| A | [frontend/src/features/notes/ui/reminderDisplay.js](../../frontend/src/features/notes/ui/reminderDisplay.js) |
| A | [frontend/src/features/notes/ui/reminderPicker.js](../../frontend/src/features/notes/ui/reminderPicker.js) |
| A | [frontend/src/features/notes/ui/selection.js](../../frontend/src/features/notes/ui/selection.js) |
| A | [frontend/src/features/notes/ui/text.js](../../frontend/src/features/notes/ui/text.js) |
| A | [frontend/src/features/notes/ui/toolbar.js](../../frontend/src/features/notes/ui/toolbar.js) |
| A | [frontend/src/features/notes/ui/windowLayout.js](../../frontend/src/features/notes/ui/windowLayout.js) |
| A | [frontend/src/shared/ui/confetti.js](../../frontend/src/shared/ui/confetti.js) |
## Deployment copies (42)
| Status | File |
| --- | --- |
| A | [static/js/features/notes/application/agentActions.js](../../static/js/features/notes/application/agentActions.js) |
| A | [static/js/features/notes/application/cardOperations.js](../../static/js/features/notes/application/cardOperations.js) |
| A | [static/js/features/notes/application/checklistOperations.js](../../static/js/features/notes/application/checklistOperations.js) |
| A | [static/js/features/notes/application/deletion.js](../../static/js/features/notes/application/deletion.js) |
| A | [static/js/features/notes/application/drafts.js](../../static/js/features/notes/application/drafts.js) |
| A | [static/js/features/notes/application/queries.js](../../static/js/features/notes/application/queries.js) |
| A | [static/js/features/notes/application/reminders.js](../../static/js/features/notes/application/reminders.js) |
| A | [static/js/features/notes/application/selection.js](../../static/js/features/notes/application/selection.js) |
| A | [static/js/features/notes/application/state.js](../../static/js/features/notes/application/state.js) |
| A | [static/js/features/notes/application/submission.js](../../static/js/features/notes/application/submission.js) |
| A | [static/js/features/notes/application/workflows.js](../../static/js/features/notes/application/workflows.js) |
| A | [static/js/features/notes/domain/noteRules.js](../../static/js/features/notes/domain/noteRules.js) |
| A | [static/js/features/notes/domain/recurrence.js](../../static/js/features/notes/domain/recurrence.js) |
| A | [static/js/features/notes/index.js](../../static/js/features/notes/index.js) |
| A | [static/js/features/notes/infrastructure/http.js](../../static/js/features/notes/infrastructure/http.js) |
| A | [static/js/features/notes/infrastructure/notifications.js](../../static/js/features/notes/infrastructure/notifications.js) |
| A | [static/js/features/notes/infrastructure/storage.js](../../static/js/features/notes/infrastructure/storage.js) |
| A | [static/js/features/notes/ui/cardChecklist.js](../../static/js/features/notes/ui/cardChecklist.js) |
| A | [static/js/features/notes/ui/cardInteractions.js](../../static/js/features/notes/ui/cardInteractions.js) |
| A | [static/js/features/notes/ui/cardRendering.js](../../static/js/features/notes/ui/cardRendering.js) |
| A | [static/js/features/notes/ui/cardReorder.js](../../static/js/features/notes/ui/cardReorder.js) |
| A | [static/js/features/notes/ui/checklist.js](../../static/js/features/notes/ui/checklist.js) |
| A | [static/js/features/notes/ui/checklistReorder.js](../../static/js/features/notes/ui/checklistReorder.js) |
| A | [static/js/features/notes/ui/clipboard.js](../../static/js/features/notes/ui/clipboard.js) |
| A | [static/js/features/notes/ui/colors.js](../../static/js/features/notes/ui/colors.js) |
| A | [static/js/features/notes/ui/draftEditor.js](../../static/js/features/notes/ui/draftEditor.js) |
| A | [static/js/features/notes/ui/drawing.js](../../static/js/features/notes/ui/drawing.js) |
| A | [static/js/features/notes/ui/editor.js](../../static/js/features/notes/ui/editor.js) |
| A | [static/js/features/notes/ui/filters.js](../../static/js/features/notes/ui/filters.js) |
| A | [static/js/features/notes/ui/layout.js](../../static/js/features/notes/ui/layout.js) |
| A | [static/js/features/notes/ui/lifecycle.js](../../static/js/features/notes/ui/lifecycle.js) |
| A | [static/js/features/notes/ui/mobileEditor.js](../../static/js/features/notes/ui/mobileEditor.js) |
| A | [static/js/features/notes/ui/mobileReorder.js](../../static/js/features/notes/ui/mobileReorder.js) |
| A | [static/js/features/notes/ui/panel.js](../../static/js/features/notes/ui/panel.js) |
| A | [static/js/features/notes/ui/quickAdd.js](../../static/js/features/notes/ui/quickAdd.js) |
| A | [static/js/features/notes/ui/reminderDisplay.js](../../static/js/features/notes/ui/reminderDisplay.js) |
| A | [static/js/features/notes/ui/reminderPicker.js](../../static/js/features/notes/ui/reminderPicker.js) |
| A | [static/js/features/notes/ui/selection.js](../../static/js/features/notes/ui/selection.js) |
| A | [static/js/features/notes/ui/text.js](../../static/js/features/notes/ui/text.js) |
| A | [static/js/features/notes/ui/toolbar.js](../../static/js/features/notes/ui/toolbar.js) |
| A | [static/js/features/notes/ui/windowLayout.js](../../static/js/features/notes/ui/windowLayout.js) |
| A | [static/js/shared/ui/confetti.js](../../static/js/shared/ui/confetti.js) |
## Compatibility and deployment configuration (5)
| Status | File |
| --- | --- |
| M | [.gitattributes](../../.gitattributes) |
| M | [frontend/modules.json](../../frontend/modules.json) |
| M | [static/js/compare/vote.js](../../static/js/compare/vote.js) |
| M | [static/js/notes.js](../../static/js/notes.js) |
| M | [static/sw.js](../../static/sw.js) |
## Tests (21)
| Status | File |
| --- | --- |
| A | [tests/browser/notesNative.mjs](../../tests/browser/notesNative.mjs) |
| A | [tests/browser/notesOffline.mjs](../../tests/browser/notesOffline.mjs) |
| A | [tests/browser/notesReference.mjs](../../tests/browser/notesReference.mjs) |
| A | [tests/fixtures/notesRecurrenceBaseline.mjs](../../tests/fixtures/notesRecurrenceBaseline.mjs) |
| A | [tests/helpers/notes_source.py](../../tests/helpers/notes_source.py) |
| A | [tests/notesApplication.test.mjs](../../tests/notesApplication.test.mjs) |
| A | [tests/notesCharacterization.test.mjs](../../tests/notesCharacterization.test.mjs) |
| M | [tests/notesDraftAutosave.test.mjs](../../tests/notesDraftAutosave.test.mjs) |
| A | [tests/notesInfrastructure.test.mjs](../../tests/notesInfrastructure.test.mjs) |
| A | [tests/notesRecurrence.test.mjs](../../tests/notesRecurrence.test.mjs) |
| A | [tests/notesReminders.test.mjs](../../tests/notesReminders.test.mjs) |
| A | [tests/notesWorkflows.test.mjs](../../tests/notesWorkflows.test.mjs) |
| M | [tests/selectedNotesContext.test.mjs](../../tests/selectedNotesContext.test.mjs) |
| M | [tests/test_notes_document_mutual_minimize.py](../../tests/test_notes_document_mutual_minimize.py) |
| M | [tests/test_notes_dom_xss_helpers.py](../../tests/test_notes_dom_xss_helpers.py) |
| M | [tests/test_notes_mobile_checklist_toggle_static.py](../../tests/test_notes_mobile_checklist_toggle_static.py) |
| A | [tests/test_notes_reference_browser.py](../../tests/test_notes_reference_browser.py) |
| A | [tests/test_notes_reference_contracts_js.py](../../tests/test_notes_reference_contracts_js.py) |
| M | [tests/test_notes_search_reset_on_reopen_js.py](../../tests/test_notes_search_reset_on_reopen_js.py) |
| M | [tests/test_notes_select_esc_listener_js.py](../../tests/test_notes_select_esc_listener_js.py) |
| M | [tests/test_notes_sidebar_new_note.py](../../tests/test_notes_sidebar_new_note.py) |
## Runtime validation (2)
| Status | File |
| --- | --- |
| M | [scripts/verify_frontend_runtime.py](../../scripts/verify_frontend_runtime.py) |
| A | [scripts/verify_notes_browser_runtime.py](../../scripts/verify_notes_browser_runtime.py) |
## Documentation and execution receipts (9)
| Status | File |
| --- | --- |
| A | [docs/frontend-architecture/evidence/notes/implementation.sha256](../../docs/frontend-architecture/evidence/notes/implementation.sha256) |
| A | [docs/frontend-architecture/evidence/notes/native-desktop.png](../../docs/frontend-architecture/evidence/notes/native-desktop.png) |
| A | [docs/frontend-architecture/evidence/notes/native-mobile.png](../../docs/frontend-architecture/evidence/notes/native-mobile.png) |
| M | [docs/frontend-architecture/handbook.md](../../docs/frontend-architecture/handbook.md) |
| A | [docs/frontend-architecture/notes-case-study.md](../../docs/frontend-architecture/notes-case-study.md) |
| A | [docs/frontend-architecture/notes-changed-files.md](../../docs/frontend-architecture/notes-changed-files.md) |
| A | [docs/frontend-architecture/notes-module-inventory.md](../../docs/frontend-architecture/notes-module-inventory.md) |
| M | [docs/frontend-architecture/notes-roadmap.md](../../docs/frontend-architecture/notes-roadmap.md) |
| A | [docs/frontend-architecture/notes-validation.md](../../docs/frontend-architecture/notes-validation.md) |
See [the case study](notes-case-study.md) for ownership and handoff,
[the module inventory](notes-module-inventory.md) for line counts, and
[the validation report](notes-validation.md) for exact executed checks.
@@ -0,0 +1,56 @@
# Notes source and deployment inventory
The feature has 41 maintained modules. The unchanged F1 action-menu proof and
shared confetti bring the full manifest to 43. Outputs are copied byte-for-byte;
count maintained source once. The legacy entry is a five-line compatibility facade.
| Source under frontend/src | Deployment under static | Lines | Layer |
| --- | --- | ---: | --- |
| [features/notes/application/agentActions.js](../../frontend/src/features/notes/application/agentActions.js) | [js/features/notes/application/agentActions.js](../../static/js/features/notes/application/agentActions.js) | 67 | application |
| [features/notes/application/cardOperations.js](../../frontend/src/features/notes/application/cardOperations.js) | [js/features/notes/application/cardOperations.js](../../static/js/features/notes/application/cardOperations.js) | 79 | application |
| [features/notes/application/checklistOperations.js](../../frontend/src/features/notes/application/checklistOperations.js) | [js/features/notes/application/checklistOperations.js](../../static/js/features/notes/application/checklistOperations.js) | 68 | application |
| [features/notes/application/deletion.js](../../frontend/src/features/notes/application/deletion.js) | [js/features/notes/application/deletion.js](../../static/js/features/notes/application/deletion.js) | 51 | application |
| [features/notes/application/drafts.js](../../frontend/src/features/notes/application/drafts.js) | [js/features/notes/application/drafts.js](../../static/js/features/notes/application/drafts.js) | 55 | application |
| [features/notes/application/queries.js](../../frontend/src/features/notes/application/queries.js) | [js/features/notes/application/queries.js](../../static/js/features/notes/application/queries.js) | 66 | application |
| [features/notes/application/reminders.js](../../frontend/src/features/notes/application/reminders.js) | [js/features/notes/application/reminders.js](../../static/js/features/notes/application/reminders.js) | 182 | application |
| [features/notes/application/selection.js](../../frontend/src/features/notes/application/selection.js) | [js/features/notes/application/selection.js](../../static/js/features/notes/application/selection.js) | 39 | application |
| [features/notes/application/state.js](../../frontend/src/features/notes/application/state.js) | [js/features/notes/application/state.js](../../static/js/features/notes/application/state.js) | 72 | application |
| [features/notes/application/submission.js](../../frontend/src/features/notes/application/submission.js) | [js/features/notes/application/submission.js](../../static/js/features/notes/application/submission.js) | 69 | application |
| [features/notes/application/workflows.js](../../frontend/src/features/notes/application/workflows.js) | [js/features/notes/application/workflows.js](../../static/js/features/notes/application/workflows.js) | 78 | application |
| [features/notes/domain/noteRules.js](../../frontend/src/features/notes/domain/noteRules.js) | [js/features/notes/domain/noteRules.js](../../static/js/features/notes/domain/noteRules.js) | 87 | domain |
| [features/notes/domain/recurrence.js](../../frontend/src/features/notes/domain/recurrence.js) | [js/features/notes/domain/recurrence.js](../../static/js/features/notes/domain/recurrence.js) | 269 | domain |
| [features/notes/index.js](../../frontend/src/features/notes/index.js) | [js/features/notes/index.js](../../static/js/features/notes/index.js) | 518 | composition |
| [features/notes/infrastructure/http.js](../../frontend/src/features/notes/infrastructure/http.js) | [js/features/notes/infrastructure/http.js](../../static/js/features/notes/infrastructure/http.js) | 29 | infrastructure |
| [features/notes/infrastructure/notifications.js](../../frontend/src/features/notes/infrastructure/notifications.js) | [js/features/notes/infrastructure/notifications.js](../../static/js/features/notes/infrastructure/notifications.js) | 20 | infrastructure |
| [features/notes/infrastructure/storage.js](../../frontend/src/features/notes/infrastructure/storage.js) | [js/features/notes/infrastructure/storage.js](../../static/js/features/notes/infrastructure/storage.js) | 63 | infrastructure |
| [features/notes/ui/cardChecklist.js](../../frontend/src/features/notes/ui/cardChecklist.js) | [js/features/notes/ui/cardChecklist.js](../../static/js/features/notes/ui/cardChecklist.js) | 148 | ui |
| [features/notes/ui/cardInteractions.js](../../frontend/src/features/notes/ui/cardInteractions.js) | [js/features/notes/ui/cardInteractions.js](../../static/js/features/notes/ui/cardInteractions.js) | 337 | ui |
| [features/notes/ui/cardRendering.js](../../frontend/src/features/notes/ui/cardRendering.js) | [js/features/notes/ui/cardRendering.js](../../static/js/features/notes/ui/cardRendering.js) | 244 | ui |
| [features/notes/ui/cardReorder.js](../../frontend/src/features/notes/ui/cardReorder.js) | [js/features/notes/ui/cardReorder.js](../../static/js/features/notes/ui/cardReorder.js) | 181 | ui |
| [features/notes/ui/checklist.js](../../frontend/src/features/notes/ui/checklist.js) | [js/features/notes/ui/checklist.js](../../static/js/features/notes/ui/checklist.js) | 190 | ui |
| [features/notes/ui/checklistReorder.js](../../frontend/src/features/notes/ui/checklistReorder.js) | [js/features/notes/ui/checklistReorder.js](../../static/js/features/notes/ui/checklistReorder.js) | 111 | ui |
| [features/notes/ui/clipboard.js](../../frontend/src/features/notes/ui/clipboard.js) | [js/features/notes/ui/clipboard.js](../../static/js/features/notes/ui/clipboard.js) | 82 | ui |
| [features/notes/ui/colors.js](../../frontend/src/features/notes/ui/colors.js) | [js/features/notes/ui/colors.js](../../static/js/features/notes/ui/colors.js) | 111 | ui |
| [features/notes/ui/draftEditor.js](../../frontend/src/features/notes/ui/draftEditor.js) | [js/features/notes/ui/draftEditor.js](../../static/js/features/notes/ui/draftEditor.js) | 67 | ui |
| [features/notes/ui/drawing.js](../../frontend/src/features/notes/ui/drawing.js) | [js/features/notes/ui/drawing.js](../../static/js/features/notes/ui/drawing.js) | 442 | ui |
| [features/notes/ui/editor.js](../../frontend/src/features/notes/ui/editor.js) | [js/features/notes/ui/editor.js](../../static/js/features/notes/ui/editor.js) | 518 | ui |
| [features/notes/ui/filters.js](../../frontend/src/features/notes/ui/filters.js) | [js/features/notes/ui/filters.js](../../static/js/features/notes/ui/filters.js) | 139 | ui |
| [features/notes/ui/layout.js](../../frontend/src/features/notes/ui/layout.js) | [js/features/notes/ui/layout.js](../../static/js/features/notes/ui/layout.js) | 120 | ui |
| [features/notes/ui/lifecycle.js](../../frontend/src/features/notes/ui/lifecycle.js) | [js/features/notes/ui/lifecycle.js](../../static/js/features/notes/ui/lifecycle.js) | 36 | ui |
| [features/notes/ui/mobileEditor.js](../../frontend/src/features/notes/ui/mobileEditor.js) | [js/features/notes/ui/mobileEditor.js](../../static/js/features/notes/ui/mobileEditor.js) | 257 | ui |
| [features/notes/ui/mobileReorder.js](../../frontend/src/features/notes/ui/mobileReorder.js) | [js/features/notes/ui/mobileReorder.js](../../static/js/features/notes/ui/mobileReorder.js) | 214 | ui |
| [features/notes/ui/panel.js](../../frontend/src/features/notes/ui/panel.js) | [js/features/notes/ui/panel.js](../../static/js/features/notes/ui/panel.js) | 506 | ui |
| [features/notes/ui/quickAdd.js](../../frontend/src/features/notes/ui/quickAdd.js) | [js/features/notes/ui/quickAdd.js](../../static/js/features/notes/ui/quickAdd.js) | 170 | ui |
| [features/notes/ui/reminderDisplay.js](../../frontend/src/features/notes/ui/reminderDisplay.js) | [js/features/notes/ui/reminderDisplay.js](../../static/js/features/notes/ui/reminderDisplay.js) | 129 | ui |
| [features/notes/ui/reminderPicker.js](../../frontend/src/features/notes/ui/reminderPicker.js) | [js/features/notes/ui/reminderPicker.js](../../static/js/features/notes/ui/reminderPicker.js) | 334 | ui |
| [features/notes/ui/selection.js](../../frontend/src/features/notes/ui/selection.js) | [js/features/notes/ui/selection.js](../../static/js/features/notes/ui/selection.js) | 61 | ui |
| [features/notes/ui/text.js](../../frontend/src/features/notes/ui/text.js) | [js/features/notes/ui/text.js](../../static/js/features/notes/ui/text.js) | 39 | ui |
| [features/notes/ui/toolbar.js](../../frontend/src/features/notes/ui/toolbar.js) | [js/features/notes/ui/toolbar.js](../../static/js/features/notes/ui/toolbar.js) | 78 | ui |
| [features/notes/ui/windowLayout.js](../../frontend/src/features/notes/ui/windowLayout.js) | [js/features/notes/ui/windowLayout.js](../../static/js/features/notes/ui/windowLayout.js) | 114 | ui |
| [shared/ui/confetti.js](../../frontend/src/shared/ui/confetti.js) | [js/shared/ui/confetti.js](../../static/js/shared/ui/confetti.js) | 28 | shared UI |
Maintained Notes feature total: 6440 lines. Original entry: 5,471 lines.
Legacy facade after decomposition: five lines. Shared confetti: 28 lines.
See [the case study](notes-case-study.md) for responsibilities and public/state/lifecycle
contracts, and [the validation report](notes-validation.md) for executed evidence.
+9 -4
View File
@@ -1,15 +1,20 @@
# 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.
Status: N0 through N6 implemented as an unpublished Notes review diff after F1
merged in PR #6701. Actual decisions, interfaces, module ownership, checkpoint
receipts and execution limits are in the [case study](notes-case-study.md) and
[validation report](notes-validation.md). The proposal below retains the original
responsibility map for comparison. Notes integration and the recipient's independent
H reproduction remain pending.
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`.
Historical assessment baseline: `8feb50e89468235cd3e0986da80edbd3acaf2c40`.
The implementation starts from merged F1 at
`1cc56cb9d78f3e2399d1d61155b71d771a17b85b`.
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
@@ -0,0 +1,261 @@
# Notes implementation validation and checkpoint receipts
Implementation baseline: `1cc56cb9d78f3e2399d1d61155b71d771a17b85b`, updated via
non-destructive fast-forward to latest official `upstream/dev` at
`86b886c611338282af6d4a717246ea4e256f58df` (incorporating PRs #6704 and #6693).
Independent AGY audit returned CONDITIONAL PASS. Pre-publication corrective
pass verified issue template, PR template, visual evidence paths, and em-dash removal.
The official integration branch is `dev`. Tracking issue #6712; PR #6713.
## Final executed evidence
| Check | Result |
| --- | --- |
| Notes Python tests, discovered by filename, plus F1 publication/graph/offline/loader tests | 172 passed, one release-smoke skip without `APP_PORT` |
| Node architecture, Notes oracle, domain, infrastructure, application, reminders, drafts, selection and workflow tests | 46 passed |
| Native Python lifespan/static distribution probe | Passed for every mapped output, with source/Node absent |
| Existing Docker image distribution probe | Passed with networking disabled and candidate files mounted read-only |
| Real authenticated native app, desktop and touch-mobile | Passed CRUD persistence, owner identity, actual Chat selected-context indicator/reset and the original release smoke scenario |
| Real Chromium first evaluation of Notes offline | Passed; every maintained output cached, facade/canonical API identity shared |
| Source/output parity and F1 boundaries | Passed, 43 maintained modules and nine composition-only legacy exceptions |
| Foundation-based full pytest execution | 13 failed, 13,909 passed, 60 skipped, six subtests passed, 192 warnings, 517.04 seconds |
| Targeted unchanged F1 baseline reproduction | Same 13 failures, 77 passed, six warnings |
| Targeted untouched latest `dev` reproduction | Three inherited failures, 87 passed, six warnings |
| Final indexed latest `dev` plus Notes overlay, full pytest | Three same-environment baseline failures, 13,923 passed, 60 skipped, six subtests passed, 192 warnings, 512.09 seconds |
| Untouched latest `dev` readiness checks, without the prewarm override | Five passed |
The foundation-based full repository result is not green. Its failures reproduce against
the unchanged foundation in a disposable archive, with the same interpreter and
closed Chroma port. None was disabled, relabeled or fixed in this Notes change.
The focused Notes suite and required source/deployment gates are green. The final
code/test overlay is fingerprinted in [implementation.sha256](evidence/notes/implementation.sha256).
The only post-full-suite test-source edit removes a terminal
blank line from the browser wrapper; its content was compared ignoring terminal
whitespace and all three focused contract/browser checks passed again. Raw command
outputs are kept out of the repository; the counts above are the record.
The first full run reported 19 failures. Six were introduced validation issues:
the SW cache name did not follow the existing numeric convention; a temporary
probe environment input was not registered by the configuration-reference checker;
and a Notes source assertion still expected the original default object spelling.
The cache name now follows the convention, the probe passes its output list as a
command argument, and the assertion verifies the new public binding. All 58 tests
in those affected files passed after correction. The foundation-based full run has only the
13 reproduced baseline failures below.
### Reproduced baseline failures
- `tests/test_aux_llm_owner_scope.py::test_auto_compaction_utility_endpoint_keeps_chat_owner`
- `tests/test_chatgpt_reasoning_and_grouping.py::test_history_and_chat_routes_support_reasoning_effort`
- `tests/test_containment_enforcement.py::test_namespace_handshake_closes_model_stdin`
- `tests/test_containment_enforcement.py::test_execution_environment_cannot_replace_probed_bwrap[False]`
- `tests/test_context_compactor.py::TestMaybeCompactFourthMessage::test_does_not_crash_on_none_content_turn`
- `tests/test_context_compactor.py::TestMaybeCompactFourthMessage::test_handles_multimodal_list_content`
- `tests/test_css_computed_style_snapshot.py::test_computed_styles_match_the_committed_baseline`
- `tests/test_history_compact_tool_calls.py::test_manual_compact_tolerates_chatmessage_with_none_content`
- `tests/test_history_compact_tool_calls.py::test_manual_compact_tolerates_dict_message_with_none_content`
- `tests/test_history_compact_tool_calls.py::test_registered_manual_compact_route_tolerates_none_content`
- `tests/test_history_compact_tool_calls.py::test_registered_manual_compact_route_uses_session_owner`
- `tests/test_model_helper_owner_scope.py::test_history_compact_resolves_with_owner_scope`
- `tests/test_readiness.py::test_tool_index_is_reported_but_not_critical_by_default`
These cover existing compaction fixtures/source assertions, namespace containment,
settings-row CSS baseline drift and readiness expectations. The baseline reproduction used
`git archive` into `/tmp/odysseus-notes-baseline-OitxsU`, not another branch/worktree.
## Reproducible commands
### Latest integration target
Final upstream inspection found `86b886c611338282af6d4a717246ea4e256f58df`.
PR #6704 repairs ten of the foundation's reproduced failures; PR #6693 adds
remote-only skill gating. Neither changes a file owned by the Notes diff.
The branch remains on the requested foundation, with no merge or rebase.
An archive of that latest target plus the exact final Notes code/test overlay is
validated in `/tmp/odysseus-notes-latest-VmZrgC` using the same full-suite command.
The fixture has a local Git index, initialized from the latest target and updated
with the review files, but no commit. This enables the tracked-publication checks.
The first archive-only attempt had one Git-metadata failure and seven checkout-only
skips. Both affected files subsequently passed all 19 tests with metadata present;
the complete suite was then rerun in that indexed fixture.
An untouched archive at `/tmp/odysseus-notes-latest-baseline-mfE4ss` reproduced
the remaining two containment failures and the readiness failure: three failed,
87 passed.
These are reproduced local-environment failures, not a claim that official CI is
currently failing. The readiness assertion expects prewarming enabled, whereas
the full-run safety command explicitly sets `ODYSSEUS_TOOL_INDEX_PREWARM=0`.
With that override absent, all five readiness tests pass on untouched latest `dev`.
The two namespace tests receive extra `Failed to create stream fd` stderr in this
host's contained process output. That same output occurs without the Notes overlay;
no backend containment code was changed or a failing assertion weakened.
Reproduce those two checks on the supported CI runner before claiming a green
full-suite certification.
In the final indexed full-suite run the only failures are `test_namespace_handshake_closes_model_stdin`,
`test_execution_environment_cannot_replace_probed_bwrap[False]`, and
`test_tool_index_is_reported_but_not_critical_by_default`. The missing-Git-metadata
publication failure and the seven checkout-only skips are absent from this run.
No Notes regression was reported. Feature code matched N6 at publication. Later
commits on the PR changed `ui/reminderPicker.js` (date input value assigned through
the DOM property) and, after review, `index.js`, `ui/layout.js` and
`application/state.js`; `implementation.sha256` covers the current files.
To reproduce the integration rehearsal, extract the latest target with `git archive`
into a fresh fixture and copy only files in the changed-file inventory on top.
Do not overlay foundation files that are unchanged in this PR. Install the locked
development dependencies in the fixture, then run the commands below there.
While this review diff is uncommitted, this prepares an equivalent Linux fixture
without changing either task worktree:
```sh
notes_fixture=$(mktemp -d /tmp/ody-notes-integration-XXXXXX)
git archive 86b886c611338282af6d4a717246ea4e256f58df | tar -x -C "$notes_fixture"
{ git diff --name-only -z; git ls-files --others --exclude-standard -z; } |
tar --null -T - -cf - | tar -xf - -C "$notes_fixture"
git -C "$notes_fixture" init -q
git -C "$notes_fixture" fetch --no-write-fetch-head "$PWD" 86b886c611338282af6d4a717246ea4e256f58df
git -C "$notes_fixture" read-tree 86b886c611338282af6d4a717246ea4e256f58df
(cd "$notes_fixture" && npm ci --ignore-scripts --no-audit --no-fund && git add --all)
# Run the validation commands below from notes_fixture, using an isolated Python environment.
```
Use a real dependency directory for this recipe; a symlink named `node_modules`
can bypass the repository's directory-only ignore pattern. No checkpoint or
fixture commit is needed. After integration, validate the actual integrated checkout
directly, rather than relying on the uncommitted-diff recipe.
### Commands
Use the actual tested interpreter, or install the repository requirements in your
own isolated virtualenv. Node development dependencies were installed with the
existing lockfile and `npm ci --ignore-scripts --no-audit --no-fund`.
```sh
python3 scripts/build_frontend.py
python3 scripts/build_frontend.py --check
node scripts/check_frontend.mjs
node scripts/check_frontend.mjs --inventory
ENDPOINT_URL=http://127.0.0.1:1 node --test \
tests/frontendArchitecture.test.mjs tests/noteTestOracle.test.mjs \
tests/notesDraftAutosave.test.mjs tests/selectedNotesContext.test.mjs \
tests/notesCharacterization.test.mjs tests/notesRecurrence.test.mjs \
tests/notesInfrastructure.test.mjs tests/notesApplication.test.mjs \
tests/notesReminders.test.mjs tests/notesWorkflows.test.mjs
CHROMADB_HOST=127.0.0.1 CHROMADB_PORT=1 \
python -m pytest -q -rs \
$(rg --files tests -g 'test_*note*.py') \
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
python scripts/verify_frontend_runtime.py
python scripts/verify_notes_browser_runtime.py
docker run --rm --network none \
--mount type=bind,src="$PWD",dst=/candidate,readonly \
--workdir /candidate --entrypoint python \
<existing-odysseus-image> scripts/verify_frontend_runtime.py
CHROMADB_HOST=127.0.0.1 CHROMADB_PORT=1 \
ODYSSEUS_STARTUP_WARMUPS=0 ODYSSEUS_TOOL_INDEX_PREWARM=0 \
python -m pytest -q -rfs
git diff --check
```
`ENDPOINT_URL` satisfies the existing oracle's import configuration; its endpoint-
driving main does not run in these unit tests. Port 1 is the closed loopback target
used for pytest; the two runtime scripts reserve their own non-listening Chroma port.
The native browser script creates a private distribution/database and a temporary
authenticated user, then terminates the server and removes that fixture. It uses
the real application and Notes API rather than mocked browser routes.
The deterministic browser harness uses stub API responses and the real Notes ESM,
DOM and stylesheet graph. It verifies namespace/default methods and arities,
aliases, remount/search reset, rejected/accepted attachments, draft persistence,
drawing/undo and listener/portal cleanup. It also checks larger-touch reorder after
re-render, active-drag close cleanup and cancellation of a mobile selection hold.
It includes the mobile viewport meta tag,
tap-to-read/edit behavior and the existing selection jiggle. The original monolith
can be replayed with `NOTES_BASELINE=1`; it is read from the frozen F1 revision.
## Browser and packaging evidence
Actual app screenshots: [desktop](evidence/notes/native-desktop.png) and
[mobile](evidence/notes/native-mobile.png). These are Chromium screenshots of the
disposable authenticated app at 1440 x 1000 and 390 x 844, not rendered mockups.
The deterministic before/after fixture had an identical mobile PNG and only 14
changed desktop pixels in the header region; no layout/style change was observed.
CSS, app bootstrap and HTML have no implementation diff.
The native and Docker distribution probe serves all manifest outputs without
`frontend/`, `node_modules/` or Node on the server path. It checks response bytes,
JS MIME, revalidation, CSP, shell startup, static confinement and source-tree 404.
Docker used existing image
`sha256:cc2d47e2327d573af01c6b027f23d2ab0f2ee9b85d658e9eb8065bd02b9c3515`.
This is not a fresh Dockerfile build or deployment.
Tested locally: Linux, Python 3.14.4, Node 22.23.1 and Playwright/Chromium 1.62.1.
CI's exact Python 3.11/Node 20 pair, Firefox/WebKit, macOS/Windows executable builds,
live model/email notification delivery and full root offline reload were not run.
The Docker runtime uses its own existing dependency versions. Shared active-window
gesture interleavings are not exhaustively covered. No broader platform certification
is claimed.
The full-run safety overrides differ from official CI's plain pytest command.
For the readiness default-contract check, use
`env -u ODYSSEUS_TOOL_INDEX_PREWARM python -m pytest -q tests/test_readiness.py`.
Keep explicit warmup/storage isolation on the disposable live-app probes.
## Internal checkpoints
Checkpoint snapshots are kept with the maintainer handoff, not in the repository.
The hashes below identify them. They contain scoped source/output/facade/manifest/test
overlays, not secrets or user data. Each starts from the same frozen baseline; they
are not separate PRs or release candidates.
| Stage | Boundary and executed exit evidence | Snapshot SHA-256 |
| --- | --- | --- |
| N0 | Public/consumer/state/lifecycle map; 95 baseline Python and 11 Node passes; four new characterization tests; desktop/touch harness; 33 F1 publication/graph checks | `db0a2e6e68fec3442ce350ffb0acf47bcdf2a5266f7c64c2bf910a1c41c4fb62` |
| N1 | Pure recurrence/date rules; eight Node and nine focused Python passes; differential oracle in four time zones; parity/boundaries | `fd0638470744706fbbd5787cb47bd2078a93c9b854139f89f8fdc169c1f353c6` |
| N2 | HTTP/storage adapters; fourteen Node and seven focused Python passes; exact requests, keys and fallback contracts | `30826fb7972d1b9e3a790f97ee81f66dcb3dc58eb7d23e24d4f46e6d841b7c73` |
| N3 | Single state owner and CRUD/undo boundary; ten Node and seven focused Python passes; read-only state and archive rollback | `4bc5293519b8438d0126a61887bea64195dcdb7d59bcfaaba8ed4ae8c38be439` |
| N4 | Page-owned reminder workflow; four reminder contract tests and seven focused Python passes; singleton interval, catch-up, markers and fallback | `febb835c4958c8758e4b7c23ce89f3ec8d103c2cc28f89817d258b3791a9fcdf` |
| N5 | Complete UI/application integration; 44 Node passes, targeted source guards and actual browser checks; drawing binding correction, read-only state and disposal | `e147f645bdb2161a4b1dc6cf10c36ba6d0c214d8e12fccb478835043eaaf3e94` |
| N6 | Final facade, identity, exact offline graph, native/Docker checks, final editor-deletion/reorder ownership review, touch cleanup regression checks and maintainer review package | `c450e91f9fbfe5f0cd805e596b92a76383f22a74e9515ca3e3f6154aebf5f61f` |
The N6 overlay contained the final code, tests, manifest and attributes at
publication, not the evidence directory itself. From the repository root, run
`sha256sum -c docs/frontend-architecture/evidence/notes/implementation.sha256`
to verify the current implementation files.
No checkpoint commits were made. To reproduce an overlay, create a fresh temporary
directory, extract `git archive 1cc56cb9...` there, then extract the desired
snapshot from the handoff on top. Install the locked test dependencies
in that fixture and run its relevant commands. Begin from the baseline rather than
overlaying snapshots onto a later dirty tree. For live rollback, release a complete
matching artifact through the authorized release workflow.
Temporary mechanical extraction scripts are not part of the supported deliverable.
They were archived locally, then removed. The first generated patch accidentally targeted
the separate foundation worktree; only those edits were reversed, absolute paths
were adopted, and that worktree's clean state was verified afterward.
## Open PR overlaps and handoff limits
The following remained open at the read-only GitHub refresh:
- [#6585](https://github.com/odysseus-dev/odysseus/pull/6585): optional Notes field removal in the backend. Existing null-field behavior is retained here.
- [#6647](https://github.com/odysseus-dev/odysseus/pull/6647): read-only Notes intent routing.
- [#6652](https://github.com/odysseus-dev/odysseus/pull/6652): `manage_notes` registry dispatch.
- [#6470](https://github.com/odysseus-dev/odysseus/pull/6470): shared theme, typography, sidebar and modal changes.
- [#6695](https://github.com/odysseus-dev/odysseus/pull/6695): extension system/platform integration.
Their changes were not merged or reimplemented. Shared UI exceptions, root-SW scope,
the original last-completion fetch race, temporary-note correlation and canvas-draft
limits are documented in the case study. Backend owner checks and PR #6495 contracts
remain authoritative.