From a72a422534c063523a2c3c48e023d60fe5803148 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 5 Oct 2026 16:39:47 -0400 Subject: [PATCH 1/3] docs(plugin): the instruction surfaces teach moments - reading a line that arrived at one, correcting a misfire, and giving a new rule its moments (milestone 458 step 8, #4926) Until now only the tool arguments knew moments existed. The guidance surfaces described rules as reached by resemblance alone: - using-scribe: a short reflex paragraph and a new reference file, moments.md. It covers reading "at , reached by ", the reply held once at reply.report, map_action / unmap_action offered in one line, and the step 7 proposal line answered with judge_rule_moments. - writing-records: asks WHEN a rule applies as well as what it is about. A rule, preference or process about a point in the work gets moments=[...] as it is written, and the trigger stays as the net. - missed-retrieval: a missed WHEN is mounted or mapped, not reworded. A misfire is unmounted or unmapped. - _INSTRUCTIONS: one clause (list_moments; mount rules about WHEN), 1594 of 1600 chars. - static context: injected lines include the rules mounted on a moment that was reached. - test_guidance_ownership: three owned topics, so the text cannot quietly drop out. Plugin minted. Co-Authored-By: Claude Opus 5.5 --- plugin/.claude-plugin/plugin.json | 2 +- plugin/hooks/scribe_static_context.md | 6 +- plugin/skills/using-scribe/SKILL.md | 9 +++ .../skills/using-scribe/missed-retrieval.md | 15 +++++ plugin/skills/using-scribe/moments.md | 66 +++++++++++++++++++ plugin/skills/using-scribe/writing-records.md | 20 +++++- src/scribe/mcp/server.py | 9 +-- tests/test_guidance_ownership.py | 13 ++++ 8 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 plugin/skills/using-scribe/moments.md diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index c8493faf..1c8d3f57 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "scribe", "description": "Scribe for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Scribe skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting), and syncs your saved Scribe Processes as skills (/scribe:sync).", - "version": "2026.10.05.2003", + "version": "2026.10.05.2039", "author": { "name": "Bryan Van Deusen" }, diff --git a/plugin/hooks/scribe_static_context.md b/plugin/hooks/scribe_static_context.md index 062af163..64550e14 100644 --- a/plugin/hooks/scribe_static_context.md +++ b/plugin/hooks/scribe_static_context.md @@ -16,8 +16,10 @@ What only Claude Code needs said: Scribe works alongside them. - **Lines injected beside your work are retrieval.** When the operator sends a message, and before a write or a command, Scribe may add rules, preferences, - notes and prior art that resemble what you are doing. Open the ones that - apply; using-scribe says what a quiet turn means. + notes and prior art that resemble what you are doing — and, when a tool call + or the reply ending a turn reaches a moment of work, the rules mounted on + it. Open the ones that apply; using-scribe says what a quiet turn means and + how to correct a moment that fired wrongly. - **Compact at clean seams.** Because work is recorded as you go, a compaction is safe once in-flight state is logged. After finishing a block of work in a long session, log it to Scribe, then tell the operator it's a good moment to diff --git a/plugin/skills/using-scribe/SKILL.md b/plugin/skills/using-scribe/SKILL.md index 850ffbf0..8fcba475 100644 --- a/plugin/skills/using-scribe/SKILL.md +++ b/plugin/skills/using-scribe/SKILL.md @@ -87,6 +87,12 @@ Two constraints on *how* that's achieved: then the global rules plus that project's own, never another project's. `enter_project(id)` lists the project's own rules by title. + **A rule about WHEN arrives at its moment, by lookup.** A rule mounted on a + moment of work (`list_moments`: `work.deliver`, `reply.report`, …) arrives + whenever an action reaches that moment, in a line naming both — nothing + said needs to resemble it. [moments.md](moments.md) says how to read those + lines, how to correct a moment that misfires, and when to mount one. + **`kind` says how much force a record carries, and it is never something to infer.** A **rule** must be followed: ignoring it breaks something or crosses a boundary. A **preference** records how the operator wants work @@ -288,6 +294,9 @@ moment rather than on every turn: before giving a note a check. - [missed-retrieval.md](missed-retrieval.md) — a rule that missed the moment it governed, or keeps arriving where it doesn't apply. +- [moments.md](moments.md) — a line that says a rule arrived *at* a moment, a + reply held for one read, an action that reached the wrong moment or none, + and a line proposing a mount. ## You are the judge of what the record says diff --git a/plugin/skills/using-scribe/missed-retrieval.md b/plugin/skills/using-scribe/missed-retrieval.md index c062d255..affaa00a 100644 --- a/plugin/skills/using-scribe/missed-retrieval.md +++ b/plugin/skills/using-scribe/missed-retrieval.md @@ -11,6 +11,21 @@ does **either noticer**: the operator saying *"that should have fired"*, and you noticing it yourself — you reached for a rule nobody offered you, or you were handed the same rule five times and set it aside five times. +**First ask whether it missed a WHEN or a WHAT.** A rule about a point in the +work — it governs delivering, finishing, verifying, reporting, whatever the +work is about — is not a trigger to reword: no wording resembles every piece +of work that reaches that point. Mount it on the moment instead +(`update_rule(moments=[...])`, from `list_moments`); a mount is a lookup, and +it arrives every time the moment happens. When it is already mounted and still +did not arrive, the action did not reach the moment on this install — offer +`map_action` for that action. The reverse — a mounted rule arriving where it +does not apply — is a mount or a mapping that is wrong: take the moment off +the rule, or `unmap_action` the action that reached it. Each of these +changes what the operator's sessions receive, so offer it in one line and make +it on their yes. [moments.md](moments.md) has the +detail. A rule about a subject missed its WHAT, and the rest of this page is +for that. + **Take it to the record first and the dial second.** A rule's `when_to_apply` IS the text its similarity score is computed against, so when a rule misses a moment it governs, the overwhelmingly likely cause is that its trigger does not diff --git a/plugin/skills/using-scribe/moments.md b/plugin/skills/using-scribe/moments.md new file mode 100644 index 00000000..31ddb5eb --- /dev/null +++ b/plugin/skills/using-scribe/moments.md @@ -0,0 +1,66 @@ +# Moments — rules that arrive when the work reaches a point + +Part of the using-scribe skill. Read it when a line says a rule arrived *at* a +moment, when a reply is held for one read, when an action reached the wrong +moment or none, and when a line proposes mounting a rule. + +## What a moment is + +Most rules reach you by resemblance: what you are doing looks like what the +rule is about. A rule about WHEN — finishing, delivering, verifying, +reporting — resembles nothing said at that point, so resemblance misses it. +Such a rule is **mounted** on the moments of work it belongs to, and arrives +by lookup whenever an action reaches one. `list_moments` names them, each +with what is happening at it and the kinds of action that typically reach it: +`work.deliver` when work is sent beyond the place it was made, `work.finish` +when a piece of work is declared done, `reply.report` at the reply that ends a +turn, and so on. A named procedure is its own moment, `skill.`, reached when it is +loaded. + +Which ACTIONS reach a moment is the install's: one operator delivers with a +push, another with a deploy script. Shipped defaults cover the common ones, +and each install corrects them for itself. + +## Reading a line that arrived at a moment + +The line names the moment and the action that reached it — *"at work.deliver, +reached by `git push`"* — and the rule, with `get_rule(N)` to read it. Read it +as you would any rule that arrived beside your work: it binds just as hard, +and it came because of what you are doing now, not because of what you said. + +The reply that ends a turn is a moment too. When a rule mounted at +`reply.report` has not been opened this session, the reply may be held once +with its name: open it, then send the reply — unchanged, if it already does +what the rule asks. The same rule is never held twice. + +## When the moment is wrong — correct it in the session + +A moment can fire on an action that is not that moment here, or an action can +plainly be a moment and fire nothing. Either way the fix is one call, and it +belongs in the session that noticed, not on a settings page: + +- **An action reached the wrong moment** — the line names a moment that is not + what you did: offer `unmap_action(tool, moment, match, reason)`. +- **An action was a moment and nothing arrived** — the operator ships with + their own script, and nothing mounted on `work.deliver` came: + offer `map_action(tool, moment, match, reason)`. + +Offer it in one line, the way the operator would say it ("that deploy script +is a deliver and nothing fired — map it?"), and make it on their yes. The +correction lasts for every later session on the install; `list_moments` shows +what each action reaches now. + +## When a line proposes a mount + +A rule that keeps being opened just after the same moment, across several +sessions, probably belongs on that moment. A line says so, naming the rule, +the moment and how often. It is a question for the operator, not a change you +make: offer it in one line, and record their answer with +`judge_rule_moments` — `confirm` mounts the rule, `reject` with their reason +stops the question being asked again. + +The same tool answers proposals from a pass over the rules +(`rules_to_mount`, `propose_rule_moments`, `rule_moment_proposals`): a pass +proposes, and only the operator's yes mounts. Writing a NEW rule is different +— its moments are part of writing it, and +[writing-records.md](writing-records.md) says how. diff --git a/plugin/skills/using-scribe/writing-records.md b/plugin/skills/using-scribe/writing-records.md index 1de8793e..854d4eee 100644 --- a/plugin/skills/using-scribe/writing-records.md +++ b/plugin/skills/using-scribe/writing-records.md @@ -1,13 +1,15 @@ # Writing a rule, a lesson, or a note that asserts a fact Part of the using-scribe skill. Read it before `create_rule`, -`create_project_rule`, `create_preference` or `create_lesson`; when a lesson +`create_project_rule`, `create_preference`, `create_process` or +`create_lesson`; when a lesson arrives that names the situation you are actually in; when the operator decides how some area of the work must behave; and before filling `verify_with` or `expires_when` on a note. ## Contents - Where a new rule goes — its home, its trigger, what already covers the moment +- When it applies — the moments a rule, a preference or a process is for - A ruling goes on the System it governs - A lesson grows each time it proves itself - A lesson names the rule it is an instance of @@ -39,6 +41,22 @@ with no trigger is not a quiet rule, it is an unreachable one. Write the moment in the words a session actually produces — the command, the error, the half-formed ask — not the category it belongs to. +**Then ask WHEN it applies, as well as what it is about.** A trigger is +matched by resemblance, and a rule about a point in the work — finishing, +delivering, verifying, reporting, asking — resembles nothing said at that +point. Give such a rule its moments as you write it: `list_moments` names +them, and `moments=[...]` on `create_rule`, `create_project_rule` or +`create_preference` mounts it, so it arrives whenever an action reaches one. +Keep the trigger anyway: it is the net for the moments nobody mapped. A rule +about a subject — a library, a file, a style — has no moment; it is reached +by meaning, and leaving `moments` empty is the answer, not an omission. A rule +often has both: a point in the work, and the words a session uses at it. + +A stored **process** says the same about itself: `create_process(moments=…)` +names the moments the procedure is for, so loading it reaches them and the +rules mounted there arrive with it. A rule that only applies inside one +procedure mounts on that procedure's own moment, `skill.`. + **Before writing one, ask what already covers that moment.** `what_might_apply("the moment you are about to write a record for")` — fifty candidates and no bar, so an existing record cannot hide under a threshold the diff --git a/src/scribe/mcp/server.py b/src/scribe/mcp/server.py index 5b1d5ce2..1457a511 100644 --- a/src/scribe/mcp/server.py +++ b/src/scribe/mcp/server.py @@ -43,14 +43,15 @@ from quart import Quart # decision #4027 and the notes it supersedes. _INSTRUCTIONS = """ Scribe is the operator's system of record, and yours: recall before acting, -record as you go, keep one copy here rather than in local memory files. Each -practice below is stated in full in the using-scribe skill (if your client -reads Agent Skills) and in each tool's description. +record as you go, keep one copy here, not in local memory files. Each +practice is stated in full in the using-scribe skill and each tool's +description. - Start with enter_project(id): the project, open work, Systems and design system. An `inception` key: ask what it inherits, then decide_project_inception. -- Rules are not preloaded; one arrives when your work matches it. Before a +- Rules are not preloaded; one arrives when your work matches it or reaches + a moment it is mounted on (list_moments; mount rules about WHEN). Before a consequential act, what_might_apply("what you are about to do"); search(content_type="rule") reads one you suspect. Silence means nothing matched, not none. Rules bind; preferences guide and you keep them current; diff --git a/tests/test_guidance_ownership.py b/tests/test_guidance_ownership.py index 7fd8b707..fba0bfea 100644 --- a/tests/test_guidance_ownership.py +++ b/tests/test_guidance_ownership.py @@ -185,6 +185,19 @@ TOPICS: tuple[Topic, ...] = ( Topic("where a new rule goes, and its trigger", U, ("create_project_rule", "when_to_apply"), "whichever home it gets"), Topic("a rule vs the other entities", U, ("standing instruction",), "first ask whether it's a rule at all"), + # Moments (milestone 458 step 8). Delivery at a moment and its in-session + # corrections are owned by using-scribe's moments.md; the index carries one + # clause, and a new rule's moments are asked where its home and trigger are. + Topic("rules mounted on a moment arrive by lookup; correct a misfire in-session", U, + ("list_moments", "map_action", "unmap_action", "judge_rule_moments"), + "it belongs in the session that noticed, not on a settings page", + index=("list_moments", "moment")), + Topic("a new rule about when gets its moments as it is written", U, + ("moments=[...]", "create_process(moments="), + "then ask when it applies, as well as what it is about"), + Topic("a missed when is mounted or mapped, not reworded", U, + ("update_rule(moments=",), + "first ask whether it missed a when or a what"), # Milestone 416 step 9: the tuning tools shipped in #4102/#4104 and were # named on NO instruction surface — measured, `retrieval_tuning_history` # returned zero events. Machinery with no route to it. From 8afd6da8aff195aa4f677b0159e855a14720316e Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 5 Oct 2026 16:44:22 -0400 Subject: [PATCH 2/3] docs(plugin): reference files point at each other in words, not links - one level deep (milestone 458 step 8, #4926) Co-Authored-By: Claude Opus 5.5 --- plugin/skills/using-scribe/missed-retrieval.md | 4 ++-- plugin/skills/using-scribe/moments.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/skills/using-scribe/missed-retrieval.md b/plugin/skills/using-scribe/missed-retrieval.md index affaa00a..92524151 100644 --- a/plugin/skills/using-scribe/missed-retrieval.md +++ b/plugin/skills/using-scribe/missed-retrieval.md @@ -22,8 +22,8 @@ did not arrive, the action did not reach the moment on this install — offer does not apply — is a mount or a mapping that is wrong: take the moment off the rule, or `unmap_action` the action that reached it. Each of these changes what the operator's sessions receive, so offer it in one line and make -it on their yes. [moments.md](moments.md) has the -detail. A rule about a subject missed its WHAT, and the rest of this page is +it on their yes. The skill's moments +reference has the detail. A rule about a subject missed its WHAT, and the rest of this page is for that. **Take it to the record first and the dial second.** A rule's `when_to_apply` diff --git a/plugin/skills/using-scribe/moments.md b/plugin/skills/using-scribe/moments.md index 31ddb5eb..22840e8b 100644 --- a/plugin/skills/using-scribe/moments.md +++ b/plugin/skills/using-scribe/moments.md @@ -62,5 +62,5 @@ stops the question being asked again. The same tool answers proposals from a pass over the rules (`rules_to_mount`, `propose_rule_moments`, `rule_moment_proposals`): a pass proposes, and only the operator's yes mounts. Writing a NEW rule is different -— its moments are part of writing it, and -[writing-records.md](writing-records.md) says how. +— its moments are part of writing it, and the +skill's guide to writing records says how. From 4e1320120dd40f17801956587b1ec1479ccec8b1 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 5 Oct 2026 18:19:59 -0400 Subject: [PATCH 3/3] feat(moments): a mount that keeps arriving where it does not apply proposes its own removal (milestone 458 step 7b, #4955) The open-after-moment signal proposes a mount; nothing proposed taking one off, so a wrong mount was noise at every occurrence until someone happened to notice. rule_misfired(rule_id, moment, why, reached_by) records a report against a MOUNTED pair, counted per distinct day (the MCP door carries no session id) on a new rule_moment_judgments.misfire column (migration 0119, backup v24). At three days the response carries a line asking the agent to offer the operator the fix - reject takes the rule off, unmap_action stops the action reaching the moment, confirm keeps the mount and stops the asking - and Settings > Moments lists it as an unmount proposal with the reasons and the actions that reached it. A re-mount clears the count. Taught in moments.md, missed-retrieval.md and the reply hold's wording. Co-Authored-By: Claude Opus 5.5 --- alembic/versions/0119_rule_moment_misfires.py | 36 +++ frontend/src/api/moments.ts | 26 ++- frontend/src/components/MomentProposals.vue | 76 ++++++- plugin/.claude-plugin/plugin.json | 2 +- .../skills/using-scribe/missed-retrieval.md | 3 +- plugin/skills/using-scribe/moments.md | 23 +- src/scribe/mcp/server.py | 3 + src/scribe/mcp/tools/moments.py | 58 ++++- src/scribe/models/rule_moment_judgment.py | 6 + src/scribe/services/backup.py | 8 +- src/scribe/services/lesson_rules.py | 10 +- src/scribe/services/moment_delivery.py | 13 +- src/scribe/services/rule_moment_judgments.py | 205 +++++++++++++++++- tests/test_guidance_ownership.py | 3 + .../test_integration_rule_moment_judgments.py | 141 +++++++++++- tests/test_integration_rule_moments.py | 17 +- tests/test_moments.py | 4 +- tests/test_rule_moment_judgments.py | 44 ++++ tests/test_services_backup.py | 2 +- 19 files changed, 636 insertions(+), 44 deletions(-) create mode 100644 alembic/versions/0119_rule_moment_misfires.py diff --git a/alembic/versions/0119_rule_moment_misfires.py b/alembic/versions/0119_rule_moment_misfires.py new file mode 100644 index 00000000..655a4f74 --- /dev/null +++ b/alembic/versions/0119_rule_moment_misfires.py @@ -0,0 +1,36 @@ +"""rule_moment_judgments.misfire — a mount reported as arriving where it does +not apply (milestone 458 step 7b, #4955) + +Revision ID: 0119 +Revises: 0118 +Create Date: 2026-10-05 + +The open-after-moment signal proposes a mount; nothing proposed taking one +off. This column is the evidence for that direction: each time a session says +a mounted rule arrived at a moment where it did not apply, the report is +counted here per distinct day, with its reason and the action that reached +the moment. At the bar it becomes an unmount proposal for the operator. + +A column on the judgment row rather than a table of its own: a misfire is +always about a (rule, moment) pair that is mounted, and that pair already has +its row. Nullable and no backfill — nobody has reported a misfire yet. +""" +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql +from alembic import op + +revision = "0119" +down_revision = "0118" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column( + "rule_moment_judgments", + sa.Column("misfire", postgresql.JSONB(), nullable=True), + ) + + +def downgrade() -> None: + op.drop_column("rule_moment_judgments", "misfire") diff --git a/frontend/src/api/moments.ts b/frontend/src/api/moments.ts index 70dd0b07..381b567b 100644 --- a/frontend/src/api/moments.ts +++ b/frontend/src/api/moments.ts @@ -101,19 +101,32 @@ export function unmapAction(change: MappingChange): Promise { } /** - * Proposals that a rule belongs on a moment (milestone 458 step 7) — from a - * pass that read the rule, or from the rule being opened just after the - * moment fired. `GET /api/retrieval/moments/proposals`, the payload the + * Proposals about a rule's moments, waiting on a person. A `mount` proposal + * (milestone 458 step 7) comes from a pass that read the rule, or from the + * rule being opened just after the moment fired. An `unmount` proposal (step + * 7b) comes from sessions reporting that the mount arrived where it did not + * apply. `GET /api/retrieval/moments/proposals`, the payload the * `rule_moment_proposals` MCP tool returns. */ -export type ProposalSource = "pass" | "signal" | "edit"; +export type ProposalSource = "pass" | "signal" | "edit" | "misfire"; + +export interface MisfireReason { + why: string; + reached_by: string; + at: string; +} export interface MomentProposal { + proposal: "mount" | "unmount"; moment: string; source: ProposalSource; why: string; + /** For a misfire, `situations` counts distinct days and `co_surfaced` the reports. */ evidence: { situations: number; projects: number; co_surfaced: number }; created_at: string | null; + /** Unmount proposals only: the newest reasons given, and how often each action reached the moment. */ + reasons?: MisfireReason[]; + reached_by?: Record; } export interface RuleProposals { @@ -151,7 +164,10 @@ export function getProposals(ruleId?: number): Promise { return apiGet(`/api/retrieval/moments/proposals${ruleId ? `?rule_id=${ruleId}` : ""}`); } -/** A confirm MOUNTS the rule on the moment; a reject stops it being proposed again. */ +/** + * A confirm MOUNTS the rule on the moment (on an unmount proposal, keeps it); + * a reject unmounts it if mounted and stops the pair being proposed again. + */ export function judgeProposals(judgments: MomentJudgment[]): Promise { return apiPost("/api/retrieval/moments/proposals/judge", { judgments }); } diff --git a/frontend/src/components/MomentProposals.vue b/frontend/src/components/MomentProposals.vue index 2250bf29..c5af5293 100644 --- a/frontend/src/components/MomentProposals.vue +++ b/frontend/src/components/MomentProposals.vue @@ -7,11 +7,13 @@ import { useMomentsStore } from "@/stores/moments"; import { useToastStore } from "@/stores/toast"; /** - * Proposals that a rule belongs on a moment, waiting on a person (milestone - * 458 step 7). They come from a pass that read the rule, or from the rule - * being opened just after the moment fired in several sessions. Nothing here - * is mounted until "Mount" is pressed — the same judgment `judge_rule_moments` - * makes in a session, through the same service. + * Proposals about a rule's moments, waiting on a person. A mount proposal + * (milestone 458 step 7) comes from a pass that read the rule, or from the + * rule being opened just after the moment fired in several sessions; nothing + * is mounted until "Mount" is pressed. An unmount proposal (step 7b) comes + * from sessions reporting that the mount arrived where it did not apply; + * nothing is taken off until "Take it off" is pressed. Both are the judgment + * `judge_rule_moments` makes in a session, through the same service. */ const store = useMomentsStore(); const toast = useToastStore(); @@ -35,27 +37,48 @@ function key(rule: RuleProposals, p: MomentProposal): string { return `${rule.id}:${p.moment}`; } +function plural(n: number, one: string, many: string): string { + return `${n} ${n === 1 ? one : many}`; +} + function sourceLabel(p: MomentProposal): string { + if (p.proposal === "unmount") { + return `reported as not applying here on ${plural(p.evidence.situations, "day", "days")}`; + } if (p.source === "signal") { return `opened just after this moment in ${p.evidence.situations} sessions`; } return "proposed from the rule's text"; } +// What the action that reached the moment was, most frequent first — so the +// operator can see whether it is the rule or one action that keeps misfiring. +function actions(p: MomentProposal): string[] { + return Object.entries(p.reached_by ?? {}) + .sort((a, b) => b[1] - a[1]) + .map(([action, n]) => `${action} ×${n}`); +} + +function note(p: MomentProposal, verdict: Verdict): string { + if (p.proposal === "unmount") { + return verdict === "confirm" ? "Kept in Settings." : "Taken off in Settings."; + } + return verdict === "confirm" ? "Mounted in Settings." : "Declined in Settings."; +} + async function decide(rule: RuleProposals, p: MomentProposal, verdict: Verdict) { busy.value = key(rule, p); try { const out = await judgeProposals([{ - rule_id: rule.id, moment: p.moment, verdict, - note: verdict === "confirm" ? "Mounted in Settings." : "Declined in Settings.", + rule_id: rule.id, moment: p.moment, verdict, note: note(p, verdict), }]); if (out.refused.length) { toast.show(out.refused[0].error, "error"); return; } await load(); - // A mount changes the per-moment counts the list below shows. - if (verdict === "confirm") await store.load(true); + // A mount or an unmount changes the per-moment counts the list below shows. + if ((p.proposal === "unmount") === (verdict === "reject")) await store.load(true); } catch (e) { toast.show(apiErrorMessage(e, "Could not record that"), "error"); } finally { @@ -75,8 +98,10 @@ onMounted(load);