instar 1.3.35 → 1.3.36

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.
Files changed (50) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +53 -3
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  6. package/dist/config/ConfigDefaults.js +15 -0
  7. package/dist/config/ConfigDefaults.js.map +1 -1
  8. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  9. package/dist/core/PostUpdateMigrator.js +15 -0
  10. package/dist/core/PostUpdateMigrator.js.map +1 -1
  11. package/dist/core/featureRolloutScan.d.ts +23 -0
  12. package/dist/core/featureRolloutScan.d.ts.map +1 -1
  13. package/dist/core/featureRolloutScan.js +102 -0
  14. package/dist/core/featureRolloutScan.js.map +1 -1
  15. package/dist/core/types.d.ts +45 -0
  16. package/dist/core/types.d.ts.map +1 -1
  17. package/dist/core/types.js.map +1 -1
  18. package/dist/monitoring/ReleaseReadinessSentinel.d.ts +172 -0
  19. package/dist/monitoring/ReleaseReadinessSentinel.d.ts.map +1 -0
  20. package/dist/monitoring/ReleaseReadinessSentinel.js +315 -0
  21. package/dist/monitoring/ReleaseReadinessSentinel.js.map +1 -0
  22. package/dist/monitoring/releaseReadinessWiring.d.ts +54 -0
  23. package/dist/monitoring/releaseReadinessWiring.d.ts.map +1 -0
  24. package/dist/monitoring/releaseReadinessWiring.js +241 -0
  25. package/dist/monitoring/releaseReadinessWiring.js.map +1 -0
  26. package/dist/scaffold/templates.d.ts.map +1 -1
  27. package/dist/scaffold/templates.js +5 -0
  28. package/dist/scaffold/templates.js.map +1 -1
  29. package/dist/server/AgentServer.d.ts +1 -0
  30. package/dist/server/AgentServer.d.ts.map +1 -1
  31. package/dist/server/AgentServer.js +1 -0
  32. package/dist/server/AgentServer.js.map +1 -1
  33. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  34. package/dist/server/CapabilityIndex.js +14 -0
  35. package/dist/server/CapabilityIndex.js.map +1 -1
  36. package/dist/server/routes.d.ts +3 -0
  37. package/dist/server/routes.d.ts.map +1 -1
  38. package/dist/server/routes.js +55 -0
  39. package/dist/server/routes.js.map +1 -1
  40. package/package.json +1 -1
  41. package/scripts/analyze-release.js +221 -0
  42. package/scripts/upgrade-guide-validator.mjs +120 -0
  43. package/src/data/builtin-manifest.json +63 -63
  44. package/src/scaffold/templates/jobs/instar/release-readiness-check.md +24 -0
  45. package/src/scaffold/templates.ts +5 -0
  46. package/upgrades/1.3.36.md +39 -0
  47. package/upgrades/side-effects/release-readiness-visibility-pr2a.md +27 -0
  48. package/upgrades/side-effects/release-readiness-visibility-pr2b.md +26 -0
  49. package/upgrades/side-effects/release-readiness-visibility-pr2c.md +37 -0
  50. package/upgrades/side-effects/release-readiness-visibility-pr3.md +33 -0
@@ -0,0 +1,172 @@
1
+ /**
2
+ * ReleaseReadinessSentinel — Layer B of the release-readiness-visibility spec.
3
+ *
4
+ * Watches whether a release is blocked/stalling and makes it impossible to miss.
5
+ * The original incident (2026-05-27): npm publishing silently stalled since
6
+ * v1.3.26 — green merges, green CI, no published release, no alert. The publish
7
+ * workflow's NEXT.md gate is a *silent skip*: a stalled release is
8
+ * indistinguishable from "nothing to publish". This sentinel is the missing
9
+ * visibility — a recurring, near-silent check that surfaces a stuck release as
10
+ * a single, deduped, escalating Attention item.
11
+ *
12
+ * Design (spec §4.2):
13
+ * - Reads canonical `main` (FETCH_HEAD), not the local checkout, so a stale
14
+ * dev branch can't produce a false "all clear".
15
+ * - "Blocked" is decoupled from NEXT.md state (so Layer A's auto-draft can't
16
+ * silence the alarm by clearing the file): unreleased feature/fix commits
17
+ * exist AND the guide can't publish (missing/template/unreviewed), OR the
18
+ * analyzer reports critical/high coverage gaps.
19
+ * - Near-silent: silent below threshold (state + log only); ONE Attention
20
+ * item per stall episode above it, keyed on the OLDEST unreleased commit
21
+ * SHA (stable across ticks — not a resettable per-tick id), priority scaled
22
+ * by backlog age, 12h hysteresis on re-raise after an auto-resolve.
23
+ * - Fail-loud: any evaluation failure (fetch error, analyzer error) raises a
24
+ * low-priority Attention item — never a silent catch (that would re-create
25
+ * the exact bug this fixes).
26
+ * - Lifecycle owner: detect → surface → auto-resolve → reap, with
27
+ * resolveEpisodesInRange consulted by the publish-finalize path.
28
+ * - Repo-gated: needs an analyzable instar git repo (dev/maintainer env). On
29
+ * an npm-installed agent with no such repo the deps make the check inert.
30
+ * - Supervision: Tier 0 (mechanical computation + fixed-template wording).
31
+ */
32
+ import { EventEmitter } from 'node:events';
33
+ /** Parsed subset of `analyze-release.js --json` the sentinel needs. */
34
+ export interface AnalyzerReport {
35
+ lastTag: string;
36
+ commitCount: number;
37
+ analysis: {
38
+ commitClassification: {
39
+ features: number;
40
+ fixes: number;
41
+ };
42
+ };
43
+ guideCoverage: {
44
+ criticalGaps: number;
45
+ highGaps: number;
46
+ };
47
+ }
48
+ export interface OldestCommit {
49
+ sha: string;
50
+ dateMs: number;
51
+ }
52
+ export interface ReadinessEpisode {
53
+ /** Stable key: SHA of the oldest unreleased commit at first detection. */
54
+ oldestSha: string;
55
+ firstDetectedMs: number;
56
+ /** Set when an Attention item was raised (age crossed the threshold). */
57
+ openedMs?: number;
58
+ attentionId?: string;
59
+ lastPriority?: ReadinessPriority;
60
+ resolvedMs?: number;
61
+ resolvedReason?: 'published' | 'stale' | 'rolled-back' | 'cleared';
62
+ }
63
+ export interface ReadinessState {
64
+ episodes: ReadinessEpisode[];
65
+ /** Episodes resolved recently — used for the re-raise hysteresis window. */
66
+ recentResolves: Array<{
67
+ oldestSha: string;
68
+ resolvedMs: number;
69
+ }>;
70
+ /** Last failure-episode key we signaled, to dedupe fail-loud notices. */
71
+ lastFailureKey?: string;
72
+ lastTickAt?: number;
73
+ lastSignalAt?: number;
74
+ cacheHeadSha?: string;
75
+ canonicalRemoteOverridden?: boolean;
76
+ /** Runtime kill-switch set by POST /release-readiness/rollback. When true,
77
+ * tick() no-ops without evaluating. Cleared by /release-readiness/enable. */
78
+ disabled?: boolean;
79
+ rollbackHistory?: Array<{
80
+ ts: number;
81
+ sessionId?: string;
82
+ sourceIp?: string;
83
+ reason?: string;
84
+ }>;
85
+ }
86
+ export type ReadinessPriority = 'LOW' | 'MEDIUM' | 'HIGH';
87
+ export interface AttentionItem {
88
+ id: string;
89
+ title: string;
90
+ summary: string;
91
+ category?: string;
92
+ priority?: string;
93
+ }
94
+ export interface ReleaseReadinessSentinelDeps {
95
+ /** Fetch canonical main; resolve FETCH_HEAD sha. ok:false ⇒ unreachable. */
96
+ fetchCanonical(): Promise<{
97
+ ok: boolean;
98
+ headSha?: string;
99
+ }>;
100
+ /** Run analyze-release.js --json --ref against the canonical ref. null ⇒ error. */
101
+ runAnalyzer(ref: string): Promise<AnalyzerReport | null>;
102
+ /** Oldest unreleased feature/fix commit relative to lastTag..ref. */
103
+ oldestUnreleasedCommit(lastTag: string, ref: string): Promise<OldestCommit | null>;
104
+ /** True when the publish gate would refuse the guide (missing/template/unreviewed). */
105
+ guideBlocksPublish(): Promise<boolean>;
106
+ /** Best-effort: seed NEXT.md via Layer A (analyze-release.js --draft-guide). */
107
+ draftGuide(ref: string): Promise<void>;
108
+ /** Post an Attention item; resolves to true on delivery. */
109
+ postAttention(item: AttentionItem): Promise<boolean>;
110
+ /** Resolve an open Attention item by id. */
111
+ resolveAttention(id: string, reason: string): Promise<boolean>;
112
+ /** Load persisted state (or a fresh empty state). */
113
+ loadState(): ReadinessState;
114
+ /** Persist state atomically. */
115
+ saveState(state: ReadinessState): void;
116
+ /** Is `sha` an ancestor of `ref`? (for resolveEpisodesInRange) */
117
+ isAncestor(sha: string, ref: string): Promise<boolean>;
118
+ /** Append a structured audit line (sentinel-events.jsonl). */
119
+ audit(event: Record<string, unknown>): void;
120
+ now(): number;
121
+ }
122
+ export interface ReleaseReadinessSentinelConfig {
123
+ enabled?: boolean;
124
+ tickIntervalMs?: number;
125
+ backlogAgeDaysSilent?: number;
126
+ backlogAgeDaysLow?: number;
127
+ backlogAgeDaysMedium?: number;
128
+ backlogAgeDaysHigh?: number;
129
+ hysteresisHours?: number;
130
+ staleEpisodeTtlDays?: number;
131
+ }
132
+ export declare class ReleaseReadinessSentinel extends EventEmitter {
133
+ private readonly deps;
134
+ private readonly cfg;
135
+ private tickHandle;
136
+ constructor(deps: ReleaseReadinessSentinelDeps, cfg?: ReleaseReadinessSentinelConfig);
137
+ start(): void;
138
+ stop(): void;
139
+ /** Priority for a given backlog age, or null when below the silent threshold. */
140
+ priorityForAge(ageDays: number): ReadinessPriority | null;
141
+ /** The cron entry point. Top-level catch converts any error into a loud signal. */
142
+ tick(): Promise<void>;
143
+ /** Read-only snapshot of current state (for GET /release-readiness). */
144
+ snapshot(): ReadinessState;
145
+ /**
146
+ * Runtime kill-switch (POST /release-readiness/rollback). Disables future
147
+ * ticks, resolves all open Attention items, and is ITSELF loud — raises a
148
+ * HIGH-priority Attention item + audits — so the rollback can never be a
149
+ * silent way to mute the alarm (iter-3 Adversarial V5).
150
+ */
151
+ rollback(meta?: {
152
+ sessionId?: string;
153
+ sourceIp?: string;
154
+ }): Promise<void>;
155
+ /** Re-arm after a rollback. */
156
+ enable(): void;
157
+ private evaluate;
158
+ private isBlocked;
159
+ private raiseGapOnly;
160
+ private failLoud;
161
+ private resolveAll;
162
+ /**
163
+ * Called by the publish-finalize path (and the /rollback route via 'rolled-back').
164
+ * Resolves every open episode whose oldestSha is an ancestor of newTagSha —
165
+ * correct even when the oldest-unreleased SHA churned during the open window.
166
+ */
167
+ resolveEpisodesInRange(newTagSha: string, reason?: ReadinessEpisode['resolvedReason']): Promise<void>;
168
+ /** TTL-reap episodes/resolves so state doesn't grow unbounded. */
169
+ private reap;
170
+ static emptyState(): ReadinessState;
171
+ }
172
+ //# sourceMappingURL=ReleaseReadinessSentinel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReleaseReadinessSentinel.d.ts","sourceRoot":"","sources":["../../src/monitoring/ReleaseReadinessSentinel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,315 @@
1
+ /**
2
+ * ReleaseReadinessSentinel — Layer B of the release-readiness-visibility spec.
3
+ *
4
+ * Watches whether a release is blocked/stalling and makes it impossible to miss.
5
+ * The original incident (2026-05-27): npm publishing silently stalled since
6
+ * v1.3.26 — green merges, green CI, no published release, no alert. The publish
7
+ * workflow's NEXT.md gate is a *silent skip*: a stalled release is
8
+ * indistinguishable from "nothing to publish". This sentinel is the missing
9
+ * visibility — a recurring, near-silent check that surfaces a stuck release as
10
+ * a single, deduped, escalating Attention item.
11
+ *
12
+ * Design (spec §4.2):
13
+ * - Reads canonical `main` (FETCH_HEAD), not the local checkout, so a stale
14
+ * dev branch can't produce a false "all clear".
15
+ * - "Blocked" is decoupled from NEXT.md state (so Layer A's auto-draft can't
16
+ * silence the alarm by clearing the file): unreleased feature/fix commits
17
+ * exist AND the guide can't publish (missing/template/unreviewed), OR the
18
+ * analyzer reports critical/high coverage gaps.
19
+ * - Near-silent: silent below threshold (state + log only); ONE Attention
20
+ * item per stall episode above it, keyed on the OLDEST unreleased commit
21
+ * SHA (stable across ticks — not a resettable per-tick id), priority scaled
22
+ * by backlog age, 12h hysteresis on re-raise after an auto-resolve.
23
+ * - Fail-loud: any evaluation failure (fetch error, analyzer error) raises a
24
+ * low-priority Attention item — never a silent catch (that would re-create
25
+ * the exact bug this fixes).
26
+ * - Lifecycle owner: detect → surface → auto-resolve → reap, with
27
+ * resolveEpisodesInRange consulted by the publish-finalize path.
28
+ * - Repo-gated: needs an analyzable instar git repo (dev/maintainer env). On
29
+ * an npm-installed agent with no such repo the deps make the check inert.
30
+ * - Supervision: Tier 0 (mechanical computation + fixed-template wording).
31
+ */
32
+ import { EventEmitter } from 'node:events';
33
+ const DEFAULTS = {
34
+ enabled: false,
35
+ tickIntervalMs: 6 * 60 * 60 * 1000, // 6h
36
+ backlogAgeDaysSilent: 2,
37
+ backlogAgeDaysLow: 2,
38
+ backlogAgeDaysMedium: 4,
39
+ backlogAgeDaysHigh: 7,
40
+ hysteresisHours: 12,
41
+ staleEpisodeTtlDays: 30,
42
+ };
43
+ const DAY_MS = 24 * 60 * 60 * 1000;
44
+ const HOUR_MS = 60 * 60 * 1000;
45
+ export class ReleaseReadinessSentinel extends EventEmitter {
46
+ deps;
47
+ cfg;
48
+ tickHandle = null;
49
+ constructor(deps, cfg = {}) {
50
+ super();
51
+ this.deps = deps;
52
+ this.cfg = { ...DEFAULTS, ...cfg };
53
+ }
54
+ start() {
55
+ if (!this.cfg.enabled || this.tickHandle)
56
+ return;
57
+ this.tickHandle = setInterval(() => {
58
+ void this.tick();
59
+ }, this.cfg.tickIntervalMs);
60
+ if (typeof this.tickHandle.unref === 'function')
61
+ this.tickHandle.unref();
62
+ }
63
+ stop() {
64
+ if (this.tickHandle) {
65
+ clearInterval(this.tickHandle);
66
+ this.tickHandle = null;
67
+ }
68
+ }
69
+ /** Priority for a given backlog age, or null when below the silent threshold. */
70
+ priorityForAge(ageDays) {
71
+ if (ageDays >= this.cfg.backlogAgeDaysHigh)
72
+ return 'HIGH';
73
+ if (ageDays >= this.cfg.backlogAgeDaysMedium)
74
+ return 'MEDIUM';
75
+ if (ageDays >= this.cfg.backlogAgeDaysLow)
76
+ return 'LOW';
77
+ return null;
78
+ }
79
+ /** The cron entry point. Top-level catch converts any error into a loud signal. */
80
+ async tick() {
81
+ const state = this.deps.loadState();
82
+ state.lastTickAt = this.deps.now();
83
+ if (state.disabled) {
84
+ this.deps.audit({ kind: 'release-readiness', event: 'tick-skipped-disabled' });
85
+ this.deps.saveState(state);
86
+ return;
87
+ }
88
+ try {
89
+ await this.evaluate(state);
90
+ this.deps.saveState(state);
91
+ }
92
+ catch (err) {
93
+ // Fail-loud — a silent catch here would re-create the very silent-failure
94
+ // bug this sentinel exists to fix.
95
+ await this.failLoud(state, 'tick', err);
96
+ this.deps.saveState(state);
97
+ }
98
+ }
99
+ /** Read-only snapshot of current state (for GET /release-readiness). */
100
+ snapshot() {
101
+ return this.deps.loadState();
102
+ }
103
+ /**
104
+ * Runtime kill-switch (POST /release-readiness/rollback). Disables future
105
+ * ticks, resolves all open Attention items, and is ITSELF loud — raises a
106
+ * HIGH-priority Attention item + audits — so the rollback can never be a
107
+ * silent way to mute the alarm (iter-3 Adversarial V5).
108
+ */
109
+ async rollback(meta = {}) {
110
+ const state = this.deps.loadState();
111
+ state.disabled = true;
112
+ await this.resolveAll(state, 'rolled-back');
113
+ state.rollbackHistory ??= [];
114
+ state.rollbackHistory.push({ ts: this.deps.now(), sessionId: meta.sessionId, sourceIp: meta.sourceIp, reason: 'rollback' });
115
+ this.deps.audit({ kind: 'release-readiness', event: 'rollback', sessionId: meta.sessionId, sourceIp: meta.sourceIp });
116
+ await this.deps.postAttention({
117
+ id: 'release-readiness-rolled-back',
118
+ title: 'Release-readiness alarm disabled',
119
+ summary: `The release-readiness watchdog was disabled via /release-readiness/rollback${meta.sessionId ? ` by session ${meta.sessionId}` : ''} at ${new Date(this.deps.now()).toISOString()}. Re-enable via POST /release-readiness/enable.`,
120
+ category: 'degradation',
121
+ priority: 'HIGH',
122
+ });
123
+ this.deps.saveState(state);
124
+ }
125
+ /** Re-arm after a rollback. */
126
+ enable() {
127
+ const state = this.deps.loadState();
128
+ state.disabled = false;
129
+ this.deps.audit({ kind: 'release-readiness', event: 'enabled' });
130
+ this.deps.saveState(state);
131
+ }
132
+ async evaluate(state) {
133
+ const fetched = await this.deps.fetchCanonical();
134
+ if (!fetched.ok) {
135
+ await this.failLoud(state, 'fetch', new Error('canonical ref unreachable'));
136
+ return;
137
+ }
138
+ state.cacheHeadSha = fetched.headSha;
139
+ const ref = fetched.headSha ?? 'FETCH_HEAD';
140
+ // Keep NEXT.md seeded (Layer A). Best-effort: a draft failure is not a
141
+ // readiness failure.
142
+ try {
143
+ await this.deps.draftGuide(ref);
144
+ }
145
+ catch {
146
+ /* draft is advisory; the readiness signal below is what matters */
147
+ }
148
+ const report = await this.deps.runAnalyzer(ref);
149
+ if (!report) {
150
+ await this.failLoud(state, 'analyzer', new Error('analyze-release returned no report'));
151
+ return;
152
+ }
153
+ const blocked = this.isBlocked(report, await this.deps.guideBlocksPublish());
154
+ if (!blocked) {
155
+ // Backlog clear — resolve any open episodes.
156
+ await this.resolveAll(state, 'cleared');
157
+ // A successful evaluation clears any prior fail-loud episode.
158
+ state.lastFailureKey = undefined;
159
+ this.reap(state);
160
+ return;
161
+ }
162
+ const oldest = await this.deps.oldestUnreleasedCommit(report.lastTag, ref);
163
+ if (!oldest) {
164
+ // Blocked by coverage gaps but no datable commit — surface at LOW.
165
+ await this.raiseGapOnly(state, report);
166
+ this.reap(state);
167
+ return;
168
+ }
169
+ const ageDays = (this.deps.now() - oldest.dateMs) / DAY_MS;
170
+ const priority = this.priorityForAge(ageDays);
171
+ let episode = state.episodes.find((e) => e.oldestSha === oldest.sha && !e.resolvedMs);
172
+ if (!episode) {
173
+ episode = { oldestSha: oldest.sha, firstDetectedMs: this.deps.now() };
174
+ state.episodes.push(episode);
175
+ }
176
+ if (!priority) {
177
+ // Below the silent threshold — recorded, no message.
178
+ this.reap(state);
179
+ return;
180
+ }
181
+ // Hysteresis: don't re-raise the same sha within the window of a recent resolve.
182
+ const recent = state.recentResolves.find((r) => r.oldestSha === oldest.sha);
183
+ if (recent && this.deps.now() - recent.resolvedMs < this.cfg.hysteresisHours * HOUR_MS && !episode.openedMs) {
184
+ this.reap(state);
185
+ return;
186
+ }
187
+ if (!episode.openedMs || episode.lastPriority !== priority) {
188
+ const id = episode.attentionId ?? `release-readiness-${oldest.sha.slice(0, 12)}`;
189
+ const days = Math.floor(ageDays);
190
+ const delivered = await this.deps.postAttention({
191
+ id,
192
+ title: 'Release blocked — unreleased work is piling up',
193
+ summary: `${report.analysis.commitClassification.features + report.analysis.commitClassification.fixes} ` +
194
+ `unreleased feature/fix commit(s) since ${report.lastTag}; oldest is ${days} day(s) old and publishing is blocked ` +
195
+ `(NEXT.md needs review/coverage). Oldest commit ${oldest.sha.slice(0, 12)}.`,
196
+ category: 'degradation',
197
+ priority,
198
+ });
199
+ episode.openedMs = episode.openedMs ?? this.deps.now();
200
+ episode.attentionId = id;
201
+ episode.lastPriority = priority;
202
+ state.lastSignalAt = this.deps.now();
203
+ this.deps.audit({ kind: 'release-readiness', event: 'signal', oldestSha: oldest.sha, priority, ageDays: days, delivered });
204
+ this.emit('signal', { oldestSha: oldest.sha, priority });
205
+ }
206
+ this.reap(state);
207
+ }
208
+ isBlocked(report, guideBlocks) {
209
+ const featureOrFix = report.analysis.commitClassification.features + report.analysis.commitClassification.fixes;
210
+ const backlogBlocked = featureOrFix > 0 && guideBlocks;
211
+ const coverageBlocked = report.guideCoverage.criticalGaps + report.guideCoverage.highGaps > 0;
212
+ return backlogBlocked || coverageBlocked;
213
+ }
214
+ async raiseGapOnly(state, report) {
215
+ const id = 'release-readiness-coverage-gaps';
216
+ let episode = state.episodes.find((e) => e.oldestSha === id && !e.resolvedMs);
217
+ if (!episode) {
218
+ episode = { oldestSha: id, firstDetectedMs: this.deps.now() };
219
+ state.episodes.push(episode);
220
+ }
221
+ if (!episode.openedMs) {
222
+ const delivered = await this.deps.postAttention({
223
+ id,
224
+ title: 'Release blocked — upgrade-guide coverage gaps',
225
+ summary: `analyze-release reports ${report.guideCoverage.criticalGaps + report.guideCoverage.highGaps} critical/high coverage gap(s) in the upgrade guide. Publishing is blocked until they are addressed.`,
226
+ category: 'degradation',
227
+ priority: 'LOW',
228
+ });
229
+ episode.openedMs = this.deps.now();
230
+ episode.attentionId = id;
231
+ episode.lastPriority = 'LOW';
232
+ state.lastSignalAt = this.deps.now();
233
+ this.deps.audit({ kind: 'release-readiness', event: 'signal-gaps', delivered });
234
+ }
235
+ }
236
+ async failLoud(state, stage, err) {
237
+ const key = `failure:${stage}`;
238
+ this.deps.audit({ kind: 'release-readiness', event: 'eval-failed', stage, error: String(err) });
239
+ if (state.lastFailureKey === key)
240
+ return; // dedupe per failure episode
241
+ state.lastFailureKey = key;
242
+ await this.deps.postAttention({
243
+ id: `release-readiness-eval-failure-${stage}`,
244
+ title: 'Release-readiness check could not evaluate',
245
+ summary: `The release-readiness check failed at the "${stage}" stage: ${String(err)}. Last evaluated ${state.lastSignalAt ? new Date(state.lastSignalAt).toISOString() : 'never'}.`,
246
+ category: 'degradation',
247
+ priority: 'LOW',
248
+ });
249
+ state.lastSignalAt = this.deps.now();
250
+ this.emit('eval-failed', { stage });
251
+ }
252
+ async resolveAll(state, reason) {
253
+ for (const e of state.episodes) {
254
+ if (e.resolvedMs)
255
+ continue;
256
+ if (e.attentionId)
257
+ await this.deps.resolveAttention(e.attentionId, reason ?? 'cleared');
258
+ e.resolvedMs = this.deps.now();
259
+ e.resolvedReason = reason;
260
+ state.recentResolves.push({ oldestSha: e.oldestSha, resolvedMs: this.deps.now() });
261
+ this.deps.audit({ kind: 'release-readiness', event: 'resolved', oldestSha: e.oldestSha, reason });
262
+ }
263
+ }
264
+ /**
265
+ * Called by the publish-finalize path (and the /rollback route via 'rolled-back').
266
+ * Resolves every open episode whose oldestSha is an ancestor of newTagSha —
267
+ * correct even when the oldest-unreleased SHA churned during the open window.
268
+ */
269
+ async resolveEpisodesInRange(newTagSha, reason = 'published') {
270
+ const state = this.deps.loadState();
271
+ let changed = false;
272
+ for (const e of state.episodes) {
273
+ if (e.resolvedMs)
274
+ continue;
275
+ if (e.oldestSha.startsWith('release-readiness-'))
276
+ continue; // synthetic (gap) episodes
277
+ const ancestor = await this.deps.isAncestor(e.oldestSha, newTagSha);
278
+ if (ancestor) {
279
+ if (e.attentionId)
280
+ await this.deps.resolveAttention(e.attentionId, reason ?? 'published');
281
+ e.resolvedMs = this.deps.now();
282
+ e.resolvedReason = reason;
283
+ state.recentResolves.push({ oldestSha: e.oldestSha, resolvedMs: this.deps.now() });
284
+ this.deps.audit({ kind: 'release-readiness', event: 'resolved-in-range', oldestSha: e.oldestSha, newTagSha, reason });
285
+ changed = true;
286
+ }
287
+ }
288
+ if (changed)
289
+ this.deps.saveState(state);
290
+ }
291
+ /** TTL-reap episodes/resolves so state doesn't grow unbounded. */
292
+ reap(state) {
293
+ const ttl = this.cfg.staleEpisodeTtlDays * DAY_MS;
294
+ const now = this.deps.now();
295
+ for (const e of state.episodes) {
296
+ if (!e.resolvedMs && now - e.firstDetectedMs > ttl) {
297
+ // Backlog vanished without a finalize (e.g. branch abandoned) — reap loudly.
298
+ e.resolvedMs = now;
299
+ e.resolvedReason = 'stale';
300
+ if (e.attentionId)
301
+ void this.deps.resolveAttention(e.attentionId, 'stale');
302
+ this.deps.audit({ kind: 'release-readiness', event: 'reaped-stale', oldestSha: e.oldestSha });
303
+ }
304
+ }
305
+ state.episodes = state.episodes.filter((e) => !e.resolvedMs || now - (e.resolvedMs ?? 0) < ttl);
306
+ state.recentResolves = state.recentResolves.filter((r) => now - r.resolvedMs < this.cfg.hysteresisHours * HOUR_MS * 2);
307
+ if ((state.rollbackHistory?.length ?? 0) > 50) {
308
+ state.rollbackHistory = state.rollbackHistory.slice(-50);
309
+ }
310
+ }
311
+ static emptyState() {
312
+ return { episodes: [], recentResolves: [] };
313
+ }
314
+ }
315
+ //# sourceMappingURL=ReleaseReadinessSentinel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReleaseReadinessSentinel.js","sourceRoot":"","sources":["../../src/monitoring/ReleaseReadinessSentinel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * releaseReadinessWiring — builds the real I/O dependencies for the
3
+ * ReleaseReadinessSentinel (Layer B of release-readiness-visibility).
4
+ *
5
+ * Extracted from server.ts so the dependency construction is itself
6
+ * unit-testable (Testing Integrity Standard: every dependency-injected
7
+ * component needs wiring-integrity tests proving deps are real functions, not
8
+ * nulls or silent no-ops).
9
+ *
10
+ * Repo-gated: the sentinel analyzes the instar git repo, which only exists in
11
+ * the dev/maintainer environment. `isAnalyzableRepo` lets the server decide
12
+ * whether to construct + start the sentinel at all on a given install.
13
+ *
14
+ * All git goes through SafeGitExecutor (audited funnel, execFileSync — no shell).
15
+ * Canonical-remote reads are allow-listed to github.com:JKHeadley/instar so a
16
+ * config override to a fork raises a HIGH-priority signal rather than silently
17
+ * trusting a stale fork (spec §4.2.2).
18
+ */
19
+ import type { ReleaseReadinessSentinelDeps, ReadinessState, AttentionItem } from './ReleaseReadinessSentinel.js';
20
+ /** Anchored on the known canonical host — not "any URL ending in JKHeadley/instar". */
21
+ export declare const CANONICAL_REMOTE_RE: RegExp;
22
+ export interface ReleaseReadinessWiringOpts {
23
+ /** The instar git checkout to analyze (Echo: the agent home). */
24
+ repoPath: string;
25
+ /** Path to .instar/state/release-readiness.json. */
26
+ statePath: string;
27
+ /** Path to logs/sentinel-events.jsonl. */
28
+ auditPath: string;
29
+ /** Local server port + auth for Attention posting. */
30
+ port: number;
31
+ authToken: string;
32
+ /** Configured canonical remote name (default: auto-detect a JKHeadley/instar remote). */
33
+ canonicalRemote?: string;
34
+ fetchTimeoutMs?: number;
35
+ fetchImpl?: typeof fetch;
36
+ now?: () => number;
37
+ }
38
+ /** Is `repoPath` an instar git checkout we can actually analyze? */
39
+ export declare function isAnalyzableRepo(repoPath: string): boolean;
40
+ /** Resolve the canonical-main remote name and whether it was overridden to a non-canonical URL. */
41
+ export declare function resolveCanonicalRemote(repoPath: string, configured?: string): {
42
+ remote: string;
43
+ overridden: boolean;
44
+ };
45
+ export declare function buildReleaseReadinessDeps(opts: ReleaseReadinessWiringOpts): ReleaseReadinessSentinelDeps;
46
+ export declare function loadReadinessState(statePath: string, canonicalRemoteOverridden?: boolean): ReadinessState;
47
+ export declare function saveReadinessState(statePath: string, state: ReadinessState): void;
48
+ export type AttentionPoster = (item: AttentionItem) => Promise<boolean>;
49
+ export declare function makeAttentionPoster(opts: {
50
+ port: number;
51
+ authToken: string;
52
+ fetchImpl?: typeof fetch;
53
+ }): AttentionPoster;
54
+ //# sourceMappingURL=releaseReadinessWiring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"releaseReadinessWiring.d.ts","sourceRoot":"","sources":["../../src/monitoring/releaseReadinessWiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EACV,4BAA4B,EAC5B,cAAc,EAGd,aAAa,EACd,MAAM,+BAA+B,CAAC;AAGvC,uFAAuF;AACvF,eAAO,MAAM,mBAAmB,QAA0E,CAAC;AAE3G,MAAM,WAAW,0BAA0B;IACzC,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAU1D;AAED,mGAAmG;AACnG,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAoBzC;AAgBD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,0BAA0B,GAAG,4BAA4B,CAuIxG;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,yBAAyB,UAAQ,GAAG,cAAc,CAevG;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAKjF;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAExE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,eAAe,CAclB"}