@elvisw/dsh-magic-context 0.0.0-stage → 0.14.0
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/LICENSE +23 -0
- package/NOTICE +93 -0
- package/README.md +501 -2
- package/README.zh-CN.md +398 -0
- package/cordis.patch.yml +20 -0
- package/lib/client.js +2753 -0
- package/lib/core/compartments.js +424 -0
- package/lib/core/corpus-store.js +176 -0
- package/lib/core/directive-detect.js +147 -0
- package/lib/core/domain-reconcile.js +394 -0
- package/lib/core/domains.js +127 -0
- package/lib/core/embedding-local.js +247 -0
- package/lib/core/global-autostage.js +223 -0
- package/lib/core/global-domains.js +302 -0
- package/lib/core/journal.js +248 -0
- package/lib/core/llm-deadline.js +53 -0
- package/lib/core/machine-domain.js +106 -0
- package/lib/core/maintenance.js +380 -0
- package/lib/core/memory-bridge.js +818 -0
- package/lib/core/memory-categories.js +84 -0
- package/lib/core/memory-scoring.js +65 -0
- package/lib/core/memory-search.js +414 -0
- package/lib/core/memory-write.js +1176 -0
- package/lib/core/policy.js +111 -0
- package/lib/core/profile-confirmation.js +55 -0
- package/lib/core/profile-review.js +375 -0
- package/lib/core/profile-write.js +283 -0
- package/lib/core/reference-seeds.js +63 -0
- package/lib/core/route-health.js +106 -0
- package/lib/core/stores.js +1021 -0
- package/lib/core/tagger.js +7 -0
- package/lib/core/types.js +15 -0
- package/lib/core/vector-backfill.js +140 -0
- package/lib/core/vector-lane.js +184 -0
- package/lib/dsh/command-surface.js +376 -0
- package/lib/dsh/compaction-engine.js +231 -0
- package/lib/dsh/config-switches.js +56 -0
- package/lib/dsh/config.js +13 -0
- package/lib/dsh/context-pressure.js +36 -0
- package/lib/dsh/drop-landing.js +42 -0
- package/lib/dsh/emergency.js +89 -0
- package/lib/dsh/flush-command.js +148 -0
- package/lib/dsh/global-review.js +412 -0
- package/lib/dsh/historian.js +907 -0
- package/lib/dsh/live-journal.js +81 -0
- package/lib/dsh/maintenance-wiring.js +229 -0
- package/lib/dsh/management-pass.js +564 -0
- package/lib/dsh/memory-lane.js +535 -0
- package/lib/dsh/pre-step.js +675 -0
- package/lib/dsh/project-drift.js +116 -0
- package/lib/dsh/recomp.js +192 -0
- package/lib/dsh/reduce-guidance.js +188 -0
- package/lib/dsh/seams.js +588 -0
- package/lib/dsh/search-hint.js +186 -0
- package/lib/dsh/session-notice.js +100 -0
- package/lib/dsh/status-command.js +75 -0
- package/lib/dsh/telemetry.js +349 -0
- package/lib/dsh/tool-bridge.js +1009 -0
- package/lib/dsh/volatile.js +89 -0
- package/lib/index.js +931 -0
- package/package.json +108 -4
- package/tools/migrate-profile-package-name.mjs +317 -0
- package/vendor/onnx/LICENSE +27 -0
- package/vendor/onnx/ort-wasm-simd-threaded.mjs +59 -0
- package/vendor/onnx/ort-wasm-simd-threaded.wasm +0 -0
- package/vendor/onnx/ort.node.min.mjs +8 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WP5 — the `user` domain's COLLECTION half: historian observations →
|
|
3
|
+
* Magic Context's `user_memory_candidates`.
|
|
4
|
+
*
|
|
5
|
+
* Why a separate writer from `memory-write.ts`: candidates are not memories.
|
|
6
|
+
* They are the staging pool a review pass later promotes into `user_memories`,
|
|
7
|
+
* and MC keeps them in their own table with their own lifetime. Sharing the
|
|
8
|
+
* memory writer's connection would couple two different fences (this one needs
|
|
9
|
+
* the candidate table, the memory one needs `memories`), so it gets its own
|
|
10
|
+
* open/probe/fence sequence — the same three steps, its own table.
|
|
11
|
+
*
|
|
12
|
+
* Deliberate properties, each with a reason:
|
|
13
|
+
*
|
|
14
|
+
* - **fail-open.** A candidate that cannot be written is a missed opportunity,
|
|
15
|
+
* never a failure of the turn: the caller journals and continues. The
|
|
16
|
+
* compartment is already durable when this runs.
|
|
17
|
+
* - **fail-CLOSED on a newer contract.** If `context.db` is ahead of the
|
|
18
|
+
* version this adapter understands, the column layout of the candidate table
|
|
19
|
+
* is unknown, so the writer refuses instead of writing a guessed row. Same
|
|
20
|
+
* fence as `memory-write.ts`.
|
|
21
|
+
* - **the table is optional.** Older contracts (and every synthetic fixture
|
|
22
|
+
* that does not care about the profile lane) lack it; that is not an error,
|
|
23
|
+
* it is a lane that does not exist yet.
|
|
24
|
+
* - **no dedup across passes, dedup inside one.** MC's requirement for a
|
|
25
|
+
* promoted profile fact is cross-session RECURRENCE, so the same observation
|
|
26
|
+
* arriving in a later compartment is the evidence, not noise. Within a single
|
|
27
|
+
* batch a repeat is just the model saying the same thing twice.
|
|
28
|
+
*/
|
|
29
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
30
|
+
import { existsSync } from 'node:fs';
|
|
31
|
+
import { MEMORY_SCHEMA_MAX_VERSION } from "./memory-bridge.js";
|
|
32
|
+
import { errText } from "./types.js";
|
|
33
|
+
/** Anything longer is truncated: a runaway observation must not become a row. */
|
|
34
|
+
export const MAX_OBSERVATION_CHARS = 600;
|
|
35
|
+
/**
|
|
36
|
+
* Trim, drop empties and duplicates, and bound the length. Pure, so the rules
|
|
37
|
+
* are testable without a database; the truncation is silent by design (a long
|
|
38
|
+
* observation is still a useful candidate, just a bounded one) and the count of
|
|
39
|
+
* what was dropped is reported to the caller for the journal.
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeObservations(observations) {
|
|
42
|
+
const kept = [];
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
let skipped = 0;
|
|
45
|
+
for (const raw of observations) {
|
|
46
|
+
if (typeof raw !== 'string') {
|
|
47
|
+
skipped += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const trimmed = raw.trim();
|
|
51
|
+
if (trimmed === '' || seen.has(trimmed)) {
|
|
52
|
+
skipped += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
seen.add(trimmed);
|
|
56
|
+
kept.push(trimmed.length > MAX_OBSERVATION_CHARS ? trimmed.slice(0, MAX_OBSERVATION_CHARS) : trimmed);
|
|
57
|
+
}
|
|
58
|
+
return { kept, skipped };
|
|
59
|
+
}
|
|
60
|
+
const INSERT_CANDIDATE = `
|
|
61
|
+
INSERT INTO user_memory_candidates(
|
|
62
|
+
content, session_id, source_compartment_start, source_compartment_end, created_at
|
|
63
|
+
) VALUES(?, ?, ?, ?, ?)
|
|
64
|
+
`;
|
|
65
|
+
const SELECT_CANDIDATES = `
|
|
66
|
+
SELECT id, content, session_id AS sessionId,
|
|
67
|
+
source_compartment_start AS compartmentStart,
|
|
68
|
+
source_compartment_end AS compartmentEnd,
|
|
69
|
+
created_at AS createdAt
|
|
70
|
+
FROM user_memory_candidates
|
|
71
|
+
ORDER BY created_at ASC, id ASC
|
|
72
|
+
LIMIT ?
|
|
73
|
+
`;
|
|
74
|
+
/**
|
|
75
|
+
* Promotion (WP7). `status` is written explicitly rather than left to the
|
|
76
|
+
* column default: the render lane reads `status = 'active'`, and a profile row
|
|
77
|
+
* that arrives with any other value would be invisible while looking written.
|
|
78
|
+
*/
|
|
79
|
+
const INSERT_PROFILE = `
|
|
80
|
+
INSERT INTO user_memories(
|
|
81
|
+
content, status, promoted_at, source_candidate_ids, created_at, updated_at,
|
|
82
|
+
source_candidate_provenance
|
|
83
|
+
) VALUES(?, 'active', ?, ?, ?, ?, ?)
|
|
84
|
+
`;
|
|
85
|
+
const SELECT_PROFILES = `
|
|
86
|
+
SELECT id, content, promoted_at AS promotedAt,
|
|
87
|
+
source_candidate_ids AS sourceCandidateIds
|
|
88
|
+
FROM user_memories
|
|
89
|
+
WHERE status = 'active'
|
|
90
|
+
ORDER BY promoted_at ASC, id ASC
|
|
91
|
+
`;
|
|
92
|
+
export function openProfileWriter(options) {
|
|
93
|
+
const now = options.now ?? Date.now;
|
|
94
|
+
const unavailable = (detail) => {
|
|
95
|
+
options.logger.warn(`magic-context profile writer disabled: ${detail} (${options.dbPath})`);
|
|
96
|
+
return {
|
|
97
|
+
dbPath: options.dbPath,
|
|
98
|
+
status: { ok: false, detail },
|
|
99
|
+
recordCandidates: () => ({ inserted: 0, skipped: 0 }),
|
|
100
|
+
listCandidates: () => [],
|
|
101
|
+
countCandidates: () => 0,
|
|
102
|
+
expireCandidates: () => 0,
|
|
103
|
+
deleteCandidates: () => 0,
|
|
104
|
+
listActiveProfiles: () => [],
|
|
105
|
+
retireProfile: () => false,
|
|
106
|
+
promoteProfile: () => undefined,
|
|
107
|
+
close: () => undefined,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
if (!existsSync(options.dbPath))
|
|
111
|
+
return unavailable('database not found');
|
|
112
|
+
let db;
|
|
113
|
+
try {
|
|
114
|
+
db = new DatabaseSync(options.dbPath);
|
|
115
|
+
// Same order as the memory writer (contract §10.1): the busy timeout must
|
|
116
|
+
// precede WAL, because acquiring the mode itself can need the lock.
|
|
117
|
+
db.exec(`PRAGMA busy_timeout = ${String(options.busyTimeoutMs ?? 5_000)}`);
|
|
118
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return unavailable(`cannot open (${errText(error)})`);
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all()
|
|
125
|
+
.map(row => row.name));
|
|
126
|
+
if (!tables.has('user_memory_candidates')) {
|
|
127
|
+
db.close();
|
|
128
|
+
return unavailable('user_memory_candidates table missing');
|
|
129
|
+
}
|
|
130
|
+
const row = db.prepare('SELECT MAX(version) AS version FROM schema_migrations WHERE version < 10000').get();
|
|
131
|
+
const version = row?.version ?? null;
|
|
132
|
+
if (version !== null && version > MEMORY_SCHEMA_MAX_VERSION) {
|
|
133
|
+
db.close();
|
|
134
|
+
return unavailable(`corpus version ${String(version)} is newer than this adapter understands (${String(MEMORY_SCHEMA_MAX_VERSION)})`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
db.close();
|
|
139
|
+
return unavailable(`contract probe failed (${errText(error)})`);
|
|
140
|
+
}
|
|
141
|
+
const insert = db.prepare(INSERT_CANDIDATE);
|
|
142
|
+
const selectCandidates = db.prepare(SELECT_CANDIDATES);
|
|
143
|
+
const countCandidates = db.prepare('SELECT COUNT(*) AS c FROM user_memory_candidates');
|
|
144
|
+
const deleteCandidate = db.prepare('DELETE FROM user_memory_candidates WHERE id = ?');
|
|
145
|
+
const expireCandidates = db.prepare('DELETE FROM user_memory_candidates WHERE created_at < ?');
|
|
146
|
+
const insertProfile = db.prepare(INSERT_PROFILE);
|
|
147
|
+
const selectProfiles = db.prepare(SELECT_PROFILES);
|
|
148
|
+
const retireProfileStatement = db.prepare("UPDATE user_memories SET status = 'dismissed', updated_at = ? WHERE id = ? AND status = 'active'");
|
|
149
|
+
/** Run `fn` inside BEGIN IMMEDIATE, rolling back on any throw. */
|
|
150
|
+
const inTransaction = (fn) => {
|
|
151
|
+
db.exec('BEGIN IMMEDIATE');
|
|
152
|
+
try {
|
|
153
|
+
const result = fn();
|
|
154
|
+
db.exec('COMMIT');
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
try {
|
|
159
|
+
db.exec('ROLLBACK');
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
/* the transaction may already be gone */
|
|
163
|
+
}
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return {
|
|
168
|
+
dbPath: options.dbPath,
|
|
169
|
+
status: { ok: true },
|
|
170
|
+
recordCandidates: (input) => {
|
|
171
|
+
const { kept, skipped } = normalizeObservations(input.observations);
|
|
172
|
+
if (kept.length === 0)
|
|
173
|
+
return { inserted: 0, skipped };
|
|
174
|
+
const timestamp = now();
|
|
175
|
+
const start = input.compartmentStart ?? null;
|
|
176
|
+
const end = input.compartmentEnd ?? null;
|
|
177
|
+
try {
|
|
178
|
+
inTransaction(() => {
|
|
179
|
+
for (const content of kept) {
|
|
180
|
+
insert.run(content, input.sessionId, start, end, timestamp);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
return { inserted: kept.length, skipped };
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
options.logger.warn(`magic-context profile candidate write failed: ${errText(error)}`);
|
|
187
|
+
return { inserted: 0, skipped };
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
listCandidates: (limit) => {
|
|
191
|
+
if (!Number.isFinite(limit) || limit <= 0)
|
|
192
|
+
return [];
|
|
193
|
+
try {
|
|
194
|
+
return selectCandidates.all(limit);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
options.logger.warn(`magic-context profile candidate read failed: ${errText(error)}`);
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
countCandidates: () => {
|
|
202
|
+
try {
|
|
203
|
+
return countCandidates.get()?.c ?? 0;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
expireCandidates: (cutoffMs) => {
|
|
210
|
+
try {
|
|
211
|
+
return Number(expireCandidates.run(cutoffMs).changes);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
options.logger.warn(`magic-context profile candidate expiry failed: ${errText(error)}`);
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
deleteCandidates: (ids) => {
|
|
219
|
+
if (ids.length === 0)
|
|
220
|
+
return 0;
|
|
221
|
+
try {
|
|
222
|
+
return inTransaction(() => {
|
|
223
|
+
let removed = 0;
|
|
224
|
+
for (const id of ids)
|
|
225
|
+
removed += Number(deleteCandidate.run(id).changes);
|
|
226
|
+
return removed;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
options.logger.warn(`magic-context profile candidate consumption failed: ${errText(error)}`);
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
listActiveProfiles: () => {
|
|
235
|
+
try {
|
|
236
|
+
return selectProfiles.all().map((row) => {
|
|
237
|
+
let ids = [];
|
|
238
|
+
try {
|
|
239
|
+
const parsed = JSON.parse(row.sourceCandidateIds ?? '[]');
|
|
240
|
+
if (Array.isArray(parsed))
|
|
241
|
+
ids = parsed.filter((id) => typeof id === 'number');
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// A malformed provenance list is not worth failing a read over.
|
|
245
|
+
}
|
|
246
|
+
return { id: row.id, content: row.content, promotedAt: row.promotedAt, sourceCandidateIds: ids };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
options.logger.warn(`magic-context profile read failed: ${errText(error)}`);
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
retireProfile: (id, nowMs) => {
|
|
255
|
+
try {
|
|
256
|
+
return Number(retireProfileStatement.run(nowMs, id).changes) === 1;
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
options.logger.warn(`magic-context profile retirement failed: ${errText(error)}`);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
promoteProfile: (input) => {
|
|
264
|
+
const content = input.content.trim();
|
|
265
|
+
if (content === '')
|
|
266
|
+
return undefined;
|
|
267
|
+
const timestamp = now();
|
|
268
|
+
try {
|
|
269
|
+
return inTransaction(() => {
|
|
270
|
+
const result = insertProfile.run(content, input.promotedAt, JSON.stringify([...input.candidateIds]), timestamp, timestamp, input.provenance ?? null);
|
|
271
|
+
return Number(result.lastInsertRowid);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
options.logger.warn(`magic-context profile promotion failed: ${errText(error)}`);
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
close: () => {
|
|
280
|
+
db.close();
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Highest score first: the scale runs from here down to the trivial spans. */
|
|
2
|
+
export const REFERENCE_ANCHORS = [
|
|
3
|
+
{ importance: 96, title: "Fail-closed tenant context", episodeType: "design,feature", gist: "Acorn Suite made JWT tenant_id the single tenant authority across REST, GraphQL, and Prisma; slug/h…" },
|
|
4
|
+
{ importance: 95, title: "Chorus: op-log-as-source-of-truth architecture established", episodeType: "design,infra", gist: "Chorus op log is the source of truth; snapshots derived on-read and cached; compaction at 10 k ops/…" },
|
|
5
|
+
{ importance: 95, title: "Standby-safe status writer", episodeType: "design,infra,feature", gist: "The durable decision was to allow standby replicas to do read-heavy reconcile work while centralizi…" },
|
|
6
|
+
{ importance: 94, title: "Tenant isolation: chose Postgres RLS over schema-per-tenant", episodeType: "design", gist: "Chose RLS with `tenant_id` and `SET LOCAL app.current_tenant` per request. BYPASSRLS reserved for t…" },
|
|
7
|
+
{ importance: 92, title: "Tenant context from membership", episodeType: "design,feature", gist: "Tenant authorization now flows from membership state rather than browser-selected tenant IDs. The d…" },
|
|
8
|
+
{ importance: 91, title: "Leader election thrash from leases held across pod terminations", episodeType: "investigation,bug,refactor", gist: "Operator leader-election bug: custom shutdown-error suppression shim swallowed lease releases, leav…" },
|
|
9
|
+
{ importance: 91, title: "Luminary: run_id idempotency model established for all pipeline runs", episodeType: "design,feature", gist: "All Luminary ETL loads idempotent via run_id; DB-enforced deduplication; any run can be safely retr…" },
|
|
10
|
+
{ importance: 91, title: "Per-namespace QueueShard leases", episodeType: "design,infra,feature", gist: "Harborlight sharding is namespace-scoped: one operator binary can run cluster-wide, but failover is…" },
|
|
11
|
+
{ importance: 88, title: "Moved CAN notify out of ISR", episodeType: "bug,infra", gist: "CAN RX no longer allocates or sends BLE notifications inside the ISR; frames are buffered and flush…" },
|
|
12
|
+
{ importance: 87, title: "NaN in mixed-precision training traced to clip_grad_norm_ before scaler.unscale_", episodeType: "investigation,bug", gist: "Mixed-precision NaN root cause: gradient clipping must run after the GradScaler unscale, never befo…" },
|
|
13
|
+
{ importance: 86, title: "CRDT merge ordering: hybrid logical clocks over wall-clock LWW", episodeType: "design,feature", gist: "Conflict resolution is HLC-based with a 300s skew cap; per-character editing uses Yjs." },
|
|
14
|
+
{ importance: 86, title: "Auditable proration credit ledger", episodeType: "design,feature,release", gist: "Billetto made proration credits auditable by storing them in its own ledger table rather than Strip…" },
|
|
15
|
+
{ importance: 82, title: "Stopped contrastive loss cliff", episodeType: "investigation,bug", gist: "Nereid contrastive training stabilized after restoring per-worker augmentation seeds." },
|
|
16
|
+
{ importance: 79, title: "Idempotency strategy for Stripe webhook handlers", episodeType: "design,bug", gist: "Stripe webhook idempotency keys on event.id with stored response replay; old signature-based dedup…" },
|
|
17
|
+
{ importance: 78, title: "Tethys: JWT refresh not re-fetching tenant roles after role change", episodeType: "investigation,bug,refactor", gist: "Tethys token refresh now embeds freshly-fetched tenant_roles; revoked role lag bounded by access_to…" },
|
|
18
|
+
{ importance: 74, title: "HLS segment numbering migration from 5-digit to 9-digit indices", episodeType: "refactor,infra", gist: "HLS segment indices widened after a live-channel index wraparound." },
|
|
19
|
+
{ importance: 74, title: "Partition stats invalidation fence", episodeType: "investigation,bug", gist: "Partition membership changes now fence planner statistics through partition_epoch. The durable poin…" },
|
|
20
|
+
{ importance: 72, title: "CRDT ordering by Lamport site", episodeType: "design,refactor,investigation", gist: "CollabPad CRDT ordering now follows document clocks rather than server receive time." },
|
|
21
|
+
{ importance: 71, title: "BLE bonding records lost across firmware OTA — bond table erase fix", episodeType: "bug,feature", gist: "OTA was erasing the SDK bond table; carved a dedicated partition to protect it." },
|
|
22
|
+
{ importance: 71, title: "Vesper: controller leader election via Kubernetes Lease", episodeType: "feature,infra", gist: "Vesper uses Lease-based leader election; only one replica reconciles at a time; handoff within one…" },
|
|
23
|
+
{ importance: 68, title: "boltpkg: plugin lifecycle hook API design and documentation", episodeType: "design,docs", gist: "boltpkg plugin lifecycle uses explicit ordered hooks in dependency-declaration order; schema_versio…" },
|
|
24
|
+
{ importance: 68, title: "Added subscription day fact table", episodeType: "feature,infra", gist: "Subscription MRR history moved into a daily fact table instead of being reconstructed from raw bill…" },
|
|
25
|
+
{ importance: 67, title: "Matchmaking bucket calibration after season-3 player distribution shift", episodeType: "feature", gist: "Matchmaking buckets narrowed and bucket expansion made asymmetric to stop top-skill players from ge…" },
|
|
26
|
+
{ importance: 61, title: "Preserved PDF rotation before OCR", episodeType: "investigation,bug", gist: "PDF page rotation is captured before OCR rasterization so Tesseract sees the correct orientation." },
|
|
27
|
+
{ importance: 58, title: "Moved synonym rollout to shadow index", episodeType: "infra,refactor", gist: "Synonym edits are now shipped by building and promoting a shadow index, not by reloading the hot on…" },
|
|
28
|
+
{ importance: 54, title: "Kept ranked parties intact", episodeType: "feature", gist: "Ranked duo matchmaking now preserves parties even when it increases queue time." },
|
|
29
|
+
{ importance: 52, title: "Slowly-changing-dim fix in dim_customer dbt model", episodeType: "refactor,bug", gist: "Customer SCD moved from custom triggers to dbt snapshots." },
|
|
30
|
+
{ importance: 48, title: "CSP nonce missing on injected MV3 content script", episodeType: "bug", gist: "Content script moved to the isolated world to sidestep page CSP." },
|
|
31
|
+
{ importance: 46, title: "Tesseract pinned to 5.3.3 after multi-column regression in 5.3.4", episodeType: "bug,infra", gist: "Tesseract pinned to 5.3.3 due to a multi-column regression in 5.3.4." },
|
|
32
|
+
{ importance: 41, title: "Added Snapbench record examples", episodeType: "feature,docs", gist: "Snapbench recording examples became runnable instead of descriptive only." },
|
|
33
|
+
{ importance: 38, title: "Add --json output flag to status command", episodeType: "feature", gist: "Status command gained a `--json` output mode." },
|
|
34
|
+
{ importance: 38, title: "Meridian: fix HLS segment pts discontinuity causing playback stutters", episodeType: "bug", gist: "HLS segment boundary pts continuity enforced; -copyts required in Meridian mux args." },
|
|
35
|
+
{ importance: 36, title: "Statistics-refresh hook on bulk-load completion in query planner integration", episodeType: "feature,refactor", gist: "Bulk loader triggers asynchronous statistics refresh on large loads to keep the planner from pickin…" },
|
|
36
|
+
{ importance: 36, title: "Removed Electron pane jitter", episodeType: "bug", gist: "Electron pane positioning was made stable under fractional display scaling." },
|
|
37
|
+
{ importance: 33, title: "Polaris: extract dataset preprocessing into offline cached stage", episodeType: "refactor", gist: "Polaris has a separate preprocessing stage; training loop reads from data/preprocessed/ cache." },
|
|
38
|
+
{ importance: 31, title: "SKU import defaults to preview", episodeType: "feature,docs", gist: "The SKU importer now defaults to a safe preview workflow instead of assuming live writes. The durab…" },
|
|
39
|
+
{ importance: 29, title: "Routed malformed telemetry to DLQ", episodeType: "infra", gist: "Invalid IoT telemetry is retained in a DLQ instead of silently disappearing." },
|
|
40
|
+
{ importance: 27, title: "Scoped mobile offline sync MVP", episodeType: "feature,design", gist: "Mobile offline sync shipped with a narrow notes-only scope." },
|
|
41
|
+
{ importance: 27, title: "Manual page rotation escape hatch", episodeType: "feature,bug", gist: "Low-confidence scan rotation is now handled through a manual override path, with automation reserve…" },
|
|
42
|
+
{ importance: 24, title: "Cut v2.4.1 patch release for the segment-wraparound fix", episodeType: "release", gist: "Patch release 2.4.1." },
|
|
43
|
+
{ importance: 24, title: "Ferrite: write amplification spike investigation — inconclusive", episodeType: "investigation", gist: "Ferrite WA spike is expected LSM behavior during level merges; no fix needed; defaults appropriate." },
|
|
44
|
+
{ importance: 22, title: "Narrowed May orders replay", episodeType: "infra,investigation", gist: "This was a scoped replay, not a lasting data-model change: the useful outcome was knowing the repai…" },
|
|
45
|
+
{ importance: 21, title: "Collapsed package harness helpers", episodeType: "refactor", gist: "Package resolver test setup was deduplicated without behavior changes." },
|
|
46
|
+
{ importance: 20, title: "README install path correction for darwin-arm64", episodeType: "docs", gist: "README install path fixed for Apple Silicon." },
|
|
47
|
+
{ importance: 17, title: "Rename internal `bilUtil` helper to `invoiceMath`", episodeType: "refactor", gist: "Naming cleanup in the billing service." },
|
|
48
|
+
{ importance: 14, title: "Inconclusive investigation into nightly training throughput drift", episodeType: "investigation", gist: "Inconclusive performance investigation, parked." },
|
|
49
|
+
{ importance: 14, title: "Vesper: add kind cluster to CI integration test pipeline", episodeType: "infra", gist: "Vesper CI now runs integration tests against a real kind cluster instead of envtest mock." },
|
|
50
|
+
{ importance: 12, title: "Dropped table-detector detour", episodeType: "investigation,docs", gist: "This was a discarded OCR detour: table detection did not improve enough documents to justify replac…" },
|
|
51
|
+
{ importance: 9, title: "CLI nightly dry run", episodeType: "release", gist: "A CLI nightly dry run passed and left no artifacts." },
|
|
52
|
+
{ importance: 8, title: "Tried replacing controller-runtime cache with informer-direct; reverted", episodeType: "refactor", gist: "False start: attempted-and-reverted controller-runtime cache bypass." },
|
|
53
|
+
{ importance: 7, title: "Ledger: invoice_status column rename reversed after reporting breakage", episodeType: "bug", gist: "Column rename reverted; reporting service has raw SQL dependency on invoice_status that must be upd…" },
|
|
54
|
+
{ importance: 7, title: "Discarded Percy baseline theory", episodeType: "investigation", gist: "A false Playwright/Percy lead was ruled out." },
|
|
55
|
+
{ importance: 7, title: "Added --dry-run flag to transcode CLI", episodeType: "feature", gist: "Added dry-run mode to video transcode CLI." },
|
|
56
|
+
{ importance: 7, title: "Upload button spacing", episodeType: "bug", gist: "Minor upload empty-state spacing tweak." },
|
|
57
|
+
{ importance: 5, title: "Aborted feature flag tweak — wrong env", episodeType: "feature", gist: "Flag-toggle misfire, reverted." },
|
|
58
|
+
{ importance: 5, title: "Canopy: popup container width CSS typo (300 px → 400 px)", episodeType: "bug", gist: "CSS typo fixed." },
|
|
59
|
+
{ importance: 4, title: "Noted CAN bus arbitration failure under high load", episodeType: "infra", gist: "Captured high-load CAN bus limitation in firmware docs." },
|
|
60
|
+
{ importance: 3, title: "Typo fix in CRDT conflict error message", episodeType: "bug", gist: "Typo fix." },
|
|
61
|
+
{ importance: 3, title: "boltpkg: fix 'dependancy' typo in resolver error message", episodeType: "bug", gist: "Typo fixed." },
|
|
62
|
+
{ importance: 3, title: "Renamed docs Recipes to Cookbook", episodeType: "docs", gist: "Docs examples section was renamed." },
|
|
63
|
+
];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route health — the volatile "this route is quota-exhausted" observation lane.
|
|
3
|
+
*
|
|
4
|
+
* Two decisions live here, both deliberately tiny:
|
|
5
|
+
*
|
|
6
|
+
* 1. **What counts as an exhausted quota.** Wording only — the canonical
|
|
7
|
+
* provider-neutral wording family DSH itself recognizes (`insufficient
|
|
8
|
+
* quota/balance/credits`, `quota/usage limit exceeded…`), plus the Chinese
|
|
9
|
+
* limit wording this deployment actually produces (`已达到 7 天使用上限`).
|
|
10
|
+
* Transient throttling is NOT this: `quota resets in one minute` says the
|
|
11
|
+
* route is alive, so it must not arm a skip (DSH's own classifier makes the
|
|
12
|
+
* same distinction).
|
|
13
|
+
*
|
|
14
|
+
* 2. **How long one observation holds.** A fixed hold, nothing persisted. The
|
|
15
|
+
* signal's owner is real traffic — the host's `agent/request-error` events
|
|
16
|
+
* keep re-arming it while the main conversation keeps failing on that
|
|
17
|
+
* route, and it simply expires after the hold when nothing does. That is
|
|
18
|
+
* the whole point: an externally reset quota (admin reset, pay-as-you-go
|
|
19
|
+
* enabled, early recovery) cannot be trapped by a stale guess, because
|
|
20
|
+
* there is no durable state to go stale — the worst case is one hold's
|
|
21
|
+
* worth of skipped maintenance, and the maintenance tick's own next probe
|
|
22
|
+
* re-discovers the truth at the cost of one cheap rejected request.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Exhausted-quota wording. `detail` is whatever the failure surface exposed
|
|
26
|
+
* (message preferred, code as a fallback) — callers concatenate what they have.
|
|
27
|
+
*/
|
|
28
|
+
export function isQuotaExhaustedDetail(detail) {
|
|
29
|
+
return /insufficient[\s_-]*(?:quota|balance|credits?)/i.test(detail)
|
|
30
|
+
|| /(?:quota|usage[\s_-]*limit)[\s_-]*(?:exceeded|exhausted|reached)/i.test(detail)
|
|
31
|
+
|| /exceed(?:ed|s)?[\s_-]*(?:(?:your|the)[\s_-]*)?(?:current[\s_-]*)?quota/i.test(detail)
|
|
32
|
+
|| /(?:balance|credits?)[\s_-]*(?:exhausted|depleted)/i.test(detail)
|
|
33
|
+
|| /使用上限|额度(?:已)?(?:用尽|耗尽)|余额不足/.test(detail);
|
|
34
|
+
}
|
|
35
|
+
/** How long one quota observation suppresses the maintenance lane. */
|
|
36
|
+
export const ROUTE_QUOTA_HOLD_MS = 45 * 60_000;
|
|
37
|
+
/**
|
|
38
|
+
* The observation map. Volatile by design: process lifetime, no persistence,
|
|
39
|
+
* no reset-time parsing. `now` is always injected so the hold is testable.
|
|
40
|
+
*/
|
|
41
|
+
export class RouteHealth {
|
|
42
|
+
quotaAt = new Map();
|
|
43
|
+
/** Arm the skip for one provider. Idempotent; the latest observation wins. */
|
|
44
|
+
observeQuotaFailure(provider, at) {
|
|
45
|
+
if (provider.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
this.quotaAt.set(provider, at);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Milliseconds since the latest quota observation for this provider, when
|
|
51
|
+
* that observation is still inside the hold; `undefined` when the route is
|
|
52
|
+
* not suppressed (no observation, or an expired one).
|
|
53
|
+
*/
|
|
54
|
+
quotaSince(provider, now, holdMs = ROUTE_QUOTA_HOLD_MS) {
|
|
55
|
+
const at = this.quotaAt.get(provider);
|
|
56
|
+
if (at === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
const age = now - at;
|
|
59
|
+
return age >= 0 && age < holdMs ? age : undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Read-only copy, for journaling and the doctor surface. */
|
|
62
|
+
snapshot(now, holdMs = ROUTE_QUOTA_HOLD_MS) {
|
|
63
|
+
const out = new Map();
|
|
64
|
+
for (const [provider, at] of this.quotaAt) {
|
|
65
|
+
if (now - at < holdMs)
|
|
66
|
+
out.set(provider, at);
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The skip decision the maintenance tick actually uses — deliberately narrower
|
|
73
|
+
* than the observation. The event carries a provider but no model, while a
|
|
74
|
+
* quota is route-scoped (provider/model): the live 2026-10-09 incident had
|
|
75
|
+
* provider-a/model-a exhausted while provider-a/model-b
|
|
76
|
+
* verified normally, and a provider-keyed skip would have suppressed the live
|
|
77
|
+
* route. So an observation may only suppress the main conversation's OWN
|
|
78
|
+
* exact route: the tick route must equal the last session route in provider
|
|
79
|
+
* AND model, and the observation must be for that provider. A pinned route
|
|
80
|
+
* that diverges from the main conversation never skips — one cheap rejected
|
|
81
|
+
* request beats silently stalling a working route.
|
|
82
|
+
*/
|
|
83
|
+
export function quotaSkipSince(health, tickRoute, lastSessionRoute, now, holdMs = ROUTE_QUOTA_HOLD_MS) {
|
|
84
|
+
if (tickRoute === undefined || lastSessionRoute === undefined)
|
|
85
|
+
return undefined;
|
|
86
|
+
if (tickRoute.provider !== lastSessionRoute.provider || tickRoute.model !== lastSessionRoute.model)
|
|
87
|
+
return undefined;
|
|
88
|
+
return health.quotaSince(tickRoute.provider, now, holdMs);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The maintenance lane's route, resolved once per tick.
|
|
92
|
+
*
|
|
93
|
+
* Priority is explicit over following: a pinned `maintenance.verifyProvider`
|
|
94
|
+
* wins (it is the rollback lever), the main conversation's latest route is the
|
|
95
|
+
* default, and neither being available means the lane runs its LLM-less steps
|
|
96
|
+
* only — exactly the pre-existing behavior. "Latest" is last-seen-wins across
|
|
97
|
+
* concurrent sessions, which is fine for a lane whose only requirement is "a
|
|
98
|
+
* route the user currently trusts".
|
|
99
|
+
*/
|
|
100
|
+
export function resolveMaintenanceRoute(explicit, lastSession) {
|
|
101
|
+
if (explicit !== undefined && explicit.provider.length > 0 && explicit.model.length > 0)
|
|
102
|
+
return explicit;
|
|
103
|
+
if (lastSession !== undefined && lastSession.provider.length > 0 && lastSession.model.length > 0)
|
|
104
|
+
return lastSession;
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|