@tiphys/kernel 0.0.0 → 0.1.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/AGENTS.md +611 -0
- package/LICENSE +202 -0
- package/assurance-modes.yaml +278 -0
- package/checklists/clean-room.yaml +325 -0
- package/checklists/env-failure-diagnosis.yaml +68 -0
- package/checklists/flake-playbook.yaml +68 -0
- package/checklists/hazard-review.yaml +144 -0
- package/checklists/plan-review.yaml +103 -0
- package/dist/bin/tiphys.d.ts +2 -0
- package/dist/bin/tiphys.js +14 -0
- package/dist/src/brief.d.ts +32 -0
- package/dist/src/brief.js +59 -0
- package/dist/src/checklists.d.ts +169 -0
- package/dist/src/checklists.js +310 -0
- package/dist/src/checks.d.ts +828 -0
- package/dist/src/checks.js +3314 -0
- package/dist/src/cli.d.ts +9 -0
- package/dist/src/cli.js +57 -0
- package/dist/src/commands/brief.d.ts +92 -0
- package/dist/src/commands/brief.js +320 -0
- package/dist/src/commands/checklist.d.ts +42 -0
- package/dist/src/commands/checklist.js +168 -0
- package/dist/src/commands/doctor.d.ts +35 -0
- package/dist/src/commands/doctor.js +605 -0
- package/dist/src/commands/gates.d.ts +9 -0
- package/dist/src/commands/gates.js +360 -0
- package/dist/src/commands/init.d.ts +30 -0
- package/dist/src/commands/init.js +128 -0
- package/dist/src/commands/lock.d.ts +1 -0
- package/dist/src/commands/lock.js +229 -0
- package/dist/src/commands/mode.d.ts +40 -0
- package/dist/src/commands/mode.js +134 -0
- package/dist/src/commands/plan.d.ts +20 -0
- package/dist/src/commands/plan.js +105 -0
- package/dist/src/commands/pool.d.ts +1 -0
- package/dist/src/commands/pool.js +128 -0
- package/dist/src/commands/spawn.d.ts +1 -0
- package/dist/src/commands/spawn.js +146 -0
- package/dist/src/commands/status.d.ts +19 -0
- package/dist/src/commands/status.js +120 -0
- package/dist/src/commands/teardown.d.ts +1 -0
- package/dist/src/commands/teardown.js +79 -0
- package/dist/src/commands/tuition.d.ts +21 -0
- package/dist/src/commands/tuition.js +218 -0
- package/dist/src/commands/validate.d.ts +78 -0
- package/dist/src/commands/validate.js +360 -0
- package/dist/src/commands/watch.d.ts +1 -0
- package/dist/src/commands/watch.js +172 -0
- package/dist/src/exec/env.d.ts +128 -0
- package/dist/src/exec/env.js +190 -0
- package/dist/src/fleet.d.ts +51 -0
- package/dist/src/fleet.js +80 -0
- package/dist/src/gates/adapters/http-json.d.ts +5 -0
- package/dist/src/gates/adapters/http-json.js +283 -0
- package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
- package/dist/src/gates/adapters/migrations-command.js +373 -0
- package/dist/src/gates/citations.d.ts +408 -0
- package/dist/src/gates/citations.js +1163 -0
- package/dist/src/gates/coverage.d.ts +284 -0
- package/dist/src/gates/coverage.js +701 -0
- package/dist/src/gates/credentials.d.ts +74 -0
- package/dist/src/gates/credentials.js +533 -0
- package/dist/src/gates/deploy.d.ts +1 -0
- package/dist/src/gates/deploy.js +33 -0
- package/dist/src/gates/manifest.d.ts +99 -0
- package/dist/src/gates/manifest.js +208 -0
- package/dist/src/gates/migrations.d.ts +1 -0
- package/dist/src/gates/migrations.js +36 -0
- package/dist/src/gates/pin.d.ts +114 -0
- package/dist/src/gates/pin.js +154 -0
- package/dist/src/gates/red-witness.d.ts +22 -0
- package/dist/src/gates/red-witness.js +390 -0
- package/dist/src/gates/release.d.ts +283 -0
- package/dist/src/gates/release.js +820 -0
- package/dist/src/gates/result.d.ts +116 -0
- package/dist/src/gates/result.js +91 -0
- package/dist/src/gates/run.d.ts +566 -0
- package/dist/src/gates/run.js +1536 -0
- package/dist/src/gates/schemas/citation-config.schema.json +59 -0
- package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
- package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
- package/dist/src/gates/schemas/gate-result.schema.json +160 -0
- package/dist/src/gates/schemas/phase-declaration.schema.json +42 -0
- package/dist/src/gates/schemas/release-record.schema.json +119 -0
- package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
- package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
- package/dist/src/gates/scope.d.ts +131 -0
- package/dist/src/gates/scope.js +1018 -0
- package/dist/src/gates/suite.d.ts +217 -0
- package/dist/src/gates/suite.js +927 -0
- package/dist/src/gates/validate.d.ts +121 -0
- package/dist/src/gates/validate.js +414 -0
- package/dist/src/hooks.d.ts +32 -0
- package/dist/src/hooks.js +62 -0
- package/dist/src/liveness.d.ts +321 -0
- package/dist/src/liveness.js +396 -0
- package/dist/src/lock.d.ts +178 -0
- package/dist/src/lock.js +500 -0
- package/dist/src/modes.d.ts +149 -0
- package/dist/src/modes.js +258 -0
- package/dist/src/path-identity.d.ts +2 -0
- package/dist/src/path-identity.js +10 -0
- package/dist/src/plan.d.ts +73 -0
- package/dist/src/plan.js +153 -0
- package/dist/src/pool.d.ts +130 -0
- package/dist/src/pool.js +721 -0
- package/dist/src/roles.d.ts +430 -0
- package/dist/src/roles.js +734 -0
- package/dist/src/spawn.d.ts +177 -0
- package/dist/src/spawn.js +332 -0
- package/dist/src/status.d.ts +91 -0
- package/dist/src/status.js +119 -0
- package/dist/src/task.d.ts +264 -0
- package/dist/src/task.js +305 -0
- package/dist/src/teardown.d.ts +32 -0
- package/dist/src/teardown.js +314 -0
- package/dist/src/tuition.d.ts +159 -0
- package/dist/src/tuition.js +311 -0
- package/dist/src/validate.d.ts +230 -0
- package/dist/src/validate.js +732 -0
- package/dist/src/version.d.ts +3 -0
- package/dist/src/version.js +38 -0
- package/dist/src/watcher.d.ts +275 -0
- package/dist/src/watcher.js +859 -0
- package/dist/src/witness/run.d.ts +274 -0
- package/dist/src/witness/run.js +1327 -0
- package/dist/src/witness/spec.d.ts +102 -0
- package/dist/src/witness/spec.js +253 -0
- package/dist/tsconfig.src.tsbuildinfo +1 -0
- package/gate-registry.yaml +390 -0
- package/gates.manifest.json +195 -0
- package/package.json +57 -3
- package/role-model-config.yaml +88 -0
- package/roles/README.md +128 -0
- package/roles/_shared-dispatch-contract.md +87 -0
- package/roles/adversarial-plan-reviewer.md +80 -0
- package/roles/clean-room-reviewer.md +140 -0
- package/roles/implementer.md +460 -0
- package/roles/investigator.md +138 -0
- package/roles/plan-writer.md +95 -0
- package/schemas/README.md +81 -0
- package/schemas/assurance-modes.schema.json +264 -0
- package/schemas/charter.schema.json +166 -0
- package/schemas/checklist.schema.json +114 -0
- package/schemas/decision-record.schema.json +88 -0
- package/schemas/final-report.schema.json +90 -0
- package/schemas/finding.schema.json +106 -0
- package/schemas/gate-registry.schema.json +260 -0
- package/schemas/mechanism-index.schema.json +94 -0
- package/schemas/plan.schema.json +300 -0
- package/schemas/report.schema.json +579 -0
- package/schemas/role-brief.schema.json +105 -0
- package/schemas/role-model-config.schema.json +90 -0
- package/schemas/status-line.schema.json +40 -0
- package/schemas/tuition.schema.json +191 -0
- package/schemas/verdict.schema.json +289 -0
- package/schemas/work-history.schema.json +183 -0
- package/templates/charter.example.yaml +54 -0
- package/templates/decision-record.example.yaml +27 -0
- package/templates/final-report.example.yaml +80 -0
- package/templates/plan.example.yaml +87 -0
- package/templates/report.example.yaml +236 -0
- package/templates/warnings.md +74 -0
- package/templates/work-history.example.yaml +185 -0
- package/tuition/README.md +76 -0
- package/tuition/T-001.yaml +48 -0
- package/tuition/T-002.yaml +51 -0
- package/tuition/T-003.yaml +100 -0
- package/tuition/T-004.yaml +52 -0
- package/tuition/T-005.yaml +72 -0
- package/tuition/T-006.yaml +81 -0
- package/tuition/T-007.yaml +56 -0
- package/tuition/T-008.yaml +111 -0
- package/tuition/T-009.yaml +50 -0
- package/tuition/T-015.yaml +36 -0
- package/tuition/T-016.yaml +36 -0
- package/tuition/T-017.yaml +46 -0
- package/tuition/T-018.yaml +84 -0
- package/tuition/T-021.yaml +40 -0
- package/tuition/T-022.yaml +36 -0
- package/tuition/mechanism-index.yaml +256 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/**
|
|
5
|
+
* Locate this package's own package.json by walking up from the running
|
|
6
|
+
* module's directory. The walk is depth-independent on purpose: the same
|
|
7
|
+
* source runs from src/ (native type stripping) and from dist/src/
|
|
8
|
+
* (compiled output), which sit at different depths below the package root.
|
|
9
|
+
*/
|
|
10
|
+
function findOwnPackageJson() {
|
|
11
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
for (;;) {
|
|
13
|
+
const candidate = join(dir, "package.json");
|
|
14
|
+
if (existsSync(candidate)) {
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
const parent = dirname(dir);
|
|
18
|
+
if (parent === dir) {
|
|
19
|
+
throw new Error("package.json not found above " + import.meta.url);
|
|
20
|
+
}
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function readOwnVersion() {
|
|
25
|
+
const raw = readFileSync(findOwnPackageJson(), "utf8");
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
if (typeof parsed !== "object" ||
|
|
28
|
+
parsed === null ||
|
|
29
|
+
typeof parsed.version !== "string") {
|
|
30
|
+
throw new Error("package.json has no string version field");
|
|
31
|
+
}
|
|
32
|
+
return parsed.version;
|
|
33
|
+
}
|
|
34
|
+
/** The version subcommand: print the package.json version, exit 0. */
|
|
35
|
+
export function printVersion() {
|
|
36
|
+
process.stdout.write(`${readOwnVersion()}\n`);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import type { Fleet } from "./fleet.ts";
|
|
2
|
+
import type { WatchCadence } from "./liveness.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The watcher (kernel plan v1, M1-P5 step 1; R-078, R-079; DR-0007;
|
|
5
|
+
* plan constraints C-1, C-2 and C-3).
|
|
6
|
+
*
|
|
7
|
+
* ONE CORE, TWO ENTRY MODES (DR-0007). Resident mode (tiphys watch) is a
|
|
8
|
+
* plain foreground process the caller owns. Single-pass mode (tiphys
|
|
9
|
+
* watch --once) performs exactly one evaluation of the same wake sources
|
|
10
|
+
* and exits, for external triggers on reclaimable substrates. Both modes
|
|
11
|
+
* run the same evaluation function over the same on-disk state; there is
|
|
12
|
+
* no adapter and no second implementation.
|
|
13
|
+
*
|
|
14
|
+
* C-3, and why there is nothing here to guard: the kernel never runs a
|
|
15
|
+
* watcher outside the caller's own foreground process. No flag exists
|
|
16
|
+
* that would make it outlive its caller, this module launches no child
|
|
17
|
+
* process of any kind, and arming is explicit: the operator starts it
|
|
18
|
+
* and the beacon is what proves it ran (FM-054, where about 30 minutes
|
|
19
|
+
* of supervision were lost to a shell that helpfully moved one out of
|
|
20
|
+
* sight).
|
|
21
|
+
*
|
|
22
|
+
* C-2: nothing here has any notion of a running program. Wake sources
|
|
23
|
+
* are FILES, currency comes from files, and the only clock question ever
|
|
24
|
+
* asked is "is this timestamp older than that one".
|
|
25
|
+
*
|
|
26
|
+
* C-1: task currency is read from tasks/<id>/meta.json status and the
|
|
27
|
+
* turn-end file, never from a log tail. state/last-wake.json is written
|
|
28
|
+
* and never read back by any code path in this kernel; deleting it or
|
|
29
|
+
* filling it with garbage changes no decision this module makes.
|
|
30
|
+
*
|
|
31
|
+
* REASON LINES (grammar harvested from firstmate, FM-002):
|
|
32
|
+
*
|
|
33
|
+
* signal <task-id> turn-end a turn-end file appeared for an open task
|
|
34
|
+
* stale <task-id> deadline an open task passed its executor.json
|
|
35
|
+
* deadline with no turn-end (PR-207)
|
|
36
|
+
* check <name> a one-shot check was requested through
|
|
37
|
+
* state/check-request
|
|
38
|
+
* heartbeat <n> the nth heartbeat since the last reset
|
|
39
|
+
*
|
|
40
|
+
* A wake is ONE line on stdout and then exit 0. The watcher surfaces
|
|
41
|
+
* wakes and never classifies, triages or absorbs them (FM-057:
|
|
42
|
+
* firstmate's absorb-triage grew to most of its 1126 lines because its
|
|
43
|
+
* completion signals were weak; a proposal to add classification here is
|
|
44
|
+
* a signal-design red flag, not a feature request).
|
|
45
|
+
*
|
|
46
|
+
* THE STALE ENUMERATION, and why it has exactly two entries in M1. The
|
|
47
|
+
* plan's grammar is "stale <what>" for "an open task whose worktree or
|
|
48
|
+
* meta is in a contradictory state, enumerated in the module docs", and
|
|
49
|
+
* PR-207 names the first entry:
|
|
50
|
+
*
|
|
51
|
+
* stale <task-id> deadline the executor record's deadline passed and
|
|
52
|
+
* no turn-end arrived (PR-207)
|
|
53
|
+
* stale <task-id> meta tasks/<id>/meta.json EXISTS and does not
|
|
54
|
+
* parse, so this module cannot establish
|
|
55
|
+
* whether the task is open (fix round,
|
|
56
|
+
* second reviewer finding 2)
|
|
57
|
+
*
|
|
58
|
+
* The second entry is the plan's own "meta is in a contradictory state"
|
|
59
|
+
* clause, not an invention: a record that exists and cannot be read is
|
|
60
|
+
* the one case where staying quiet would mean asserting "nothing to
|
|
61
|
+
* supervise here" from evidence this module does not have. A task
|
|
62
|
+
* directory with NO meta.json at all is a different thing (a spawn in
|
|
63
|
+
* progress or a rollback residue) and is not surfaced.
|
|
64
|
+
*
|
|
65
|
+
* Tuition T-002 asked for "task open, no turn-end, worktree dirty" as a
|
|
66
|
+
* wake condition; the declared-deadline half of that is what this module
|
|
67
|
+
* detects, from file evidence alone, and it is witnessed against a spawn
|
|
68
|
+
* that was genuinely stopped mid-payload rather than against a
|
|
69
|
+
* hand-built file state. The deadline-less half is not invented here,
|
|
70
|
+
* because the plan's own not-proven list states that abandonment of a
|
|
71
|
+
* task launched WITHOUT a deadline is not auto-detected in M1.
|
|
72
|
+
*
|
|
73
|
+
* WHAT AN EVALUATION IS, and why it matters for the beacon (PR-206,
|
|
74
|
+
* PR-009). An EVALUATION is a pass over the wake sources that also
|
|
75
|
+
* rewrites state/watcher.beacon: it happens when a resident watcher
|
|
76
|
+
* starts, on every heartbeat tick, whenever a wake is surfaced, and on
|
|
77
|
+
* every watch --once pass including a no-wake one. Between heartbeats a
|
|
78
|
+
* resident watcher SCANS its wake sources every poll interval (and
|
|
79
|
+
* immediately on a filesystem change); a scan that surfaces nothing
|
|
80
|
+
* writes nothing. That is what makes the beacon mean "supervision
|
|
81
|
+
* executed on schedule" rather than "a process is alive", and it is what
|
|
82
|
+
* puts the oldest healthy beacon age at one backoff cap plus one poll
|
|
83
|
+
* interval, which is the bound the guard's threshold has to clear
|
|
84
|
+
* (PR-009, enforced in src/liveness.ts).
|
|
85
|
+
*
|
|
86
|
+
* CADENCE LIVES ON DISK (FM-006, FM-045), never in process memory, so a
|
|
87
|
+
* resident restart and a single pass share one schedule:
|
|
88
|
+
* state/watcher.cadence.json holds {lastHeartbeatAt, backoffStreak}. On
|
|
89
|
+
* a virgin fleet the first evaluation initializes it to the current time
|
|
90
|
+
* and surfaces no heartbeat; the first heartbeat falls due one base
|
|
91
|
+
* interval later (PR-205).
|
|
92
|
+
*
|
|
93
|
+
* Turn-end currency (PR-204). A turn-end wake is surfaced at most once
|
|
94
|
+
* across both modes. state/watcher.seen.json records, per task, the
|
|
95
|
+
* identity of the last surfaced turn-end (size, modification time and a
|
|
96
|
+
* content signature, FM-005). The record is advanced under a claim file,
|
|
97
|
+
* written stage-then-rename and confirmed by reading it back, which is
|
|
98
|
+
* the discipline src/lock.ts established (it is mirrored rather than
|
|
99
|
+
* reused: the lock's primitive is lease-shaped and src/lock.ts is not in
|
|
100
|
+
* this phase's edit scope). The wake is appended to state/last-wake.json
|
|
101
|
+
* BEFORE the seen record advances (enqueue-before-suppress, FM-046: a
|
|
102
|
+
* stop between the two duplicates a wake rather than dropping it). Two
|
|
103
|
+
* passes racing on the same turn-end therefore resolve to exactly one
|
|
104
|
+
* surfacing; the loser reports no-wake.
|
|
105
|
+
*
|
|
106
|
+
* NO HEALTH FROM AN ABSENCE OF EVIDENCE (fix round, the property both
|
|
107
|
+
* reviewers' blocking findings share). Silence from this module means
|
|
108
|
+
* "there is nothing to surface", and it may only be said about states
|
|
109
|
+
* this module has POSITIVELY ESTABLISHED. Wherever it cannot establish
|
|
110
|
+
* one, the output is the loud one, never the quiet one:
|
|
111
|
+
*
|
|
112
|
+
* - a seen-state claim it could not take within the bounded wait is a
|
|
113
|
+
* STUCK CLAIM, not ordinary contention: nonzero exit, one reason
|
|
114
|
+
* line naming the file, and NO beacon write, so the guard sees
|
|
115
|
+
* supervision stop. This is src/lock.ts's rule, adopted verbatim
|
|
116
|
+
* rather than reinvented (see the note below).
|
|
117
|
+
* - a meta.json that exists and does not parse is surfaced as
|
|
118
|
+
* "stale <task-id> meta" rather than skipped.
|
|
119
|
+
* - any other raise ends the pass loudly (see the classification
|
|
120
|
+
* below).
|
|
121
|
+
*
|
|
122
|
+
* WHERE THE STUCK-CLAIM RULE COMES FROM, recorded because it should have
|
|
123
|
+
* reached this module the first time and did not: src/lock.ts's
|
|
124
|
+
* applyLeaseMutation has used the identical O_EXCL claim-file pattern
|
|
125
|
+
* since M1-P3, and its module doc (src/lock.ts, "No steal protocol
|
|
126
|
+
* exists on purpose (FM-058)") states the rule this module now follows:
|
|
127
|
+
* a claim file left behind by a mutation that stopped makes later
|
|
128
|
+
* mutations FAIL LOUDLY after a bounded wait, naming the file for manual
|
|
129
|
+
* removal, and no code ever breaks someone else's claim. That behavior
|
|
130
|
+
* was established by M1-P3's CR-204 after the U-2 race investigation
|
|
131
|
+
* (delivery/verification/u2-race-flake-investigation.md,
|
|
132
|
+
* delivery/review/clean-room-m1-p3.md). The first draft of this module
|
|
133
|
+
* copied the mechanism and dropped the loudness, which the second
|
|
134
|
+
* M1-P5 reviewer reproduced as a permanent, silent loss of every future
|
|
135
|
+
* turn-end wake. Anyone adding a third claim-file user should read
|
|
136
|
+
* src/lock.ts first.
|
|
137
|
+
*
|
|
138
|
+
* HOW A RAISED ERROR IS CLASSIFIED, decided once and applied
|
|
139
|
+
* structurally, because this module does filesystem work in a loop and
|
|
140
|
+
* the previous phase's two worst defects were raises walking past
|
|
141
|
+
* handlers that only understood returned failures (M1-P4 F-1 and F-2):
|
|
142
|
+
*
|
|
143
|
+
* - A file that is ABSENT is not an error. Task directories appear and
|
|
144
|
+
* vanish under a concurrent teardown, so every read of task state
|
|
145
|
+
* goes through a helper that turns ENOENT into "not there" and
|
|
146
|
+
* rethrows everything else.
|
|
147
|
+
* - Any OTHER raise ends the pass with a reason line and a nonzero
|
|
148
|
+
* exit. The watcher stops loudly instead of looping blind: a stopped
|
|
149
|
+
* watcher stops advancing the beacon, and the liveness guard then
|
|
150
|
+
* tells the operator on the next spawn, teardown or doctor.
|
|
151
|
+
* - The whole pass is wrapped by runStep (src/task.ts), so this holds
|
|
152
|
+
* for every site in it without depending on remembering it at each.
|
|
153
|
+
*/
|
|
154
|
+
/** Exit code of a --once pass that found nothing actionable. */
|
|
155
|
+
export declare const NO_WAKE_EXIT = 3;
|
|
156
|
+
/** Wake-source and cadence state files, all under the fleet's state/. */
|
|
157
|
+
export declare const CADENCE_STATE_FILE: string;
|
|
158
|
+
export declare const SEEN_STATE_FILE: string;
|
|
159
|
+
export declare const LAST_WAKE_FILE: string;
|
|
160
|
+
export declare const CHECK_REQUEST_FILE: string;
|
|
161
|
+
export declare function cadencePath(fleet: Fleet): string;
|
|
162
|
+
export declare function seenPath(fleet: Fleet): string;
|
|
163
|
+
export declare function lastWakePath(fleet: Fleet): string;
|
|
164
|
+
export declare function checkRequestPath(fleet: Fleet): string;
|
|
165
|
+
/** Identity of a turn-end file (FM-005), the seen-state's unit. */
|
|
166
|
+
export interface SignalIdentity {
|
|
167
|
+
size: number;
|
|
168
|
+
mtimeMs: number;
|
|
169
|
+
signature: string;
|
|
170
|
+
}
|
|
171
|
+
export type SeenState = Record<string, SignalIdentity>;
|
|
172
|
+
export declare function sameIdentity(a: SignalIdentity, b: SignalIdentity): boolean;
|
|
173
|
+
export declare function readSeenState(fleet: Fleet): SeenState;
|
|
174
|
+
/** Cadence state (JSON per DR-0006, plan decision D-3). */
|
|
175
|
+
export interface CadenceState {
|
|
176
|
+
lastHeartbeatAt: string;
|
|
177
|
+
backoffStreak: number;
|
|
178
|
+
}
|
|
179
|
+
export declare function readCadenceState(fleet: Fleet): CadenceState | undefined;
|
|
180
|
+
export declare function writeCadenceState(fleet: Fleet, state: CadenceState): void;
|
|
181
|
+
/** The doubling heartbeat interval, capped (FM-044). */
|
|
182
|
+
export declare function intervalMsFor(streak: number, cadence: WatchCadence): number;
|
|
183
|
+
/** When the next heartbeat falls due, from on-disk state alone. */
|
|
184
|
+
export declare function nextHeartbeatDueMs(state: CadenceState, cadence: WatchCadence): number;
|
|
185
|
+
/**
|
|
186
|
+
* Rewrite the beacon. The timestamp STRICTLY increases even when two
|
|
187
|
+
* evaluations land in the same millisecond, because criterion 8 turns on
|
|
188
|
+
* a no-wake pass advancing it and "advanced" has to mean something a
|
|
189
|
+
* reader can compare.
|
|
190
|
+
*/
|
|
191
|
+
export declare function writeBeacon(fleet: Fleet, nowMs: number, streak: number, cadence: WatchCadence): number;
|
|
192
|
+
/** A surfaced wake, or the heartbeat that stands in for one. */
|
|
193
|
+
export type Wake = {
|
|
194
|
+
kind: "signal";
|
|
195
|
+
taskId: string;
|
|
196
|
+
event: "turn-end";
|
|
197
|
+
identity: SignalIdentity;
|
|
198
|
+
} | {
|
|
199
|
+
kind: "stale";
|
|
200
|
+
taskId: string;
|
|
201
|
+
what: "deadline" | "meta";
|
|
202
|
+
} | {
|
|
203
|
+
kind: "check";
|
|
204
|
+
name: string;
|
|
205
|
+
};
|
|
206
|
+
export declare function wakeLine(wake: Wake): string;
|
|
207
|
+
export declare function heartbeatLine(n: number): string;
|
|
208
|
+
export type ScanResult = {
|
|
209
|
+
ok: true;
|
|
210
|
+
wake: Wake | undefined;
|
|
211
|
+
} | {
|
|
212
|
+
ok: false;
|
|
213
|
+
reason: string;
|
|
214
|
+
};
|
|
215
|
+
/** Scan, with the module's raise classification applied structurally. */
|
|
216
|
+
export declare function scanWakeSources(fleet: Fleet, nowMs: number): ScanResult;
|
|
217
|
+
export type SurfaceResult = {
|
|
218
|
+
kind: "surfaced";
|
|
219
|
+
line: string;
|
|
220
|
+
} | {
|
|
221
|
+
kind: "lost";
|
|
222
|
+
} | {
|
|
223
|
+
kind: "stuck";
|
|
224
|
+
reason: string;
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Surface a wake: make it durable, suppress repeats where the plan says
|
|
228
|
+
* repeats are wrong, and return the line to print. "lost" means another
|
|
229
|
+
* pass owns this wake and this one must report no-wake; "stuck" means
|
|
230
|
+
* this pass could not establish anything and must say so out loud.
|
|
231
|
+
*/
|
|
232
|
+
export declare function surfaceWake(fleet: Fleet, wake: Wake, nowMs: number): Promise<SurfaceResult>;
|
|
233
|
+
export interface WatchOptions {
|
|
234
|
+
cadence: WatchCadence;
|
|
235
|
+
/**
|
|
236
|
+
* Resident mode only: exit with a heartbeat line after this many
|
|
237
|
+
* heartbeats HAVE TICKED IN THIS RUN. It is a bound on the run, not a
|
|
238
|
+
* position in the schedule, so a bounded run behaves the same way on a
|
|
239
|
+
* fleet that has been supervised for hours as on a virgin one. The
|
|
240
|
+
* schedule itself stays on disk (FM-006), which is why the number the
|
|
241
|
+
* line reports is the cadence ordinal and can be larger than this
|
|
242
|
+
* bound.
|
|
243
|
+
*/
|
|
244
|
+
maxHeartbeats: number | undefined;
|
|
245
|
+
/** Injectable clock; tests use the real one, this is for determinism. */
|
|
246
|
+
now?: () => number;
|
|
247
|
+
}
|
|
248
|
+
export interface PassOutcome {
|
|
249
|
+
code: number;
|
|
250
|
+
/** stdout line, without its newline; empty means print nothing. */
|
|
251
|
+
line: string;
|
|
252
|
+
/** stderr reason for a nonzero, non-no-wake outcome. */
|
|
253
|
+
reason?: string;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* A heartbeat evaluation: advance the streak, record it, rewrite the
|
|
257
|
+
* beacon. Returns the heartbeat ordinal since the last cadence reset,
|
|
258
|
+
* which is the n in "heartbeat <n>".
|
|
259
|
+
*/
|
|
260
|
+
export declare function heartbeatTick(fleet: Fleet, state: CadenceState, cadence: WatchCadence, nowMs: number): number;
|
|
261
|
+
/**
|
|
262
|
+
* Single-pass mode: exactly one evaluation, then exit. A heartbeat that
|
|
263
|
+
* is due is surfaced (this pass may be the only supervision this fleet
|
|
264
|
+
* gets), and a pass with nothing actionable prints nothing and exits
|
|
265
|
+
* with the documented no-wake code. The beacon is rewritten either way
|
|
266
|
+
* (PR-206).
|
|
267
|
+
*/
|
|
268
|
+
export declare function runOnce(fleet: Fleet, options: WatchOptions): Promise<PassOutcome>;
|
|
269
|
+
/**
|
|
270
|
+
* Resident mode: a plain foreground loop the caller owns. It evaluates
|
|
271
|
+
* once at startup (so an already-pending wake is surfaced immediately
|
|
272
|
+
* and the beacon proves arming), then alternates cheap scans at the poll
|
|
273
|
+
* interval with heartbeat evaluations on the backoff schedule.
|
|
274
|
+
*/
|
|
275
|
+
export declare function runResident(fleet: Fleet, options: WatchOptions): Promise<PassOutcome>;
|