instar 0.28.102 → 0.28.103
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/.claude/hooks/instar/before-prompt-recall.js +90 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/jobMigrate.d.ts +46 -0
- package/dist/commands/jobMigrate.d.ts.map +1 -0
- package/dist/commands/jobMigrate.js +292 -0
- package/dist/commands/jobMigrate.js.map +1 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +88 -42
- package/dist/commands/server.js.map +1 -1
- package/dist/core/BackupManager.d.ts +27 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +56 -1
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/GitStateManager.d.ts +8 -0
- package/dist/core/GitStateManager.d.ts.map +1 -1
- package/dist/core/GitStateManager.js +31 -1
- package/dist/core/GitStateManager.js.map +1 -1
- package/dist/core/MigratorStepEngine.d.ts +170 -0
- package/dist/core/MigratorStepEngine.d.ts.map +1 -0
- package/dist/core/MigratorStepEngine.js +352 -0
- package/dist/core/MigratorStepEngine.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +76 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +252 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PreCompactionFlush.d.ts +101 -0
- package/dist/core/PreCompactionFlush.d.ts.map +1 -0
- package/dist/core/PreCompactionFlush.js +282 -0
- package/dist/core/PreCompactionFlush.js.map +1 -0
- package/dist/core/PromptBuildRecall.d.ts +97 -0
- package/dist/core/PromptBuildRecall.d.ts.map +1 -0
- package/dist/core/PromptBuildRecall.js +152 -0
- package/dist/core/PromptBuildRecall.js.map +1 -0
- package/dist/core/selectIntelligenceProvider.d.ts +41 -0
- package/dist/core/selectIntelligenceProvider.d.ts.map +1 -0
- package/dist/core/selectIntelligenceProvider.js +89 -0
- package/dist/core/selectIntelligenceProvider.js.map +1 -0
- package/dist/lifeline/ServerSupervisor.d.ts +183 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +443 -0
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/memory/NativeModuleHealer.d.ts +89 -0
- package/dist/memory/NativeModuleHealer.d.ts.map +1 -1
- package/dist/memory/NativeModuleHealer.js +371 -1
- package/dist/memory/NativeModuleHealer.js.map +1 -1
- package/dist/memory/SemanticMemory.d.ts +131 -0
- package/dist/memory/SemanticMemory.d.ts.map +1 -1
- package/dist/memory/SemanticMemory.js +315 -0
- package/dist/memory/SemanticMemory.js.map +1 -1
- package/dist/messaging/DeliveryRetryManager.d.ts +60 -0
- package/dist/messaging/DeliveryRetryManager.d.ts.map +1 -1
- package/dist/messaging/DeliveryRetryManager.js +104 -0
- package/dist/messaging/DeliveryRetryManager.js.map +1 -1
- package/dist/monitoring/DegradationReporter.d.ts +164 -0
- package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
- package/dist/monitoring/DegradationReporter.js +309 -1
- package/dist/monitoring/DegradationReporter.js.map +1 -1
- package/dist/monitoring/ErrorCodeExtractor.d.ts +69 -0
- package/dist/monitoring/ErrorCodeExtractor.d.ts.map +1 -0
- package/dist/monitoring/ErrorCodeExtractor.js +131 -0
- package/dist/monitoring/ErrorCodeExtractor.js.map +1 -0
- package/dist/monitoring/PromptGate.d.ts +34 -0
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +81 -1
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/monitoring/Redactor.d.ts +58 -0
- package/dist/monitoring/Redactor.d.ts.map +1 -0
- package/dist/monitoring/Redactor.js +143 -0
- package/dist/monitoring/Redactor.js.map +1 -0
- package/dist/monitoring/probes/LifelineProbe.d.ts +7 -0
- package/dist/monitoring/probes/LifelineProbe.d.ts.map +1 -1
- package/dist/monitoring/probes/LifelineProbe.js +7 -0
- package/dist/monitoring/probes/LifelineProbe.js.map +1 -1
- package/dist/monitoring/probes/__shared.d.ts +50 -0
- package/dist/monitoring/probes/__shared.d.ts.map +1 -0
- package/dist/monitoring/probes/__shared.js +51 -0
- package/dist/monitoring/probes/__shared.js.map +1 -0
- package/dist/remediation/IntentJournal.d.ts +64 -0
- package/dist/remediation/IntentJournal.d.ts.map +1 -0
- package/dist/remediation/IntentJournal.js +118 -0
- package/dist/remediation/IntentJournal.js.map +1 -0
- package/dist/remediation/MachineLock.d.ts +137 -0
- package/dist/remediation/MachineLock.d.ts.map +1 -0
- package/dist/remediation/MachineLock.js +314 -0
- package/dist/remediation/MachineLock.js.map +1 -0
- package/dist/remediation/PrimaryAggregatorLease.d.ts +205 -0
- package/dist/remediation/PrimaryAggregatorLease.d.ts.map +1 -0
- package/dist/remediation/PrimaryAggregatorLease.js +450 -0
- package/dist/remediation/PrimaryAggregatorLease.js.map +1 -0
- package/dist/remediation/RemediationContext.d.ts +63 -0
- package/dist/remediation/RemediationContext.d.ts.map +1 -0
- package/dist/remediation/RemediationContext.js +109 -0
- package/dist/remediation/RemediationContext.js.map +1 -0
- package/dist/remediation/RemediationKeyVault.d.ts +123 -0
- package/dist/remediation/RemediationKeyVault.d.ts.map +1 -0
- package/dist/remediation/RemediationKeyVault.js +564 -0
- package/dist/remediation/RemediationKeyVault.js.map +1 -0
- package/dist/remediation/Remediator.d.ts +288 -0
- package/dist/remediation/Remediator.d.ts.map +1 -0
- package/dist/remediation/Remediator.js +520 -0
- package/dist/remediation/Remediator.js.map +1 -0
- package/dist/remediation/RemediatorBootstrap.d.ts +141 -0
- package/dist/remediation/RemediatorBootstrap.d.ts.map +1 -0
- package/dist/remediation/RemediatorBootstrap.js +255 -0
- package/dist/remediation/RemediatorBootstrap.js.map +1 -0
- package/dist/remediation/TrustElevationSource.d.ts +153 -0
- package/dist/remediation/TrustElevationSource.d.ts.map +1 -0
- package/dist/remediation/TrustElevationSource.js +208 -0
- package/dist/remediation/TrustElevationSource.js.map +1 -0
- package/dist/remediation/audit/AuditProjection.d.ts +37 -0
- package/dist/remediation/audit/AuditProjection.d.ts.map +1 -0
- package/dist/remediation/audit/AuditProjection.js +118 -0
- package/dist/remediation/audit/AuditProjection.js.map +1 -0
- package/dist/remediation/audit/AuditWriter.d.ts +79 -0
- package/dist/remediation/audit/AuditWriter.d.ts.map +1 -0
- package/dist/remediation/audit/AuditWriter.js +132 -0
- package/dist/remediation/audit/AuditWriter.js.map +1 -0
- package/dist/remediation/channels/CliApprovalChannel.d.ts +37 -0
- package/dist/remediation/channels/CliApprovalChannel.d.ts.map +1 -0
- package/dist/remediation/channels/CliApprovalChannel.js +45 -0
- package/dist/remediation/channels/CliApprovalChannel.js.map +1 -0
- package/dist/remediation/channels/TelegramApprovalChannel.d.ts +37 -0
- package/dist/remediation/channels/TelegramApprovalChannel.d.ts.map +1 -0
- package/dist/remediation/channels/TelegramApprovalChannel.js +45 -0
- package/dist/remediation/channels/TelegramApprovalChannel.js.map +1 -0
- package/dist/remediation/runbooks/db-corruption.d.ts +86 -0
- package/dist/remediation/runbooks/db-corruption.d.ts.map +1 -0
- package/dist/remediation/runbooks/db-corruption.js +190 -0
- package/dist/remediation/runbooks/db-corruption.js.map +1 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.d.ts +108 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.d.ts.map +1 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.js +172 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.js.map +1 -0
- package/dist/remediation/runbooks/node-abi-mismatch.d.ts +70 -0
- package/dist/remediation/runbooks/node-abi-mismatch.d.ts.map +1 -0
- package/dist/remediation/runbooks/node-abi-mismatch.js +216 -0
- package/dist/remediation/runbooks/node-abi-mismatch.js.map +1 -0
- package/dist/remediation/runbooks/supervisor-preflight.d.ts +114 -0
- package/dist/remediation/runbooks/supervisor-preflight.d.ts.map +1 -0
- package/dist/remediation/runbooks/supervisor-preflight.js +299 -0
- package/dist/remediation/runbooks/supervisor-preflight.js.map +1 -0
- package/dist/scheduler/AgentMdAtomicSave.d.ts +85 -0
- package/dist/scheduler/AgentMdAtomicSave.d.ts.map +1 -0
- package/dist/scheduler/AgentMdAtomicSave.js +169 -0
- package/dist/scheduler/AgentMdAtomicSave.js.map +1 -0
- package/dist/scheduler/AgentMdReconcile.d.ts +62 -0
- package/dist/scheduler/AgentMdReconcile.d.ts.map +1 -0
- package/dist/scheduler/AgentMdReconcile.js +212 -0
- package/dist/scheduler/AgentMdReconcile.js.map +1 -0
- package/dist/scheduler/DisabledBodyDrift.d.ts +70 -0
- package/dist/scheduler/DisabledBodyDrift.d.ts.map +1 -0
- package/dist/scheduler/DisabledBodyDrift.js +149 -0
- package/dist/scheduler/DisabledBodyDrift.js.map +1 -0
- package/dist/scheduler/InstallBuiltinJobs.d.ts +53 -0
- package/dist/scheduler/InstallBuiltinJobs.d.ts.map +1 -0
- package/dist/scheduler/InstallBuiltinJobs.js +212 -0
- package/dist/scheduler/InstallBuiltinJobs.js.map +1 -0
- package/dist/scheduler/JobLoader.d.ts.map +1 -1
- package/dist/scheduler/JobLoader.js +48 -0
- package/dist/scheduler/JobLoader.js.map +1 -1
- package/dist/scheduler/MigrationInvariants.d.ts +62 -0
- package/dist/scheduler/MigrationInvariants.d.ts.map +1 -0
- package/dist/scheduler/MigrationInvariants.js +215 -0
- package/dist/scheduler/MigrationInvariants.js.map +1 -0
- package/dist/scheduler/MigrationLedger.d.ts +84 -0
- package/dist/scheduler/MigrationLedger.d.ts.map +1 -0
- package/dist/scheduler/MigrationLedger.js +114 -0
- package/dist/scheduler/MigrationLedger.js.map +1 -0
- package/dist/server/fileRoutes.d.ts.map +1 -1
- package/dist/server/fileRoutes.js +7 -0
- package/dist/server/fileRoutes.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +141 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +3 -2
- package/scripts/classify-default-drift.mjs +238 -0
- package/scripts/eli16-overview-check.mjs +85 -0
- package/scripts/instar-dev-precommit.js +60 -1
- package/scripts/lint-degradation-emit-sites.js +116 -0
- package/scripts/lint-no-direct-destructive.js +12 -0
- package/scripts/protect-migration-guarantee.js +61 -0
- package/scripts/regen-default-job-templates.mjs +111 -0
- package/scripts/verify-runbook-pr-signature.js +366 -0
- package/src/data/builtin-manifest.json +93 -93
- package/src/scaffold/bootstrap.ts +138 -0
- package/src/scaffold/keys/instar-release-pub.pem +3 -0
- package/src/scaffold/templates/jobs/instar/.gitkeep +0 -0
- package/src/scaffold/templates/jobs/instar/commitment-detection.md +28 -0
- package/src/scaffold/templates/jobs/instar/evolution-overdue-check.md +28 -0
- package/src/scaffold/templates/jobs/instar/evolution-proposal-evaluate.md +30 -0
- package/src/scaffold/templates/jobs/instar/evolution-proposal-implement.md +25 -0
- package/src/scaffold/templates/jobs/instar/health-check.md +14 -0
- package/src/scaffold/templates/jobs/instar/identity-review.md +42 -0
- package/src/scaffold/templates/jobs/instar/insight-harvest.md +31 -0
- package/src/scaffold/templates/jobs/instar/overseer-development.md +23 -0
- package/src/scaffold/templates/jobs/instar/overseer-guardian.md +28 -0
- package/src/scaffold/templates/jobs/instar/overseer-infrastructure.md +26 -0
- package/src/scaffold/templates/jobs/instar/overseer-learning.md +28 -0
- package/src/scaffold/templates/jobs/instar/overseer-maintenance.md +27 -0
- package/src/scaffold/templates/jobs/instar/reflection-trigger.md +40 -0
- package/src/scaffold/templates/jobs/instar/relationship-maintenance.md +16 -0
- package/src/scaffold/templates.ts +1616 -0
- package/upgrades/0.28.102.md +6 -7
- package/upgrades/0.28.103.md +471 -0
- package/upgrades/side-effects/a47-primary-aggregator-lease.md +82 -0
- package/upgrades/side-effects/agentmd-atomic-save.md +66 -0
- package/upgrades/side-effects/agentmd-loader-200jobs-perf-benchmark.md +50 -0
- package/upgrades/side-effects/agentmd-reconcile-boot-lifecycle.md +66 -0
- package/upgrades/side-effects/api-safety-guard.md +80 -0
- package/upgrades/side-effects/c1-runbook-pr-gate.md +83 -0
- package/upgrades/side-effects/disabled-body-drift-detection.md +59 -0
- package/upgrades/side-effects/drift-classifier-release-time.md +71 -0
- package/upgrades/side-effects/eli16-overview-required-gate.md +77 -0
- package/upgrades/side-effects/f1-remediation-key-vault.md +195 -0
- package/upgrades/side-effects/f2-redactor-errorcode-extractor.md +130 -0
- package/upgrades/side-effects/f3-degradation-reporter-shim.md +105 -0
- package/upgrades/side-effects/f4-machine-locks-intent-journal-audit.md +117 -0
- package/upgrades/side-effects/f5-trust-elevation-source.md +258 -0
- package/upgrades/side-effects/f6-supervisor-handshake.md +234 -0
- package/upgrades/side-effects/f7-post-update-migrator-atomic-step.md +129 -0
- package/upgrades/side-effects/f8-remediator-skeleton.md +153 -0
- package/upgrades/side-effects/f8-rest-enforcement.md +72 -0
- package/upgrades/side-effects/file-viewer-jobs-instar-never-editable.md +41 -0
- package/upgrades/side-effects/frontmatter-interpolation-breakout-test.md +48 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-2.md +78 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-3.md +76 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-4-endpoints.md +62 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-5.md +67 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-6.md +53 -0
- package/upgrades/side-effects/jobs-endpoints-auth-verification.md +52 -0
- package/upgrades/side-effects/migration-guarantee-suite.md +61 -0
- package/upgrades/side-effects/migration-runtime-invariant-gate.md +59 -0
- package/upgrades/side-effects/migration-telemetry-invariant-8.md +68 -0
- package/upgrades/side-effects/npm-pack-templates-smoke.md +51 -0
- package/upgrades/side-effects/openclaw-import-before-prompt-build.md +84 -0
- package/upgrades/side-effects/openclaw-import-pre-compaction-flush.md +85 -0
- package/upgrades/side-effects/prompt-gate-no-prompt-cache.md +258 -0
- package/upgrades/side-effects/tier2-degradation-reporter-live-wire.md +146 -0
- package/upgrades/side-effects/w1-node-abi-mismatch-runbook.md +154 -0
- package/upgrades/side-effects/w2-supervisor-preflight-runbook.md +176 -0
- package/upgrades/side-effects/w3-messaging-delivery-failed-runbook.md +185 -0
- package/upgrades/side-effects/w4-db-corruption-runbook.md +114 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* supervisor-preflight — the W-2 `ApprovedRunbook` wrapping
|
|
3
|
+
* `ServerSupervisor.preflightSelfHeal` per spec A34 (composed-multi-step
|
|
4
|
+
* surface alignment).
|
|
5
|
+
*
|
|
6
|
+
* SELF-HEALING-REMEDIATOR-V2-SPEC §A6 (errorCode provenance — structured
|
|
7
|
+
* sources only), §A9 (verify asserts durability not just liveness), §A21
|
|
8
|
+
* (verify probe error → verify-inconclusive, distinct from verify-failed),
|
|
9
|
+
* §A34 (W-2 is ONE runbook composing six heal steps; verify produces a
|
|
10
|
+
* SINGLE durable-state check after all steps), §A36 (essential requires
|
|
11
|
+
* blastRadius='machine'), §A57 Tier-2 (W-2 ships after F-5, F-6, F-7,
|
|
12
|
+
* F-8-rest are on main).
|
|
13
|
+
*
|
|
14
|
+
* Surface: `ServerSupervisor.invokeFromRemediator(ctx)` (added by W-2).
|
|
15
|
+
* The legacy in-line `preflightSelfHeal()` call inside `spawnServer()`
|
|
16
|
+
* stays unchanged as the boot-path safety net; this runbook is the
|
|
17
|
+
* parallel, Remediator-orchestrated path.
|
|
18
|
+
*
|
|
19
|
+
* Match contract:
|
|
20
|
+
* eventPrefilter.errorCode = ['BIND_FAILURE', 'CRASH_LOOP',
|
|
21
|
+
* 'SUPERVISOR_DEGRADED']
|
|
22
|
+
* eventPrefilter.provenance = ['native-binding', 'subsystem-explicit']
|
|
23
|
+
*
|
|
24
|
+
* `'free-text'` is intentionally NOT in the prefilter — §A6 mandates that
|
|
25
|
+
* runbooks only fire on structured-provenance events. The match() callback
|
|
26
|
+
* narrows further to lifeline/server subsystems so a same-errorCode event
|
|
27
|
+
* about an unrelated subsystem (e.g., memory) routes to no-matching-runbook.
|
|
28
|
+
*
|
|
29
|
+
* Verify (§A9):
|
|
30
|
+
* The verify step asserts the durable lifeline state.json marker exists
|
|
31
|
+
* and was written recently (post-restart). Clean marker → verified-healthy.
|
|
32
|
+
* Missing or corrupt marker → verify-failed. Probe path error (filesystem
|
|
33
|
+
* I/O throws) → verify-inconclusive per §A21 — distinct from verify-failed.
|
|
34
|
+
*
|
|
35
|
+
* essential:
|
|
36
|
+
* This runbook is `essential: true`. A wedged supervisor (bind-failure
|
|
37
|
+
* crash loop, shadow-install missing, better-sqlite3 ABI mismatch,
|
|
38
|
+
* merged-conflict settings.json) DoSes every server-mediated capability:
|
|
39
|
+
* Telegram relay, dashboard, jobs scheduler, threadline. Without the
|
|
40
|
+
* heal, the agent is offline. blastRadius is 'machine' (preflight
|
|
41
|
+
* touches shadow-install/node_modules, the node symlink, the git tree,
|
|
42
|
+
* and the lifeline state directory), which satisfies §A36's validator.
|
|
43
|
+
*
|
|
44
|
+
* A15 partial-upgrade rule (Tier-2 build-acceleration carve-out):
|
|
45
|
+
* F-6 (the supervisor handshake the W-2 runbook depends on) merged on
|
|
46
|
+
* 2026-05-13. The 7-day lag rule applies to PRODUCTION CUTOVER — turning
|
|
47
|
+
* the runbook live for end-users — not to the BUILD of the runbook code
|
|
48
|
+
* itself. The wrapper is constructible and unit-testable today; live
|
|
49
|
+
* activation gates on the separate `wrappers-active-after` config flag
|
|
50
|
+
* (defaults to false) and is the Tier-3 wiring PR's concern.
|
|
51
|
+
*/
|
|
52
|
+
import path from 'node:path';
|
|
53
|
+
import fs from 'node:fs';
|
|
54
|
+
import { markerPath } from '../../lifeline/startupMarker.js';
|
|
55
|
+
// ── Verify probe ─────────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* §A9 durable-state assertion for the supervisor-preflight runbook. The
|
|
58
|
+
* preflight heals six prerequisites:
|
|
59
|
+
*
|
|
60
|
+
* 1. shadow-install
|
|
61
|
+
* 2. node symlink
|
|
62
|
+
* 3. stuck git rebase
|
|
63
|
+
* 4. better-sqlite3 ABI mismatch
|
|
64
|
+
* 5. stale lifeline lock
|
|
65
|
+
* 6. settings.json merge-conflict
|
|
66
|
+
*
|
|
67
|
+
* Verifying every step in isolation would multiply the verify surface six-
|
|
68
|
+
* fold and re-implement detection logic the preflight body already owns.
|
|
69
|
+
* Per §A34, the verify produces a SINGLE durable-state check: did the
|
|
70
|
+
* lifeline re-spawn cleanly after the heal? The signal we read is the
|
|
71
|
+
* `lifeline-started-at.json` startup marker — written unconditionally
|
|
72
|
+
* by every lifeline startup, including the post-preflight one.
|
|
73
|
+
*
|
|
74
|
+
* Returns:
|
|
75
|
+
* - kind: 'ok' — marker present, well-formed, fresh-enough
|
|
76
|
+
* - kind: 'failed' — marker missing or corrupt (post-preflight
|
|
77
|
+
* spawn did NOT happen → heal did not durably
|
|
78
|
+
* recover the supervisor)
|
|
79
|
+
* - kind: 'inconclusive' — filesystem probe threw (cannot determine
|
|
80
|
+
* durability one way or the other → §A21)
|
|
81
|
+
*/
|
|
82
|
+
export function verifyLifelineDurable(stateDir, options = {}) {
|
|
83
|
+
const maxAgeMs = options.maxAgeMs ?? 10 * 60_000; // 10 min default
|
|
84
|
+
const now = options.nowMs ?? Date.now();
|
|
85
|
+
try {
|
|
86
|
+
const p = markerPath(stateDir);
|
|
87
|
+
let raw;
|
|
88
|
+
try {
|
|
89
|
+
raw = fs.readFileSync(p, 'utf-8');
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const code = err.code;
|
|
93
|
+
if (code === 'ENOENT') {
|
|
94
|
+
return {
|
|
95
|
+
kind: 'failed',
|
|
96
|
+
reason: `lifeline startup marker missing at ${p}`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// EACCES, EIO, etc — probe error, not durability failure.
|
|
100
|
+
return {
|
|
101
|
+
kind: 'inconclusive',
|
|
102
|
+
reason: `marker readFileSync threw (${code ?? 'unknown'}): ${err.message}`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = JSON.parse(raw);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return {
|
|
111
|
+
kind: 'failed',
|
|
112
|
+
reason: `marker JSON parse failed: ${err.message.slice(0, 200)}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!parsed ||
|
|
116
|
+
typeof parsed !== 'object' ||
|
|
117
|
+
typeof parsed.startedAt !== 'string' ||
|
|
118
|
+
typeof parsed.pid !== 'number' ||
|
|
119
|
+
typeof parsed.version !== 'string') {
|
|
120
|
+
return {
|
|
121
|
+
kind: 'failed',
|
|
122
|
+
reason: 'marker shape invalid (missing startedAt/pid/version)',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const startedAtMs = Date.parse(parsed.startedAt);
|
|
126
|
+
if (!Number.isFinite(startedAtMs)) {
|
|
127
|
+
return {
|
|
128
|
+
kind: 'failed',
|
|
129
|
+
reason: `marker startedAt is unparseable: ${parsed.startedAt}`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const ageMs = now - startedAtMs;
|
|
133
|
+
if (ageMs > maxAgeMs) {
|
|
134
|
+
return {
|
|
135
|
+
kind: 'failed',
|
|
136
|
+
reason: `marker is stale (ageMs=${ageMs} > maxAgeMs=${maxAgeMs}) — lifeline did not respawn post-heal`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// Negative age = clock skew or marker from the future. Treat as probe
|
|
140
|
+
// error (§A21) rather than failure — we cannot confidently say durability
|
|
141
|
+
// is broken when the clocks disagree.
|
|
142
|
+
if (ageMs < -60_000) {
|
|
143
|
+
return {
|
|
144
|
+
kind: 'inconclusive',
|
|
145
|
+
reason: `marker startedAt is in the future (ageMs=${ageMs}) — clock-skew probe error`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return { kind: 'ok', markerStartedAt: parsed.startedAt };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
return {
|
|
152
|
+
kind: 'inconclusive',
|
|
153
|
+
reason: `verify wrapper threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
let _verifyImpl = null;
|
|
158
|
+
export function _setVerifyImplForTesting(fn) {
|
|
159
|
+
_verifyImpl = fn;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Allow tests to inject a stub supervisor so the runbook can run end-to-end
|
|
163
|
+
* without spawning a real tmux session or touching the on-disk shadow-install.
|
|
164
|
+
*/
|
|
165
|
+
let _supervisorForTesting = null;
|
|
166
|
+
export function _setSupervisorForTesting(s) {
|
|
167
|
+
_supervisorForTesting = s;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The state directory the verify step reads the lifeline marker from. Tests
|
|
171
|
+
* inject a tmpdir; production callers configure this at runbook-registration
|
|
172
|
+
* via a closure (the runbook factory) — for now, allow override.
|
|
173
|
+
*/
|
|
174
|
+
let _stateDirForVerify = null;
|
|
175
|
+
export function _setStateDirForVerify(dir) {
|
|
176
|
+
_stateDirForVerify = dir;
|
|
177
|
+
}
|
|
178
|
+
// ── Match helpers ────────────────────────────────────────────────────────
|
|
179
|
+
const SUPERVISOR_SUBSYSTEMS = new Set([
|
|
180
|
+
'lifeline',
|
|
181
|
+
'server',
|
|
182
|
+
'supervisor',
|
|
183
|
+
// Legacy emit-sites tag the server subsystem as 'http' in some places.
|
|
184
|
+
// Match() narrows to those by inspecting reason text.
|
|
185
|
+
]);
|
|
186
|
+
const SUPERVISOR_ERROR_CODES = ['BIND_FAILURE', 'CRASH_LOOP', 'SUPERVISOR_DEGRADED'];
|
|
187
|
+
// ── ApprovedRunbook ──────────────────────────────────────────────────────
|
|
188
|
+
export const supervisorPreflightRunbook = {
|
|
189
|
+
id: 'supervisor-preflight',
|
|
190
|
+
// Lower than W-1 (node-abi-mismatch=100). When a BIND_FAILURE event also
|
|
191
|
+
// carries a NATIVE_MODULE_ABI_MISMATCH errorCode (rare but possible during
|
|
192
|
+
// crash-loop diagnosis), W-1's runbook is the right surface to dispatch
|
|
193
|
+
// because it's the precise heal. supervisor-preflight is the broad heal
|
|
194
|
+
// for the bind-failure root-cause class.
|
|
195
|
+
priority: 90,
|
|
196
|
+
surface: 'supervisor',
|
|
197
|
+
eventPrefilter: {
|
|
198
|
+
errorCode: SUPERVISOR_ERROR_CODES,
|
|
199
|
+
// §A6: NOT free-text. Only structured-provenance events fire this runbook.
|
|
200
|
+
provenance: ['native-binding', 'subsystem-explicit'],
|
|
201
|
+
},
|
|
202
|
+
match: (event) => {
|
|
203
|
+
// Narrow to lifeline/server subsystems. Defence-in-depth: a BIND_FAILURE
|
|
204
|
+
// event for an unrelated subsystem (e.g., a future memory-bind-failure)
|
|
205
|
+
// should NOT trigger a supervisor preflight.
|
|
206
|
+
if (SUPERVISOR_SUBSYSTEMS.has(event.subsystem))
|
|
207
|
+
return true;
|
|
208
|
+
// Legacy free-text subsystems may use 'http' or 'tunnel' — inspect the
|
|
209
|
+
// reason for an explicit supervisor/server/lifeline mention.
|
|
210
|
+
const reasonText = event.reason.full ?? event.reason.redacted ?? '';
|
|
211
|
+
return /\b(server|lifeline|supervisor)\b/i.test(reasonText);
|
|
212
|
+
},
|
|
213
|
+
preconditions: async (_event) => {
|
|
214
|
+
// Cheap guard: stateDir must be set + accessible (the supervisor cannot
|
|
215
|
+
// heal without somewhere to read/write shadow-install).
|
|
216
|
+
if (!_stateDirForVerify)
|
|
217
|
+
return true; // Production callers wire the dir
|
|
218
|
+
try {
|
|
219
|
+
fs.accessSync(_stateDirForVerify, fs.constants.R_OK | fs.constants.W_OK);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
surfaceCallable: async (ctx) => {
|
|
227
|
+
const supervisor = _supervisorForTesting;
|
|
228
|
+
if (!supervisor) {
|
|
229
|
+
// Production callers must wire a supervisor instance via the runbook
|
|
230
|
+
// factory pattern. Until that lands (Tier-3 dispatcher wiring), the
|
|
231
|
+
// surfaceCallable returns a failure that surfaces in the audit
|
|
232
|
+
// projection as `surfaceCallable returned outcome=failure`.
|
|
233
|
+
return {
|
|
234
|
+
outcome: 'failure',
|
|
235
|
+
details: {
|
|
236
|
+
reason: 'no-supervisor-wired',
|
|
237
|
+
attemptId: ctx.attemptId,
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const result = await supervisor.invokeFromRemediator(ctx);
|
|
242
|
+
// The Remediator's `ExecutionResult` type matches the supervisor's
|
|
243
|
+
// `SupervisorRemediatorExecutionResult` shape; pass through verbatim.
|
|
244
|
+
return result;
|
|
245
|
+
},
|
|
246
|
+
verify: async (ctx) => {
|
|
247
|
+
const impl = _verifyImpl;
|
|
248
|
+
let probe;
|
|
249
|
+
if (impl) {
|
|
250
|
+
probe = impl(ctx);
|
|
251
|
+
}
|
|
252
|
+
else if (_stateDirForVerify) {
|
|
253
|
+
probe = verifyLifelineDurable(_stateDirForVerify);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
probe = {
|
|
257
|
+
kind: 'inconclusive',
|
|
258
|
+
reason: 'no stateDir wired for verify — cannot read lifeline marker',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
switch (probe.kind) {
|
|
262
|
+
case 'ok':
|
|
263
|
+
return {
|
|
264
|
+
outcome: 'verified-healthy',
|
|
265
|
+
reason: `lifeline marker present (startedAt=${probe.markerStartedAt}); preflight durable`,
|
|
266
|
+
};
|
|
267
|
+
case 'failed':
|
|
268
|
+
return { outcome: 'verify-failed', reason: probe.reason };
|
|
269
|
+
case 'inconclusive':
|
|
270
|
+
return { outcome: 'verify-inconclusive', reason: probe.reason };
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
blastRadius: 'machine',
|
|
274
|
+
reversibility: 'reversible',
|
|
275
|
+
// 3 minutes. Cold-cache shadow-install reinstall + better-sqlite3 rebuild
|
|
276
|
+
// can both run in one preflight cycle; each takes ~30-60s on slow hardware.
|
|
277
|
+
// The deadline race in F-8 will abort with `aborted-deadline` if exceeded.
|
|
278
|
+
expectedRuntimeMs: 180_000,
|
|
279
|
+
// §A36: essential requires blastRadius='machine' (satisfied above). A
|
|
280
|
+
// wedged supervisor DoSes every server-mediated capability, so this is the
|
|
281
|
+
// canonical essential runbook for the supervisor surface.
|
|
282
|
+
essential: true,
|
|
283
|
+
};
|
|
284
|
+
// Re-export the path helper so consumers can compute the marker location
|
|
285
|
+
// without re-importing from `src/lifeline/startupMarker.ts`.
|
|
286
|
+
export { markerPath as supervisorMarkerPath };
|
|
287
|
+
// For tests/observability — list the durable state assertions.
|
|
288
|
+
export const VERIFIED_HEAL_TARGETS = [
|
|
289
|
+
'shadow-install',
|
|
290
|
+
'node-symlink',
|
|
291
|
+
'git-rebase',
|
|
292
|
+
'better-sqlite3-abi',
|
|
293
|
+
'stale-lifeline-lock',
|
|
294
|
+
'settings-json',
|
|
295
|
+
];
|
|
296
|
+
/** No-op compile-time use to keep `path` import alive for future helpers. */
|
|
297
|
+
const _ = path;
|
|
298
|
+
void _;
|
|
299
|
+
//# sourceMappingURL=supervisor-preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor-preflight.js","sourceRoot":"","sources":["../../../src/remediation/runbooks/supervisor-preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentMdAtomicSave — race-safe two-rename commit for agentmd job edits.
|
|
3
|
+
*
|
|
4
|
+
* Per INSTAR-JOBS-AS-AGENTMD spec §Design Principles "Override = fork;
|
|
5
|
+
* race-safe two-rename commit":
|
|
6
|
+
*
|
|
7
|
+
* The save sequence is md-first, manifest-last:
|
|
8
|
+
* 1. Write `<file>.md.new` (staged body).
|
|
9
|
+
* 2. Write `<schedule>.json.new` (staged manifest).
|
|
10
|
+
* 3. rename(<file>.md.new → <file>.md) ← rename A
|
|
11
|
+
* 4. rename(<schedule>.json.new → <schedule>.json) ← rename B
|
|
12
|
+
*
|
|
13
|
+
* SIGKILL between rename A and rename B leaves a consistent state: the
|
|
14
|
+
* new body is on disk paired with the OLD manifest. The loader handles
|
|
15
|
+
* this gracefully — it loads the new body and the old manifest's
|
|
16
|
+
* schedule, which is a strictly-progressed state.
|
|
17
|
+
*
|
|
18
|
+
* SIGKILL before rename A is also consistent: the old body + old
|
|
19
|
+
* manifest are intact; the `.new` staged files are orphaned and the next
|
|
20
|
+
* boot's reconcile() can either reapply or discard them.
|
|
21
|
+
*
|
|
22
|
+
* Re-applying a half-committed save is idempotent because each rename is
|
|
23
|
+
* atomic at the filesystem level and overwrites any pre-existing target.
|
|
24
|
+
*
|
|
25
|
+
* This module is the canonical save helper for both Dashboard edits and
|
|
26
|
+
* future CLI edit commands. It is NOT yet wired into a UI consumer — the
|
|
27
|
+
* Phase 4 Dashboard UI rewrite is the consumer. Shipping the helper
|
|
28
|
+
* separately so the spec's atomicity guarantee has a tested
|
|
29
|
+
* implementation regardless of when the UI lands.
|
|
30
|
+
*/
|
|
31
|
+
export interface AtomicSaveInput {
|
|
32
|
+
/** Final on-disk path of the .md body (e.g., `.instar/jobs/user/<slug>.md`). */
|
|
33
|
+
mdPath: string;
|
|
34
|
+
/** Final on-disk path of the per-slug manifest (e.g.,
|
|
35
|
+
* `.instar/jobs/schedule/<slug>.json`). */
|
|
36
|
+
manifestPath: string;
|
|
37
|
+
/** Bytes to write to mdPath. Caller is responsible for any normalization. */
|
|
38
|
+
mdBody: string;
|
|
39
|
+
/** Object to JSON-serialize into manifestPath. */
|
|
40
|
+
manifest: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface AtomicSaveResult {
|
|
43
|
+
ok: true;
|
|
44
|
+
mdWritten: string;
|
|
45
|
+
manifestWritten: string;
|
|
46
|
+
}
|
|
47
|
+
export interface AtomicSaveFailure {
|
|
48
|
+
ok: false;
|
|
49
|
+
stage: 'stage-md' | 'stage-manifest' | 'commit-md' | 'commit-manifest';
|
|
50
|
+
reason: string;
|
|
51
|
+
partial: {
|
|
52
|
+
mdNewExists: boolean;
|
|
53
|
+
manifestNewExists: boolean;
|
|
54
|
+
mdCommitted: boolean;
|
|
55
|
+
manifestCommitted: boolean;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Two-rename atomic save. Writes the body first, then the manifest. Each
|
|
60
|
+
* write goes to a `.new` sibling, then renames atomically over the final
|
|
61
|
+
* path. On failure at any stage, returns a structured `AtomicSaveFailure`
|
|
62
|
+
* the caller can use to drive recovery / Issues-card surface.
|
|
63
|
+
*
|
|
64
|
+
* Implementation notes:
|
|
65
|
+
* - We DO NOT delete the staged `.new` files on failure — leaving them
|
|
66
|
+
* in place gives reconcile() something to reason about. The next
|
|
67
|
+
* successful save will overwrite them.
|
|
68
|
+
* - `fs.renameSync` is atomic on POSIX filesystems for paths on the
|
|
69
|
+
* same filesystem (always the case here — both live under .instar/).
|
|
70
|
+
* - `fs.writeFileSync` with `flag: 'w'` truncates; we use it
|
|
71
|
+
* deliberately so a half-written `.new` on retry is replaced.
|
|
72
|
+
*/
|
|
73
|
+
export declare function atomicSaveAgentMdJob(input: AtomicSaveInput): AtomicSaveResult | AtomicSaveFailure;
|
|
74
|
+
/**
|
|
75
|
+
* Discover any `.new` staged files left over from a crashed save. Used by
|
|
76
|
+
* reconcile() to surface "interrupted save" Issues-card rows.
|
|
77
|
+
*/
|
|
78
|
+
export declare function listStagedNewFiles(jobsRootDir: string): string[];
|
|
79
|
+
/**
|
|
80
|
+
* Clean up an orphaned `.new` file. Used by reconcile() when the operator
|
|
81
|
+
* chooses "Discard staged changes" in the Issues-card UI. Routes through
|
|
82
|
+
* SafeFsExecutor per the destructive-tool funnel.
|
|
83
|
+
*/
|
|
84
|
+
export declare function discardStagedFile(stagedPath: string): void;
|
|
85
|
+
//# sourceMappingURL=AgentMdAtomicSave.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentMdAtomicSave.d.ts","sourceRoot":"","sources":["../../src/scheduler/AgentMdAtomicSave.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf;gDAC4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,IAAI,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,GAAG,iBAAiB,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,iBAAiB,CAiFjG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAQhE;AAiBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAE1D"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentMdAtomicSave — race-safe two-rename commit for agentmd job edits.
|
|
3
|
+
*
|
|
4
|
+
* Per INSTAR-JOBS-AS-AGENTMD spec §Design Principles "Override = fork;
|
|
5
|
+
* race-safe two-rename commit":
|
|
6
|
+
*
|
|
7
|
+
* The save sequence is md-first, manifest-last:
|
|
8
|
+
* 1. Write `<file>.md.new` (staged body).
|
|
9
|
+
* 2. Write `<schedule>.json.new` (staged manifest).
|
|
10
|
+
* 3. rename(<file>.md.new → <file>.md) ← rename A
|
|
11
|
+
* 4. rename(<schedule>.json.new → <schedule>.json) ← rename B
|
|
12
|
+
*
|
|
13
|
+
* SIGKILL between rename A and rename B leaves a consistent state: the
|
|
14
|
+
* new body is on disk paired with the OLD manifest. The loader handles
|
|
15
|
+
* this gracefully — it loads the new body and the old manifest's
|
|
16
|
+
* schedule, which is a strictly-progressed state.
|
|
17
|
+
*
|
|
18
|
+
* SIGKILL before rename A is also consistent: the old body + old
|
|
19
|
+
* manifest are intact; the `.new` staged files are orphaned and the next
|
|
20
|
+
* boot's reconcile() can either reapply or discard them.
|
|
21
|
+
*
|
|
22
|
+
* Re-applying a half-committed save is idempotent because each rename is
|
|
23
|
+
* atomic at the filesystem level and overwrites any pre-existing target.
|
|
24
|
+
*
|
|
25
|
+
* This module is the canonical save helper for both Dashboard edits and
|
|
26
|
+
* future CLI edit commands. It is NOT yet wired into a UI consumer — the
|
|
27
|
+
* Phase 4 Dashboard UI rewrite is the consumer. Shipping the helper
|
|
28
|
+
* separately so the spec's atomicity guarantee has a tested
|
|
29
|
+
* implementation regardless of when the UI lands.
|
|
30
|
+
*/
|
|
31
|
+
import fs from 'node:fs';
|
|
32
|
+
import path from 'node:path';
|
|
33
|
+
import { SafeFsExecutor } from '../core/SafeFsExecutor.js';
|
|
34
|
+
/**
|
|
35
|
+
* Two-rename atomic save. Writes the body first, then the manifest. Each
|
|
36
|
+
* write goes to a `.new` sibling, then renames atomically over the final
|
|
37
|
+
* path. On failure at any stage, returns a structured `AtomicSaveFailure`
|
|
38
|
+
* the caller can use to drive recovery / Issues-card surface.
|
|
39
|
+
*
|
|
40
|
+
* Implementation notes:
|
|
41
|
+
* - We DO NOT delete the staged `.new` files on failure — leaving them
|
|
42
|
+
* in place gives reconcile() something to reason about. The next
|
|
43
|
+
* successful save will overwrite them.
|
|
44
|
+
* - `fs.renameSync` is atomic on POSIX filesystems for paths on the
|
|
45
|
+
* same filesystem (always the case here — both live under .instar/).
|
|
46
|
+
* - `fs.writeFileSync` with `flag: 'w'` truncates; we use it
|
|
47
|
+
* deliberately so a half-written `.new` on retry is replaced.
|
|
48
|
+
*/
|
|
49
|
+
export function atomicSaveAgentMdJob(input) {
|
|
50
|
+
const { mdPath, manifestPath, mdBody, manifest } = input;
|
|
51
|
+
const mdNew = mdPath + '.new';
|
|
52
|
+
const manifestNew = manifestPath + '.new';
|
|
53
|
+
const partial = {
|
|
54
|
+
mdNewExists: false,
|
|
55
|
+
manifestNewExists: false,
|
|
56
|
+
mdCommitted: false,
|
|
57
|
+
manifestCommitted: false,
|
|
58
|
+
};
|
|
59
|
+
// Stage 1: ensure both parent directories exist.
|
|
60
|
+
try {
|
|
61
|
+
fs.mkdirSync(path.dirname(mdPath), { recursive: true });
|
|
62
|
+
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
stage: 'stage-md',
|
|
68
|
+
reason: `Failed to create parent directories: ${err instanceof Error ? err.message : String(err)}`,
|
|
69
|
+
partial,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Stage 2: write the body to its .new sibling.
|
|
73
|
+
try {
|
|
74
|
+
fs.writeFileSync(mdNew, mdBody, { encoding: 'utf-8', flag: 'w' });
|
|
75
|
+
partial.mdNewExists = true;
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
stage: 'stage-md',
|
|
81
|
+
reason: `Failed to stage md body: ${err instanceof Error ? err.message : String(err)}`,
|
|
82
|
+
partial,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Stage 3: write the manifest to its .new sibling.
|
|
86
|
+
try {
|
|
87
|
+
fs.writeFileSync(manifestNew, JSON.stringify(manifest, null, 2) + '\n', { encoding: 'utf-8', flag: 'w' });
|
|
88
|
+
partial.manifestNewExists = true;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
stage: 'stage-manifest',
|
|
94
|
+
reason: `Failed to stage manifest: ${err instanceof Error ? err.message : String(err)}`,
|
|
95
|
+
partial,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Stage 4 (rename A): commit md-first. After this, the body is durable.
|
|
99
|
+
try {
|
|
100
|
+
fs.renameSync(mdNew, mdPath);
|
|
101
|
+
partial.mdCommitted = true;
|
|
102
|
+
partial.mdNewExists = false;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
stage: 'commit-md',
|
|
108
|
+
reason: `Failed to commit md body: ${err instanceof Error ? err.message : String(err)}`,
|
|
109
|
+
partial,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Stage 5 (rename B): commit manifest-last. SIGKILL between renames A
|
|
113
|
+
// and B leaves the new body + old manifest, which is a consistent
|
|
114
|
+
// strictly-progressed state.
|
|
115
|
+
try {
|
|
116
|
+
fs.renameSync(manifestNew, manifestPath);
|
|
117
|
+
partial.manifestCommitted = true;
|
|
118
|
+
partial.manifestNewExists = false;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
stage: 'commit-manifest',
|
|
124
|
+
reason: `Failed to commit manifest: ${err instanceof Error ? err.message : String(err)}`,
|
|
125
|
+
partial,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return { ok: true, mdWritten: mdPath, manifestWritten: manifestPath };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Discover any `.new` staged files left over from a crashed save. Used by
|
|
132
|
+
* reconcile() to surface "interrupted save" Issues-card rows.
|
|
133
|
+
*/
|
|
134
|
+
export function listStagedNewFiles(jobsRootDir) {
|
|
135
|
+
const staged = [];
|
|
136
|
+
walk(jobsRootDir, (file) => {
|
|
137
|
+
if (file.endsWith('.md.new') || file.endsWith('.json.new')) {
|
|
138
|
+
staged.push(file);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return staged;
|
|
142
|
+
}
|
|
143
|
+
function walk(dir, visit) {
|
|
144
|
+
if (!fs.existsSync(dir))
|
|
145
|
+
return;
|
|
146
|
+
let entries;
|
|
147
|
+
try {
|
|
148
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
for (const e of entries) {
|
|
154
|
+
const p = path.join(dir, e.name);
|
|
155
|
+
if (e.isDirectory())
|
|
156
|
+
walk(p, visit);
|
|
157
|
+
else if (e.isFile())
|
|
158
|
+
visit(p);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Clean up an orphaned `.new` file. Used by reconcile() when the operator
|
|
163
|
+
* chooses "Discard staged changes" in the Issues-card UI. Routes through
|
|
164
|
+
* SafeFsExecutor per the destructive-tool funnel.
|
|
165
|
+
*/
|
|
166
|
+
export function discardStagedFile(stagedPath) {
|
|
167
|
+
SafeFsExecutor.safeUnlinkSync(stagedPath, { operation: 'AgentMdAtomicSave discard staged .new file' });
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=AgentMdAtomicSave.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentMdAtomicSave.js","sourceRoot":"","sources":["../../src/scheduler/AgentMdAtomicSave.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentMdReconcile — boot-time consistency check for the agentmd job tree.
|
|
3
|
+
*
|
|
4
|
+
* Per INSTAR-JOBS-AS-AGENTMD spec §Runtime "Load lifecycle (boot)":
|
|
5
|
+
*
|
|
6
|
+
* reconcile() runs at boot, surfaces orphan/shadow/missing on boot.
|
|
7
|
+
* Output goes to Dashboard Issues card.
|
|
8
|
+
*
|
|
9
|
+
* Categories surfaced:
|
|
10
|
+
*
|
|
11
|
+
* - ORPHAN-MANIFEST: A `<slug>.json` per-slug manifest exists but the
|
|
12
|
+
* `.md` it references does NOT. Scheduler cannot resolve the body;
|
|
13
|
+
* entry is excluded from jobs[] and surfaced as Issues-card row.
|
|
14
|
+
*
|
|
15
|
+
* - SHADOW-MD: A `<slug>.md` exists in either `instar/` or `user/` but
|
|
16
|
+
* has no per-slug manifest. The body is on disk but the scheduler
|
|
17
|
+
* has no schedule for it — Issues-card "Add to schedule (disabled)"
|
|
18
|
+
* or "Delete file" actions apply.
|
|
19
|
+
*
|
|
20
|
+
* - MISSING-FROM-JOBS-JSON: A legacy `jobs.json` entry has no
|
|
21
|
+
* corresponding per-slug manifest AND no `.md` under user/ or
|
|
22
|
+
* instar/. Mid-migration state; not yet copied over.
|
|
23
|
+
*
|
|
24
|
+
* - STAGED-NEW: A `.md.new` or `.json.new` left over from a crashed
|
|
25
|
+
* atomic save (see AgentMdAtomicSave). Issues-card "Apply" /
|
|
26
|
+
* "Discard" actions apply.
|
|
27
|
+
*
|
|
28
|
+
* - CASE-COLLISION: Two slug files differ only by case (NFC normalized)
|
|
29
|
+
* in the same directory. Both are excluded from jobs[] by the
|
|
30
|
+
* loader; this row surfaces them together.
|
|
31
|
+
*
|
|
32
|
+
* Pure function — reads the file system, returns a structured report.
|
|
33
|
+
* The caller (boot lifecycle in JobLoader; Phase 4 Dashboard read
|
|
34
|
+
* endpoint) decides how to present the rows.
|
|
35
|
+
*/
|
|
36
|
+
export type ReconcileFindingKind = 'orphan-manifest' | 'shadow-md' | 'missing-from-jobs-json' | 'staged-new' | 'case-collision';
|
|
37
|
+
export interface ReconcileFinding {
|
|
38
|
+
kind: ReconcileFindingKind;
|
|
39
|
+
/** Affected slug (when computable). */
|
|
40
|
+
slug?: string;
|
|
41
|
+
/** Affected file path (absolute). */
|
|
42
|
+
filePath?: string;
|
|
43
|
+
/** Two-pane comparison: for case-collision, both paths. */
|
|
44
|
+
conflictingPaths?: string[];
|
|
45
|
+
/** One-line operator-facing summary. */
|
|
46
|
+
summary: string;
|
|
47
|
+
/** Stable severity ordering for the Issues card. */
|
|
48
|
+
severity: 'info' | 'warning' | 'error';
|
|
49
|
+
}
|
|
50
|
+
export interface ReconcileReport {
|
|
51
|
+
findings: ReconcileFinding[];
|
|
52
|
+
summary: {
|
|
53
|
+
total: number;
|
|
54
|
+
byKind: Record<ReconcileFindingKind, number>;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface ReconcileOptions {
|
|
58
|
+
/** State directory root (e.g., `<projectDir>/.instar/`). */
|
|
59
|
+
stateDir: string;
|
|
60
|
+
}
|
|
61
|
+
export declare function reconcileAgentMdTree(opts: ReconcileOptions): ReconcileReport;
|
|
62
|
+
//# sourceMappingURL=AgentMdReconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentMdReconcile.d.ts","sourceRoot":"","sources":["../../src/scheduler/AgentMdReconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA8BH,MAAM,MAAM,oBAAoB,GAC5B,iBAAiB,GACjB,WAAW,GACX,wBAAwB,GACxB,YAAY,GACZ,gBAAgB,CAAC;AAErB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;KAC9C,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAUD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAoH5E"}
|