@mindrian_os/install 1.13.0-beta.13 → 1.13.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +21 -11
- package/README.md +74 -572
- package/commands/act.md +1 -0
- package/commands/admin.md +1 -0
- package/commands/analyze-needs.md +1 -0
- package/commands/analyze-systems.md +1 -0
- package/commands/analyze-timing.md +1 -0
- package/commands/auto-explore.md +1 -0
- package/commands/beautiful-question.md +1 -0
- package/commands/brain-derive.md +1 -0
- package/commands/build-knowledge.md +1 -0
- package/commands/build-thesis.md +1 -0
- package/commands/causal.md +1 -0
- package/commands/challenge-assumptions.md +1 -0
- package/commands/compare-ventures.md +1 -0
- package/commands/dashboard.md +1 -0
- package/commands/deep-grade.md +1 -0
- package/commands/diagnose.md +1 -0
- package/commands/diagnostics.md +1 -0
- package/commands/doctor.md +1 -0
- package/commands/dominant-designs.md +1 -0
- package/commands/explain-decision.md +1 -0
- package/commands/explore-domains.md +1 -0
- package/commands/explore-futures.md +1 -0
- package/commands/explore-trends.md +1 -0
- package/commands/export.md +1 -0
- package/commands/feynman-timeline-refresh.md +78 -0
- package/commands/file-meeting.md +1 -0
- package/commands/find-analogies.md +1 -0
- package/commands/find-bottlenecks.md +1 -0
- package/commands/find-connections.md +1 -0
- package/commands/funding.md +1 -0
- package/commands/grade.md +1 -0
- package/commands/graph.md +1 -0
- package/commands/hat-briefing.md +1 -0
- package/commands/heal.md +1 -0
- package/commands/help.md +1 -0
- package/commands/hmi-status.md +1 -0
- package/commands/jtbd.md +1 -0
- package/commands/leadership.md +1 -0
- package/commands/lean-canvas.md +1 -0
- package/commands/macro-trends.md +1 -0
- package/commands/map-unknowns.md +1 -0
- package/commands/memory.md +1 -0
- package/commands/models.md +1 -0
- package/commands/mos-reason.md +1 -0
- package/commands/mullins.md +1 -0
- package/commands/new-project.md +1 -0
- package/commands/onboard.md +1 -0
- package/commands/operator.md +1 -0
- package/commands/opportunities.md +1 -0
- package/commands/organize.md +1 -0
- package/commands/persona.md +1 -0
- package/commands/pipeline.md +1 -0
- package/commands/present.md +1 -0
- package/commands/publish.md +1 -0
- package/commands/query.md +1 -0
- package/commands/radar.md +1 -0
- package/commands/reanalyze.md +1 -0
- package/commands/research.md +1 -0
- package/commands/room.md +1 -0
- package/commands/rooms.md +1 -0
- package/commands/root-cause.md +1 -0
- package/commands/rs-experts.md +1 -0
- package/commands/rs-explain.md +1 -0
- package/commands/rs-fetch.md +1 -0
- package/commands/rs-thesis.md +1 -0
- package/commands/scenario-plan.md +1 -0
- package/commands/scheduled-tasks.md +1 -0
- package/commands/score-innovation.md +1 -0
- package/commands/scout.md +1 -0
- package/commands/setup.md +1 -0
- package/commands/snapshot.md +1 -0
- package/commands/speakers.md +1 -0
- package/commands/splash.md +1 -0
- package/commands/status.md +1 -0
- package/commands/structure-argument.md +1 -0
- package/commands/suggest-next.md +1 -0
- package/commands/systems-thinking.md +1 -0
- package/commands/think-hats.md +1 -0
- package/commands/update.md +1 -0
- package/commands/user-needs.md +1 -0
- package/commands/validate.md +1 -0
- package/commands/value-proposition.md +1 -0
- package/commands/vault.md +1 -0
- package/commands/visualize.md +1 -0
- package/commands/whitespace.md +1 -0
- package/commands/wiki.md +1 -0
- package/lib/brain/framework-chain-slice.cjs +193 -0
- package/lib/core/cache-prune.cjs +114 -8
- package/lib/core/feynman/ROOM.md +25 -0
- package/lib/core/feynman/timeline-renderer.cjs +197 -0
- package/lib/core/feynman/timeline-runner.cjs +281 -0
- package/lib/core/install-state.cjs +242 -0
- package/lib/core/navigation/edges.cjs +86 -0
- package/lib/core/navigation/insights.cjs +37 -0
- package/lib/core/navigation/memory-events.cjs +39 -0
- package/lib/core/navigation/packet.cjs +89 -9
- package/lib/core/navigation/projections.cjs +201 -0
- package/lib/core/navigation.cjs +25 -0
- package/lib/mcp/larry-server-instructions.md +1 -1
- package/lib/memory/brain-cypher-chain-slice.test.cjs +368 -0
- package/lib/memory/f-selector-ranker.test.cjs +593 -0
- package/lib/memory/navigation-projections.test.cjs +241 -0
- package/lib/memory/navigation-write-edge.test.cjs +206 -0
- package/lib/memory/packet-chain-hint.test.cjs +407 -0
- package/lib/memory/packet-schema-validation.test.cjs +317 -0
- package/lib/memory/per-command-jtbd-derivation.test.cjs +130 -0
- package/lib/memory/per-command-teaching.test.cjs +110 -0
- package/lib/memory/run-feynman-tests.cjs +36 -0
- package/lib/memory/selector-decisions.test.cjs +417 -0
- package/lib/memory/selector-miss.test.cjs +290 -0
- package/lib/workflow/f-selector-ranker.cjs +420 -0
- package/lib/workflow/selector-decisions.cjs +368 -0
- package/package.json +1 -1
- package/references/design/email-template-standard.md +1 -1
- package/references/user-research/2026-04-05-leah-lawrence-session.md +3 -3
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
4
|
+
*
|
|
5
|
+
* Phase 125-06 -- F-selector decision recording + decay weight (D7 implementation).
|
|
6
|
+
* =================================================================================
|
|
7
|
+
* Implements CONTEXT.md D7: when the user picks F.1 (defer) or F.2 (reject) on a
|
|
8
|
+
* ranked command, emit THREE side-effects atomically:
|
|
9
|
+
*
|
|
10
|
+
* 1. memory_event row (event_type='f_selector_decision') via
|
|
11
|
+
* navigation.logMemoryEvent
|
|
12
|
+
* 2. typed cascade edge (DEFERRED | REJECTED) via navigation.writeEdge
|
|
13
|
+
* (Plan 00 primitive)
|
|
14
|
+
* 3. decay-weight applied to next N rankings (via applyDecayWeight reader,
|
|
15
|
+
* consumed by Plan 05 ranker's opts._applyDecayWeight IoC hook)
|
|
16
|
+
*
|
|
17
|
+
* All writes route through the navigation.cjs chokepoint. ZERO direct room-db
|
|
18
|
+
* access. ZERO direct loads of internal navigation submodules (the chokepoint
|
|
19
|
+
* is the only door). The grep audit in lib/memory/selector-decisions.test.cjs
|
|
20
|
+
* Test 7 enforces these invariants structurally on every CI run.
|
|
21
|
+
*
|
|
22
|
+
* Canon binding:
|
|
23
|
+
* Part 4: every choice is graph data; defer/reject become typed edges.
|
|
24
|
+
* Part 7: reuse before build; we ship as thin glue over Plan 00's
|
|
25
|
+
* writeEdge + Phase 109's logMemoryEvent + Phase 109's
|
|
26
|
+
* findRecentChanges -- not a single net-new SQL statement.
|
|
27
|
+
* Part 8: LOCAL only; no Brain calls; no user content leaves the room.
|
|
28
|
+
* Part 9: SQL remembers and navigates; this module reads-and-writes
|
|
29
|
+
* through the navigation chokepoint, never around it.
|
|
30
|
+
*
|
|
31
|
+
* Function signatures LOCKED in 125-CONTEXT.md "Function signatures":
|
|
32
|
+
* recordSelectorDecision({decision, command, framework, reason?, roomState})
|
|
33
|
+
* -> {decision_id, edge_id} on success | {ok: false, reason, detail?} on failure
|
|
34
|
+
* applyDecayWeight(base_score, command_id, roomState) -> number
|
|
35
|
+
*
|
|
36
|
+
* D7 exponential decay formula (CONTEXT.md verbatim):
|
|
37
|
+
* adjusted_score = base_score * (1 - exp(-(invocations_since_decision / N)))
|
|
38
|
+
* where N = 5 (DECAY_WINDOW).
|
|
39
|
+
*
|
|
40
|
+
* At decision time (n=0): factor = 0 (command pushed to bottom).
|
|
41
|
+
* 5 invocations later: factor ~ 0.6321.
|
|
42
|
+
* 10 invocations later: factor ~ 0.8647.
|
|
43
|
+
* 15+ invocations later: factor approaches 1 (decision effectively expires).
|
|
44
|
+
*
|
|
45
|
+
* License: BSL 1.1.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const navigation = require('../core/navigation.cjs');
|
|
49
|
+
|
|
50
|
+
// CONTEXT.md Open Question #6 lean: decay window N=5 invocations. Tunable per-
|
|
51
|
+
// room via .mos/config.json in v2; fixed at 5 for v1. Exposed as a module
|
|
52
|
+
// constant so consumers (Plan 05 ranker, debug tooling, future tuning passes)
|
|
53
|
+
// can reference DECAY_WINDOW symbolically rather than hard-coding 5.
|
|
54
|
+
const DECAY_WINDOW = 5;
|
|
55
|
+
|
|
56
|
+
// CONTEXT.md RESEARCH G-09 lean: exclude commands whose decay factor is below
|
|
57
|
+
// this threshold from the top-K list entirely (freshly-rejected commands
|
|
58
|
+
// shouldn't appear in the very next selector). Plan 05 ranker may wire this
|
|
59
|
+
// helper via opts to filter. Threshold 0.1 means "first invocation after a
|
|
60
|
+
// decision suppresses the command; from invocation 1 onwards the command
|
|
61
|
+
// resurfaces with the decayed weight". Configurable in v2.
|
|
62
|
+
const EXCLUSION_THRESHOLD = 0.1;
|
|
63
|
+
|
|
64
|
+
// CONTEXT.md Open Question #7 lean: clock-driven expiry alongside the
|
|
65
|
+
// invocation-count decay. DEFERRED edges carry expires_at 30 days from
|
|
66
|
+
// decision time; REJECTED edges have no expiry. This handles long-idle rooms
|
|
67
|
+
// gracefully (a deferred command shouldn't stay suppressed for a year if the
|
|
68
|
+
// user simply walked away from the room for weeks).
|
|
69
|
+
const DEFAULT_DEFER_EXPIRY_DAYS = 30;
|
|
70
|
+
|
|
71
|
+
function _defaultExpiresAt() {
|
|
72
|
+
return new Date(Date.now() + DEFAULT_DEFER_EXPIRY_DAYS * 24 * 3600 * 1000).toISOString();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// recordSelectorDecision -- the D7 writer.
|
|
77
|
+
//
|
|
78
|
+
// CONTEXT.md locked signature:
|
|
79
|
+
// recordSelectorDecision({decision, command, framework, reason?, roomState})
|
|
80
|
+
// -> {decision_id, edge_id}
|
|
81
|
+
//
|
|
82
|
+
// db handle is read from roomState.db per the design lock (callers populate
|
|
83
|
+
// roomState.db before invocation). This keeps the signature stable as the
|
|
84
|
+
// roomState shape evolves and prevents accidental signature drift.
|
|
85
|
+
//
|
|
86
|
+
// Returns {ok: true, decision_id, edge_id} on success, or
|
|
87
|
+
// {ok: false, reason, detail?} on validation/write failure. Defensive --
|
|
88
|
+
// never throws on caller input.
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
function recordSelectorDecision(args) {
|
|
91
|
+
const o = args || {};
|
|
92
|
+
const decision = o.decision;
|
|
93
|
+
const command = o.command;
|
|
94
|
+
const framework = o.framework;
|
|
95
|
+
// Reason is optional. CONTEXT.md acceptance: "Reason is optional -- when
|
|
96
|
+
// omitted, payload.reason is null AND edge.properties.reason is null."
|
|
97
|
+
const reason = (typeof o.reason === 'string' && o.reason.length > 0) ? o.reason : null;
|
|
98
|
+
const roomState = o.roomState || {};
|
|
99
|
+
const db = roomState.db;
|
|
100
|
+
const score_at_decision = (typeof o.score_at_decision === 'number') ? o.score_at_decision : null;
|
|
101
|
+
|
|
102
|
+
// Validation gate (defensive; never throws).
|
|
103
|
+
if (decision !== 'defer' && decision !== 'reject') {
|
|
104
|
+
return { ok: false, reason: 'invalid_decision' };
|
|
105
|
+
}
|
|
106
|
+
if (typeof command !== 'string' || command.length === 0) {
|
|
107
|
+
return { ok: false, reason: 'invalid_command' };
|
|
108
|
+
}
|
|
109
|
+
if (typeof framework !== 'string' || framework.length === 0) {
|
|
110
|
+
return { ok: false, reason: 'invalid_framework' };
|
|
111
|
+
}
|
|
112
|
+
if (!db) {
|
|
113
|
+
// Locked design: db must be on roomState.db. Callers populate this.
|
|
114
|
+
return { ok: false, reason: 'invalid_db' };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const investment_level_at_decision =
|
|
118
|
+
(typeof roomState.investment_level === 'number') ? roomState.investment_level : 0;
|
|
119
|
+
const edgeSemantic = (decision === 'defer') ? 'DEFERRED' : 'REJECTED';
|
|
120
|
+
const expiresAt = (decision === 'defer') ? _defaultExpiresAt() : null;
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------
|
|
123
|
+
// Step 1: write memory_event via the navigation chokepoint.
|
|
124
|
+
// CONTEXT.md memory_event payload schema (verbatim):
|
|
125
|
+
// {
|
|
126
|
+
// event_type: 'f_selector_decision', // overridden by logMemoryEvent
|
|
127
|
+
// decision: 'defer' | 'reject',
|
|
128
|
+
// command, framework,
|
|
129
|
+
// reason: string | null,
|
|
130
|
+
// edge_semantic: 'DEFERRED' | 'REJECTED',
|
|
131
|
+
// expires_at: ISO string | null,
|
|
132
|
+
// score_at_decision: number | null,
|
|
133
|
+
// investment_level_at_decision: number,
|
|
134
|
+
// source_path: 'f-selector:' + command,
|
|
135
|
+
// created_by: 'user',
|
|
136
|
+
// }
|
|
137
|
+
// ---------------------------------------------------------------------
|
|
138
|
+
const memEvent = navigation.logMemoryEvent(db, 'f_selector_decision', {
|
|
139
|
+
decision,
|
|
140
|
+
command,
|
|
141
|
+
framework,
|
|
142
|
+
reason,
|
|
143
|
+
edge_semantic: edgeSemantic,
|
|
144
|
+
expires_at: expiresAt,
|
|
145
|
+
score_at_decision,
|
|
146
|
+
investment_level_at_decision,
|
|
147
|
+
source_path: 'f-selector:' + command,
|
|
148
|
+
created_by: 'user',
|
|
149
|
+
});
|
|
150
|
+
if (!memEvent || !memEvent.ok) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
reason: 'memory_event_failed',
|
|
154
|
+
detail: memEvent ? memEvent.reason : 'unknown',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const decision_id = memEvent.eventId;
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------
|
|
160
|
+
// Step 2: write typed cascade edge via Plan 00's writeEdge primitive.
|
|
161
|
+
// CONTEXT.md edge schemas:
|
|
162
|
+
// DEFERRED: {reason, decision_id, expires_at}
|
|
163
|
+
// REJECTED: {reason, decision_id} (no expires_at)
|
|
164
|
+
// ---------------------------------------------------------------------
|
|
165
|
+
const edgeProps = (decision === 'defer')
|
|
166
|
+
? { reason, decision_id, expires_at: expiresAt }
|
|
167
|
+
: { reason, decision_id };
|
|
168
|
+
|
|
169
|
+
const edgeResult = navigation.writeEdge(db, {
|
|
170
|
+
source_id: 'cmd:' + command,
|
|
171
|
+
target_id: 'framework:' + framework,
|
|
172
|
+
edge_type: edgeSemantic,
|
|
173
|
+
properties: edgeProps,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (!edgeResult || !edgeResult.ok) {
|
|
177
|
+
return {
|
|
178
|
+
ok: false,
|
|
179
|
+
reason: 'edge_write_failed',
|
|
180
|
+
detail: edgeResult ? edgeResult.reason : 'unknown',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
decision_id,
|
|
187
|
+
edge_id: edgeResult.edge_id,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// _invocationsSinceDecision -- internal counter helper.
|
|
193
|
+
//
|
|
194
|
+
// Counts framework_invoked memory_event rows newer than the most recent
|
|
195
|
+
// f_selector_decision for this command. When roomState provides a pre-
|
|
196
|
+
// computed counter (test injection or upstream cache), prefer that --
|
|
197
|
+
// keeps applyDecayWeight pure for unit tests AND db-backed for production.
|
|
198
|
+
//
|
|
199
|
+
// Returns:
|
|
200
|
+
// number (>= 0) when a decision is on record.
|
|
201
|
+
// Infinity when no decision is on record (no decay applies).
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
function _invocationsSinceDecision(db, command, roomState) {
|
|
204
|
+
// Test seam: explicit pre-computed counter wins.
|
|
205
|
+
if (roomState
|
|
206
|
+
&& roomState.invocationsSinceDecision
|
|
207
|
+
&& typeof roomState.invocationsSinceDecision[command] === 'number') {
|
|
208
|
+
return roomState.invocationsSinceDecision[command];
|
|
209
|
+
}
|
|
210
|
+
if (!db) {
|
|
211
|
+
// No db and no counter -> treat as no decision (return Infinity sentinel).
|
|
212
|
+
return Infinity;
|
|
213
|
+
}
|
|
214
|
+
// Find the most recent f_selector_decision for this command.
|
|
215
|
+
// findRecentChanges returns rows ordered by created_at DESC; we scan up to
|
|
216
|
+
// 100 rows (the decision frequency is human-paced -- 100 is generous).
|
|
217
|
+
const recent = navigation.findRecentChanges(db, 0, {
|
|
218
|
+
eventType: 'f_selector_decision',
|
|
219
|
+
limit: 100,
|
|
220
|
+
});
|
|
221
|
+
let lastDecisionAt = null;
|
|
222
|
+
for (const row of recent) {
|
|
223
|
+
if (row && row.properties && row.properties.command === command) {
|
|
224
|
+
if (lastDecisionAt === null || row.createdAt > lastDecisionAt) {
|
|
225
|
+
lastDecisionAt = row.createdAt;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (lastDecisionAt === null) return Infinity; // no decision -> no decay
|
|
230
|
+
// Count framework_invoked events strictly newer than lastDecisionAt.
|
|
231
|
+
// findRecentChanges uses strict '>' on created_at per Phase 109.
|
|
232
|
+
const invocations = navigation.findRecentChanges(db, lastDecisionAt, {
|
|
233
|
+
eventType: 'framework_invoked',
|
|
234
|
+
limit: 1000,
|
|
235
|
+
});
|
|
236
|
+
return invocations.length;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// applyDecayWeight -- D7 exponential decay reader.
|
|
241
|
+
//
|
|
242
|
+
// CONTEXT.md locked signature:
|
|
243
|
+
// applyDecayWeight(base_score: number, command_id: string, roomState) -> number
|
|
244
|
+
//
|
|
245
|
+
// Pure when roomState supplies invocationsSinceDecision[command_id].
|
|
246
|
+
// Reads via navigation.findRecentChanges when db is on roomState.db.
|
|
247
|
+
//
|
|
248
|
+
// Decay formula:
|
|
249
|
+
// factor = 1 - exp(-(n / DECAY_WINDOW))
|
|
250
|
+
// adjusted = base_score * factor
|
|
251
|
+
//
|
|
252
|
+
// Edge cases:
|
|
253
|
+
// - No decision recorded -> returns base_score unchanged.
|
|
254
|
+
// - Non-finite or non-number base_score -> returns 0 (defensive).
|
|
255
|
+
// - Null/undefined roomState -> returns base_score (no history = no decay).
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
function applyDecayWeight(base_score, command_id, roomState) {
|
|
258
|
+
if (typeof base_score !== 'number' || !isFinite(base_score)) return 0;
|
|
259
|
+
if (typeof command_id !== 'string' || command_id.length === 0) return base_score;
|
|
260
|
+
const rs = (roomState && typeof roomState === 'object') ? roomState : {};
|
|
261
|
+
const db = rs.db ? rs.db : null;
|
|
262
|
+
const n = _invocationsSinceDecision(db, command_id, rs);
|
|
263
|
+
if (n === Infinity) return base_score; // no decision recorded; no decay
|
|
264
|
+
const factor = 1 - Math.exp(-(n / DECAY_WINDOW));
|
|
265
|
+
return base_score * factor;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// shouldExclude -- RESEARCH G-09 helper.
|
|
270
|
+
//
|
|
271
|
+
// Plan 05 ranker may wire this to filter freshly-rejected commands from
|
|
272
|
+
// top-K entirely (when their decay factor is below EXCLUSION_THRESHOLD).
|
|
273
|
+
// Returns true when the command should be excluded; false otherwise.
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
function shouldExclude(command_id, roomState) {
|
|
276
|
+
const rs = (roomState && typeof roomState === 'object') ? roomState : {};
|
|
277
|
+
const db = rs.db ? rs.db : null;
|
|
278
|
+
const n = _invocationsSinceDecision(db, command_id, rs);
|
|
279
|
+
if (n === Infinity) return false;
|
|
280
|
+
const factor = 1 - Math.exp(-(n / DECAY_WINDOW));
|
|
281
|
+
return factor < EXCLUSION_THRESHOLD;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Phase 125-07 -- D8 ranker-miss capture. Writes memory_event only (no edge --
|
|
286
|
+
// miss is temporal signal per CONTEXT.md D8 + RESEARCH G-10). Canon Part 8:
|
|
287
|
+
// user_intent is LOCAL-only; lives in room.db; never crosses to Brain.
|
|
288
|
+
// Consumer (F-selector renderer) is responsible for the follow-up /mos:do call;
|
|
289
|
+
// recordSelectorMiss never routes.
|
|
290
|
+
//
|
|
291
|
+
// CONTEXT.md locked signature:
|
|
292
|
+
// recordSelectorMiss({top_k_offered, user_intent, roomState}) -> {miss_id}
|
|
293
|
+
//
|
|
294
|
+
// db handle is read from roomState.db per the locked design; callers populate
|
|
295
|
+
// roomState.db before invocation (mirrors recordSelectorDecision pattern).
|
|
296
|
+
//
|
|
297
|
+
// Returns {ok: true, miss_id} on success, or {ok: false, reason, detail?} on
|
|
298
|
+
// validation/write failure. Defensive -- never throws on caller input.
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
function recordSelectorMiss(args) {
|
|
301
|
+
const o = args || {};
|
|
302
|
+
const top_k_offered = o.top_k_offered;
|
|
303
|
+
const user_intent = o.user_intent;
|
|
304
|
+
const roomState = o.roomState || {};
|
|
305
|
+
const db = roomState.db;
|
|
306
|
+
|
|
307
|
+
// Validation gate (defensive; never throws). db check is first because
|
|
308
|
+
// without it the chokepoint call would throw a more confusing error.
|
|
309
|
+
if (!db) {
|
|
310
|
+
return { ok: false, reason: 'invalid_db' };
|
|
311
|
+
}
|
|
312
|
+
if (!Array.isArray(top_k_offered)) {
|
|
313
|
+
return { ok: false, reason: 'invalid_top_k_offered' };
|
|
314
|
+
}
|
|
315
|
+
if (typeof user_intent !== 'string' || user_intent.length === 0) {
|
|
316
|
+
return { ok: false, reason: 'invalid_user_intent' };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Sanitize top_k_offered: keep ONLY {command, score} fields. Defense-in-
|
|
320
|
+
// depth so callers passing full RankedItem objects (with teaching /
|
|
321
|
+
// jtbd_summary / why / source / framework / investment_level) don't
|
|
322
|
+
// accidentally write those extra fields into the payload. The miss capture
|
|
323
|
+
// is a tuning signal, not a snapshot of the full ranker output.
|
|
324
|
+
const cleanOffered = top_k_offered
|
|
325
|
+
.filter((x) => x && typeof x.command === 'string' && typeof x.score === 'number')
|
|
326
|
+
.map((x) => ({ command: x.command, score: x.score }));
|
|
327
|
+
|
|
328
|
+
const investment_level_at_decision =
|
|
329
|
+
(typeof roomState.investment_level === 'number') ? roomState.investment_level : 0;
|
|
330
|
+
|
|
331
|
+
// Write memory_event via the navigation chokepoint. NO cascade edge --
|
|
332
|
+
// miss is a temporal signal per D8 (and the absence of an edge is what
|
|
333
|
+
// makes recordSelectorMiss strictly weaker than recordSelectorDecision).
|
|
334
|
+
// Canon Part 8: user_intent never leaves room.db; never crosses to Brain.
|
|
335
|
+
const result = navigation.logMemoryEvent(db, 'f_selector_miss', {
|
|
336
|
+
top_k_offered: cleanOffered,
|
|
337
|
+
user_intent,
|
|
338
|
+
investment_level_at_decision,
|
|
339
|
+
source_path: 'f-selector:miss',
|
|
340
|
+
created_by: 'user',
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
if (!result || !result.ok) {
|
|
344
|
+
return {
|
|
345
|
+
ok: false,
|
|
346
|
+
reason: 'memory_event_failed',
|
|
347
|
+
detail: result ? result.reason : 'unknown',
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return { ok: true, miss_id: result.eventId };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
recordSelectorDecision,
|
|
356
|
+
applyDecayWeight,
|
|
357
|
+
shouldExclude,
|
|
358
|
+
recordSelectorMiss,
|
|
359
|
+
DECAY_WINDOW,
|
|
360
|
+
EXCLUSION_THRESHOLD,
|
|
361
|
+
DEFAULT_DEFER_EXPIRY_DAYS,
|
|
362
|
+
// Test seam (private; consumed only by lib/memory/selector-decisions.test.cjs
|
|
363
|
+
// + lib/memory/selector-miss.test.cjs).
|
|
364
|
+
_test: {
|
|
365
|
+
_invocationsSinceDecision,
|
|
366
|
+
_defaultExpiresAt,
|
|
367
|
+
},
|
|
368
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindrian_os/install",
|
|
3
|
-
"version": "1.13.0-beta.
|
|
3
|
+
"version": "1.13.0-beta.16",
|
|
4
4
|
"description": "Install MindrianOS into Claude Code with one command -- `npx @mindrian_os/install`. Ships the MindrianOS plugin (Larry + PWS methodology + Data Room) plus a setup/diagnostics CLI (install/doctor/update).",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"mcp": "node bin/mindrian-mcp-server.cjs",
|
|
@@ -138,7 +138,7 @@ Rotate colors: #a63d2f, #1e3a6e, #c4a43c, #2d6b4a
|
|
|
138
138
|
<td width="16" style="background:#2d6b4a;height:3px;"> </td>
|
|
139
139
|
</tr></table>
|
|
140
140
|
<p style="...font-size:11px;color:#a09a90;">Sender Name</p>
|
|
141
|
-
<p style="...font-size:10px;color:#444;">MindrianOS -- PWS Methodology
|
|
141
|
+
<p style="...font-size:10px;color:#444;">MindrianOS -- PWS Methodology by Prof. Lawrence Aronhime</p>
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
## Rules
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# User Session: Lawrence +
|
|
1
|
+
# User Session: Lawrence + an admin-key holder -- Blueprint Phase AI Consultancy
|
|
2
2
|
|
|
3
3
|
**Date:** 2026-04-05
|
|
4
|
-
**Users:** Lawrence Aronhime (professor, power user),
|
|
4
|
+
**Users:** Lawrence Aronhime (professor, power user), an admin-key holder (daughter, new user)
|
|
5
5
|
**Environment:** macOS, Sonnet 4.6, Claude Pro, v1.7.1
|
|
6
6
|
**Project:** Blueprint Phase AI Consultancy
|
|
7
7
|
**Duration:** CLI session ~60min, meeting ~1h57m
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
### Participants
|
|
64
64
|
- **Jonathan Sagir** -- developer, demonstrating MindrianOS
|
|
65
65
|
- **Lawrence Aronhime** -- professor (Larry model), power user, demonstrating to Leah
|
|
66
|
-
- **
|
|
66
|
+
- **an admin-key holder** -- daughter, new user, fashion/design background, has her own Blueprint Phase project
|
|
67
67
|
|
|
68
68
|
### Key User Experience Observations
|
|
69
69
|
|