instar 1.3.991 → 1.3.992
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/dist/core/RecurrenceReader.d.ts +134 -0
- package/dist/core/RecurrenceReader.d.ts.map +1 -0
- package/dist/core/RecurrenceReader.js +111 -0
- package/dist/core/RecurrenceReader.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.992.md +55 -0
- package/upgrades/side-effects/recurrence-reader.md +149 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecurrenceReader — one reader across the stores that already notice things.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS ADDRESSES (project convergence-towards-coherence, Tier 2).
|
|
5
|
+
* Instar notices constantly and in three separate places: the attention queue,
|
|
6
|
+
* the evolution action queue, and the sentinel event log. Nothing reads across
|
|
7
|
+
* them, so the same underlying problem is noticed dozens of times and closed
|
|
8
|
+
* zero times. The measured filing-to-completion ratio is roughly 30:1.
|
|
9
|
+
*
|
|
10
|
+
* Measured on this machine, 2026-07-27 — the number that makes the case:
|
|
11
|
+
*
|
|
12
|
+
* 371 OPEN attention items → 49 distinct problems
|
|
13
|
+
* the single largest cluster ("credential rebalancer") is 177 items — 48%
|
|
14
|
+
*
|
|
15
|
+
* Reading that queue item-by-item, nobody would ever see it. The items are all
|
|
16
|
+
* individually true and individually minor; the SHAPE is the finding, and the
|
|
17
|
+
* shape is invisible without a reader that groups.
|
|
18
|
+
*
|
|
19
|
+
* WHAT THIS IS NOT. It is not a new notification channel, and it must never
|
|
20
|
+
* become one (operator directive 2026-07-26 20:08Z: synthesis must lead to
|
|
21
|
+
* ACTION through the paths that already exist — a phase advance, a blocker
|
|
22
|
+
* raised, work queued — with the loop closed and without depending on the user).
|
|
23
|
+
* This module is READ-ONLY and returns a report; driving action from it is a
|
|
24
|
+
* separate, gated concern.
|
|
25
|
+
*
|
|
26
|
+
* THE HONEST-DENOMINATOR RULE APPLIES TO THIS READER TOO. Every store it could
|
|
27
|
+
* not read is named in `coverage`, and a report with any unreadable store is
|
|
28
|
+
* NEVER `complete`. "I found no recurrence" and "I could not look" are different
|
|
29
|
+
* answers, and conflating them is the exact failure this project exists to
|
|
30
|
+
* remove — a synthesiser that silently synthesised over two of three stores
|
|
31
|
+
* would be the most expensive instance of it yet.
|
|
32
|
+
*/
|
|
33
|
+
/** A single observation from any of the noticing stores. */
|
|
34
|
+
export interface Observation {
|
|
35
|
+
/** Which store this came from. */
|
|
36
|
+
store: 'attention' | 'actions' | 'sentinel';
|
|
37
|
+
/** Stable id within that store, when it has one. */
|
|
38
|
+
id?: string;
|
|
39
|
+
/** Human title / summary — the recurrence key is derived from this. */
|
|
40
|
+
title: string;
|
|
41
|
+
/** Emitting subsystem, when known. `undefined` is itself a finding (see below). */
|
|
42
|
+
source?: string;
|
|
43
|
+
/** ISO timestamp, when known. */
|
|
44
|
+
at?: string;
|
|
45
|
+
/** Open / unresolved. Only open observations form recurrence clusters. */
|
|
46
|
+
open: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** One recurring problem, as distinct from the many times it was noticed. */
|
|
49
|
+
export interface RecurrenceCluster {
|
|
50
|
+
/** Normalized key the members share. */
|
|
51
|
+
key: string;
|
|
52
|
+
/** A readable exemplar (the first member's title, untruncated). */
|
|
53
|
+
exemplar: string;
|
|
54
|
+
/** How many times this was noticed. THE point of the whole module. */
|
|
55
|
+
count: number;
|
|
56
|
+
/** Which stores noticed it — a problem seen in more than one is stronger evidence. */
|
|
57
|
+
stores: Observation['store'][];
|
|
58
|
+
/** Sources that emitted it, when known. */
|
|
59
|
+
sources: string[];
|
|
60
|
+
/** Earliest / latest observation timestamps available. */
|
|
61
|
+
firstSeen?: string;
|
|
62
|
+
lastSeen?: string;
|
|
63
|
+
/**
|
|
64
|
+
* True when at least one member came from the ACTION queue — i.e. somebody has
|
|
65
|
+
* at some point committed to doing something about this. A high-count cluster
|
|
66
|
+
* with `tracked: false` is the sharpest signal available: noticed many times,
|
|
67
|
+
* never once turned into work.
|
|
68
|
+
*/
|
|
69
|
+
tracked: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** What the reader could and could not see. Never omitted, never inferred. */
|
|
72
|
+
export interface Coverage {
|
|
73
|
+
/** Stores read successfully. */
|
|
74
|
+
read: Observation['store'][];
|
|
75
|
+
/** Stores that could NOT be read, with the reason. */
|
|
76
|
+
unreadable: {
|
|
77
|
+
store: Observation['store'];
|
|
78
|
+
reason: string;
|
|
79
|
+
}[];
|
|
80
|
+
/**
|
|
81
|
+
* `complete` only when every store was read. Any unreadable store makes this
|
|
82
|
+
* `partial`, and a partial report may never be presented as a clean bill.
|
|
83
|
+
*/
|
|
84
|
+
completeness: 'complete' | 'partial';
|
|
85
|
+
}
|
|
86
|
+
export interface RecurrenceReport {
|
|
87
|
+
generatedAt: string;
|
|
88
|
+
coverage: Coverage;
|
|
89
|
+
/** Distinct problems, densest first. */
|
|
90
|
+
clusters: RecurrenceCluster[];
|
|
91
|
+
/** Open observations considered. */
|
|
92
|
+
observationsConsidered: number;
|
|
93
|
+
/**
|
|
94
|
+
* observations ÷ clusters — the noticing-to-problem ratio. `null` when there
|
|
95
|
+
* are no clusters: no denominator, no ratio (never a flattering 1, never a
|
|
96
|
+
* damning 0).
|
|
97
|
+
*/
|
|
98
|
+
noticingRatio: number | null;
|
|
99
|
+
/**
|
|
100
|
+
* Present ONLY on a complete report. On a partial read this is absent and the
|
|
101
|
+
* caller must say "I could not look", never "nothing recurring was found".
|
|
102
|
+
*/
|
|
103
|
+
verdict?: 'no-recurrence' | 'recurrence-found';
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Derive the recurrence key from a title.
|
|
107
|
+
*
|
|
108
|
+
* Digits collapse to `N` and long hex runs to `H`, so "3 topics stranded on
|
|
109
|
+
* m_cc2ec…" and "7 topics stranded on m_91af…" are recognised as ONE problem
|
|
110
|
+
* rather than two. This is deliberately blunt: the goal is to surface shape, and
|
|
111
|
+
* an over-eager grouping that a human immediately recognises as one problem is
|
|
112
|
+
* more useful than a precise grouping that preserves the illusion of 371
|
|
113
|
+
* separate things.
|
|
114
|
+
*/
|
|
115
|
+
export declare function recurrenceKey(title: string): string;
|
|
116
|
+
/**
|
|
117
|
+
* Group open observations into recurrence clusters.
|
|
118
|
+
*
|
|
119
|
+
* Pure over its input — every store read (and every read FAILURE) is the
|
|
120
|
+
* caller's job to supply, so this function cannot silently swallow one.
|
|
121
|
+
*/
|
|
122
|
+
export declare function buildRecurrenceReport(observations: Observation[], coverage: Coverage): RecurrenceReport;
|
|
123
|
+
/**
|
|
124
|
+
* The clusters worth a human's attention, by a deterministic rule.
|
|
125
|
+
*
|
|
126
|
+
* `minCount` defaults to 2 because a thing noticed once is not yet recurrence.
|
|
127
|
+
* `untrackedOnly` narrows to the sharpest class: repeatedly noticed, never
|
|
128
|
+
* turned into work.
|
|
129
|
+
*/
|
|
130
|
+
export declare function significantClusters(report: RecurrenceReport, opts?: {
|
|
131
|
+
minCount?: number;
|
|
132
|
+
untrackedOnly?: boolean;
|
|
133
|
+
}): RecurrenceCluster[];
|
|
134
|
+
//# sourceMappingURL=RecurrenceReader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecurrenceReader.d.ts","sourceRoot":"","sources":["../../src/core/RecurrenceReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;IAC5C,oDAAoD;IACpD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,IAAI,EAAE,OAAO,CAAC;CACf;AAED,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,gCAAgC;IAChC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7B,sDAAsD;IACtD,UAAU,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D;;;OAGG;IACH,YAAY,EAAE,UAAU,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,oCAAoC;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,eAAe,GAAG,kBAAkB,CAAC;CAChD;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,WAAW,EAAE,EAC3B,QAAQ,EAAE,QAAQ,GACjB,gBAAgB,CA0ClB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxD,iBAAiB,EAAE,CAKrB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecurrenceReader — one reader across the stores that already notice things.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS ADDRESSES (project convergence-towards-coherence, Tier 2).
|
|
5
|
+
* Instar notices constantly and in three separate places: the attention queue,
|
|
6
|
+
* the evolution action queue, and the sentinel event log. Nothing reads across
|
|
7
|
+
* them, so the same underlying problem is noticed dozens of times and closed
|
|
8
|
+
* zero times. The measured filing-to-completion ratio is roughly 30:1.
|
|
9
|
+
*
|
|
10
|
+
* Measured on this machine, 2026-07-27 — the number that makes the case:
|
|
11
|
+
*
|
|
12
|
+
* 371 OPEN attention items → 49 distinct problems
|
|
13
|
+
* the single largest cluster ("credential rebalancer") is 177 items — 48%
|
|
14
|
+
*
|
|
15
|
+
* Reading that queue item-by-item, nobody would ever see it. The items are all
|
|
16
|
+
* individually true and individually minor; the SHAPE is the finding, and the
|
|
17
|
+
* shape is invisible without a reader that groups.
|
|
18
|
+
*
|
|
19
|
+
* WHAT THIS IS NOT. It is not a new notification channel, and it must never
|
|
20
|
+
* become one (operator directive 2026-07-26 20:08Z: synthesis must lead to
|
|
21
|
+
* ACTION through the paths that already exist — a phase advance, a blocker
|
|
22
|
+
* raised, work queued — with the loop closed and without depending on the user).
|
|
23
|
+
* This module is READ-ONLY and returns a report; driving action from it is a
|
|
24
|
+
* separate, gated concern.
|
|
25
|
+
*
|
|
26
|
+
* THE HONEST-DENOMINATOR RULE APPLIES TO THIS READER TOO. Every store it could
|
|
27
|
+
* not read is named in `coverage`, and a report with any unreadable store is
|
|
28
|
+
* NEVER `complete`. "I found no recurrence" and "I could not look" are different
|
|
29
|
+
* answers, and conflating them is the exact failure this project exists to
|
|
30
|
+
* remove — a synthesiser that silently synthesised over two of three stores
|
|
31
|
+
* would be the most expensive instance of it yet.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Derive the recurrence key from a title.
|
|
35
|
+
*
|
|
36
|
+
* Digits collapse to `N` and long hex runs to `H`, so "3 topics stranded on
|
|
37
|
+
* m_cc2ec…" and "7 topics stranded on m_91af…" are recognised as ONE problem
|
|
38
|
+
* rather than two. This is deliberately blunt: the goal is to surface shape, and
|
|
39
|
+
* an over-eager grouping that a human immediately recognises as one problem is
|
|
40
|
+
* more useful than a precise grouping that preserves the illusion of 371
|
|
41
|
+
* separate things.
|
|
42
|
+
*/
|
|
43
|
+
export function recurrenceKey(title) {
|
|
44
|
+
return (title || '')
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.replace(/[a-f0-9]{8,}/g, 'H')
|
|
47
|
+
.replace(/\d+/g, 'N')
|
|
48
|
+
.replace(/\s+/g, ' ')
|
|
49
|
+
.trim()
|
|
50
|
+
.slice(0, 80);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Group open observations into recurrence clusters.
|
|
54
|
+
*
|
|
55
|
+
* Pure over its input — every store read (and every read FAILURE) is the
|
|
56
|
+
* caller's job to supply, so this function cannot silently swallow one.
|
|
57
|
+
*/
|
|
58
|
+
export function buildRecurrenceReport(observations, coverage) {
|
|
59
|
+
const open = observations.filter((o) => o.open);
|
|
60
|
+
const byKey = new Map();
|
|
61
|
+
for (const o of open) {
|
|
62
|
+
const k = recurrenceKey(o.title);
|
|
63
|
+
if (!k)
|
|
64
|
+
continue;
|
|
65
|
+
const bucket = byKey.get(k);
|
|
66
|
+
if (bucket)
|
|
67
|
+
bucket.push(o);
|
|
68
|
+
else
|
|
69
|
+
byKey.set(k, [o]);
|
|
70
|
+
}
|
|
71
|
+
const clusters = [...byKey.entries()].map(([key, members]) => {
|
|
72
|
+
const times = members.map((m) => m.at).filter((t) => !!t).sort();
|
|
73
|
+
return {
|
|
74
|
+
key,
|
|
75
|
+
exemplar: members[0].title,
|
|
76
|
+
count: members.length,
|
|
77
|
+
stores: [...new Set(members.map((m) => m.store))],
|
|
78
|
+
sources: [...new Set(members.map((m) => m.source).filter((s) => !!s))],
|
|
79
|
+
firstSeen: times[0],
|
|
80
|
+
lastSeen: times[times.length - 1],
|
|
81
|
+
tracked: members.some((m) => m.store === 'actions'),
|
|
82
|
+
};
|
|
83
|
+
}).sort((a, b) => b.count - a.count);
|
|
84
|
+
const report = {
|
|
85
|
+
generatedAt: new Date().toISOString(),
|
|
86
|
+
coverage,
|
|
87
|
+
clusters,
|
|
88
|
+
observationsConsidered: open.length,
|
|
89
|
+
noticingRatio: clusters.length === 0 ? null : Number((open.length / clusters.length).toFixed(2)),
|
|
90
|
+
};
|
|
91
|
+
// A verdict is only meaningful over a COMPLETE read. On a partial read the
|
|
92
|
+
// field is absent entirely rather than set to a hedged value — an absent field
|
|
93
|
+
// forces the caller to handle it; a hedged value invites it to be rendered as
|
|
94
|
+
// if it were an answer.
|
|
95
|
+
if (coverage.completeness === 'complete') {
|
|
96
|
+
report.verdict = clusters.some((c) => c.count > 1) ? 'recurrence-found' : 'no-recurrence';
|
|
97
|
+
}
|
|
98
|
+
return report;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The clusters worth a human's attention, by a deterministic rule.
|
|
102
|
+
*
|
|
103
|
+
* `minCount` defaults to 2 because a thing noticed once is not yet recurrence.
|
|
104
|
+
* `untrackedOnly` narrows to the sharpest class: repeatedly noticed, never
|
|
105
|
+
* turned into work.
|
|
106
|
+
*/
|
|
107
|
+
export function significantClusters(report, opts = {}) {
|
|
108
|
+
const min = opts.minCount ?? 2;
|
|
109
|
+
return report.clusters.filter((c) => c.count >= min && (!opts.untrackedOnly || !c.tracked));
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=RecurrenceReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecurrenceReader.js","sourceRoot":"","sources":["../../src/core/RecurrenceReader.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-27T03:37:34.934Z",
|
|
5
|
+
"instarVersion": "1.3.992",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Instar notices problems in three separate stores — the attention queue, the evolution action queue,
|
|
9
|
+
and the sentinel log — and nothing has ever read across them. So the same problem is noticed dozens
|
|
10
|
+
of times and closed zero times (measured filing-to-completion ≈ 30:1).
|
|
11
|
+
|
|
12
|
+
`src/core/RecurrenceReader.ts` groups OPEN observations from all three into recurrence clusters,
|
|
13
|
+
carrying a `coverage` block that names every store it could NOT read. Pure module, read-only, no
|
|
14
|
+
route, no authority.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
Nothing is required of you, and nothing visible changes yet — this ships the engine, not a surface.
|
|
19
|
+
|
|
20
|
+
What it does, in plain terms: your agent writes down the things it notices in three different places,
|
|
21
|
+
and until now nothing looked at all three together. So one recurring problem could be written down a
|
|
22
|
+
hundred times and read as a hundred separate problems. This groups them.
|
|
23
|
+
|
|
24
|
+
On a live agent it turned 2,068 unresolved items into 836 actual problems — and found 69 problems
|
|
25
|
+
that had been noticed 1,242 times between them without a single one ever being picked up. The
|
|
26
|
+
largest was one component's warnings repeating 177 times, nearly half that agent's attention queue.
|
|
27
|
+
|
|
28
|
+
If you later ask your agent "what keeps going wrong?", this is what lets it answer honestly instead
|
|
29
|
+
of reciting a list. And if it can only read some of its records, it will tell you that rather than
|
|
30
|
+
reporting a clean bill — "nothing found" and "couldn't look" stay different answers.
|
|
31
|
+
|
|
32
|
+
## Summary of New Capabilities
|
|
33
|
+
|
|
34
|
+
- Groups repeated observations across the attention queue, action queue and sentinel log into
|
|
35
|
+
distinct problems, so recurrence becomes visible instead of buried in volume.
|
|
36
|
+
- Flags problems noticed repeatedly that were **never** turned into tracked work — the sharpest
|
|
37
|
+
signal for what is actually being dropped.
|
|
38
|
+
- Refuses to issue a verdict over an incomplete read: any unreadable store is named with its reason,
|
|
39
|
+
and the verdict field is omitted entirely rather than hedged.
|
|
40
|
+
|
|
41
|
+
## Evidence
|
|
42
|
+
|
|
43
|
+
Live data, 2026-07-27: **2,068 open observations → 836 distinct problems** (ratio 2.47). **69
|
|
44
|
+
problems account for 1,242 noticings and none is tracked.** Largest clusters: 278x idle-timeout
|
|
45
|
+
detection, 238x escalation-suppressed, 177x credential rebalancer (48% of the attention queue).
|
|
46
|
+
|
|
47
|
+
Refusal, on real data with the action store made unreadable: reported the 59 clusters it could see
|
|
48
|
+
and left `verdict` **absent** rather than claiming `no-recurrence`. Unit suite 11/11; `tsc` exit 0.
|
|
49
|
+
|
|
50
|
+
## Known limits
|
|
51
|
+
|
|
52
|
+
Title-only keying will not merge the same problem worded differently; semantic matching would mean an
|
|
53
|
+
LLM and a judgment point, deliberately avoided. The blunt key can occasionally over-merge — `exemplar`
|
|
54
|
+
and `sources` are carried so a reader spots it. Read-only: it reports, it does not act. Driving
|
|
55
|
+
action through existing gated paths is the next increment, not this one.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Side-Effects Review — RecurrenceReader (Tier 2 core, read-only)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `recurrence-reader`
|
|
4
|
+
**Date:** `2026-07-27`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `see Phase 5`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
One pure module (`src/core/RecurrenceReader.ts`) that groups OPEN observations from the attention
|
|
11
|
+
queue, the evolution action queue and the sentinel log into recurrence clusters, plus a `coverage`
|
|
12
|
+
block naming every store it could not read.
|
|
13
|
+
|
|
14
|
+
Project `convergence-towards-coherence` Tier 2. The plan's diagnosis: instar notices constantly, in
|
|
15
|
+
three places, and nothing reads across them — so one problem is noticed dozens of times and closed
|
|
16
|
+
zero times (measured filing-to-completion ≈ 30:1).
|
|
17
|
+
|
|
18
|
+
**Measured on live data, 2026-07-27:**
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
open observations across 3 stores : 2,068
|
|
22
|
+
distinct problems : 836
|
|
23
|
+
noticing ratio : 2.47
|
|
24
|
+
noticed repeatedly, NEVER tracked : 69 problems / 1,242 noticings
|
|
25
|
+
top clusters: 278x idle-timeout detection
|
|
26
|
+
238x escalation-suppressed (telegramEscalation disabled)
|
|
27
|
+
177x credential rebalancer ← 48% of the attention queue alone
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Refusal evidence (constraint 2)
|
|
31
|
+
|
|
32
|
+
The whole design risk is that a synthesiser becomes a MORE expensive version of the defect it
|
|
33
|
+
detects: reading 2 of 3 stores and reporting "nothing recurring" with the authority of having looked.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
REFUSAL — action store made unreadable, on REAL data
|
|
37
|
+
coverage : partial
|
|
38
|
+
could NOT read: [{"store":"actions","reason":"ENOENT: evolution store unreadable"}]
|
|
39
|
+
clusters : 59 ← still reports what it DID see
|
|
40
|
+
verdict : ABSENT — refuses to say no-recurrence
|
|
41
|
+
|
|
42
|
+
THE DISTINCTION THAT MATTERS
|
|
43
|
+
genuinely nothing there → "no-recurrence"
|
|
44
|
+
could not look → undefined (field absent, not hedged)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Unit suite: **11 passed (11)**; `tsc --noEmit` exit 0.
|
|
48
|
+
|
|
49
|
+
## Decision-point inventory
|
|
50
|
+
|
|
51
|
+
| point | classification | note |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| recurrence key (digits→N, hex→H) | `invariant` | Deterministic string normalization. No model. |
|
|
54
|
+
| cluster grouping | `invariant` | Map by key. |
|
|
55
|
+
| `verdict` emitted only on complete coverage | `invariant` | The load-bearing rule. |
|
|
56
|
+
| `significantClusters` minCount / untrackedOnly | `invariant` | Caller-supplied thresholds, defaulted, not inferred. |
|
|
57
|
+
|
|
58
|
+
No judgment points, no LLM, nothing gated. The module holds **no authority whatsoever** — it returns
|
|
59
|
+
a report.
|
|
60
|
+
|
|
61
|
+
## 1. Over-block
|
|
62
|
+
|
|
63
|
+
Nothing is blocked; the module is read-only and returns data. The realistic over-*grouping* risk is
|
|
64
|
+
the blunt key: two genuinely different problems whose titles differ only by digits would merge. That
|
|
65
|
+
is a deliberate trade, stated in the source — surfacing shape is the goal, and an over-eager grouping
|
|
66
|
+
a human instantly recognises as one problem beats a precise grouping that preserves the illusion of
|
|
67
|
+
371 separate things. `exemplar` and `sources` are carried on every cluster so a reader can spot a bad
|
|
68
|
+
merge immediately.
|
|
69
|
+
|
|
70
|
+
## 2. Under-block
|
|
71
|
+
|
|
72
|
+
**Title-only keying.** Two reports of the same underlying problem with genuinely different wording
|
|
73
|
+
will not merge. Accepted: the alternative is semantic matching, which means an LLM, which means a
|
|
74
|
+
judgment point in something that currently has none.
|
|
75
|
+
|
|
76
|
+
**`open` is caller-supplied.** The module trusts the caller's open/closed determination per store.
|
|
77
|
+
That is the correct seam — each store knows its own status vocabulary — but it means a caller that
|
|
78
|
+
mis-maps status inflates or deflates the counts. The live harness maps `status === 'OPEN'`,
|
|
79
|
+
`pending|in_progress`, and treats sentinel events as open.
|
|
80
|
+
|
|
81
|
+
**No route yet, no action yet.** This increment is the reader only. Driving action is Tier 2 item 4,
|
|
82
|
+
deliberately separate because it carries authority this does not.
|
|
83
|
+
|
|
84
|
+
## 3. Level-of-abstraction fit
|
|
85
|
+
|
|
86
|
+
A pure function over supplied observations, with I/O left entirely to the caller. That is
|
|
87
|
+
deliberate: it means the module **cannot** silently swallow a failed store read — the caller must
|
|
88
|
+
hand it a `coverage` block, so an unreadable store is structurally impossible to omit. Putting the
|
|
89
|
+
reads inside would have made "forgot to report the failure" a one-line mistake.
|
|
90
|
+
|
|
91
|
+
## 4. Signal vs authority compliance
|
|
92
|
+
|
|
93
|
+
Textbook signal-producer. It returns a report and holds zero blocking, gating or notifying authority.
|
|
94
|
+
`docs/signal-vs-authority.md` satisfied — and this is the exact seam the operator flagged: synthesis
|
|
95
|
+
must drive action through EXISTING gated paths, never become a new notification channel. Keeping the
|
|
96
|
+
reader authority-free is what makes that possible later.
|
|
97
|
+
|
|
98
|
+
## 5. Interactions
|
|
99
|
+
|
|
100
|
+
- **Attention queue / evolution actions / sentinel log** — read-only consumers, no writes, no schema
|
|
101
|
+
change. Nothing else observes this module yet.
|
|
102
|
+
- **Nothing shadows or is shadowed.** New module, no existing caller.
|
|
103
|
+
|
|
104
|
+
## 6. External surfaces
|
|
105
|
+
|
|
106
|
+
**None in this increment.** No route, no config, no persisted state, no user-visible behaviour. A
|
|
107
|
+
route is the obvious next step and is deliberately not here.
|
|
108
|
+
|
|
109
|
+
## 6b. Operator-surface quality
|
|
110
|
+
|
|
111
|
+
`coverage.unreadable[].reason` carries the actual failure text so a caller can say *why* it could not
|
|
112
|
+
look, not merely that it could not. `noticingRatio` is `null` — never `0` — when there is no
|
|
113
|
+
denominator, so a client that ignores the contract gets an obviously-missing value rather than a
|
|
114
|
+
plausible wrong one.
|
|
115
|
+
|
|
116
|
+
## 7. Multi-machine posture
|
|
117
|
+
|
|
118
|
+
**Posture: `machine-local`.** `machine-local-justification: physical-credential-locality` — the three
|
|
119
|
+
stores are per-machine records of what THAT machine noticed, and observation titles routinely carry
|
|
120
|
+
machine ids, topic ids and account emails. Replicating them to synthesise centrally would multiply
|
|
121
|
+
at-rest exposure of that context across every machine. The correct cross-machine read is the existing
|
|
122
|
+
pool-scope fan-out (`?scope=pool`), which serves each machine's own data from that machine — a
|
|
123
|
+
follow-up for whoever adds the route, noted rather than assumed.
|
|
124
|
+
|
|
125
|
+
## 8. Rollback cost
|
|
126
|
+
|
|
127
|
+
**Zero.** One new module and one new test file, with no callers. Deleting them removes the feature
|
|
128
|
+
entirely; nothing else changes. No persisted state, no migration, no config.
|
|
129
|
+
|
|
130
|
+
## Phase 5 — Second-pass review
|
|
131
|
+
|
|
132
|
+
Not a gate, sentinel, guard or watchdog; no block/allow authority; no session lifecycle or trust
|
|
133
|
+
surface; no LLM. High-risk trigger list not engaged. Author lenses:
|
|
134
|
+
|
|
135
|
+
**Adversarial — "how would I make this useless?"** By letting it report a clean verdict over a
|
|
136
|
+
partial read. That is the one thing it structurally cannot do, asserted from both directions
|
|
137
|
+
(complete-and-empty → `no-recurrence`; incomplete → field absent) and demonstrated on real data.
|
|
138
|
+
|
|
139
|
+
**"Would it have caught the incident?"** The incident is the project's premise, and yes — 2,068
|
|
140
|
+
noticings collapsing to 836 problems with 69 untracked recurrers is precisely the shape nobody could
|
|
141
|
+
see. It found it on first run.
|
|
142
|
+
|
|
143
|
+
**"Symptom or cause?"** Cause, for the invisibility. NOT for the recurrence itself: this makes the
|
|
144
|
+
69 untracked recurrers visible, it does not close them. Closing them is item 4, and claiming
|
|
145
|
+
otherwise would be the filing-as-progress failure the project exists to remove.
|
|
146
|
+
|
|
147
|
+
**Weakest point:** the blunt recurrence key. It will occasionally merge two things a human would
|
|
148
|
+
separate. Mitigated by carrying `exemplar` + `sources`, and preferable to under-grouping — but it is
|
|
149
|
+
the assumption most likely to need revisiting once a human reads a real report.
|