mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-15 19:32:21 +02:00
* 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>
261 lines
16 KiB
Markdown
261 lines
16 KiB
Markdown
# 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.
|