@latysh-development/monolith 0.0.0-stage → 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +179 -2
- package/dist/brief/cli.d.ts +30 -0
- package/dist/brief/cli.d.ts.map +1 -0
- package/dist/brief/cli.js +85 -0
- package/dist/brief/collect.d.ts +17 -0
- package/dist/brief/collect.d.ts.map +1 -0
- package/dist/brief/collect.js +342 -0
- package/dist/brief/config.d.ts +40 -0
- package/dist/brief/config.d.ts.map +1 -0
- package/dist/brief/config.js +85 -0
- package/dist/brief/constants.d.ts +12 -0
- package/dist/brief/constants.d.ts.map +1 -0
- package/dist/brief/constants.js +11 -0
- package/dist/brief/docs.d.ts +53 -0
- package/dist/brief/docs.d.ts.map +1 -0
- package/dist/brief/docs.js +229 -0
- package/dist/brief/freshness.d.ts +104 -0
- package/dist/brief/freshness.d.ts.map +1 -0
- package/dist/brief/freshness.js +263 -0
- package/dist/brief/git-signals.d.ts +17 -0
- package/dist/brief/git-signals.d.ts.map +1 -0
- package/dist/brief/git-signals.js +101 -0
- package/dist/brief/index.d.ts +11 -0
- package/dist/brief/index.d.ts.map +1 -0
- package/dist/brief/index.js +5 -0
- package/dist/brief/reconcile.d.ts +75 -0
- package/dist/brief/reconcile.d.ts.map +1 -0
- package/dist/brief/reconcile.js +259 -0
- package/dist/brief/render.d.ts +67 -0
- package/dist/brief/render.d.ts.map +1 -0
- package/dist/brief/render.js +458 -0
- package/dist/brief/sanitize.d.ts +38 -0
- package/dist/brief/sanitize.d.ts.map +1 -0
- package/dist/brief/sanitize.js +48 -0
- package/dist/brief/selectors.d.ts +36 -0
- package/dist/brief/selectors.d.ts.map +1 -0
- package/dist/brief/selectors.js +75 -0
- package/dist/brief/story.d.ts +18 -0
- package/dist/brief/story.d.ts.map +1 -0
- package/dist/brief/story.js +59 -0
- package/dist/brief/types.d.ts +126 -0
- package/dist/brief/types.d.ts.map +1 -0
- package/dist/brief/types.js +11 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +199 -0
- package/dist/cli/init-args.d.ts +16 -0
- package/dist/cli/init-args.d.ts.map +1 -0
- package/dist/cli/init-args.js +24 -0
- package/dist/cli/ui-args.d.ts +23 -0
- package/dist/cli/ui-args.d.ts.map +1 -0
- package/dist/cli/ui-args.js +66 -0
- package/dist/cli/ui.d.ts +40 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +180 -0
- package/dist/coordinatorium/checkpoint.d.ts +44 -0
- package/dist/coordinatorium/checkpoint.d.ts.map +1 -0
- package/dist/coordinatorium/checkpoint.js +124 -0
- package/dist/coordinatorium/cli.d.ts +39 -0
- package/dist/coordinatorium/cli.d.ts.map +1 -0
- package/dist/coordinatorium/cli.js +203 -0
- package/dist/coordinatorium/errors.d.ts +112 -0
- package/dist/coordinatorium/errors.d.ts.map +1 -0
- package/dist/coordinatorium/errors.js +145 -0
- package/dist/coordinatorium/index.d.ts +11 -0
- package/dist/coordinatorium/index.d.ts.map +1 -0
- package/dist/coordinatorium/index.js +5 -0
- package/dist/coordinatorium/lock.d.ts +187 -0
- package/dist/coordinatorium/lock.d.ts.map +1 -0
- package/dist/coordinatorium/lock.js +659 -0
- package/dist/coordinatorium/mission.d.ts +32 -0
- package/dist/coordinatorium/mission.d.ts.map +1 -0
- package/dist/coordinatorium/mission.js +87 -0
- package/dist/coordinatorium/state-keeper.d.ts +115 -0
- package/dist/coordinatorium/state-keeper.d.ts.map +1 -0
- package/dist/coordinatorium/state-keeper.js +396 -0
- package/dist/coordinatorium/types.d.ts +69 -0
- package/dist/coordinatorium/types.d.ts.map +1 -0
- package/dist/coordinatorium/types.js +27 -0
- package/dist/discovery/compose.d.ts +28 -0
- package/dist/discovery/compose.d.ts.map +1 -0
- package/dist/discovery/compose.js +159 -0
- package/dist/discovery/contracts.d.ts +211 -0
- package/dist/discovery/contracts.d.ts.map +1 -0
- package/dist/discovery/contracts.js +555 -0
- package/dist/discovery/docs.d.ts +278 -0
- package/dist/discovery/docs.d.ts.map +1 -0
- package/dist/discovery/docs.js +1182 -0
- package/dist/discovery/facts.d.ts +49 -0
- package/dist/discovery/facts.d.ts.map +1 -0
- package/dist/discovery/facts.js +541 -0
- package/dist/discovery/index.d.ts +8 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +9 -0
- package/dist/discovery/orchestrate.d.ts +35 -0
- package/dist/discovery/orchestrate.d.ts.map +1 -0
- package/dist/discovery/orchestrate.js +32 -0
- package/dist/discovery/render.d.ts +36 -0
- package/dist/discovery/render.d.ts.map +1 -0
- package/dist/discovery/render.js +278 -0
- package/dist/discovery/traverse.d.ts +197 -0
- package/dist/discovery/traverse.d.ts.map +1 -0
- package/dist/discovery/traverse.js +510 -0
- package/dist/durable/assess.d.ts +105 -0
- package/dist/durable/assess.d.ts.map +1 -0
- package/dist/durable/assess.js +239 -0
- package/dist/durable/canonical.d.ts +25 -0
- package/dist/durable/canonical.d.ts.map +1 -0
- package/dist/durable/canonical.js +78 -0
- package/dist/durable/cli.d.ts +37 -0
- package/dist/durable/cli.d.ts.map +1 -0
- package/dist/durable/cli.js +141 -0
- package/dist/durable/errors.d.ts +192 -0
- package/dist/durable/errors.d.ts.map +1 -0
- package/dist/durable/errors.js +323 -0
- package/dist/durable/fs-store.d.ts +254 -0
- package/dist/durable/fs-store.d.ts.map +1 -0
- package/dist/durable/fs-store.js +1493 -0
- package/dist/durable/index.d.ts +20 -0
- package/dist/durable/index.d.ts.map +1 -0
- package/dist/durable/index.js +13 -0
- package/dist/durable/log.d.ts +33 -0
- package/dist/durable/log.d.ts.map +1 -0
- package/dist/durable/log.js +157 -0
- package/dist/durable/memory-store.d.ts +51 -0
- package/dist/durable/memory-store.d.ts.map +1 -0
- package/dist/durable/memory-store.js +302 -0
- package/dist/durable/projection.d.ts +35 -0
- package/dist/durable/projection.d.ts.map +1 -0
- package/dist/durable/projection.js +328 -0
- package/dist/durable/prune-check.d.ts +34 -0
- package/dist/durable/prune-check.d.ts.map +1 -0
- package/dist/durable/prune-check.js +72 -0
- package/dist/durable/register.d.ts +100 -0
- package/dist/durable/register.d.ts.map +1 -0
- package/dist/durable/register.js +532 -0
- package/dist/durable/retention.d.ts +49 -0
- package/dist/durable/retention.d.ts.map +1 -0
- package/dist/durable/retention.js +59 -0
- package/dist/durable/runtime.d.ts +499 -0
- package/dist/durable/runtime.d.ts.map +1 -0
- package/dist/durable/runtime.js +1288 -0
- package/dist/durable/seams.d.ts +44 -0
- package/dist/durable/seams.d.ts.map +1 -0
- package/dist/durable/seams.js +21 -0
- package/dist/durable/testing.d.ts +11 -0
- package/dist/durable/testing.d.ts.map +1 -0
- package/dist/durable/testing.js +25 -0
- package/dist/durable/types.d.ts +442 -0
- package/dist/durable/types.d.ts.map +1 -0
- package/dist/durable/types.js +75 -0
- package/dist/graph/builder.d.ts +11 -0
- package/dist/graph/builder.d.ts.map +1 -0
- package/dist/graph/builder.js +153 -0
- package/dist/graph/identity.d.ts +37 -0
- package/dist/graph/identity.d.ts.map +1 -0
- package/dist/graph/identity.js +46 -0
- package/dist/graph/index.d.ts +7 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +3 -0
- package/dist/graph/resolver.d.ts +41 -0
- package/dist/graph/resolver.d.ts.map +1 -0
- package/dist/graph/resolver.js +36 -0
- package/dist/graph/types.d.ts +42 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +1 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +2 -0
- package/dist/http/server.d.ts +76 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +195 -0
- package/dist/http/ui-assets.d.ts +22 -0
- package/dist/http/ui-assets.d.ts.map +1 -0
- package/dist/http/ui-assets.js +100 -0
- package/dist/ideas/cli.d.ts +33 -0
- package/dist/ideas/cli.d.ts.map +1 -0
- package/dist/ideas/cli.js +158 -0
- package/dist/ideas/format.d.ts +38 -0
- package/dist/ideas/format.d.ts.map +1 -0
- package/dist/ideas/format.js +194 -0
- package/dist/ideas/index.d.ts +6 -0
- package/dist/ideas/index.d.ts.map +1 -0
- package/dist/ideas/index.js +3 -0
- package/dist/ideas/store.d.ts +55 -0
- package/dist/ideas/store.d.ts.map +1 -0
- package/dist/ideas/store.js +262 -0
- package/dist/ideas/types.d.ts +54 -0
- package/dist/ideas/types.d.ts.map +1 -0
- package/dist/ideas/types.js +26 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/init/index.d.ts +12 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +90 -0
- package/dist/init/prompts.d.ts +3 -0
- package/dist/init/prompts.d.ts.map +1 -0
- package/dist/init/prompts.js +47 -0
- package/dist/init/scaffold.d.ts +29 -0
- package/dist/init/scaffold.d.ts.map +1 -0
- package/dist/init/scaffold.js +208 -0
- package/dist/init/types.d.ts +14 -0
- package/dist/init/types.d.ts.map +1 -0
- package/dist/init/types.js +1 -0
- package/dist/managed/index.d.ts +73 -0
- package/dist/managed/index.d.ts.map +1 -0
- package/dist/managed/index.js +222 -0
- package/dist/readmodel/dashboard.d.ts +40 -0
- package/dist/readmodel/dashboard.d.ts.map +1 -0
- package/dist/readmodel/dashboard.js +236 -0
- package/dist/readmodel/index.d.ts +3 -0
- package/dist/readmodel/index.d.ts.map +1 -0
- package/dist/readmodel/index.js +1 -0
- package/dist/readmodel/types.d.ts +104 -0
- package/dist/readmodel/types.d.ts.map +1 -0
- package/dist/readmodel/types.js +15 -0
- package/dist/root/index.d.ts +186 -0
- package/dist/root/index.d.ts.map +1 -0
- package/dist/root/index.js +430 -0
- package/dist/root/safe-write.d.ts +268 -0
- package/dist/root/safe-write.d.ts.map +1 -0
- package/dist/root/safe-write.js +966 -0
- package/dist/root/write-artifacts.d.ts +20 -0
- package/dist/root/write-artifacts.d.ts.map +1 -0
- package/dist/root/write-artifacts.js +42 -0
- package/dist/scriptorium/compile.d.ts +4 -0
- package/dist/scriptorium/compile.d.ts.map +1 -0
- package/dist/scriptorium/compile.js +185 -0
- package/dist/scriptorium/frontmatter.d.ts +13 -0
- package/dist/scriptorium/frontmatter.d.ts.map +1 -0
- package/dist/scriptorium/frontmatter.js +90 -0
- package/dist/scriptorium/index.d.ts +3 -0
- package/dist/scriptorium/index.d.ts.map +1 -0
- package/dist/scriptorium/index.js +1 -0
- package/dist/scriptorium/markdown.d.ts +31 -0
- package/dist/scriptorium/markdown.d.ts.map +1 -0
- package/dist/scriptorium/markdown.js +88 -0
- package/dist/scriptorium/types.d.ts +78 -0
- package/dist/scriptorium/types.d.ts.map +1 -0
- package/dist/scriptorium/types.js +1 -0
- package/dist/types/enforcement.d.ts +11 -0
- package/dist/types/enforcement.d.ts.map +1 -0
- package/dist/types/enforcement.js +1 -0
- package/dist/types/integrity.d.ts +33 -0
- package/dist/types/integrity.d.ts.map +1 -0
- package/dist/types/integrity.js +1 -0
- package/dist/types/project-definition.d.ts +30 -0
- package/dist/types/project-definition.d.ts.map +1 -0
- package/dist/types/project-definition.js +1 -0
- package/dist/ui/assets/cosmic-glass-bg-Dwx47zPu.webp +0 -0
- package/dist/ui/assets/index-C4NhMaq7.js +28 -0
- package/dist/ui/assets/index-DMZ7ZvZY.css +1 -0
- package/dist/ui/index.html +13 -0
- package/dist/update/cli.d.ts +8 -0
- package/dist/update/cli.d.ts.map +1 -0
- package/dist/update/cli.js +115 -0
- package/dist/update/index.d.ts +78 -0
- package/dist/update/index.d.ts.map +1 -0
- package/dist/update/index.js +285 -0
- package/dist/validate/index.d.ts +6 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +214 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +1 -0
- package/package.json +67 -4
- package/templates/.gitkeep +0 -0
- package/templates/default-instance/instruction/.cursorrules +59 -0
- package/templates/default-instance/instruction/.windsurfrules +59 -0
- package/templates/default-instance/instruction/CLAUDE.md +57 -0
- package/templates/default-instance/managed/agent.md +20 -0
- package/templates/default-instance/managed/bootstrap.md +7 -0
- package/templates/default-instance/managed/index-idea.md +5 -0
- package/templates/default-instance/managed/routing-idea.md +14 -0
- package/templates/default-instance/monolith/AGENT.md +1 -0
- package/templates/default-instance/monolith/context/boot/HANDOFF.md +49 -0
- package/templates/default-instance/monolith/context/boot/WORK_SESSION.md +30 -0
- package/templates/default-instance/monolith/context/execution/current-state.md +30 -0
- package/templates/default-instance/monolith/context/maps/dependency-map.md +38 -0
- package/templates/default-instance/monolith/context/maps/project-map.md +85 -0
- package/templates/default-instance/monolith/context/maps/roadmap-index.md +41 -0
- package/templates/default-instance/monolith/context/maps/routing-map.md +151 -0
- package/templates/default-instance/monolith/context/maps/semantic-map.md +75 -0
- package/templates/default-instance/monolith/context/models/model-registry.md +37 -0
- package/templates/default-instance/monolith/context/models/privacy-classification.md +35 -0
- package/templates/default-instance/monolith/context/templates/plan-template.md +56 -0
- package/templates/default-instance/monolith/governance/README.md +29 -0
- package/templates/default-instance/monolith/governance/RULE_SCHEMA.md +58 -0
- package/templates/default-instance/monolith/learning/INDEX.md +48 -0
- package/templates/default-instance/monolith/learning/injection-map.md +49 -0
- package/templates/default-instance/monolith/learning/taxonomy.md +30 -0
- package/templates/default-instance/monolith/memory/errors/README.md +26 -0
- package/templates/default-instance/monolith/memory/errors/SCHEMA.md +47 -0
- package/templates/default-instance/monolith/memory/errors/TAXONOMY.md +27 -0
- package/templates/default-instance/monolith/memory/long-term/architecture-principles.md +31 -0
- package/templates/default-instance/monolith/memory/long-term/decisions.md +36 -0
- package/templates/default-instance/monolith/memory/long-term/execution-history.md +20 -0
- package/templates/default-instance/monolith/memory/long-term/tech-debt.md +25 -0
- package/templates/default-instance/monolith/memory/working/active.md +41 -0
- package/templates/default-instance/monolith/memory/working/governance-result.md +0 -0
- package/templates/default-instance/monolith/memory/working/integrity-result.md +0 -0
- package/templates/default-instance/monolith/memory/working/story-index.md +28 -0
- package/templates/default-instance/monolith/protocols/boot/boot-sequence.md +59 -0
- package/templates/default-instance/monolith/protocols/cognition/cognitive-modes.md +90 -0
- package/templates/default-instance/monolith/protocols/cognition/dry-run.md +56 -0
- package/templates/default-instance/monolith/protocols/cognition/execution-trace-format.md +52 -0
- package/templates/default-instance/monolith/protocols/cognition/semantic-planning.md +51 -0
- package/templates/default-instance/monolith/protocols/execution/hybrid-execution.md +37 -0
- package/templates/default-instance/monolith/protocols/execution/local-llm-governance.md +42 -0
- package/templates/default-instance/monolith/protocols/governance/command-policy.md +51 -0
- package/templates/default-instance/monolith/protocols/governance/core-invariants.md +40 -0
- package/templates/default-instance/monolith/protocols/governance/memory-governance.md +54 -0
- package/templates/default-instance/monolith/protocols/governance/metadata-schema.md +62 -0
- package/templates/default-instance/monolith/protocols/governance/skills.md +53 -0
- package/templates/default-instance/monolith/protocols/governance/work-session.md +46 -0
- package/templates/default-instance/monolith/skills/INDEX.md +32 -0
- package/templates/default-instance/monolith/skills/current-status/SKILL.md +47 -0
- package/templates/default-instance/monolith/skills/finalize-story/SKILL.md +86 -0
- package/templates/default-instance/monolith/skills/global-status/SKILL.md +56 -0
- package/templates/default-instance/monolith/skills/idea-capture/SKILL.md +157 -0
- package/templates/default-instance/monolith/skills/self-improvement/SKILL.md +77 -0
- package/templates/default-instance/monolith/state.json +11 -0
- package/templates/default-instance/monolith/tests/INDEX.md +57 -0
- package/templates/default-instance/monolith/tests/scenarios/architecture-review.md +32 -0
- package/templates/default-instance/monolith/tests/scenarios/debug-failure.md +32 -0
- package/templates/default-instance/monolith/tests/scenarios/dry-run-activation.md +33 -0
- package/templates/default-instance/monolith/tests/scenarios/execution-trace-verification.md +31 -0
- package/templates/default-instance/monolith/tests/scenarios/finalize-story.md +35 -0
- package/templates/default-instance/monolith/tests/scenarios/implement-feature.md +36 -0
- package/templates/default-instance/monolith/tests/scenarios/local-llm-delegation.md +31 -0
- package/templates/default-instance/monolith/tests/scenarios/planning-request.md +32 -0
- package/templates/default-instance/monolith/tests/scenarios/sensitive-context-task.md +30 -0
- package/templates/default-instance/monolith/tests/scenarios/validator-verification.md +33 -0
- package/templates/default-instance/monolith/tests/validation/expected-behavior.md +70 -0
- package/templates/default-instance/monolith/tests/validators/INDEX.md +17 -0
- package/templates/default-instance/monolith/tests/validators/delegation-semantics.md +22 -0
- package/templates/default-instance/monolith/tests/validators/mode-consistency.md +25 -0
- package/templates/default-instance/monolith/tests/validators/privacy-routing.md +23 -0
- package/templates/default-instance/monolith/tests/validators/protocol-references.md +27 -0
- package/templates/default-instance/monolith/tests/validators/routing-integrity.md +30 -0
- package/templates/default-instance/projectroot/AGENTS.md +111 -0
- package/templates/default-instance/projectroot/monolith.config.json +32 -0
- package/templates/default-instance/projectroot/project.definition.mjs.template +41 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error taxonomy for the Coordinatorium State Keeper.
|
|
3
|
+
*
|
|
4
|
+
* Every error the state keeper or mission layer can throw is one of these.
|
|
5
|
+
* The CLI maps all of them to a non-zero exit code; `code` exists so a
|
|
6
|
+
* future caller (or a test) can distinguish failure classes without
|
|
7
|
+
* string-matching messages.
|
|
8
|
+
*/
|
|
9
|
+
import type { ProjectFileErrorCode } from '../root/index.js';
|
|
10
|
+
export type CoordinatoriumErrorCode = 'STATE_CORRUPTED' | 'STATE_SCHEMA_INVALID' | 'LOCK_TIMEOUT' | 'LOCK_CREATION_FAILED' | 'LOCK_UNSAFE' | 'STATE_FILE_UNSAFE' | 'MISSION_NOT_FOUND' | 'INVALID_TRANSITION' | 'VALIDATION_ERROR' | 'MISSION_NOT_ACTIVE' | 'CHECKPOINT_NOT_FOUND';
|
|
11
|
+
export declare class CoordinatoriumError extends Error {
|
|
12
|
+
readonly code: CoordinatoriumErrorCode;
|
|
13
|
+
constructor(code: CoordinatoriumErrorCode, message: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class StateCorruptedError extends CoordinatoriumError {
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class StateSchemaError extends CoordinatoriumError {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Thrown when `state.json` cannot be read or written within the bounded-file
|
|
23
|
+
* contract (SEC-001B.1): a FIFO or other special file, a file over its size
|
|
24
|
+
* limit (`FILE_TOO_LARGE`, also raised before a write would produce an
|
|
25
|
+
* oversized file), or an unreadable file. `fileCode` is the machine-readable
|
|
26
|
+
* reason. Nothing is modified, truncated or compacted when this is thrown.
|
|
27
|
+
*/
|
|
28
|
+
export declare class StateFileError extends CoordinatoriumError {
|
|
29
|
+
readonly fileCode: ProjectFileErrorCode;
|
|
30
|
+
constructor(fileCode: ProjectFileErrorCode, detail: string);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Thrown when the lock file exists but cannot be safely inspected (a FIFO or
|
|
34
|
+
* other special file, over the lock size limit, or unreadable). The lock is
|
|
35
|
+
* NOT acquired, and the file is never deleted: a human has to look at it.
|
|
36
|
+
*/
|
|
37
|
+
export declare class LockUnsafeError extends CoordinatoriumError {
|
|
38
|
+
readonly fileCode: ProjectFileErrorCode;
|
|
39
|
+
constructor(lockPath: string, fileCode: ProjectFileErrorCode, detail: string);
|
|
40
|
+
}
|
|
41
|
+
export declare class LockTimeoutError extends CoordinatoriumError {
|
|
42
|
+
/** The last failure that prevented acquisition (e.g. a stale-lock `unlink`
|
|
43
|
+
* error), kept so a timeout never hides why the lock could not be taken.
|
|
44
|
+
* Also set as the standard `cause`. */
|
|
45
|
+
readonly lastFailure?: unknown;
|
|
46
|
+
constructor(message: string, options?: {
|
|
47
|
+
lastFailure?: unknown;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The phase of the create/publish protocol (`lock.ts`'s `createAndPublish`)
|
|
52
|
+
* that failed. `staging-cleanup` is reported only via `cleanedUp: false` on
|
|
53
|
+
* whichever phase actually failed (cleanup is always best-effort and never
|
|
54
|
+
* itself the primary reason for a throw); it is listed here so the taxonomy
|
|
55
|
+
* names every step of the protocol, not because it is a distinct throw site.
|
|
56
|
+
*/
|
|
57
|
+
export type LockCreationPhase = 'staging-open' | 'metadata-write' | 'metadata-fsync' | 'publish-link' | 'staging-cleanup';
|
|
58
|
+
export interface LockCreationErrorParams {
|
|
59
|
+
lockPath: string;
|
|
60
|
+
phase: LockCreationPhase;
|
|
61
|
+
pid: number;
|
|
62
|
+
nonce: string;
|
|
63
|
+
/** Whether this process's own staging file was successfully removed
|
|
64
|
+
* (or was already gone) after the failure. */
|
|
65
|
+
cleanedUp: boolean;
|
|
66
|
+
/** The original fs error (e.g. ENOSPC/EIO), or whatever else was thrown. */
|
|
67
|
+
cause: unknown;
|
|
68
|
+
/** `UNKNOWN`: the publish step may or may not have created the lock (SEC-001B.2).
|
|
69
|
+
* Default `NOT_PUBLISHED`. */
|
|
70
|
+
publication?: 'NOT_PUBLISHED' | 'UNKNOWN';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Thrown when a lock (the primary `state.json.lock` or the internal
|
|
74
|
+
* recovery mutex) could not be created — a staging-file open, metadata
|
|
75
|
+
* write/fsync, or publish-link step failed. Never thrown for ordinary lock
|
|
76
|
+
* contention (an already-existing canonical lock is not a failure, just a
|
|
77
|
+
* reason to wait). The original fs error is preserved as `cause`/`causeCode`,
|
|
78
|
+
* never masked.
|
|
79
|
+
*/
|
|
80
|
+
export declare class LockCreationError extends CoordinatoriumError {
|
|
81
|
+
readonly lockPath: string;
|
|
82
|
+
readonly phase: LockCreationPhase;
|
|
83
|
+
readonly pid: number;
|
|
84
|
+
readonly nonce: string;
|
|
85
|
+
readonly timestamp: string;
|
|
86
|
+
readonly cleanedUp: boolean;
|
|
87
|
+
readonly publication: 'NOT_PUBLISHED' | 'UNKNOWN';
|
|
88
|
+
readonly cause: unknown;
|
|
89
|
+
readonly causeCode: string | undefined;
|
|
90
|
+
constructor(params: LockCreationErrorParams);
|
|
91
|
+
}
|
|
92
|
+
export declare class MissionNotFoundError extends CoordinatoriumError {
|
|
93
|
+
constructor(id: string);
|
|
94
|
+
}
|
|
95
|
+
export declare class InvalidTransitionError extends CoordinatoriumError {
|
|
96
|
+
constructor(message: string);
|
|
97
|
+
}
|
|
98
|
+
export declare class ValidationError extends CoordinatoriumError {
|
|
99
|
+
constructor(message: string);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Thrown when a checkpoint is requested to be created against a mission
|
|
103
|
+
* that exists but is not `in_progress` (COORDINATORIUM-002). Reading
|
|
104
|
+
* checkpoints for such a mission is unaffected — only creation is blocked.
|
|
105
|
+
*/
|
|
106
|
+
export declare class MissionNotActiveError extends CoordinatoriumError {
|
|
107
|
+
constructor(id: string, status: string);
|
|
108
|
+
}
|
|
109
|
+
export declare class CheckpointNotFoundError extends CoordinatoriumError {
|
|
110
|
+
constructor(id: string);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/coordinatorium/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error taxonomy for the Coordinatorium State Keeper.
|
|
3
|
+
*
|
|
4
|
+
* Every error the state keeper or mission layer can throw is one of these.
|
|
5
|
+
* The CLI maps all of them to a non-zero exit code; `code` exists so a
|
|
6
|
+
* future caller (or a test) can distinguish failure classes without
|
|
7
|
+
* string-matching messages.
|
|
8
|
+
*/
|
|
9
|
+
export class CoordinatoriumError extends Error {
|
|
10
|
+
code;
|
|
11
|
+
constructor(code, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'CoordinatoriumError';
|
|
14
|
+
this.code = code;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class StateCorruptedError extends CoordinatoriumError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super('STATE_CORRUPTED', message);
|
|
20
|
+
this.name = 'StateCorruptedError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class StateSchemaError extends CoordinatoriumError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super('STATE_SCHEMA_INVALID', message);
|
|
26
|
+
this.name = 'StateSchemaError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Thrown when `state.json` cannot be read or written within the bounded-file
|
|
31
|
+
* contract (SEC-001B.1): a FIFO or other special file, a file over its size
|
|
32
|
+
* limit (`FILE_TOO_LARGE`, also raised before a write would produce an
|
|
33
|
+
* oversized file), or an unreadable file. `fileCode` is the machine-readable
|
|
34
|
+
* reason. Nothing is modified, truncated or compacted when this is thrown.
|
|
35
|
+
*/
|
|
36
|
+
export class StateFileError extends CoordinatoriumError {
|
|
37
|
+
fileCode;
|
|
38
|
+
constructor(fileCode, detail) {
|
|
39
|
+
super('STATE_FILE_UNSAFE', `${fileCode}: ${detail}`);
|
|
40
|
+
this.name = 'StateFileError';
|
|
41
|
+
this.fileCode = fileCode;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Thrown when the lock file exists but cannot be safely inspected (a FIFO or
|
|
46
|
+
* other special file, over the lock size limit, or unreadable). The lock is
|
|
47
|
+
* NOT acquired, and the file is never deleted: a human has to look at it.
|
|
48
|
+
*/
|
|
49
|
+
export class LockUnsafeError extends CoordinatoriumError {
|
|
50
|
+
fileCode;
|
|
51
|
+
constructor(lockPath, fileCode, detail) {
|
|
52
|
+
super('LOCK_UNSAFE', `Lock at "${lockPath}" cannot be safely read (${fileCode}): ${detail}`);
|
|
53
|
+
this.name = 'LockUnsafeError';
|
|
54
|
+
this.fileCode = fileCode;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export class LockTimeoutError extends CoordinatoriumError {
|
|
58
|
+
/** The last failure that prevented acquisition (e.g. a stale-lock `unlink`
|
|
59
|
+
* error), kept so a timeout never hides why the lock could not be taken.
|
|
60
|
+
* Also set as the standard `cause`. */
|
|
61
|
+
lastFailure;
|
|
62
|
+
constructor(message, options = {}) {
|
|
63
|
+
super('LOCK_TIMEOUT', message);
|
|
64
|
+
this.name = 'LockTimeoutError';
|
|
65
|
+
if (options.lastFailure !== undefined) {
|
|
66
|
+
this.lastFailure = options.lastFailure;
|
|
67
|
+
this.cause = options.lastFailure;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Thrown when a lock (the primary `state.json.lock` or the internal
|
|
73
|
+
* recovery mutex) could not be created — a staging-file open, metadata
|
|
74
|
+
* write/fsync, or publish-link step failed. Never thrown for ordinary lock
|
|
75
|
+
* contention (an already-existing canonical lock is not a failure, just a
|
|
76
|
+
* reason to wait). The original fs error is preserved as `cause`/`causeCode`,
|
|
77
|
+
* never masked.
|
|
78
|
+
*/
|
|
79
|
+
export class LockCreationError extends CoordinatoriumError {
|
|
80
|
+
lockPath;
|
|
81
|
+
phase;
|
|
82
|
+
pid;
|
|
83
|
+
nonce;
|
|
84
|
+
timestamp;
|
|
85
|
+
cleanedUp;
|
|
86
|
+
publication;
|
|
87
|
+
cause;
|
|
88
|
+
causeCode;
|
|
89
|
+
constructor(params) {
|
|
90
|
+
const causeCode = params.cause?.code;
|
|
91
|
+
const causeMessage = params.cause instanceof Error ? params.cause.message : String(params.cause);
|
|
92
|
+
const timestamp = new Date().toISOString();
|
|
93
|
+
super('LOCK_CREATION_FAILED', `Failed to create lock at "${params.lockPath}" (phase: ${params.phase}` +
|
|
94
|
+
`${causeCode ? `, cause: ${causeCode}` : ''}): ${causeMessage} ` +
|
|
95
|
+
`[pid=${params.pid} nonce=${params.nonce} at=${timestamp} cleanedUp=${params.cleanedUp}]` +
|
|
96
|
+
(params.publication === 'UNKNOWN'
|
|
97
|
+
? ` Whether the lock was created is UNKNOWN: inspect "${params.lockPath}" before retrying; nothing was retried or removed.`
|
|
98
|
+
: ''));
|
|
99
|
+
this.name = 'LockCreationError';
|
|
100
|
+
this.lockPath = params.lockPath;
|
|
101
|
+
this.phase = params.phase;
|
|
102
|
+
this.pid = params.pid;
|
|
103
|
+
this.nonce = params.nonce;
|
|
104
|
+
this.timestamp = timestamp;
|
|
105
|
+
this.cleanedUp = params.cleanedUp;
|
|
106
|
+
this.publication = params.publication ?? 'NOT_PUBLISHED';
|
|
107
|
+
this.cause = params.cause;
|
|
108
|
+
this.causeCode = causeCode;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export class MissionNotFoundError extends CoordinatoriumError {
|
|
112
|
+
constructor(id) {
|
|
113
|
+
super('MISSION_NOT_FOUND', `No mission found with id "${id}"`);
|
|
114
|
+
this.name = 'MissionNotFoundError';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export class InvalidTransitionError extends CoordinatoriumError {
|
|
118
|
+
constructor(message) {
|
|
119
|
+
super('INVALID_TRANSITION', message);
|
|
120
|
+
this.name = 'InvalidTransitionError';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export class ValidationError extends CoordinatoriumError {
|
|
124
|
+
constructor(message) {
|
|
125
|
+
super('VALIDATION_ERROR', message);
|
|
126
|
+
this.name = 'ValidationError';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Thrown when a checkpoint is requested to be created against a mission
|
|
131
|
+
* that exists but is not `in_progress` (COORDINATORIUM-002). Reading
|
|
132
|
+
* checkpoints for such a mission is unaffected — only creation is blocked.
|
|
133
|
+
*/
|
|
134
|
+
export class MissionNotActiveError extends CoordinatoriumError {
|
|
135
|
+
constructor(id, status) {
|
|
136
|
+
super('MISSION_NOT_ACTIVE', `Mission "${id}" is not active (status: "${status}") — cannot create a checkpoint`);
|
|
137
|
+
this.name = 'MissionNotActiveError';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export class CheckpointNotFoundError extends CoordinatoriumError {
|
|
141
|
+
constructor(id) {
|
|
142
|
+
super('CHECKPOINT_NOT_FOUND', `No checkpoint found with id "${id}"`);
|
|
143
|
+
this.name = 'CheckpointNotFoundError';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { CoordinatoriumState, StrategicState, TacticalState, Mission, MissionStatus, MissionResult, Checkpoint, } from './types.js';
|
|
2
|
+
export { STATE_SCHEMA_VERSION, createInitialState } from './types.js';
|
|
3
|
+
export { CoordinatoriumError, StateCorruptedError, StateSchemaError, LockTimeoutError, LockUnsafeError, StateFileError, MissionNotFoundError, InvalidTransitionError, ValidationError, MissionNotActiveError, CheckpointNotFoundError, } from './errors.js';
|
|
4
|
+
export type { CoordinatoriumErrorCode } from './errors.js';
|
|
5
|
+
export { readState, mutateState, validateState, stateFilePath, STATE_FILE_NAME, MAX_STATE_BYTES, projectTarget, trustedDirectoryTarget, } from './state-keeper.js';
|
|
6
|
+
export type { StateWriteTarget, StateReadTarget } from './state-keeper.js';
|
|
7
|
+
export { startMission, listMissions, getMission, finishMission } from './mission.js';
|
|
8
|
+
export type { StartMissionInput, FinishMissionInput } from './mission.js';
|
|
9
|
+
export { createCheckpoint, listCheckpoints, getCheckpoint } from './checkpoint.js';
|
|
10
|
+
export type { CreateCheckpointInput } from './checkpoint.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/coordinatorium/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,OAAO,EACP,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EACL,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,eAAe,EACf,eAAe,EACf,aAAa,EACb,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACrF,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnF,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { STATE_SCHEMA_VERSION, createInitialState } from './types.js';
|
|
2
|
+
export { CoordinatoriumError, StateCorruptedError, StateSchemaError, LockTimeoutError, LockUnsafeError, StateFileError, MissionNotFoundError, InvalidTransitionError, ValidationError, MissionNotActiveError, CheckpointNotFoundError, } from './errors.js';
|
|
3
|
+
export { readState, mutateState, validateState, stateFilePath, STATE_FILE_NAME, MAX_STATE_BYTES, projectTarget, trustedDirectoryTarget, } from './state-keeper.js';
|
|
4
|
+
export { startMission, listMissions, getMission, finishMission } from './mission.js';
|
|
5
|
+
export { createCheckpoint, listCheckpoints, getCheckpoint } from './checkpoint.js';
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal cross-process file lock.
|
|
3
|
+
*
|
|
4
|
+
* Acquisition is a single `open(path, 'wx')` syscall — it either creates the
|
|
5
|
+
* file or fails with EEXIST, atomically, on every platform Node supports.
|
|
6
|
+
* That atomicity is what actually prevents two processes from both believing
|
|
7
|
+
* they own the lock at once; the lock file's body (a JSON owner record) exists only
|
|
8
|
+
* to support crash recovery.
|
|
9
|
+
*
|
|
10
|
+
* Three invariants govern every decision in this module:
|
|
11
|
+
*
|
|
12
|
+
* 1. Lock ownership is not time-based.
|
|
13
|
+
* The age of the lock file is never, by itself, a reason to take it.
|
|
14
|
+
* 2. Uncertainty never grants ownership.
|
|
15
|
+
* If we cannot reliably establish that the recorded owner has exited,
|
|
16
|
+
* the lock is treated as held — we wait, we do not recover it.
|
|
17
|
+
* 3. Timeout ends waiting, not ownership.
|
|
18
|
+
* `timeoutMs` bounds how long a *challenger* will wait to acquire the
|
|
19
|
+
* lock. It says nothing about how long the current owner may hold it.
|
|
20
|
+
*
|
|
21
|
+
* A fourth, narrower rule governs recovery specifically:
|
|
22
|
+
*
|
|
23
|
+
* 4. Recovering a dead owner's lock is itself serialized.
|
|
24
|
+
* "Read the lock, confirm its owner is dead, re-read it, and delete it
|
|
25
|
+
* if unchanged" is NOT one atomic operation — the re-read and the
|
|
26
|
+
* delete are two separate syscalls, and another process can complete
|
|
27
|
+
* an entire recovery (delete the stale lock, create its own) in the
|
|
28
|
+
* gap between them. A recovery mutex (`<lockPath>.recovery`, itself
|
|
29
|
+
* just another `open(..., 'wx')`-guarded file) makes "verify and then
|
|
30
|
+
* delete a specific stale lock" exclusive, so at most one process is
|
|
31
|
+
* ever in a position to delete a given dead owner's record.
|
|
32
|
+
*
|
|
33
|
+
* Known limitation: this is a local-filesystem lock built on POSIX rename/
|
|
34
|
+
* open semantics. It does not claim to provide distributed coordination,
|
|
35
|
+
* and its guarantees are not evaluated on network filesystems (NFS, SMB,
|
|
36
|
+
* etc.) where `open(..., 'wx')` atomicity is not always dependable.
|
|
37
|
+
*
|
|
38
|
+
* A fifth invariant governs *creating* a lock (primary or recovery mutex)
|
|
39
|
+
* specifically:
|
|
40
|
+
*
|
|
41
|
+
* 5. A lock is never published in a partially-written state.
|
|
42
|
+
* Metadata is written in full to a private staging file
|
|
43
|
+
* (`<lockPath>.creating.<pid>.<nonce>`, a name no other acquirer can
|
|
44
|
+
* ever guess or collide with; created `O_EXCL|O_NOFOLLOW` at mode `0600`
|
|
45
|
+
* through the shared safe-write primitive, SEC-001B.2) and only
|
|
46
|
+
* published — via `link()`,
|
|
47
|
+
* which is exactly as exclusive a create-if-absent primitive as
|
|
48
|
+
* `open(path, 'wx')` — once that write (and, if requested, an fsync)
|
|
49
|
+
* has fully succeeded. If anything fails first, the canonical path
|
|
50
|
+
* never existed because of this attempt; only this process's own
|
|
51
|
+
* uniquely-named staging file is ever touched by its own cleanup. See
|
|
52
|
+
* `createAndPublish` below and ADR-008 for the full argument.
|
|
53
|
+
*/
|
|
54
|
+
import { type FsOps, type ParentRef } from '../root/index.js';
|
|
55
|
+
/** A well-formed record (see `LockRecord`) is under 200 bytes; 256 B is generous. */
|
|
56
|
+
export declare const MAX_LOCK_BYTES = 256;
|
|
57
|
+
export interface LockOptions {
|
|
58
|
+
/** How long a challenger will wait to acquire the lock before giving up. */
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
/** Delay between acquisition attempts while waiting. */
|
|
61
|
+
retryDelayMs?: number;
|
|
62
|
+
/** Fsync the staged metadata file before publishing it as the canonical
|
|
63
|
+
* lock. A durability option, not a correctness requirement: mutual
|
|
64
|
+
* exclusion comes entirely from `linkSync`/`open(path, 'wx')` atomicity,
|
|
65
|
+
* with or without this. Default `false` — without it, metadata for a
|
|
66
|
+
* lock acquired just before a hard crash/power loss may not have reached
|
|
67
|
+
* disk yet, though the lock's atomicity and exclusivity guarantees are
|
|
68
|
+
* unaffected either way; this never claims power-loss durability when
|
|
69
|
+
* left at its default. */
|
|
70
|
+
fsyncMetadata?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Test-only hooks for forcing specific, otherwise-timing-dependent
|
|
74
|
+
* interleavings deterministically. Every hook defaults to a no-op; no
|
|
75
|
+
* production code path (in particular, `state-keeper.ts`) ever supplies
|
|
76
|
+
* one. Do not use this outside tests.
|
|
77
|
+
*/
|
|
78
|
+
export interface LockTestHooks {
|
|
79
|
+
/** Invoked right after an owner is confirmed dead, before attempting to
|
|
80
|
+
* acquire the recovery mutex. */
|
|
81
|
+
beforeRecoveryAttempt?: () => void | Promise<void>;
|
|
82
|
+
/** Invoked immediately before the confirmed-dead owner's lock file is
|
|
83
|
+
* actually unlinked, while still holding the recovery mutex. */
|
|
84
|
+
beforeStaleUnlink?: () => void | Promise<void>;
|
|
85
|
+
/** Replaces the low-level write call used while writing a lock's staged
|
|
86
|
+
* metadata, so tests can force a deterministic fault (e.g. throw ENOSPC
|
|
87
|
+
* before any byte is written, or write a short chunk and then throw) —
|
|
88
|
+
* never supplied by production code. */
|
|
89
|
+
writeMetadata?: (fd: number, buffer: Buffer, offset: number, length: number) => number;
|
|
90
|
+
/** Invoked after a staging-write/fsync failure is detected (and the fd
|
|
91
|
+
* closed) but before the best-effort cleanup of this process's own
|
|
92
|
+
* staging file — lets tests pause a failed creator at exactly the point
|
|
93
|
+
* where only its own staging file exists on disk. */
|
|
94
|
+
beforeStagingCleanupOnFailure?: () => void | Promise<void>;
|
|
95
|
+
/** Replaces the `unlink` of a confirmed-dead owner's lock during stale
|
|
96
|
+
* recovery, so tests can force a persistent failure (EACCES/EROFS/EIO). */
|
|
97
|
+
unlinkLock?: (path: string) => void;
|
|
98
|
+
/** Replaces the `/proc` and `kill(pid, 0)` probes used to identify this
|
|
99
|
+
* process and to judge a recorded owner — lets tests simulate another PID
|
|
100
|
+
* namespace or boot without real namespace support. */
|
|
101
|
+
procOps?: Partial<ProcOps>;
|
|
102
|
+
/** Replaces individual filesystem calls made by the safe-write primitive
|
|
103
|
+
* while creating, publishing and removing staging files — fault injection
|
|
104
|
+
* for publication states. Never supplied by production code. */
|
|
105
|
+
fsOps?: Partial<FsOps>;
|
|
106
|
+
/** Replaces the monotonic clock (milliseconds) that drives the deadline. */
|
|
107
|
+
now?: () => number;
|
|
108
|
+
/** Replaces the jitter source: a value in `[0, 1)`. */
|
|
109
|
+
random?: () => number;
|
|
110
|
+
/** Replaces the wait between attempts; receives the requested milliseconds. */
|
|
111
|
+
sleep?: (ms: number) => Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
export type LivenessVerdict = 'alive' | 'dead' | 'unknown';
|
|
114
|
+
/** What identifies a process beyond its PID. A PID is only meaningful inside
|
|
115
|
+
* one PID namespace of one boot, and may be reused after its owner exits. Any
|
|
116
|
+
* field is `null` when the platform cannot provide it. */
|
|
117
|
+
export interface OwnerIdentity {
|
|
118
|
+
/** Kernel boot id (`/proc/sys/kernel/random/boot_id`): distinguishes boots and hosts. */
|
|
119
|
+
boot: string | null;
|
|
120
|
+
/** PID namespace (`readlink /proc/self/ns/pid`, `pid:[inode]`). */
|
|
121
|
+
pidns: string | null;
|
|
122
|
+
/** Process start time in clock ticks since boot (`/proc/<pid>/stat`, field 22): detects PID reuse. */
|
|
123
|
+
start: string | null;
|
|
124
|
+
}
|
|
125
|
+
/** The OS probes liveness decisions rest on; injectable for tests only. */
|
|
126
|
+
export interface ProcOps {
|
|
127
|
+
platform: NodeJS.Platform;
|
|
128
|
+
pid: number;
|
|
129
|
+
readText(path: string): string;
|
|
130
|
+
readlink(path: string): string;
|
|
131
|
+
kill(pid: number, signal: 0): void;
|
|
132
|
+
}
|
|
133
|
+
/** This process's identity, or all-`null` when it cannot be established
|
|
134
|
+
* (non-Linux, or `/proc` that does not belong to our PID namespace). */
|
|
135
|
+
export declare function captureIdentity(ops?: ProcOps): OwnerIdentity;
|
|
136
|
+
/**
|
|
137
|
+
* Decide whether the owner named by `record` is alive, from verifiable
|
|
138
|
+
* identity rather than from `kill(pid, 0)` alone (H1, LOCK-FIX-001).
|
|
139
|
+
*
|
|
140
|
+
* A PID is only meaningful inside the PID namespace and boot that issued it, so
|
|
141
|
+
* `ESRCH` is NOT proof of death by itself: a process in another namespace
|
|
142
|
+
* sharing the filesystem would see a live owner's PID as "no such process".
|
|
143
|
+
* `dead` therefore requires the record's boot and PID namespace to equal ours,
|
|
144
|
+
* and either a different start time for that PID (it was reused) or two
|
|
145
|
+
* independent probes agreeing it does not exist (`/proc/<pid>` absent AND
|
|
146
|
+
* `ESRCH`). Everything else is `unknown`, which — like `alive` — forbids
|
|
147
|
+
* recovery. Rules are checked top to bottom; the first match decides. The age
|
|
148
|
+
* of the lock plays no part.
|
|
149
|
+
*/
|
|
150
|
+
export declare function judgeOwner(record: LockRecord, ours: OwnerIdentity, ops?: ProcOps): {
|
|
151
|
+
verdict: LivenessVerdict;
|
|
152
|
+
reason: string;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* The lock file's body: one line of strict JSON. `pid` and `nonce` identify the
|
|
156
|
+
* attempt; `boot`, `pidns` and `start` identify the owning process well enough
|
|
157
|
+
* for another process to judge whether it is dead (H1). There is no support for
|
|
158
|
+
* the earlier `pid:nonce` format: such a file is "unrecognised", never judged
|
|
159
|
+
* and never deleted.
|
|
160
|
+
*/
|
|
161
|
+
export interface LockRecord extends OwnerIdentity {
|
|
162
|
+
pid: number;
|
|
163
|
+
nonce: string;
|
|
164
|
+
}
|
|
165
|
+
/** The exact bytes written as a lock file's body. */
|
|
166
|
+
export declare function formatLockRecord(r: LockRecord): string;
|
|
167
|
+
/**
|
|
168
|
+
* Acquire `lockPath` exclusively. Waits up to `timeoutMs` for the current
|
|
169
|
+
* owner (if any) to release it, attempting liveness-gated, mutex-serialized
|
|
170
|
+
* recovery only when the owner is provably dead (`judgeOwner`). Returns a
|
|
171
|
+
* release function. `testHooks` exists solely to let tests force specific
|
|
172
|
+
* interleavings deterministically — production callers must never pass it.
|
|
173
|
+
*
|
|
174
|
+
* One monotonic deadline governs the whole call and is checked at the top of
|
|
175
|
+
* EVERY iteration, and the only `continue` sits at the tail after all
|
|
176
|
+
* bookkeeping, so no branch (recovery progress, aborted recovery, a failing
|
|
177
|
+
* `unlink`) can skip the check or the wait. Waits are a bounded exponential backoff with jitter in `[d/2, d]`,
|
|
178
|
+
* never longer than the time remaining. After a recovery the lock path is
|
|
179
|
+
* observed again rather than assumed free; at most
|
|
180
|
+
* `MAX_PROGRESS_WITHOUT_SLEEP` iterations in a row may run without waiting.
|
|
181
|
+
*
|
|
182
|
+
* The deadline bounds the waiting loop and the number of retries. It does not
|
|
183
|
+
* bound a single synchronous filesystem call that hangs inside the kernel
|
|
184
|
+
* (e.g. a stuck mount): no timer can run while it blocks.
|
|
185
|
+
*/
|
|
186
|
+
export declare function acquireLock(lockPath: string, options?: LockOptions, testHooks?: LockTestHooks, parent?: ParentRef): Promise<() => void>;
|
|
187
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/coordinatorium/lock.ts"],"names":[],"mappings":"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"}
|