instar 0.28.64 → 0.28.65
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/dashboard/index.html +225 -0
- package/dist/cli.js +0 -0
- package/dist/commands/init.js +6 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +44 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +12 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +158 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/CommitmentTracker.d.ts +37 -0
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +151 -5
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/monitoring/HelperWatchdog.d.ts +84 -0
- package/dist/monitoring/HelperWatchdog.d.ts.map +1 -0
- package/dist/monitoring/HelperWatchdog.js +155 -0
- package/dist/monitoring/HelperWatchdog.js.map +1 -0
- package/dist/monitoring/PresenceProxy.d.ts +71 -0
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +210 -0
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/ProxyCoordinator.d.ts +33 -0
- package/dist/monitoring/ProxyCoordinator.d.ts.map +1 -1
- package/dist/monitoring/ProxyCoordinator.js +42 -0
- package/dist/monitoring/ProxyCoordinator.js.map +1 -1
- package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
- package/dist/monitoring/SessionWatchdog.js +9 -6
- package/dist/monitoring/SessionWatchdog.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +95 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/PipeSessionSpawner.d.ts +5 -0
- package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -1
- package/dist/threadline/PipeSessionSpawner.js +11 -0
- package/dist/threadline/PipeSessionSpawner.js.map +1 -1
- package/package.json +1 -1
- package/playbook-scripts/build-state.py +102 -0
- package/src/data/builtin-manifest.json +63 -63
- package/upgrades/0.28.65.md +44 -0
- package/upgrades/side-effects/0.28.64.md +5 -0
- package/upgrades/side-effects/build-stall-visibility-fix2-3.md +125 -0
- package/upgrades/side-effects/build-stop-hook-deployment.md +98 -0
- package/upgrades/side-effects/dashboard-secrets-tab.md +86 -0
- package/upgrades/side-effects/threadline-relay-rapid-fire-pipe-guard.md +46 -0
- package/upgrades/side-effects/watchdog-mcp-version-pin.md +121 -0
- package/dist/core/InitiativeDigestJob.d.ts +0 -54
- package/dist/core/InitiativeDigestJob.d.ts.map +0 -1
- package/dist/core/InitiativeDigestJob.js +0 -128
- package/dist/core/InitiativeDigestJob.js.map +0 -1
- package/upgrades/0.28.61.md +0 -80
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* InitiativeDigestJob — periodic scan of the InitiativeTracker that
|
|
3
|
-
* pushes a Telegram notification to the user when there are actionable
|
|
4
|
-
* signals (stale / needs-user / next-check-due / ready-to-advance).
|
|
5
|
-
*
|
|
6
|
-
* Quiet by default: an empty digest sends nothing.
|
|
7
|
-
* Idempotent by default: if the exact same digest would be sent again
|
|
8
|
-
* within `resendSuppressionMs`, we skip it to avoid noise.
|
|
9
|
-
*
|
|
10
|
-
* Lives in-process (setInterval-based). Not a JobScheduler Job because
|
|
11
|
-
* we don't want to spawn a fresh Claude session just to check the
|
|
12
|
-
* tracker — this is a direct, deterministic read.
|
|
13
|
-
*/
|
|
14
|
-
import { createHash } from 'node:crypto';
|
|
15
|
-
const DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
16
|
-
const DEFAULT_RESEND_SUPPRESSION_MS = 23 * 60 * 60 * 1000;
|
|
17
|
-
const DEFAULT_INITIAL_DELAY_MS = 60 * 1000;
|
|
18
|
-
export class InitiativeDigestJob {
|
|
19
|
-
tracker;
|
|
20
|
-
sendMessage;
|
|
21
|
-
intervalMs;
|
|
22
|
-
resendSuppressionMs;
|
|
23
|
-
initialDelayMs;
|
|
24
|
-
now;
|
|
25
|
-
lastSend = null;
|
|
26
|
-
initialTimer = null;
|
|
27
|
-
intervalTimer = null;
|
|
28
|
-
running = false;
|
|
29
|
-
constructor(opts) {
|
|
30
|
-
this.tracker = opts.tracker;
|
|
31
|
-
this.sendMessage = opts.sendMessage;
|
|
32
|
-
this.intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
33
|
-
this.resendSuppressionMs = opts.resendSuppressionMs ?? DEFAULT_RESEND_SUPPRESSION_MS;
|
|
34
|
-
this.initialDelayMs = opts.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
35
|
-
this.now = opts.now ?? (() => new Date());
|
|
36
|
-
}
|
|
37
|
-
start() {
|
|
38
|
-
if (this.running)
|
|
39
|
-
return;
|
|
40
|
-
this.running = true;
|
|
41
|
-
this.initialTimer = setTimeout(() => {
|
|
42
|
-
void this.tick();
|
|
43
|
-
this.intervalTimer = setInterval(() => void this.tick(), this.intervalMs);
|
|
44
|
-
}, this.initialDelayMs);
|
|
45
|
-
}
|
|
46
|
-
stop() {
|
|
47
|
-
this.running = false;
|
|
48
|
-
if (this.initialTimer)
|
|
49
|
-
clearTimeout(this.initialTimer);
|
|
50
|
-
if (this.intervalTimer)
|
|
51
|
-
clearInterval(this.intervalTimer);
|
|
52
|
-
this.initialTimer = null;
|
|
53
|
-
this.intervalTimer = null;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Run one scan. Exposed for tests and for the manual-trigger API.
|
|
57
|
-
* Returns the digest that was scanned and whether a message was sent.
|
|
58
|
-
*/
|
|
59
|
-
async tick() {
|
|
60
|
-
const digest = this.tracker.digest(this.now());
|
|
61
|
-
if (digest.items.length === 0) {
|
|
62
|
-
return { digest, sent: false, suppressedReason: 'no-items' };
|
|
63
|
-
}
|
|
64
|
-
const signature = this.signatureOf(digest);
|
|
65
|
-
if (this.lastSend && this.lastSend.signature === signature) {
|
|
66
|
-
const lastMs = Date.parse(this.lastSend.at);
|
|
67
|
-
const ageMs = this.now().getTime() - lastMs;
|
|
68
|
-
if (Number.isFinite(lastMs) && ageMs < this.resendSuppressionMs) {
|
|
69
|
-
return { digest, sent: false, suppressedReason: 'duplicate-within-window' };
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const text = this.format(digest);
|
|
73
|
-
try {
|
|
74
|
-
await this.sendMessage(text);
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
console.error(`[initiative-digest] send failed: ${err instanceof Error ? err.message : err}`);
|
|
78
|
-
return { digest, sent: false, suppressedReason: 'send-failed' };
|
|
79
|
-
}
|
|
80
|
-
this.lastSend = { signature, at: this.now().toISOString() };
|
|
81
|
-
return { digest, sent: true };
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Stable hash of the digest (reason + initiativeId pairs, sorted) so
|
|
85
|
-
* that identical digests produce identical signatures regardless of
|
|
86
|
-
* map iteration order.
|
|
87
|
-
*/
|
|
88
|
-
signatureOf(digest) {
|
|
89
|
-
const tokens = digest.items
|
|
90
|
-
.map((i) => `${i.reason}:${i.initiativeId}`)
|
|
91
|
-
.sort()
|
|
92
|
-
.join('|');
|
|
93
|
-
return createHash('sha256').update(tokens).digest('hex');
|
|
94
|
-
}
|
|
95
|
-
format(digest) {
|
|
96
|
-
const byReason = new Map();
|
|
97
|
-
for (const item of digest.items) {
|
|
98
|
-
if (!byReason.has(item.reason))
|
|
99
|
-
byReason.set(item.reason, []);
|
|
100
|
-
byReason.get(item.reason).push({ title: item.title, detail: item.detail, id: item.initiativeId });
|
|
101
|
-
}
|
|
102
|
-
const sections = [];
|
|
103
|
-
const order = [
|
|
104
|
-
'needs-user', 'next-check-due', 'ready-to-advance', 'stale',
|
|
105
|
-
];
|
|
106
|
-
const labels = {
|
|
107
|
-
'needs-user': 'Needs you',
|
|
108
|
-
'next-check-due': 'Check-in due',
|
|
109
|
-
'ready-to-advance': 'Ready to advance',
|
|
110
|
-
'stale': 'Stale',
|
|
111
|
-
};
|
|
112
|
-
for (const reason of order) {
|
|
113
|
-
const items = byReason.get(reason);
|
|
114
|
-
if (!items || items.length === 0)
|
|
115
|
-
continue;
|
|
116
|
-
const lines = items.map((i) => ` • ${i.title} — ${i.detail}`);
|
|
117
|
-
sections.push(`*${labels[reason]}:*\n${lines.join('\n')}`);
|
|
118
|
-
}
|
|
119
|
-
const header = digest.items.length === 1
|
|
120
|
-
? '1 initiative needs attention:'
|
|
121
|
-
: `${digest.items.length} initiatives need attention:`;
|
|
122
|
-
return `${header}\n\n${sections.join('\n\n')}`;
|
|
123
|
-
}
|
|
124
|
-
getLastSend() {
|
|
125
|
-
return this.lastSend;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
//# sourceMappingURL=InitiativeDigestJob.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"InitiativeDigestJob.js","sourceRoot":"","sources":["../../src/core/InitiativeDigestJob.ts"],"names":[],"mappings":"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"}
|
package/upgrades/0.28.61.md
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# Upgrade Guide — Scheduler reports real exit code for gate skips
|
|
2
|
-
|
|
3
|
-
## What Changed
|
|
4
|
-
|
|
5
|
-
`JobScheduler.runGateAsync` previously surfaced `exit null` for every
|
|
6
|
-
legitimate non-zero gate skip, because it was reading `.status` (the
|
|
7
|
-
synchronous `spawnSync` shape) off an error from the asynchronous
|
|
8
|
-
`util.promisify(child_process.execFile)` call, which exposes exit codes
|
|
9
|
-
on `.code` instead. Every gate-skip event and activity-feed line ended
|
|
10
|
-
up coalesced to `null`, indistinguishable from a process crash or
|
|
11
|
-
signal kill.
|
|
12
|
-
|
|
13
|
-
### Before
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
Job "degradation-digest" skipped — gate returned exit null after 3 attempts
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Agents and operators reading this saw a null exit and assumed the gate
|
|
20
|
-
process had crashed or been killed. This triggered unnecessary
|
|
21
|
-
investigations — most recently, `degradation-digest` looking
|
|
22
|
-
"permanently gated (62 skips, 0 runs)" when the gate was in fact
|
|
23
|
-
exiting 1 by design (no degradation events to digest, nothing to run).
|
|
24
|
-
|
|
25
|
-
### After
|
|
26
|
-
|
|
27
|
-
The skip path now reads `.signal ?? .code ?? .status ?? null`, so:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
Job "degradation-digest" skipped — gate returned exit 1 after 3 attempts
|
|
31
|
-
Job "other-job" skipped — gate returned exit SIGKILL after 3 attempts
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Legitimate non-zero skips report their real exit code. Signal-terminated
|
|
35
|
-
gates surface their signal. `metadata.exitCode` on the `job_gate_skip`
|
|
36
|
-
event is now a `number | string | null` (was `number | null`); consumers
|
|
37
|
-
doing display or non-arithmetic comparisons continue to work unchanged.
|
|
38
|
-
|
|
39
|
-
No decision-point change. The gate still retries up to `gateRetries`
|
|
40
|
-
times, still records to the skip ledger, still emits the same event
|
|
41
|
-
shape. Only the exit-code field is now truthful.
|
|
42
|
-
|
|
43
|
-
## What to Tell Your User
|
|
44
|
-
|
|
45
|
-
Your activity feed will stop saying "gate returned exit null" for
|
|
46
|
-
healthy skips — it will show the real exit code instead, so a legitimate
|
|
47
|
-
skip stops looking like a crash. Nothing changes about when jobs run or
|
|
48
|
-
skip; we just stopped hiding why.
|
|
49
|
-
|
|
50
|
-
## Summary of New Capabilities
|
|
51
|
-
|
|
52
|
-
| Capability | How it shows up |
|
|
53
|
-
|-----------|-----------------|
|
|
54
|
-
| Truthful gate exit codes | Activity feed and `job_gate_skip` events show the real exit code (e.g. `exit 1`) or signal (e.g. `exit SIGKILL`), never `exit null` for non-zero gates |
|
|
55
|
-
| Easier triage of stuck jobs | A job that "keeps skipping" now shows WHY (exit 1 = intentional skip; exit SIGKILL = runtime kill), so operators know whether to investigate |
|
|
56
|
-
|
|
57
|
-
## Evidence
|
|
58
|
-
|
|
59
|
-
- `JobScheduler` unit tests: 35/35 passing before and after the change.
|
|
60
|
-
- Error shape captured in the feedback cluster
|
|
61
|
-
(`cluster-jobscheduler-reports-gate-exit-code-as-null-for-every-legiti`):
|
|
62
|
-
`err.code: 1`, `err.status: undefined` — confirming the root cause.
|
|
63
|
-
- Side-effects review:
|
|
64
|
-
`upgrades/side-effects/scheduler-gate-exit-code.md` covers
|
|
65
|
-
over/under-block (n/a — no block surface), level-of-abstraction fit
|
|
66
|
-
(right layer, pure diagnostic signal), signal-vs-authority compliance
|
|
67
|
-
(pure signal quality, no authority change), interactions,
|
|
68
|
-
external surfaces (activity-feed strings + `metadata.exitCode` type
|
|
69
|
-
widening), and rollback cost (one-line revert).
|
|
70
|
-
|
|
71
|
-
## Deployment Notes
|
|
72
|
-
|
|
73
|
-
No operator action required. Agents pick this up automatically on the
|
|
74
|
-
next AutoUpdater cycle.
|
|
75
|
-
|
|
76
|
-
## Rollback
|
|
77
|
-
|
|
78
|
-
Revert the single commit. `metadata.exitCode` returns to being a
|
|
79
|
-
`number | null`, activity feed returns to showing `exit null` for
|
|
80
|
-
legitimate non-zero skips. No schema or state-file changes.
|