Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
9.5 KiB
id, title, status, wave, depends_on, plan, spec
| id | title | status | wave | depends_on | plan | spec |
|---|---|---|---|---|---|---|
| 01-contract-and-mapping | Contract and mapping | done | 1 | plan.md | ../../specs/plugin-nav-sidebar-footer/spec.md |
Task 01: Contract and mapping
Widen the NavItem.section contract to accept "insights", teach
pluginDestinations() to route it to the insights NavSection, and correct
the stale prose the spec's Stale prose at the edit sites section names
(items 1–4 and 6 are corrections; item 5 is the one net-new documentation
requirement). This is the only functional-behavior change in the whole
feature — every rendering surface already reads NavSection generically and
needs no edit (see plan.md's Overview).
Files to change
-
apps/web/lib/plugins/types.ts—NavItem.section:section?: "main" | "settings" | "integrations";becomes
section?: "main" | "settings" | "integrations" | "insights";and its doc comment (currently documents only
"main"and"integrations") is extended to cover all four:"insights"renders in the sidebar footer icon row and the phone Utilities group;"settings"is accepted and renders on no surface. -
docs/plans/plugins/PLUGIN-API.md(around line 323–327) — theNavItemsnippet'ssectionunion gains"insights", matching step 1. The comment block above it currently reads:// section: "main" (default) renders as a top-level sidebar entry; // "integrations" renders inside the sidebar's Integrations section alongside // the first-party integration links (GitHub, Jira, ...). Hosts predating a // section value simply don't render items targeting it (additive change).The last sentence is false — the shipped host drops nothing; an unrecognised value degrades to
"main"'s placement. Replace it with that degrade-to-"main"rule, and add a line for where"insights"renders (sidebar footer icon row / phone Utilities group). -
docs/public/plugins-authoring.mdline 184 — theregisterNavItemrow of the Frontend hook/API matrix currently reads "section is main, integrations, or accepted-but-not-rendered settings". Addinsightsto the list and state it renders in the sidebar footer icon row and the phone Utilities group. -
apps/web/lib/navigation/plugin-destinations.ts:- The
sectionmapping line insidepluginDestinations():becomes a three-way mapping —section: item.section === "integrations" ? ("integrations" as const) : ("plugins" as const),"integrations"→"integrations","insights"→"insights", anything else (including omitted or an unrecognised string) →"plugins"."settings"items are already filtered out one line above this mapping (.filter((item) => (item.section ?? "main") !== "settings")) and stay filtered. - The docblock directly above
pluginDestinations():The second clause is false: that slot is fed only by/** * `section: "settings"` items are skipped — those render in the settings tree's * `PluginSlot`, not as destinations. */registerComponent("settings-nav", …). Replace with: settings-section items are skipped and render on no surface. - The comment above
surfaces: SIDEBAR_AND_MENU:// Plugins reach the palette through `registerShortcut`, not as nav // commands, so plugin destinations stay off that surface.registerShortcutdoes not exist anywhere in this repository. Replace with a statement that plugin destinations never declare thepalettesurface, without naming a substitute API (there is none — see spec What).
- The
-
apps/web/lib/navigation/plugin-destinations.test.ts:- Retitle the test currently named
"keeps plugin items off the palette, which plugins reach via shortcuts"to drop the false trailing clause (same falsehood as theregisterShortcutcomment above). The assertion body does not change. - Extend the
pluginItemsfixture with aninsights-section entry and add assertions: it resolves to theinsightssection on bothsidebarandmobileMenu; it is absent frompluginsIn(...)("plugins"section) results on both surfaces; it is absent from the palette result (extend the retitled test's fixture and assertion). - Add a test for an unrecognised
sectionstring (e.g. a value not in the union, cast through the fixture): it appears inpluginsIn(...)and is absent from aninsights-section resolution. - Add a test mirroring the existing "keeps two plugins that pick the same
item id apart" case, using
section: "insights"for both registrations.
- Retitle the test currently named
-
apps/web/lib/navigation/resolve-destinations.test.ts— add:- A test that
resolveDestinations({ surface: "sidebar", section: "insights", pluginItems: [...] })orders the first-partystatsentry before a plugininsightsentry. - A test that resolving
surface: "mobileMenu", section: MOBILE_MENU_UTILITY_SECTIONSwith a plugininsightsitem inpluginItemsreturns ids in the order["stats", "settings", "plugin:acme:board"]— the plugin row lands aftersettings, not adjacent tostats(spec Ordering, phone scenario).
- A test that
Acceptance
NavItem.sectionaccepts"insights"intypes.ts, mirrored inPLUGIN-API.md's Before/After snippet.pluginDestinations()mapssection: "insights"items to theinsightsNavSectionand nothing else changes about"main"/"integrations"/"settings"/unrecognised-string handling.- All six stale-prose locations from the spec's Stale prose at the edit
sites list read correctly (five corrections plus the one documentation
requirement in
plugins-authoring.md). - New and updated tests in
plugin-destinations.test.tsandresolve-destinations.test.tspass.
Verification
cd apps && pnpm --filter @kandev/web test -- lib/navigation/plugin-destinations.test.ts lib/navigation/resolve-destinations.test.ts
cd apps/web && pnpm run typecheck
Files likely touched
apps/web/lib/plugins/types.tsdocs/plans/plugins/PLUGIN-API.mddocs/public/plugins-authoring.mdapps/web/lib/navigation/plugin-destinations.tsapps/web/lib/navigation/plugin-destinations.test.tsapps/web/lib/navigation/resolve-destinations.test.ts
Dependencies
None.
Parallelism
sequential (wave 1 — task 02 and task 03 both depend on this landing first).
Inputs
- Spec sections: What, API surface (including Stale prose at the edit sites and Section-to-placement mapping), Ordering.
- Existing pattern: the
"integrations"branch already inpluginDestinations()is the template for the new"insights"branch.
Output contract
Summary of the mapping change and every stale-prose correction made, the
files changed, exact test commands run and their outcomes, and any blockers
or risks (e.g. if a consumer outside this list is found to depend on the old
three-value union). Update this file's status to done and this plan's
Wave 1 checkbox and Verification Results section in the same change.
Results
Widened NavItem.section to "main" | "settings" | "integrations" | "insights"
in apps/web/lib/plugins/types.ts (doc comment extended to cover all four
values). Mirrored the union and the degrade-to-"main" rule in
docs/plans/plugins/PLUGIN-API.md. Added insights to the registerNavItem
row of docs/public/plugins-authoring.md's Frontend hook/API matrix.
pluginDestinations() in apps/web/lib/navigation/plugin-destinations.ts now
routes section through a new sectionFor() helper: "integrations" →
"integrations", "insights" → "insights", everything else → "plugins".
Corrected the docblock above pluginDestinations() (settings items render on
no surface, not in a PluginSlot) and the comment above surfaces: SIDEBAR_AND_MENU (no registerShortcut API exists; plugin destinations never
declare palette).
Retitled plugin-destinations.test.ts's palette test to
"keeps plugin items off the palette" (dropped the false "which plugins reach
via shortcuts" clause) and extended it to also assert an insights item is
excluded. Added: insightsIn() helper; a test routing insights items to the
insights section on both surfaces; a "moves, does not add" test against the
plugins group; an unrecognised-section-string degrade test; and an
insights-section two-plugin ordering/identity test. Added two tests to
resolve-destinations.test.ts (in a new describe("resolveDestinations with plugin items", …) block, split out to stay under the 100-line
max-lines-per-function limit) proving stats precedes a plugin insights
item on the sidebar, and the mobile utility group orders stats, settings,
then the plugin item.
Commands run (from apps/):
pnpm --filter @kandev/web test -- lib/navigation/plugin-destinations.test.ts lib/navigation/resolve-destinations.test.ts— 2 files, 25 tests passed.cd web && pnpm run typecheck— passed, no errors.pnpm --filter @kandev/web lint -- lib/navigation/plugin-destinations.ts lib/navigation/plugin-destinations.test.ts lib/navigation/resolve-destinations.test.ts lib/plugins/types.ts— 0 errors, 0 warnings (fixed twosonarjs/max-lines-per-functionwarnings found on first pass: extracted anACME_BOARD_IDconstant, split the new plugin-item tests into their owndescribeblock).pnpm run i18n:ratchet— clean (no new hardcoded strings; 2 modified files, 0 added).
Files touched matched Files likely touched exactly. No blockers.