instar 1.3.550 → 1.3.552
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/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +25 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +24 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +32 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/RemoteAckStore.d.ts +71 -0
- package/dist/core/RemoteAckStore.d.ts.map +1 -0
- package/dist/core/RemoteAckStore.js +144 -0
- package/dist/core/RemoteAckStore.js.map +1 -0
- package/dist/core/types.d.ts +24 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +2 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts +33 -0
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +75 -0
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +186 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/state-coherence-registry.json +12 -0
- package/src/scaffold/templates.ts +2 -0
- package/upgrades/1.3.551.md +23 -0
- package/upgrades/1.3.552.md +23 -0
- package/upgrades/side-effects/ws41-durable-ack.md +37 -0
- package/upgrades/side-effects/ws43-role-guard.md +40 -0
- package/upgrades/ws43-role-guard.eli16.md +47 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemoteAckStore — durable queue for operator-bound attention acks that target
|
|
3
|
+
* an attention item OWNED BY A DIFFERENT MACHINE (WS4.1 follow-up, CMT-1416).
|
|
4
|
+
*
|
|
5
|
+
* The problem this closes: when an operator acknowledges/resolves a pooled
|
|
6
|
+
* attention item whose owner is briefly offline, the ack PATCH is lost — the
|
|
7
|
+
* operator's intent evaporates and the item reappears OPEN on the owner's
|
|
8
|
+
* return. This store persists the ack INTENT (with the authenticated operator
|
|
9
|
+
* principal that performed it) so it survives the owner being dark; a drain
|
|
10
|
+
* tick + a boot sweep re-deliver it when the owner comes back.
|
|
11
|
+
*
|
|
12
|
+
* Design constraints (mirrors RemoteCloseAudit / PendingInboundStore):
|
|
13
|
+
* - Append-then-compact JSONL under logs/ — no DB dependency.
|
|
14
|
+
* - Each pending ack is idempotent on (itemId, targetMachineId): a re-ack of
|
|
15
|
+
* the same item just refreshes the intent, never stacks duplicates.
|
|
16
|
+
* - The operator principal is carried as data the OWNER revalidates at apply
|
|
17
|
+
* time — this store never authorizes anything, it only remembers intent.
|
|
18
|
+
* - Best-effort writes; a failed append/compact logs loudly (an unrecorded
|
|
19
|
+
* ack-intent deserves a trace) but never throws into the ack path.
|
|
20
|
+
*
|
|
21
|
+
* This file ships behind the dark gate multiMachine.seamlessness.ws41DurableAck:
|
|
22
|
+
* when off, the route that feeds it 503s and the store is never constructed, so
|
|
23
|
+
* a single-machine or flag-off agent is a strict no-op.
|
|
24
|
+
*/
|
|
25
|
+
export interface PendingRemoteAck {
|
|
26
|
+
/** Attention item id on the OWNING machine. */
|
|
27
|
+
itemId: string;
|
|
28
|
+
/** Registry lookup key for the owning machine (never a URL). */
|
|
29
|
+
targetMachineId: string;
|
|
30
|
+
/** Normalized attention status the operator chose (e.g. DONE, ACKNOWLEDGED). */
|
|
31
|
+
status: string;
|
|
32
|
+
/** Authenticated operator principal that performed the ack (data the owner
|
|
33
|
+
* revalidates — never trusted as authorization by this store). */
|
|
34
|
+
operatorUid: string;
|
|
35
|
+
/** Operator display name (for the owner's audit; not load-bearing). */
|
|
36
|
+
operatorDisplayName?: string;
|
|
37
|
+
/** When the operator's intent was first captured (ISO). */
|
|
38
|
+
enqueuedAt: string;
|
|
39
|
+
/** Delivery attempts so far (drain backoff / give-up bound). */
|
|
40
|
+
attempts: number;
|
|
41
|
+
/** Last delivery attempt outcome class, for observability. */
|
|
42
|
+
lastOutcome?: 'pending' | 'unreachable' | 'rejected' | 'stale-superseded';
|
|
43
|
+
}
|
|
44
|
+
export declare class RemoteAckStore {
|
|
45
|
+
private readonly log;
|
|
46
|
+
private readonly filePath;
|
|
47
|
+
/** Keyed on `${itemId}::${targetMachineId}` — idempotent intent. */
|
|
48
|
+
private readonly pending;
|
|
49
|
+
private loaded;
|
|
50
|
+
constructor(stateDir: string, log?: (msg: string) => void);
|
|
51
|
+
private key;
|
|
52
|
+
private ensureLoaded;
|
|
53
|
+
private append;
|
|
54
|
+
/**
|
|
55
|
+
* Record (or refresh) the operator's ack intent for an item owned elsewhere.
|
|
56
|
+
* Idempotent on (itemId, targetMachineId): a repeat ack updates status/principal
|
|
57
|
+
* and resets attempts rather than stacking a second pending row.
|
|
58
|
+
*/
|
|
59
|
+
enqueue(intent: Omit<PendingRemoteAck, 'enqueuedAt' | 'attempts' | 'lastOutcome'>): PendingRemoteAck;
|
|
60
|
+
/** Mark a delivery attempt's outcome (drain observability + backoff input). */
|
|
61
|
+
recordAttempt(itemId: string, targetMachineId: string, outcome: PendingRemoteAck['lastOutcome']): void;
|
|
62
|
+
/** Remove a delivered (or owner-rejected-as-stale) intent — terminal. */
|
|
63
|
+
resolve(itemId: string, targetMachineId: string): void;
|
|
64
|
+
/** All still-pending intents (for the drain tick / boot sweep / observability). */
|
|
65
|
+
list(): PendingRemoteAck[];
|
|
66
|
+
/** Pending intents targeting one machine (drain when that peer comes online). */
|
|
67
|
+
listForMachine(targetMachineId: string): PendingRemoteAck[];
|
|
68
|
+
/** Count of still-pending intents (cheap status read). */
|
|
69
|
+
get size(): number;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=RemoteAckStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RemoteAckStore.d.ts","sourceRoot":"","sources":["../../src/core/RemoteAckStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf;uEACmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,kBAAkB,CAAC;CAC3E;AAED,qBAAa,cAAc;IAQvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,MAAM,CAAS;gBAGrB,QAAQ,EAAE,MAAM,EACC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB;IAK3D,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,MAAM;IAgBd;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,UAAU,GAAG,aAAa,CAAC,GAAG,gBAAgB;IAepG,+EAA+E;IAC/E,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG,IAAI;IAUtG,yEAAyE;IACzE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI;IAgBtD,mFAAmF;IACnF,IAAI,IAAI,gBAAgB,EAAE;IAK1B,iFAAiF;IACjF,cAAc,CAAC,eAAe,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAI3D,0DAA0D;IAC1D,IAAI,IAAI,IAAI,MAAM,CAGjB;CACF"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemoteAckStore — durable queue for operator-bound attention acks that target
|
|
3
|
+
* an attention item OWNED BY A DIFFERENT MACHINE (WS4.1 follow-up, CMT-1416).
|
|
4
|
+
*
|
|
5
|
+
* The problem this closes: when an operator acknowledges/resolves a pooled
|
|
6
|
+
* attention item whose owner is briefly offline, the ack PATCH is lost — the
|
|
7
|
+
* operator's intent evaporates and the item reappears OPEN on the owner's
|
|
8
|
+
* return. This store persists the ack INTENT (with the authenticated operator
|
|
9
|
+
* principal that performed it) so it survives the owner being dark; a drain
|
|
10
|
+
* tick + a boot sweep re-deliver it when the owner comes back.
|
|
11
|
+
*
|
|
12
|
+
* Design constraints (mirrors RemoteCloseAudit / PendingInboundStore):
|
|
13
|
+
* - Append-then-compact JSONL under logs/ — no DB dependency.
|
|
14
|
+
* - Each pending ack is idempotent on (itemId, targetMachineId): a re-ack of
|
|
15
|
+
* the same item just refreshes the intent, never stacks duplicates.
|
|
16
|
+
* - The operator principal is carried as data the OWNER revalidates at apply
|
|
17
|
+
* time — this store never authorizes anything, it only remembers intent.
|
|
18
|
+
* - Best-effort writes; a failed append/compact logs loudly (an unrecorded
|
|
19
|
+
* ack-intent deserves a trace) but never throws into the ack path.
|
|
20
|
+
*
|
|
21
|
+
* This file ships behind the dark gate multiMachine.seamlessness.ws41DurableAck:
|
|
22
|
+
* when off, the route that feeds it 503s and the store is never constructed, so
|
|
23
|
+
* a single-machine or flag-off agent is a strict no-op.
|
|
24
|
+
*/
|
|
25
|
+
import fs from 'node:fs';
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
export class RemoteAckStore {
|
|
28
|
+
log;
|
|
29
|
+
filePath;
|
|
30
|
+
/** Keyed on `${itemId}::${targetMachineId}` — idempotent intent. */
|
|
31
|
+
pending = new Map();
|
|
32
|
+
loaded = false;
|
|
33
|
+
constructor(stateDir, log = console.log) {
|
|
34
|
+
this.log = log;
|
|
35
|
+
this.filePath = path.join(stateDir, '..', 'logs', 'remote-ack-queue.jsonl');
|
|
36
|
+
}
|
|
37
|
+
key(itemId, targetMachineId) {
|
|
38
|
+
return `${itemId}::${targetMachineId}`;
|
|
39
|
+
}
|
|
40
|
+
ensureLoaded() {
|
|
41
|
+
if (this.loaded)
|
|
42
|
+
return;
|
|
43
|
+
this.loaded = true;
|
|
44
|
+
try {
|
|
45
|
+
const raw = fs.readFileSync(this.filePath, 'utf-8');
|
|
46
|
+
for (const line of raw.split('\n')) {
|
|
47
|
+
const t = line.trim();
|
|
48
|
+
if (!t)
|
|
49
|
+
continue;
|
|
50
|
+
try {
|
|
51
|
+
const entry = JSON.parse(t);
|
|
52
|
+
const k = this.key(entry.itemId, entry.targetMachineId);
|
|
53
|
+
if (entry._deleted) {
|
|
54
|
+
this.pending.delete(k);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.pending.set(k, entry);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// @silent-fallback-ok — a torn last line (crash mid-append) is skipped;
|
|
62
|
+
// the rest of the durable log still loads.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// @silent-fallback-ok — absent file reads as empty queue (first run).
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
append(entry) {
|
|
71
|
+
try {
|
|
72
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
73
|
+
/* state-registry: remote-ack-queue */
|
|
74
|
+
fs.appendFileSync(this.filePath, JSON.stringify(entry) + '\n');
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
// Loud, not silent: a lost ack-intent reappears as a re-OPENed item to the
|
|
78
|
+
// operator later — worth a trace even when the log file is the failure.
|
|
79
|
+
this.log(`[remote-ack] QUEUE APPEND FAILED (intent held in memory only): ${err instanceof Error ? err.message : String(err)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Record (or refresh) the operator's ack intent for an item owned elsewhere.
|
|
84
|
+
* Idempotent on (itemId, targetMachineId): a repeat ack updates status/principal
|
|
85
|
+
* and resets attempts rather than stacking a second pending row.
|
|
86
|
+
*/
|
|
87
|
+
enqueue(intent) {
|
|
88
|
+
this.ensureLoaded();
|
|
89
|
+
const k = this.key(intent.itemId, intent.targetMachineId);
|
|
90
|
+
const existing = this.pending.get(k);
|
|
91
|
+
const entry = {
|
|
92
|
+
...intent,
|
|
93
|
+
enqueuedAt: existing?.enqueuedAt ?? new Date().toISOString(),
|
|
94
|
+
attempts: 0,
|
|
95
|
+
lastOutcome: 'pending',
|
|
96
|
+
};
|
|
97
|
+
this.pending.set(k, entry);
|
|
98
|
+
this.append(entry);
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
/** Mark a delivery attempt's outcome (drain observability + backoff input). */
|
|
102
|
+
recordAttempt(itemId, targetMachineId, outcome) {
|
|
103
|
+
this.ensureLoaded();
|
|
104
|
+
const k = this.key(itemId, targetMachineId);
|
|
105
|
+
const entry = this.pending.get(k);
|
|
106
|
+
if (!entry)
|
|
107
|
+
return;
|
|
108
|
+
entry.attempts += 1;
|
|
109
|
+
entry.lastOutcome = outcome;
|
|
110
|
+
this.append(entry);
|
|
111
|
+
}
|
|
112
|
+
/** Remove a delivered (or owner-rejected-as-stale) intent — terminal. */
|
|
113
|
+
resolve(itemId, targetMachineId) {
|
|
114
|
+
this.ensureLoaded();
|
|
115
|
+
const k = this.key(itemId, targetMachineId);
|
|
116
|
+
if (!this.pending.has(k))
|
|
117
|
+
return;
|
|
118
|
+
this.pending.delete(k);
|
|
119
|
+
this.append({
|
|
120
|
+
itemId,
|
|
121
|
+
targetMachineId,
|
|
122
|
+
status: '',
|
|
123
|
+
operatorUid: '',
|
|
124
|
+
enqueuedAt: new Date().toISOString(),
|
|
125
|
+
attempts: 0,
|
|
126
|
+
_deleted: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/** All still-pending intents (for the drain tick / boot sweep / observability). */
|
|
130
|
+
list() {
|
|
131
|
+
this.ensureLoaded();
|
|
132
|
+
return Array.from(this.pending.values());
|
|
133
|
+
}
|
|
134
|
+
/** Pending intents targeting one machine (drain when that peer comes online). */
|
|
135
|
+
listForMachine(targetMachineId) {
|
|
136
|
+
return this.list().filter((e) => e.targetMachineId === targetMachineId);
|
|
137
|
+
}
|
|
138
|
+
/** Count of still-pending intents (cheap status read). */
|
|
139
|
+
get size() {
|
|
140
|
+
this.ensureLoaded();
|
|
141
|
+
return this.pending.size;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=RemoteAckStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RemoteAckStore.js","sourceRoot":"","sources":["../../src/core/RemoteAckStore.ts"],"names":[],"mappings":"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"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -357,6 +357,18 @@ export interface JobDefinition {
|
|
|
357
357
|
* re-run must be a deliberate per-job choice. Older agents' job parsers
|
|
358
358
|
* ignore unknown fields (additive-safe). */
|
|
359
359
|
resumeOnReap?: boolean;
|
|
360
|
+
/** MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.3 role-guard-at-spawn — opt this job
|
|
361
|
+
* IN as a STATE-WRITING job. A state-writing job mutates shared/replicated
|
|
362
|
+
* state that only the writable owner (the lease-holder) may touch; it must
|
|
363
|
+
* NOT spawn on a read-only standby. When `multiMachine.seamlessness.ws43RoleGuard`
|
|
364
|
+
* is on and this machine does not hold the lease, the scheduler refuses the
|
|
365
|
+
* spawn at the spawn boundary (the TOCTOU re-check — a machine awake at boot
|
|
366
|
+
* can demote mid-run while its cron tasks keep firing). The writable owner's
|
|
367
|
+
* own scheduler runs the job (the cron fires on every machine; only the
|
|
368
|
+
* owner's pass clears the guard), so the refusal re-routes by construction.
|
|
369
|
+
* Default-absent = NOT state-writing → never guarded (additive-safe; older
|
|
370
|
+
* agents' parsers ignore the unknown field). */
|
|
371
|
+
writesState?: boolean;
|
|
360
372
|
/** Tags for filtering/grouping */
|
|
361
373
|
tags?: string[];
|
|
362
374
|
/** Telegram topic ID this job reports to (auto-created if not set) */
|
|
@@ -985,7 +997,7 @@ export interface RelationshipManagerConfig {
|
|
|
985
997
|
*/
|
|
986
998
|
intelligence?: IntelligenceProvider;
|
|
987
999
|
}
|
|
988
|
-
export type SkipReason = 'disabled' | 'paused' | 'quota' | 'memory-pressure' | 'capacity' | 'claimed' | 'machine-scope' | 'already-running' | 'gate';
|
|
1000
|
+
export type SkipReason = 'disabled' | 'paused' | 'quota' | 'memory-pressure' | 'capacity' | 'claimed' | 'machine-scope' | 'already-running' | 'role-guard' | 'gate';
|
|
989
1001
|
/**
|
|
990
1002
|
* Result of a canRunJob gate check. The callback may return a plain boolean
|
|
991
1003
|
* (legacy) or this richer form so the scheduler can log/track the actual
|
|
@@ -2010,6 +2022,17 @@ export interface MultiMachineConfig {
|
|
|
2010
2022
|
* config defaults (the gate decides) — a literal `false` would force-dark dev.
|
|
2011
2023
|
*/
|
|
2012
2024
|
ws44PoolLinks?: boolean;
|
|
2025
|
+
/**
|
|
2026
|
+
* WS4.3 role-guard-at-spawn (§WS4.3 / F8, F21; CMT-1416). When on, the
|
|
2027
|
+
* scheduler refuses to spawn a STATE-WRITING job (JobDefinition.writesState)
|
|
2028
|
+
* on a machine that does NOT hold the lease — the spawn-boundary TOCTOU
|
|
2029
|
+
* re-check that closes the window where a machine awake at boot demotes to a
|
|
2030
|
+
* read-only standby mid-run while its cron tasks keep firing. DEFAULT
|
|
2031
|
+
* (undefined/false) = strict no-op (byte-for-byte today's behavior). The
|
|
2032
|
+
* writable owner's own scheduler runs the job; a refusal raises ONE deduped
|
|
2033
|
+
* attention item. Single-machine agents always hold the lease → never fires.
|
|
2034
|
+
*/
|
|
2035
|
+
ws43RoleGuard?: boolean;
|
|
2013
2036
|
/**
|
|
2014
2037
|
* WS4.4 (f) load-shed: when the fronting machine is over this 1-min
|
|
2015
2038
|
* load-per-core threshold, holder-resolution serves the last-cached
|