feat(moments): a mount that keeps arriving where it does not apply proposes its own removal (milestone 458 step 7b, #4955)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m19s
CI & Build / Python tests (push) Successful in 2m3s
CI & Build / Build & push image (push) Successful in 18s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m19s
CI & Build / Python tests (push) Successful in 2m3s
CI & Build / Build & push image (push) Successful in 18s
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 <noreply@anthropic.com>
This commit is contained in:
@@ -101,19 +101,32 @@ export function unmapAction(change: MappingChange): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<string, number>;
|
||||
}
|
||||
|
||||
export interface RuleProposals {
|
||||
@@ -151,7 +164,10 @@ export function getProposals(ruleId?: number): Promise<ProposalsPayload> {
|
||||
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<JudgeResult> {
|
||||
return apiPost("/api/retrieval/moments/proposals/judge", { judgments });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user