Status: SHIPPED in v0.8.0. CIH prefix. A non-frozen design doc like
guided-tour.md, dense-graph-pan.md,
module-system.md — no loop-*/N id, no Frozen marker.
The design below (direction approved, review round 2 locked the four
boundaries in §CIH2.2 / §CIH2.3 / §CIH3 / §CIH4) merged as PR #133 (1048df5);
the implementation — hintStore, <HintNote>, the four v1 hints, and the
Contextual help Help-menu entry on both surfaces — merged as PR #134
(23ad159).
Two defects were found live-testing this feature during the v0.8.0
pre-release usability audit and fixed before tagging (both are stabilization
fixes, not scope changes to the design below): a Register expression field
elsewhere in the app locked up mid-edit whenever a hint's own re-render timing
exposed it (PR #136, b8f54eb) — unrelated to this feature's own code, but
found while exercising it; and the Contextual Help dialog's disabled "Show
again next time" button read identically whether a hint had never shown or
had just been re-armed, with no way to tell the two apart (PR #137,
278da6a) — a genuine bug in this feature, fixed by relabeling the disabled
state "Waiting to show next time" with no change to the re-arm mechanism
itself.
This is the item guided-tour.md §GT11 named and deferred:
"Later slice — contextual inline help. Separate design + PR; adds
Contextual help to the Help menu." It was also the last item on the
Onboarding, part 2 roadmap line — that track is now complete.
The guided tour (docs/guided-tour.md, shipped) orients a first-time visitor
once, linearly, on the very first run: it names the six regions of the
editor and what each is for, then gets out of the way forever (§GT1, §GT6).
By design it is not interactive and never asks the user to do anything
(§GT4) — and it deliberately skips Monte Carlo and revision / proposal
review, calling them "modal, advanced, and situational — a better fit for
contextual inline help than a linear first-run tour" (§GT10).
That leaves a real gap: things a user only needs explained when they actually happen, not on day one before they're relevant —
- a genuinely empty canvas the first time it's reached (no first-run tour step covers "you have nothing yet, here's where to start" — GT2 step 1 says "click or drag a piece," said once, before any canvas exists to demonstrate it on);
- Monte Carlo, opened for the first time, with no explanation of what a "run" there means versus the deterministic playback bar;
- a Review overlay (proposal / revision comparison), opened for the first time, with no explanation of what a hunk or a conflict is;
- a graph that has grown dense enough that Focus / Filter / frames — all shipped, all discoverable only by noticing small Controls icons — would actually help, and nothing currently says so.
This doc is about closing that gap for what already ships — it adds no new capability, only discovery and light explanation of capability that exists today. It is deliberately not a documentation site, a video, or an interactive tutorial (those stay separate tracks — §CIH1).
- CIH2 — one shared mechanism for a lightweight, non-modal, dismissible
situational hint, replacing the two ad hoc versions of this that already
exist in
Canvas.tsx(lgr-focus-hint,lgr-suggest-note) with one reusable piece. - CIH3 — the hint inventory: which situations get a hint in v1, and which are named and deferred with a reason.
- CIH4 — a
Contextual helpentry in the Help (?) menu (desktop) and the mobile More → Help sub-sheet — the placeholder both already carry (§GT7 /HelpMenu.tsx/MobileMoreMenu.tsx: "Contextual helpis not shown (later slice)"). - CIH5 — the persistence / dismiss model: per-hint "seen, don't show again," and a Show again next time path so nothing is silently lost forever.
- CIH6 — mobile scope, decided per hint.
- a11y / reduced-motion / forced-colors parity with the tour's existing bar.
- Presentation only — no engine, wire, digest, undo, or
simulationReveffect, mirroring §GT12.
- A full documentation site, multi-page help, video or GIF introduction material — a separate track (Hanrim named it as its own later step, not this one).
- Analytics / telemetry on hint views or dismissals.
- Interactive tutorials — a step the user must perform to proceed. Every hint here only points and describes, same rule as the tour (§GT1).
- Deep-linking to a specific hint from a URL.
- Re-surfacing a dismissed hint on a schedule / after N visits — dismissed means dismissed until the user explicitly asks via Show again (§CIH4).
- Touching the guided tour itself, or its two Help-menu entries.
Two ad hoc versions of "a small dismissible canvas note" already exist in
Canvas.tsx:
{focusMode && !focusSet && (
<Panel position="top-center" className="lgr-focus-hint">…</Panel>
)}
{autoFramesExist && !suggestNoteDismissed && (
<Panel position="top-center" className="lgr-suggest-note">…<button onClick={() => setSuggestNoteDismissed(true)}>✕</button></Panel>
)}
— one with no dismiss at all (it just tracks its own condition), one with a
useState dismiss that resets on reload. Good instincts, no shared plumbing.
CIH unifies the pattern others can build on:
hintStore(new, zustand) —seen: Record<HintId, boolean>hydrated from onelocalStoragekey,markSeen(id),rearm(id). Try/catch guarded exactly liketourStore's key (§GT6.3): a read/write failure is non-fatal and never locks a hint permanently off or on. Namedseen, notdismissed— see §CIH2.1a for why that isn't just a naming choice.<HintNote id condition={condition}>(new, shared component) — computeseligible = condition && !seen[id] && !closedThisInstance(the last term is a local, unpersisteduseState, reset on remount). Renders nothing when!eligible; otherwise the hint body plus one explicit ✕ that setsclosedThisInstance = true. Two render shapes, chosen per hint: a canvas<Panel position="top-center">(reuses the existinglgr-*-hint/-noteCSS shape) for canvas-situational hints, or a plain inline block for a hint embedded inside an existing dialog / overlay (Monte Carlo, Review).- The trigger condition stays at the call site, not in a central
registry —
nodes.length === 0,mcStore.status,reviewStore.open, and so on are already live selectors exactly where each hint renders. A big central "if X then show hint Y" table would drift from real app state the first time either changes; a local condition next to the render, like the two hints already inCanvas.tsx, does not.
The first cut of this doc tied persistence to the ✕ — reasonable-looking, but wrong in practice: Monte Carlo and Review are dialogs the user opens and closes routinely. If only an explicit ✕ click ever persisted anything, a user who reads the hint once and simply closes the dialog (the overwhelmingly normal thing to do) would see the exact same hint again the next ten times they open Monte Carlo. That is nagging, not a one-time nudge.
Fixed rule: markSeen(id) fires the instant eligible is first true for
that id — at render, in a mount effect — not gated behind any user action.
The ✕ only ever controls whether this one currently-mounted instance stays
visible (closedThisInstance); by the time a user could even see the ✕, the
persisted seen[id] write has already happened. Closing the dialog without
touching the hint, clicking ✕, or the underlying condition simply going false
(e.g. Focus gets toggled on) — all three end up in the same place: the hint
was shown once, is now recorded as seen, and will not render again.
seen is per-hintId, not one global flag — already true of the data
shape (Record<HintId, boolean>), stated here as an explicit guarantee: a
new hint added in a future slice starts at seen[newId] === undefined
(falsy) for every existing user, including one who has every current
hint marked seen. Nothing about shipping hint #5 later silently suppresses it
for people who have already seen #1–#4.
The codebase already has both tiers, for a reason:
- Persisted (the new
hintStore, onelocalStoragekey,seen[id]recorded on first render per §CIH2.1a) — right for a hint tied to first encounter with a whole feature (an empty canvas, Monte Carlo's first open, Review's first open). The user needs the nudge once, ever; re-explaining it every session is noise, exactly why the tour itself is a singlecompleted | dismissedkey (§GT6). - Session-only (in-memory, resets on reload) — right for a hint tied to a recurring state that isn't really "teach this once," like the existing auto-frame suggest note: closing it frees the current look at the canvas, and if the same state recurs after a fresh load, a light reminder again is fine, not nagging.
§CIH3 decides the tier for each v1 hint; the reasoning above is the rule for future ones, so this isn't re-litigated per hint forever.
Same ethos as the tour's "overlay that reads the UI, never drives it" (§GT4), extended to a mechanism that can fire many times across a session instead of once:
- Never a modal. No OK-to-proceed button, no focus trap, no backdrop, no keyboard capture. The canvas and every control underneath stay live.
Escapedoes not dismiss a hint — only its own ✕ does.Escapealready means other things depending on context (cancel a frame draw, close a dialog); overloading it onto a passive note is exactly the kind of quiet behavior change this doc must not introduce.- Never fires while the guided tour is running or the Welcome card is up
— gated on
tourStore.phase === 'idle'. The tour already established that nothing stacks on top of it (§GT6.1); a contextual hint appearing mid-tour would be new stacking, not a repeat of an existing rule.
The first cut only ruled on CIH's own two canvas hints colliding with each other. Hanrim's review pointed out the real conflict surface is wider: a dialog-embedded CIH hint, the two existing LGR notices, and CIH's own discovery-type canvas hints can all have a true trigger condition on the same screen at once, and something has to say which one actually renders. Fixed three-tier order, highest first:
- The hint for whatever screen is currently open — Monte Carlo's hint
(#2) while the MC dialog is open, Review's hint (#3) while Review is open.
These live in their own dialog/overlay region, so in practice they never
visually compete with a canvas hint — this tier exists to say explicitly
that opening MC/Review is never blocked or delayed waiting on a canvas
hint to clear.
Added with
docs/data-import.md§DI17: the one-shotimport-first- commitcanvas note (shown right after the first spreadsheet import lands) is tier 1 too — it follows a deliberate action (the wizard's Import button) — and it DOES use the canvastop-centerslot, so the tier-2lgr-suggest-noteyields to it while it is up (Hanrim, 2026-09-19). - The two existing LGR notices —
lgr-focus-hint,lgr-suggest-note. Unchanged, out of scope here (§CIH2.4), and they keep first claim on the canvastop-centerslot over anything new. - CIH's own discovery-type canvas hints — empty-canvas (#1) and
Focus/Filter (#4). Lowest priority: either only renders when neither
tier-2 notice is currently showing. (#1 and #4 can also never coexist with
each other — their trigger conditions,
nodes.length === 0vs.nodes.length >= WORTH_IT_FLOOR, are mutually exclusive by construction.)
Post-tour cooldown. Tier-3 hints additionally do not render for
POST_TOUR_COOLDOWN_MS (placeholder default 2000ms — a UX-tuning constant,
not a structural decision) after tourStore.phase transitions from
welcome/running back to idle (a tour finish, a tour dismiss, or a
Welcome-card skip). Without this, a graph that was already past
WORTH_IT_FLOOR for the whole tour would show the Focus/Filter hint the
literal instant the tour closes — a second onboarding moment stacked
immediately onto the first, exactly the "pile-up" the tour's own "nothing
stacks on it" rule (§GT6.1) was written to prevent, just reappearing one
layer up. Tier-1 hints (#2, #3) are not subject to this cooldown — they
only ever appear because the user just took a deliberate action (opening
that dialog), which is not a pile-up risk the same way a passive canvas note
triggered by pre-existing graph size is.
lgr-focus-hint and lgr-suggest-note stay exactly as they are. Rewriting
working, already-shipped, already-tested UI onto a new shared component the
moment that component is invented is scope creep with no user-facing benefit
— it would touch two more files, two more snapshot baselines, and two more
LGR-owned behaviors for a purely internal tidiness win. If a future pass wants
to fold them in, that is its own small PR, not part of shipping new hints.
| # | Situation | Trigger | Where it renders | Tier | v1? |
|---|---|---|---|---|---|
| 1 | Empty canvas | nodes.length === 0 on the open editor, tour idle, not mid-Import |
canvas top-center <Panel> |
persisted | Yes |
| 2 | Monte Carlo, first open | the MC dialog opens | inline note in the dialog body | persisted | Yes |
| 3 | Review, first open | the Review overlay opens (desktop or the mobile Review sheet) | inline note in the overlay | persisted | Yes |
| 4 | Large graph — Focus / Filter discovery | nodes.length >= WORTH_IT_FLOOR (8 — same threshold as the auto-frame suggestion) and neither Focus nor Filter ever toggled on and tour idle + past the post-tour cooldown and (the user has made at least one canvas interaction this session or LARGE_GRAPH_HINT_DELAY_MS has elapsed since load) and no tier-2 LGR notice is currently showing (§CIH2.3a) |
canvas top-center <Panel> (desktop); a one-line note atop the mobile More sheet's Focus/Filter rows |
persisted | Yes |
| 5 | First-connection recovery (dragged from a node body, not a handle) | no reliable signal exists today — would need a new "failed drag" event, not just a missing one | — | — | Backlog — needs its own small investigation before it can even be designed |
| 6 | Run / simulation error explanation | Register / expression errors are already inline-localized at the point of failure (error.M_REG_*.message, error.EXPR_*.message in the Inspector) |
— | — | Backlog — re-assess only if real user confusion shows up; today's inline error codes may already be enough |
| 7 | Module-insert discovery | no natural one-shot "first encounter" moment the way MC/Review have an open event — the Insert-module menu is just always present in the toolbar | — | — | Backlog — revisit with real usage signal |
| 8 | Frames (as their own hint) | — | — | — | Folded into #4 — the Focus/Filter hint copy names frames as a related tool instead of adding a fourth canvas hint competing for the same top-center slot |
| 9 | Frame move (added 2026-09-20 with the frame-drag carry, §LGR6.5) | the first time a saved frame is selected on an editable desktop canvas (not mobile, not edit-locked), tour idle | canvas top-center <Panel> (hint-note) — "Drag a frame’s edge to move it together with everything inside it. Hold Ctrl (⌘ on a Mac) while dragging to move the frame alone, or Alt to move it freely, off the grid." (Ctrl / ⌘ since #344; Alt before) |
persisted (frame-move) |
Yes — tier 1 (follows a deliberate action, like #import); it takes the slot over the tier-3 discovery hints and the auto-frame note but yields to the import note (ready = tourIdle && !importHintShowing, so it is neither rendered nor consumed while that one shows and appears once it closes if the frame is still selected); listed in Contextual help for re-arm. Distinct from #8: not a discovery hint, and it only appears once the user has already made a frame. |
#1 empty canvas — copy names the palette and "or start from a Template,"
pointing at the same two entry points the tour's steps 1 and 6 named, now
shown at the moment they matter. Dismiss: the ✕, or automatically the instant
nodes.length becomes > 0 (adding any piece counts as "found it" — no
separate "got it" click needed, matching MobileOpenFileHint's
self-clearing precedent).
#2 Monte Carlo — one or two sentences: many runs, not one; the bands and final-distribution view are what to look at; dismiss via ✕ (does not auto-clear — a user could read it and still not have run yet).
#3 Review — one or two sentences: a hunk is one changed piece; accept/keep
per hunk or as a whole; a conflict means both sides touched the same thing.
Wording covers both the desktop ReviewOverlay and the mobile Review sheet
(docs/module-system.md-adjacent Slice 1C review UI) — one key, two mount
points, same as the tour's copy-reuse pattern elsewhere.
#4 Focus / Filter — one or two sentences: "Getting crowded? Focus dims everything but a 1-hop neighborhood; Filter hides by type; frames group related pieces." Dismiss via ✕, or automatically the first time the user actually turns Focus or Filter on (their own real discovery beats the hint continuing to sit there).
#4's timing gate (review round 2). nodes.length >= WORTH_IT_FLOOR alone
is not enough to render this hint — loading the bundled MMO or Coffee
Template crosses that floor on the very first paint, at the exact moment the
existing lgr-suggest-note (auto-frame suggestion) is also eligible to
show for the same graph. Two onboarding notices competing for the same
top-center slot the instant a Template opens reads as clutter, not help.
Fixed: the hint additionally waits for both of —
tourStore.phase === 'idle'and pastPOST_TOUR_COOLDOWN_MS(§CIH2.3a);- the user has made at least one canvas interaction this session (a
pan, a zoom, a selection, a node move — any of it) or
LARGE_GRAPH_HINT_DELAY_MS(placeholder default 4000ms) has elapsed since load, whichever comes first —
and even once both are true, still yields to a currently-visible tier-2 LGR notice (§CIH2.3a) rather than stacking beside it. In practice: opening a large Template shows the auto-frame suggestion (if eligible) or nothing first; the Focus/Filter hint only appears a beat later, once the user has had a moment to actually look at the graph and nothing higher-priority is already occupying the slot.
Renamed on 2026-10-02 (issue #296). The entry is not a help document: it
only turns the short one-time notes back on, and its wording now says so. The
menu item is Turn contextual tips back on, the dialog title is Manage contextual tips, the per-row button is Show next time it applies, and the
waiting state reads Will show. The Korean wording is 상황별 안내 다시 켜기 /
상황별 안내 관리 / 다음 발생 시 표시 / 표시 예정. Nothing about the mechanism changed.
The text below keeps the original names where it records the original decisions;
the Help menu's current order is in release-notes.md.
Both Help surfaces already carry the placeholder and the exact same
exclusion comment (HelpMenu.tsx, MobileMoreMenu.tsx): "Contextual help
is not shown (later slice)." This is that slice.
- A third row, after
Take a tour/About Loop Studio:Contextual help. - Opens a small dialog reusing the existing
.mcdlgshell (same shellAboutDialogalready reuses;--z-mc-dialog, same asAbout) — desktop dialog, mobile sheet-shaped likeAboutis today. - Lists the four v1 hints by name with a one-line description each, and a
다음에 다시 표시 / Show again next time button per row (renamed in
review round 2 — see below) that calls
hintStore.rearm(id)and closes the dialog. - What the button actually does (review round 2).
rearm(id)clearsseen[id]— it does not force the hint to render right then. The hint becomes eligible again and shows at the next moment its own trigger condition, tier, and cooldown rules (§CIH2.3a) are naturally satisfied — which is sometimes immediately (closing the Help dialog can itself be the qualifying "canvas interaction" for #4, if its other gates are already clear) but is never forced out of context. This is exactly why the label is "Show again next time", not a bare "Show again": a bare "Show again" reads as an immediate action, and popping a hint up disconnected from the Help dialog it was just requested from — mid-dialog-close, with no situational grounding — is precisely the kind of interruption §CIH2.3 rules out everywhere else. help.contextual.*i18n namespace, EN + KO, same catalog discipline astour.*/about.*.
- One new
localStoragekey,loop-studio/contextual-help/1— namespaced likeloop-studio/guided-tour/1(§GT6). Value: a JSON object{ [hintId]: true }for every persisted-tier hint that has been shown at least once (§CIH2.1a — recorded on render, not on explicit dismiss). Keyed perhintId, so a hint added in a future slice is unaffected by what an existing user has already seen (§CIH2.1a). Read/write wrapped in try/catch; a missing, corrupt, or unparsable value is treated as{}(every hint shows again) — never as "everything permanently dismissed." Same non-fatal principle as §GT6.3. - Session-only tier hints (none in v1's four — see §CIH2.2 — but the
mechanism supports them for later use, e.g. if a future hint follows the
suggestNoteDismissedshape) keep their own in-memoryuseState, not written to the shared key at all. - Two UX-tuning constants, not structural decisions:
POST_TOUR_COOLDOWN_MS(§CIH2.3a) andLARGE_GRAPH_HINT_DELAY_MS(§CIH3 #4). Both live as plain constants next tohintStore, not persisted, not configurable by the user — placeholders to be tuned during implementation, not locked by this doc. - No GraphDoc /
loop-revision/*digest / undo /simulationRev/ Monte-Carlo-result / selection / viewport effect from showing, dismissing, or resetting any hint — presentation only, the same contract §GT12 states for the tour.
| Hint | Mobile? | Notes |
|---|---|---|
| #1 Empty canvas | No | Mobile is view/run only and always shows a loaded graph, a Template, or MobileOpenFileHint ("nothing loaded yet, open a file") — that hint already owns this exact situation on mobile; adding a second one competing for the same moment is redundant, not additive. |
| #2 Monte Carlo | Yes | Same dialog, same trigger, mobile-fitted copy if the desktop wording references anything desktop-only (it doesn't currently). |
| #3 Review | Yes | The mobile Review sheet is its own mount point for the same content; same key, same one-time trigger. |
| #4 Focus / Filter | Yes | Both are real, reachable mobile More-sheet rows (MobileMoreMenu.tsx already wires toggleFocusMode and the Filter sub-sheet) — not desktop-only despite editing being desktop-only. Rendered as a one-line note above those two rows in the More sheet rather than a canvas <Panel> (there is no top-center canvas panel slot on the mobile view/run layout). |
Contextual help Help entry |
Yes | Both Help surfaces get the same third row (§CIH4). |
Presentation only, mirroring §GT12 exactly: contextual hints carry no
loop-*/N id, define no wire field, are revised freely, and are never
observable in an export, a digest, an undo entry, or the engine. Showing,
dismissing, auto-clearing, or resetting-via-Help leaves every one of the
following byte-for-byte unchanged: the GraphDoc and its digest, undo/redo,
simulationRev, SimState, the Monte-Carlo config/result, the current
selection, the viewport, the theme, and the active locale.
hintStore unit tests (mirrors tourStore.test.ts's shape):
- default: nothing seen.
markSeen(id)persists; a second read (simulating reload) still shows it seen.rearm(id)clears just that one id, others untouched.- a corrupt / unparsable stored value is treated as
{}, not a lock-out. - a
localStoragethrow on read or write is non-fatal — the in-memory state still updates for the session. - a new hintId is unaffected by other ids already marked seen — the
concrete regression test for §CIH2.1a's per-hint guarantee: seed
{ "empty-canvas": true, "mc-first-open": true }, assert a never-seen third id still reads as not-seen.
<HintNote> unit / component tests:
markSeen(id)fires the momenteligiblefirst becomes true — not gated on the ✕ being clicked (§CIH2.1a): mount withcondition = true, asserthintStore.seen[id]is already true before any click.- clicking ✕ hides this instance immediately without needing
seen[id]to change (it's already true) — assert viaclosedThisInstance, not via a secondmarkSeencall.
e2e, one spec per v1 hint plus the Help entry:
- each hint's trigger condition is necessary AND sufficient — it does not
show before the condition, does show once true, does not show a second
time after being shown once (with or without an explicit ✕ click — assert
both paths), does show again after
Contextual help → Show again next timeonce its own trigger/tier/cooldown conditions are next met (not forced the instant the button is pressed — assert the hint is still absent immediately after the Help dialog closes if its gating conditions aren't met yet, e.g. Focus/Filter's interaction-or-delay gate). - priority order (§CIH2.3a): with an MMO/Coffee-sized graph freshly
loaded,
lgr-suggest-note(orlgr-focus-hint) showing suppresses the Focus/Filter hint even once all its own gates are individually satisfied; once the LGR notice clears, the Focus/Filter hint becomes eligible. - post-tour cooldown (§CIH2.3a): finishing/dismissing the tour on an
already-dense graph does not show the Focus/Filter hint until
POST_TOUR_COOLDOWN_MShas passed. - the interaction-or-delay gate (§CIH3 #4): a single pan/zoom/selection
satisfies it immediately, without waiting for
LARGE_GRAPH_HINT_DELAY_MS; with zero interaction, the hint still appears once the delay elapses. - no hint renders while the guided tour / Welcome card is active (seed both states, assert absence).
- the empty-canvas hint clears itself the instant a first node is added, without needing its own ✕.
- the Focus/Filter hint clears itself the instant Focus or Filter is actually toggled on.
- Escape does not dismiss any hint (asserted directly — a real behavior contract, not an oversight to catch later).
- a11y: each hint is
role="note"or equivalent, notrole="alert"(it never demands attention); forced-colors keeps the ✕ and text visible without relying on hue alone; no motion is introduced (hints appear / disappear with the same no-transition rule the tour follows) soprefers-reduced-motionneeds no special case. - mobile: the three mobile-eligible hints render in their mobile mount points; the empty-canvas hint never renders on mobile.
Invariance: a full vitest + e2e pass showing no loop-revision/* /
GraphDoc / undo / simulationRev / Monte-Carlo-result change from any hint
interaction.
Locked — review round 1 (the v1 hint set): the four-hint set (§CIH3)
ships with a clear one-shot trigger each; frames folds into the large-graph
hint; first-connection recovery, run-error explanation, and module-insert
discovery are backlogged (§CIH3 #5–#7) rather than given a weaker invented
trigger now. The persisted-vs-session split (§CIH2.2) confirmed: all four v1
hints are persisted by the first-encounter-with-a-feature rule. Not migrating
lgr-focus-hint / lgr-suggest-note onto the new mechanism (§CIH2.4)
confirmed deliberate, not an oversight.
Locked — review round 2 (the four boundaries above):
- §CIH3 #4's timing gate —
WORTH_IT_FLOORalone is not sufficient; add the tour-idle + cooldown + interaction-or-delay + tier-2-notice-clear gates. - §CIH2.1a —
seenis recorded on first render, not on the ✕ click; the store field and API are renamed (dismissed→seen,dismiss/reset→markSeen/rearm) to make that the obviously-correct reading, not an implementation detail someone has to already know. - §CIH4 — the Help-menu button is "Show again next time", re-arms
(clears
seen[id]) rather than force-rendering; it is still subject to every one of a hint's own trigger/tier/cooldown rules. - §CIH2.3a — the two-hint canvas-collision rule is widened to a three-tier global priority (open-screen hint → existing LGR notices → CIH discovery hints) plus a post-tour cooldown.
Settled during implementation (PR #134), not a design amendment:
- The empty-canvas hint's copy points at the palette and Templates: "Start from a Template, or drag node types in from the left panel."
- The two timing constants landed at
POST_TOUR_COOLDOWN_MS = 2000andLARGE_GRAPH_HINT_DELAY_MS = 4000(src/store/hintStore.ts) — UX-tuning values, changeable without a design amendment, per §CIH5.
- This design pass — Draft PR, no code.
- Review — round 1 locked the v1 hint set and the tier rule; round 2 locked the four boundaries in §CIH9. — done.
- Implementation —
hintStore+<HintNote>+ the four v1 hints (with the §CIH2.3a priority order and §CIH3 #4 timing gate wired in from the start, not bolted on after) + theContextual helpHelp-menu entry (desktop + mobile) + i18n + the §CIH8 test set. Shipped — PR #134 (23ad159); two stabilization fixes found in thev0.8.0pre-release audit followed as PR #136 (b8f54eb) and PR #137 (278da6a), see the Status line above. - Then — Onboarding, part 2 is complete. The only work left on the current roadmap is the Productization-track follow-ups already named elsewhere (module-system §MS10 items).