14 KiB
SLICE 10 — Separate child-notebook kanban and matrix views
State-saving rule: update this file and
TASKS.mdafter every completed task and whenever work pauses. Automated checks and manual Joplin acceptance must be recorded separately.
Status
PLANNED. Implementation has not started. Begin only after SLICE8 and SLICE9 are complete and accepted.
Goal
When a kanban or matrix block uses scope: children, render a separate complete
view for the root notebook and each descendant notebook that owns eligible
cards. This keeps large notebook trees browsable without nesting subsections
inside one board or matrix.
Confirmed behavior
- Multi-view rendering applies only to the literal
scope: childrenkeyword. this-folder, numeric depths (including very large values), andscope: allkeep their current single aggregated view.- The root notebook gets its own view for cards it owns directly.
- Every descendant notebook gets its own view for cards it owns directly.
- Cards are not rolled up into ancestor views; each card appears exactly once.
- Empty notebook views are omitted.
- If every notebook is empty, show the existing overall empty board/matrix once.
- Each view heading uses a full notebook path so duplicate titles are clear.
- Views use deterministic depth-first tree order: root first, then descendants; siblings sort case-insensitively by title with folder ID as a stable tie-breaker.
- Each generated view contains the normal three kanban columns or four matrix quadrants, rather than notebook subsections inside those buckets.
- Each bucket receives independent SLICE8 visible-count state and the configured
page-size. - SLICE9 normal-note cards group by
parent_idexactly like to-do cards. - Overall scan statistics count folders and notes once. Card totals equal the sum of all non-empty notebook groups.
Example
Projects <- root view (direct cards only)
├── Home <- separate Home view
│ ├── Repairs <- separate Repairs view
│ └── Garden <- separate Garden view
└── Work <- separate Work view
└── Release <- separate Release view
If Home has no eligible cards, its view is omitted; Repairs and Garden
still appear if they have cards.
Architecture and data flow
parse scope
children -> scopeDepth: Infinity + groupByNotebook: true
other -> existing values + groupByNotebook: false
|
v
resolve root notebook + ordered scoped folder metadata
|
v
collector scans each folder once
single mode -> existing aggregate board/matrix
grouped mode -> one exact-owner board/matrix per folder
|
v
plugin payload: discriminated single or notebook-grouped result
|
v
webview
single -> current component
grouped -> repeat complete component per non-empty notebook group
(each component owns independent SLICE8 state)
Data-contract decision
Use a discriminated view layout instead of returning both aggregate and grouped copies of every card:
type KanbanLayout =
| { kind: "single"; board: KanbanBoard }
| { kind: "notebooks"; groups: NotebookKanbanGroup[] };
type MatrixLayout =
| { kind: "single"; board: MatrixBoard }
| { kind: "notebooks"; groups: NotebookMatrixGroup[] };
Each group carries folderId, notebookPath, its board/matrix, cardCount, and
item-level warnings for that notebook. Config warnings, notebook-resolution
warnings, and profile-wide soft-cap warnings remain at the overall block level.
The empty grouped case carries groups: [] and renders one existing empty state.
Implementation plan
Phase 1 — Preserve the explicit scope intent
- Add
groupByNotebook: booleantoKanbanConfigandMatrixConfiginsrc/Gtd/types.ts. - In both parsers, set
scopeDepth = InfinityandgroupByNotebook = trueonly for literalscope: children. - Default
groupByNotebookto false. - Keep
this-folder, numeric depth, invalid-value fallback, andscope: allparsing unchanged. - When
scope: allis selected, force or leavegroupByNotebookfalse;scopeAllremains the controlling flag. - Add parser tests proving
childrenis distinguishable from numeric depth and that every other scope form remains single-view.
A boolean is deliberately preferred over a broad scope-enum refactor: existing
collectors still need scopeDepth and scopeAll, while this slice needs only one
additional rendering decision.
Phase 2 — Ordered notebook metadata helper
- Extend
folderScope.tsor add a focusedfolderTree.tshelper that returns scoped folder metadata rather than only IDs when grouping is requested. - Include
id,parentId, display title, and full path for the resolved root and every descendant. - Build paths from the actual notebook tree, including ancestors above a
notebook:-selected root so headings remain unambiguous. - Order root first, then perform depth-first traversal with siblings sorted by case-insensitive title and folder ID tie-breaker.
- Handle missing/empty titles with a stable documented fallback such as
Untitled notebookwhile retaining the folder ID tie-breaker. - Defend against malformed folder data (orphan parent references and cycles) without hanging; keep every reachable scoped folder at most once.
- Leave
resolveScopedFolderIdsbehavior intact for calendars, Gantt, and non-grouped views.
Phase 3 — Grouped collector contracts
- Add shared notebook-view metadata types plus
KanbanLayoutandMatrixLayoutdiscriminated unions insrc/Gtd/types.tsor collector-local result types where appropriate. - Refactor kanban collection so each eligible card is appended to the board
associated with the current scanned
folderIdwhen grouping is enabled. - Apply done-window filtering and sort each kanban group's columns using the existing logic.
- Refactor matrix collection equivalently and sort each group's quadrants.
- Attach malformed-block/item warnings to the owning notebook group.
- Omit groups whose final
cardCountis zero, including groups emptied by done-window or eligibility filtering. - Retain the current aggregate path and result behavior when grouping is off.
- Compute overall
scannedFolders,scannedNotes, andcardCountonce, without summing repeated scans or duplicating cards. - Ensure the host view note is excluded before group counts are finalized.
Phase 4 — Plugin payloads and warning ownership
- Update
handleGetKanbanandhandleGetMatrixinsrc/index.tsto return the discriminated layout plus SLICE8pageSize. - Keep config warnings, notebook-resolution warnings, config parse errors,
and
scope: allsoft-cap warnings at the overall block level. - Send item-level warnings inside their notebook group in grouped mode.
- Preserve the current top-level warning array in single mode.
- Return a structurally valid empty layout when the source note cannot be resolved.
- Keep overall statistics visible once per source block; optionally show a compact per-notebook card count in each generated heading, but do not add separate scan totals that could imply folders were scanned repeatedly.
Phase 5 — Webview component reuse
- Separate the existing kanban wrapper concerns (overall title, errors, global warnings, stats) from rendering one kanban board component.
- Do the same for one matrix component.
- In single mode, render exactly one component with current markup and visual behavior.
- In grouped mode, iterate groups in payload order and render one full component per group beneath a notebook-path heading.
- Render each group's item warnings adjacent to that group.
- Reuse the SLICE8 incremental-list helper inside every component; closure state must not be shared between groups or buckets.
- When
groupsis empty, render the existing empty board/matrix once rather than showing a blank block. - Add only the spacing/heading CSS required to distinguish notebook views; preserve column and matrix layout styles.
Phase 6 — Automated tests
Scope/parser tests
childrensets infinite depth plus grouping.- Numeric depth remains non-grouped, including a numeric string.
this-folder, invalid scope, and omitted scope remain non-grouped.scope: allremains non-grouped and retains notebook-conflict warnings.
Folder-tree tests
- Root plus multiple children and grandchildren.
- Deterministic depth-first order regardless of input folder order.
- Case-insensitive sibling ordering and folder-ID tie-breaker.
- Duplicate titles represented by unambiguous full paths.
- A
notebook:-selected child root with its ancestor path retained. - Empty titles, orphan parents, and cycle protection.
Kanban collector tests
- Root-owned and descendant-owned cards appear in separate exact-owner groups.
- Every card appears once with no ancestor rollup.
- Empty groups are omitted while non-empty grandchildren remain.
- Done-window filtering can remove an otherwise non-empty group.
- Per-group sorting and warnings are correct.
- SLICE9 note and to-do cards group identically.
- Aggregate stats and card totals are not double-counted.
- Non-grouped scope results remain unchanged.
Matrix collector tests
- Repeat exact-owner, omission, sorting, warning, mixed-card, and total-count coverage for Skeleton and Eisenhower modes.
- Confirm all four quadrants remain local to their owning notebook group.
- Confirm non-grouped scope results remain unchanged.
Payload/rendering contracts
- Test single and grouped payload construction at the thinnest practical boundary without adding a new browser-test dependency solely for this slice.
- If SLICE8 introduced reusable DOM tests, extend them to prove independent visible counts across notebook components; otherwise cover this manually.
- Run focused parser, folder-tree, kanban, and matrix tests.
- Run the complete Jest suite and record suite/test totals.
- Run
npm run distand record the produced.jplpath. - Run
git diff --checkand the prohibited-reference audit.
Phase 7 — Documentation
- Update README.md to state that literal
scope: childrenproduces separate notebook views for kanban and matrix blocks. - Add a parent/child/grandchild example showing root ownership, omitted empty notebooks, full-path headings, and no rollups.
- State that numeric depth and
scope: allremain aggregated. - Explain that every generated bucket has independent SLICE8 expansion state.
- Explain that SLICE9 notes group by their owning notebook.
- Update SPEC.md scope parsing, folder traversal, layout payloads, warning ownership, statistics, and rendering flow.
- Add an unreleased SLICE10 entry to CHANGELOG.md without changing the package version until release scope is decided.
Phase 8 — Manual Joplin acceptance
Create a root notebook containing direct cards, multiple children, grandchildren, duplicate child titles under different parents, empty notebooks, and more than one SLICE8 batch in at least two generated views. Include SLICE9 notes and to-dos.
- Confirm root-owned cards render only in the root view.
- Confirm every descendant card renders only in its owning notebook view.
- Confirm empty notebooks are omitted but non-empty descendants still appear.
- Confirm full paths distinguish duplicate titles.
- Confirm view ordering is stable and matches the documented traversal.
- Confirm both matrix modes create separate complete matrices.
- Confirm per-group warnings appear with the correct notebook.
- Confirm overall statistics equal the unique scanned/card totals.
- Confirm independent
page-sizelimits and "List more" state across groups, buckets, and multiple source blocks. - Confirm reload resets all expanded buckets.
- Confirm
this-folder, numeric depths, andscope: allstill render one aggregated view. - Confirm notebook targeting plus
scope: childrenroots grouping at the resolved target and uses correct full paths. - Confirm sorting, styling, navigation, completion, recurrence, and note-card behavior do not regress.
- Record user sign-off here; do not mark manual acceptance complete before confirmation.
Acceptance criteria
SLICE10 is complete only when:
- Literal
scope: childrenrenders separate root/descendant views with every eligible card present exactly once in its owning notebook. - Empty groups are omitted, headings are unambiguous, and ordering is stable.
- SLICE8 state is independent per bucket and SLICE9 cards group correctly.
- Warnings and statistics are correctly owned and never double-counted.
- Every other scope form retains its prior single-view behavior.
- Focused tests, the full suite, and the production package build pass.
- Manual Joplin acceptance is explicitly confirmed.
Files expected to change
src/Gtd/types.tssrc/Gtd/parseKanbanConfig.tssrc/Gtd/parseMatrixConfig.tssrc/Gtd/folderScope.tsand/or a newsrc/Gtd/folderTree.tssrc/Gtd/collectKanban.tssrc/Gtd/collectMatrix.tssrc/index.tssrc/gtd-calendar-webview.jssrc/event-calendar.csssrc/tests/Gtd/kanban.test.tssrc/tests/Gtd/matrix.test.tssrc/tests/Gtd/resolveNotebook.test.tsand/or a new folder-tree test fileREADME.mdSPEC.mdCHANGELOG.mdSLICE10.mdTASKS.md
Out of scope
- Grouping numeric-depth or
scope: allresults. - Ancestor rollups or duplicated cards across parent/child views.
- Nested notebook subsections inside a single kanban or matrix.
- Persisting expanded/collapsed notebook-view state.
- Notebook-level filtering controls or user-selectable grouping modes.
- Applying notebook grouping to calendar or Gantt views.
Dependencies and resume point
Implement after SLICE8 and SLICE9 are complete. Start by preserving
groupByNotebook in parser tests, then build and test ordered folder metadata.
Introduce discriminated collector layouts before changing the webview. Complete
single-view regression tests before grouped manual acceptance.