@guyghost/swarm-dao-core 0.16.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/persistence/file-dao-state.repository.d.ts +35 -0
- package/dist/adapters/persistence/file-dao-state.repository.d.ts.map +1 -1
- package/dist/adapters/persistence/file-dao-state.repository.js +316 -30
- package/dist/adapters/persistence/file-dao-state.repository.js.map +1 -1
- package/dist/adapters/ship-audit/fs-ship-audit.store.d.ts.map +1 -1
- package/dist/adapters/ship-audit/fs-ship-audit.store.js +57 -2
- package/dist/adapters/ship-audit/fs-ship-audit.store.js.map +1 -1
- package/dist/application/proposals/control-proposal.use-case.d.ts +2 -1
- package/dist/application/proposals/control-proposal.use-case.d.ts.map +1 -1
- package/dist/application/proposals/control-proposal.use-case.js +16 -3
- package/dist/application/proposals/control-proposal.use-case.js.map +1 -1
- package/dist/application/proposals/create-proposal.use-case.d.ts.map +1 -1
- package/dist/application/proposals/create-proposal.use-case.js +22 -0
- package/dist/application/proposals/create-proposal.use-case.js.map +1 -1
- package/dist/application/proposals/deliberate-proposal.use-case.d.ts +4 -0
- package/dist/application/proposals/deliberate-proposal.use-case.d.ts.map +1 -1
- package/dist/application/proposals/deliberate-proposal.use-case.js +33 -13
- package/dist/application/proposals/deliberate-proposal.use-case.js.map +1 -1
- package/dist/application/proposals/record-deliberation-outputs.use-case.d.ts.map +1 -1
- package/dist/application/proposals/record-deliberation-outputs.use-case.js +16 -2
- package/dist/application/proposals/record-deliberation-outputs.use-case.js.map +1 -1
- package/dist/application/proposals/reject-proposal.use-case.d.ts +3 -3
- package/dist/application/proposals/reject-proposal.use-case.d.ts.map +1 -1
- package/dist/application/proposals/reject-proposal.use-case.js +6 -4
- package/dist/application/proposals/reject-proposal.use-case.js.map +1 -1
- package/dist/application/proposals/ship-proposal.use-case.d.ts +1 -0
- package/dist/application/proposals/ship-proposal.use-case.d.ts.map +1 -1
- package/dist/application/proposals/ship-proposal.use-case.js +31 -4
- package/dist/application/proposals/ship-proposal.use-case.js.map +1 -1
- package/dist/commands/registry.js +3 -3
- package/dist/commands/registry.js.map +1 -1
- package/dist/config.d.ts +9 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +184 -6
- package/dist/config.js.map +1 -1
- package/dist/control/gates.d.ts +10 -1
- package/dist/control/gates.d.ts.map +1 -1
- package/dist/control/gates.js +105 -43
- package/dist/control/gates.js.map +1 -1
- package/dist/delivery/execution-isolation.d.ts +3 -0
- package/dist/delivery/execution-isolation.d.ts.map +1 -1
- package/dist/delivery/execution-isolation.js +5 -0
- package/dist/delivery/execution-isolation.js.map +1 -1
- package/dist/governance/agents.d.ts.map +1 -1
- package/dist/governance/agents.js +5 -1
- package/dist/governance/agents.js.map +1 -1
- package/dist/governance/delegation.utils.d.ts +16 -0
- package/dist/governance/delegation.utils.d.ts.map +1 -1
- package/dist/governance/delegation.utils.js +68 -0
- package/dist/governance/delegation.utils.js.map +1 -1
- package/dist/governance/proposal.utils.d.ts +11 -1
- package/dist/governance/proposal.utils.d.ts.map +1 -1
- package/dist/governance/proposal.utils.js +40 -7
- package/dist/governance/proposal.utils.js.map +1 -1
- package/dist/governance/voting.d.ts +10 -2
- package/dist/governance/voting.d.ts.map +1 -1
- package/dist/governance/voting.js +41 -9
- package/dist/governance/voting.js.map +1 -1
- package/dist/host-tools/github-config.d.ts.map +1 -1
- package/dist/host-tools/github-config.js +14 -2
- package/dist/host-tools/github-config.js.map +1 -1
- package/dist/host-tools/handlers.d.ts +3 -0
- package/dist/host-tools/handlers.d.ts.map +1 -1
- package/dist/host-tools/handlers.js +91 -28
- package/dist/host-tools/handlers.js.map +1 -1
- package/dist/integrations/github.d.ts +1 -0
- package/dist/integrations/github.d.ts.map +1 -1
- package/dist/integrations/github.js +41 -0
- package/dist/integrations/github.js.map +1 -1
- package/dist/intelligence/index.d.ts +1 -0
- package/dist/intelligence/index.d.ts.map +1 -1
- package/dist/intelligence/index.js +1 -0
- package/dist/intelligence/index.js.map +1 -1
- package/dist/intelligence/roundtable.d.ts.map +1 -1
- package/dist/intelligence/roundtable.js +10 -0
- package/dist/intelligence/roundtable.js.map +1 -1
- package/dist/intelligence/runtime.d.ts +61 -0
- package/dist/intelligence/runtime.d.ts.map +1 -0
- package/dist/intelligence/runtime.js +135 -0
- package/dist/intelligence/runtime.js.map +1 -0
- package/dist/intelligence/sequential.d.ts +3 -0
- package/dist/intelligence/sequential.d.ts.map +1 -1
- package/dist/intelligence/sequential.js +30 -0
- package/dist/intelligence/sequential.js.map +1 -1
- package/dist/intelligence/swarm.d.ts +11 -1
- package/dist/intelligence/swarm.d.ts.map +1 -1
- package/dist/intelligence/swarm.js +62 -3
- package/dist/intelligence/swarm.js.map +1 -1
- package/dist/models/proposal.machine.d.ts +11 -0
- package/dist/models/proposal.machine.d.ts.map +1 -1
- package/dist/models/proposal.machine.js +14 -3
- package/dist/models/proposal.machine.js.map +1 -1
- package/dist/persistence.d.ts +23 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +77 -38
- package/dist/persistence.js.map +1 -1
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/host.d.ts +8 -1
- package/dist/utils/host.d.ts.map +1 -1
- package/dist/utils/host.js +25 -3
- package/dist/utils/host.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,50 @@
|
|
|
1
1
|
import type { DaoStateRepositoryPort } from "../../ports/repository.js";
|
|
2
2
|
import { type DAOState } from "../../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Repair ID counters from the maximum existing ids (issue #157). Shared by
|
|
5
|
+
* every load path (loadState and FileDaoStateRepository.open) so both produce
|
|
6
|
+
* identical, collision-free counters — e.g. a nextProposalId left behind a
|
|
7
|
+
* hand-edit or a restored backup no longer yields duplicate proposal ids.
|
|
8
|
+
*/
|
|
9
|
+
export declare function repairCounters(state: DAOState): void;
|
|
3
10
|
/** Instance-owned filesystem adapter. No process-global DAO state or write cache. */
|
|
4
11
|
export declare class FileDaoStateRepository implements DaoStateRepositoryPort {
|
|
5
12
|
private readonly state;
|
|
6
13
|
private readonly daoRoot;
|
|
7
14
|
private readonly writeCache;
|
|
15
|
+
private writeQueue;
|
|
16
|
+
/** Last state.json content this instance read or wrote, for cheap divergence checks. */
|
|
17
|
+
private rawStateOnDisk;
|
|
18
|
+
/** Revision this instance last read from, or wrote to, disk (issue #153). */
|
|
19
|
+
private seenRevision;
|
|
8
20
|
private constructor();
|
|
21
|
+
/** Wrap already-loaded state so compatibility loaders share the locked persist path. */
|
|
22
|
+
static fromLoaded(state: DAOState, rawStateOnDisk: string | null): FileDaoStateRepository;
|
|
9
23
|
static open(cwd: string): Promise<FileDaoStateRepository>;
|
|
10
24
|
get(): DAOState;
|
|
11
25
|
persist(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Whether persist() would write anything. Mirrors the per-file decisions in
|
|
28
|
+
* writeIfChanged/persistDecisions: when every serialized payload matches the
|
|
29
|
+
* write cache, persist is a no-op and can skip lock, check and I/O.
|
|
30
|
+
*/
|
|
31
|
+
private hasPendingWrites;
|
|
32
|
+
/**
|
|
33
|
+
* Fail-fast optimistic concurrency: if another process persisted a state
|
|
34
|
+
* this instance has not seen, refuse to overwrite it instead of silently
|
|
35
|
+
* dropping votes/proposals (last-writer-wins).
|
|
36
|
+
*
|
|
37
|
+
* Returns the revision currently on disk. Two layers:
|
|
38
|
+
* 1. stateRevision comparison (issue #153) — catches ANY divergence,
|
|
39
|
+
* including votes added to a proposal known to both processes.
|
|
40
|
+
* 2. proposal-id/counter checks — catch hand-edited files whose revision
|
|
41
|
+
* was not bumped (state.json is a documented, readable format).
|
|
42
|
+
*/
|
|
43
|
+
private checkNoConcurrentModification;
|
|
44
|
+
/** Decisions to persist: closed proposals only, ordered by id. Single source for persistDecisions and hasPendingWrites. */
|
|
45
|
+
private closedDecisions;
|
|
12
46
|
private persistDecisions;
|
|
13
47
|
private writeIfChanged;
|
|
14
48
|
}
|
|
49
|
+
export declare function withFileLock<T>(daoRoot: string, fn: () => Promise<T>): Promise<T>;
|
|
15
50
|
//# sourceMappingURL=file-dao-state.repository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-dao-state.repository.d.ts","sourceRoot":"","sources":["../../../src/adapters/persistence/file-dao-state.repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-dao-state.repository.d.ts","sourceRoot":"","sources":["../../../src/adapters/persistence/file-dao-state.repository.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAsB,KAAK,QAAQ,EAAuB,MAAM,sBAAsB,CAAC;AAU9F;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAWpD;AAoDD,qFAAqF;AACrF,qBAAa,sBAAuB,YAAW,sBAAsB;IAWjE,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,UAAU,CAAoC;IAEtD,wFAAwF;IACxF,OAAO,CAAC,cAAc,CAAgB;IAEtC,6EAA6E;IAC7E,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO;IAyBP,wFAAwF;WAC1E,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,sBAAsB;WAS5E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAqC/D,GAAG,IAAI,QAAQ;IAIT,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBrC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;;;;;;;;;OAUG;YACW,6BAA6B;IA0C3C,2HAA2H;IAC3H,OAAO,CAAC,eAAe;YAiBT,gBAAgB;YAYhB,cAAc;CAM7B;AA0BD,wBAAsB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAsDvF"}
|
|
@@ -1,65 +1,244 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { logger } from "../../observability/logging.js";
|
|
3
4
|
import { createInitialState } from "../../types/index.js";
|
|
4
5
|
function formatJson(value) {
|
|
5
6
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
6
7
|
}
|
|
8
|
+
function isPositiveInteger(value) {
|
|
9
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Repair ID counters from the maximum existing ids (issue #157). Shared by
|
|
13
|
+
* every load path (loadState and FileDaoStateRepository.open) so both produce
|
|
14
|
+
* identical, collision-free counters — e.g. a nextProposalId left behind a
|
|
15
|
+
* hand-edit or a restored backup no longer yields duplicate proposal ids.
|
|
16
|
+
*/
|
|
17
|
+
export function repairCounters(state) {
|
|
18
|
+
const highestProposalId = state.proposals.reduce((max, proposal) => {
|
|
19
|
+
const id = proposal.id;
|
|
20
|
+
return isPositiveInteger(id) ? Math.max(max, id) : max;
|
|
21
|
+
}, 0);
|
|
22
|
+
if (state.nextProposalId <= highestProposalId)
|
|
23
|
+
state.nextProposalId = highestProposalId + 1;
|
|
24
|
+
const highestAuditId = state.auditLog.reduce((max, entry) => {
|
|
25
|
+
const id = entry?.id;
|
|
26
|
+
return isPositiveInteger(id) ? Math.max(max, id) : max;
|
|
27
|
+
}, 0);
|
|
28
|
+
if (state.nextAuditId <= highestAuditId)
|
|
29
|
+
state.nextAuditId = highestAuditId + 1;
|
|
30
|
+
}
|
|
31
|
+
function readRevision(value) {
|
|
32
|
+
return isPositiveInteger(value.stateRevision) ? value.stateRevision : 0;
|
|
33
|
+
}
|
|
7
34
|
function repairState(value, daoRoot) {
|
|
8
35
|
const fallback = createInitialState(daoRoot);
|
|
9
36
|
const state = { ...fallback, ...value, daoRoot };
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
typeof
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
let repaired = false;
|
|
38
|
+
// Shape substitution means the file was damaged (partial write, manual edit,
|
|
39
|
+
// restore): keep defaults but flag it so the original gets backed up.
|
|
40
|
+
const arrayOr = (candidate, fallbackValue) => {
|
|
41
|
+
if (Array.isArray(candidate))
|
|
42
|
+
return candidate;
|
|
43
|
+
repaired = true;
|
|
44
|
+
return fallbackValue;
|
|
45
|
+
};
|
|
46
|
+
const recordOr = (candidate, fallbackValue) => {
|
|
47
|
+
// Plain objects only (review): a truthy primitive (string/number) in a
|
|
48
|
+
// corrupted state.json must be substituted, not adopted as a record.
|
|
49
|
+
if (typeof candidate === "object" && candidate !== null && !Array.isArray(candidate))
|
|
50
|
+
return candidate;
|
|
51
|
+
repaired = true;
|
|
52
|
+
return fallbackValue;
|
|
53
|
+
};
|
|
54
|
+
const intOr = (candidate, fallbackValue) => {
|
|
55
|
+
if (isPositiveInteger(candidate))
|
|
56
|
+
return candidate;
|
|
57
|
+
repaired = true;
|
|
58
|
+
return fallbackValue;
|
|
59
|
+
};
|
|
60
|
+
state.proposals = arrayOr(value.proposals, []);
|
|
61
|
+
state.agents = arrayOr(value.agents, []);
|
|
62
|
+
state.auditLog = arrayOr(value.auditLog, []);
|
|
63
|
+
state.controlResults = recordOr(value.controlResults, {});
|
|
64
|
+
state.deliveryPlans = recordOr(value.deliveryPlans, {});
|
|
65
|
+
state.artefacts = recordOr(value.artefacts, {});
|
|
66
|
+
state.outcomes = recordOr(value.outcomes, {});
|
|
67
|
+
state.snapshots = recordOr(value.snapshots, {});
|
|
68
|
+
state.verifications = recordOr(value.verifications, {});
|
|
69
|
+
state.nextProposalId = intOr(value.nextProposalId, 1);
|
|
70
|
+
state.nextAuditId = intOr(value.nextAuditId, 1);
|
|
71
|
+
state.stateRevision = readRevision(value);
|
|
72
|
+
repairCounters(state);
|
|
73
|
+
return { state, repaired };
|
|
26
74
|
}
|
|
27
75
|
/** Instance-owned filesystem adapter. No process-global DAO state or write cache. */
|
|
28
76
|
export class FileDaoStateRepository {
|
|
29
77
|
state;
|
|
30
78
|
daoRoot;
|
|
31
79
|
writeCache = new Map();
|
|
32
|
-
|
|
80
|
+
writeQueue = Promise.resolve();
|
|
81
|
+
/** Last state.json content this instance read or wrote, for cheap divergence checks. */
|
|
82
|
+
rawStateOnDisk;
|
|
83
|
+
/** Revision this instance last read from, or wrote to, disk (issue #153). */
|
|
84
|
+
seenRevision;
|
|
85
|
+
constructor(state, daoRoot, rawStateOnDisk,
|
|
86
|
+
/** Revision already extracted by the caller's parse — passing it avoids a
|
|
87
|
+
* second full JSON.parse of state.json (measured +86% on a 500-proposal
|
|
88
|
+
* reload when this constructor re-parsed). */
|
|
89
|
+
seenRevision) {
|
|
33
90
|
this.state = state;
|
|
34
91
|
this.daoRoot = daoRoot;
|
|
92
|
+
this.rawStateOnDisk = rawStateOnDisk;
|
|
93
|
+
if (seenRevision !== undefined) {
|
|
94
|
+
this.seenRevision = seenRevision;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let revision = 0;
|
|
98
|
+
if (rawStateOnDisk) {
|
|
99
|
+
try {
|
|
100
|
+
revision = readRevision(JSON.parse(rawStateOnDisk));
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
revision = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this.seenRevision = revision;
|
|
107
|
+
}
|
|
108
|
+
/** Wrap already-loaded state so compatibility loaders share the locked persist path. */
|
|
109
|
+
static fromLoaded(state, rawStateOnDisk) {
|
|
110
|
+
return new FileDaoStateRepository(state, state.daoRoot, rawStateOnDisk, isPositiveInteger(state.stateRevision) ? state.stateRevision : undefined);
|
|
35
111
|
}
|
|
36
112
|
static async open(cwd) {
|
|
37
113
|
const daoRoot = path.join(cwd, ".dao");
|
|
38
114
|
await fs.mkdir(daoRoot, { recursive: true });
|
|
39
115
|
const statePath = path.join(daoRoot, "state.json");
|
|
40
116
|
let state = createInitialState(daoRoot);
|
|
117
|
+
let rawState = null;
|
|
41
118
|
try {
|
|
42
|
-
|
|
43
|
-
state = repairState(parsed, daoRoot);
|
|
119
|
+
rawState = await fs.readFile(statePath, "utf8");
|
|
44
120
|
}
|
|
45
121
|
catch (error) {
|
|
46
|
-
if (error.code !== "ENOENT")
|
|
47
|
-
throw error;
|
|
122
|
+
if (error.code !== "ENOENT") {
|
|
123
|
+
throw new Error(`Failed to read DAO state at ${statePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (rawState !== null) {
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(rawState);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Same wrapping as loadState: a raw SyntaxError names no file and the
|
|
133
|
+
// CLI/MCP (which load through open()) would give the user no hint.
|
|
134
|
+
throw new Error(`Corrupt DAO state at ${statePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
135
|
+
}
|
|
136
|
+
const repaired = repairState(parsed, daoRoot);
|
|
137
|
+
state = repaired.state;
|
|
138
|
+
if (repaired.repaired) {
|
|
139
|
+
// The damaged original is about to be silently overwritten by the
|
|
140
|
+
// first persist: preserve it for forensics (issue #167.6).
|
|
141
|
+
const backupPath = `${statePath}.backup-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
142
|
+
await fs.copyFile(statePath, backupPath).catch(() => undefined);
|
|
143
|
+
logger.warn(`⚠ state.json needed shape repair; original backed up to ${backupPath}`);
|
|
144
|
+
}
|
|
48
145
|
}
|
|
49
|
-
return new FileDaoStateRepository(state, daoRoot);
|
|
146
|
+
return new FileDaoStateRepository(state, daoRoot, rawState, state.stateRevision);
|
|
50
147
|
}
|
|
51
148
|
get() {
|
|
52
149
|
return this.state;
|
|
53
150
|
}
|
|
54
151
|
async persist() {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
152
|
+
const task = async () => {
|
|
153
|
+
// No-op persist: nothing to write means no writer, so neither the
|
|
154
|
+
// inter-process lock nor the on-disk concurrency check is needed.
|
|
155
|
+
if (!this.hasPendingWrites())
|
|
156
|
+
return;
|
|
157
|
+
await fs.mkdir(this.daoRoot, { recursive: true });
|
|
158
|
+
await withFileLock(this.daoRoot, async () => {
|
|
159
|
+
const diskRevision = await this.checkNoConcurrentModification();
|
|
160
|
+
// Monotonic revision (issue #153): move past whatever is on disk so a
|
|
161
|
+
// stale copy of this instance can never be accepted again.
|
|
162
|
+
this.state.stateRevision = Math.max(diskRevision, this.seenRevision) + 1;
|
|
163
|
+
const statePath = path.join(this.daoRoot, "state.json");
|
|
164
|
+
await this.writeIfChanged(statePath, this.state);
|
|
165
|
+
this.rawStateOnDisk = this.writeCache.get(statePath) ?? null;
|
|
166
|
+
this.seenRevision = this.state.stateRevision;
|
|
167
|
+
await this.persistDecisions();
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
const queued = this.writeQueue.then(task, task);
|
|
171
|
+
this.writeQueue = queued.catch(() => { });
|
|
172
|
+
await queued;
|
|
58
173
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Whether persist() would write anything. Mirrors the per-file decisions in
|
|
176
|
+
* writeIfChanged/persistDecisions: when every serialized payload matches the
|
|
177
|
+
* write cache, persist is a no-op and can skip lock, check and I/O.
|
|
178
|
+
*/
|
|
179
|
+
hasPendingWrites() {
|
|
180
|
+
if (this.writeCache.get(path.join(this.daoRoot, "state.json")) !== formatJson(this.state))
|
|
181
|
+
return true;
|
|
182
|
+
const decisions = this.closedDecisions();
|
|
183
|
+
if (this.writeCache.get(path.join(this.daoRoot, "decisions", "index.json")) !== formatJson(decisions)) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
return decisions.some((decision) => this.writeCache.get(path.join(this.daoRoot, "decisions", `${decision.id.toString().padStart(3, "0")}.json`)) !==
|
|
187
|
+
formatJson(decision));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Fail-fast optimistic concurrency: if another process persisted a state
|
|
191
|
+
* this instance has not seen, refuse to overwrite it instead of silently
|
|
192
|
+
* dropping votes/proposals (last-writer-wins).
|
|
193
|
+
*
|
|
194
|
+
* Returns the revision currently on disk. Two layers:
|
|
195
|
+
* 1. stateRevision comparison (issue #153) — catches ANY divergence,
|
|
196
|
+
* including votes added to a proposal known to both processes.
|
|
197
|
+
* 2. proposal-id/counter checks — catch hand-edited files whose revision
|
|
198
|
+
* was not bumped (state.json is a documented, readable format).
|
|
199
|
+
*/
|
|
200
|
+
async checkNoConcurrentModification() {
|
|
201
|
+
let raw;
|
|
202
|
+
try {
|
|
203
|
+
raw = await fs.readFile(path.join(this.daoRoot, "state.json"), "utf8");
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
if (error.code === "ENOENT")
|
|
207
|
+
return 0;
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
// Byte-identical to what this instance last read or wrote: no other
|
|
211
|
+
// writer touched the file. Avoids a full JSON.parse of a potentially
|
|
212
|
+
// large state on every persist.
|
|
213
|
+
if (raw === this.rawStateOnDisk)
|
|
214
|
+
return this.seenRevision;
|
|
215
|
+
const onDisk = JSON.parse(raw);
|
|
216
|
+
const diskRevision = readRevision(onDisk);
|
|
217
|
+
if (diskRevision !== this.seenRevision) {
|
|
218
|
+
throw new Error(`Concurrent modification detected in ${path.join(this.daoRoot, "state.json")}: ` +
|
|
219
|
+
`disk revision (${diskRevision}) differs from the revision this instance last saw (${this.seenRevision}). ` +
|
|
220
|
+
"Reopen the repository and retry.");
|
|
221
|
+
}
|
|
222
|
+
if (!onDisk || !Array.isArray(onDisk.proposals))
|
|
223
|
+
return diskRevision;
|
|
224
|
+
const diskIds = new Set(onDisk.proposals.map((p) => p.id));
|
|
225
|
+
const memIds = new Set(this.state.proposals.map((p) => p.id));
|
|
226
|
+
for (const id of diskIds) {
|
|
227
|
+
if (!memIds.has(id)) {
|
|
228
|
+
throw new Error(`Concurrent modification detected in ${path.join(this.daoRoot, "state.json")}: ` +
|
|
229
|
+
`proposal #${String(id)} exists on disk but not in memory. Reopen the repository and retry.`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const diskNext = typeof onDisk.nextProposalId === "number" ? onDisk.nextProposalId : 1;
|
|
233
|
+
if (diskNext > this.state.nextProposalId) {
|
|
234
|
+
throw new Error(`Concurrent modification detected in ${path.join(this.daoRoot, "state.json")}: ` +
|
|
235
|
+
`disk nextProposalId (${diskNext}) is ahead of memory (${this.state.nextProposalId}). Reopen and retry.`);
|
|
236
|
+
}
|
|
237
|
+
return diskRevision;
|
|
238
|
+
}
|
|
239
|
+
/** Decisions to persist: closed proposals only, ordered by id. Single source for persistDecisions and hasPendingWrites. */
|
|
240
|
+
closedDecisions() {
|
|
241
|
+
return this.state.proposals
|
|
63
242
|
.filter((proposal) => proposal.status !== "open" && proposal.status !== "deliberating")
|
|
64
243
|
.map((proposal) => ({
|
|
65
244
|
id: proposal.id,
|
|
@@ -71,6 +250,11 @@ export class FileDaoStateRepository {
|
|
|
71
250
|
resolvedAt: proposal.resolvedAt,
|
|
72
251
|
}))
|
|
73
252
|
.sort((left, right) => left.id - right.id);
|
|
253
|
+
}
|
|
254
|
+
async persistDecisions() {
|
|
255
|
+
const decisionsDir = path.join(this.daoRoot, "decisions");
|
|
256
|
+
await fs.mkdir(decisionsDir, { recursive: true });
|
|
257
|
+
const decisions = this.closedDecisions();
|
|
74
258
|
await this.writeIfChanged(path.join(decisionsDir, "index.json"), decisions);
|
|
75
259
|
await Promise.all(decisions.map((decision) => this.writeIfChanged(path.join(decisionsDir, `${decision.id.toString().padStart(3, "0")}.json`), decision)));
|
|
76
260
|
}
|
|
@@ -78,8 +262,110 @@ export class FileDaoStateRepository {
|
|
|
78
262
|
const serialized = formatJson(value);
|
|
79
263
|
if (this.writeCache.get(filePath) === serialized)
|
|
80
264
|
return;
|
|
81
|
-
await
|
|
265
|
+
await writeAtomic(filePath, serialized);
|
|
82
266
|
this.writeCache.set(filePath, serialized);
|
|
83
267
|
}
|
|
84
268
|
}
|
|
269
|
+
async function writeAtomic(filePath, content) {
|
|
270
|
+
const tmpPath = `${filePath}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
271
|
+
try {
|
|
272
|
+
await fs.writeFile(tmpPath, content, "utf8");
|
|
273
|
+
await fs.rename(tmpPath, filePath);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
await safeUnlink(tmpPath);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function safeUnlink(p) {
|
|
281
|
+
try {
|
|
282
|
+
await fs.unlink(p);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// Ignore — file may not exist or may already be gone.
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const LOCK_FILE = "state.lock";
|
|
289
|
+
const LOCK_TIMEOUT_MS = 5000;
|
|
290
|
+
const LOCK_RETRY_MS = 25;
|
|
291
|
+
const LOCK_STALE_MS = 10000;
|
|
292
|
+
export async function withFileLock(daoRoot, fn) {
|
|
293
|
+
const lockPath = path.join(daoRoot, LOCK_FILE);
|
|
294
|
+
// Callers are responsible for creating daoRoot before locking.
|
|
295
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
296
|
+
// Unique token (issue #167.1): the release step only removes the lock file
|
|
297
|
+
// when it still holds ITS OWN token, so a stale takeover can never trick a
|
|
298
|
+
// live writer into deleting someone else's lock.
|
|
299
|
+
const token = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
300
|
+
const lockPayload = () => JSON.stringify({ pid: process.pid, ts: Date.now(), token });
|
|
301
|
+
for (;;) {
|
|
302
|
+
try {
|
|
303
|
+
await fs.writeFile(lockPath, lockPayload(), { flag: "wx" });
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
if (error.code !== "EEXIST")
|
|
308
|
+
throw error;
|
|
309
|
+
if (await isLockStale(lockPath)) {
|
|
310
|
+
await safeUnlink(lockPath);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (Date.now() >= deadline) {
|
|
314
|
+
throw new Error(`Timed out acquiring DAO lock at ${lockPath} (another writer holds it)`);
|
|
315
|
+
}
|
|
316
|
+
await new Promise((r) => setTimeout(r, LOCK_RETRY_MS));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// Heartbeat: refresh the timestamp while the critical section runs, so a
|
|
320
|
+
// legitimately long write is not declared stale by a waiting process. Only
|
|
321
|
+
// refresh while the lock file still holds OUR token (review): after a
|
|
322
|
+
// stale takeover by another writer, the old owner's heartbeat must not
|
|
323
|
+
// clobber the new owner's token.
|
|
324
|
+
const heartbeat = setInterval(() => {
|
|
325
|
+
void (async () => {
|
|
326
|
+
try {
|
|
327
|
+
const raw = await fs.readFile(lockPath, "utf8");
|
|
328
|
+
const parsed = JSON.parse(raw);
|
|
329
|
+
if (parsed.token !== token)
|
|
330
|
+
return; // we no longer own the lock file
|
|
331
|
+
await fs.writeFile(lockPath, lockPayload(), { flag: "w" });
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// Lock already gone or unreadable — nothing to refresh.
|
|
335
|
+
}
|
|
336
|
+
})();
|
|
337
|
+
}, LOCK_STALE_MS / 2);
|
|
338
|
+
try {
|
|
339
|
+
return await fn();
|
|
340
|
+
}
|
|
341
|
+
finally {
|
|
342
|
+
clearInterval(heartbeat);
|
|
343
|
+
try {
|
|
344
|
+
const raw = await fs.readFile(lockPath, "utf8");
|
|
345
|
+
const parsed = JSON.parse(raw);
|
|
346
|
+
if (parsed.token === token)
|
|
347
|
+
await safeUnlink(lockPath);
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Lock already gone or unreadable — nothing to remove.
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function isLockStale(lockPath) {
|
|
355
|
+
try {
|
|
356
|
+
const raw = await fs.readFile(lockPath, "utf8");
|
|
357
|
+
const parsed = JSON.parse(raw);
|
|
358
|
+
return typeof parsed.ts === "number" && Date.now() - parsed.ts > LOCK_STALE_MS;
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// Unreadable lock: treat missing as not-stale (retry), corrupt as stale.
|
|
362
|
+
try {
|
|
363
|
+
await fs.stat(lockPath);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
85
371
|
//# sourceMappingURL=file-dao-state.repository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-dao-state.repository.js","sourceRoot":"","sources":["../../../src/adapters/persistence/file-dao-state.repository.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file-dao-state.repository.js","sourceRoot":"","sources":["../../../src/adapters/persistence/file-dao-state.repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs-ship-audit.store.d.ts","sourceRoot":"","sources":["../../../src/adapters/ship-audit/fs-ship-audit.store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs-ship-audit.store.d.ts","sourceRoot":"","sources":["../../../src/adapters/ship-audit/fs-ship-audit.store.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIvF,qBAAa,gBAAiB,YAAW,kBAAkB;;gBAGtC,cAAc,EAAE,MAAM;IAI5B,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAgB3D,IAAI,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CAwErG"}
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
// Persists audit-challenge snapshots under <repositoryRoot>/.dao/ship-audits/
|
|
5
5
|
// as <proposalId>.json. Read-soft: a missing or unreadable file yields null
|
|
6
6
|
// (a fresh cycle), never an error — the challenge must still gate.
|
|
7
|
+
var _a;
|
|
7
8
|
import { promises as fs } from "node:fs";
|
|
8
9
|
import { join, resolve, sep } from "node:path";
|
|
10
|
+
import { writeAtomic } from "../../persistence.js";
|
|
9
11
|
const SAFE_ID = /^\d+$/;
|
|
10
12
|
export class FsShipAuditStore {
|
|
11
13
|
#directory;
|
|
@@ -35,15 +37,16 @@ export class FsShipAuditStore {
|
|
|
35
37
|
async save(snapshot) {
|
|
36
38
|
const file = this.#fileFor(snapshot.proposalId);
|
|
37
39
|
await fs.mkdir(this.#directory, { recursive: true });
|
|
38
|
-
await
|
|
40
|
+
await writeAtomic(file, `${JSON.stringify(snapshot, null, 2)}\n`);
|
|
39
41
|
}
|
|
40
42
|
async claim(proposalId) {
|
|
41
43
|
const lock = this.#fileFor(proposalId).replace(/\.json$/, ".lock");
|
|
42
44
|
await fs.mkdir(this.#directory, { recursive: true });
|
|
43
45
|
try {
|
|
44
46
|
// O_EXCL ('wx'): exactly one concurrent caller across processes wins.
|
|
47
|
+
const payload = `${JSON.stringify({ pid: process.pid, ts: Date.now() })}\n`;
|
|
45
48
|
const handle = await fs.open(lock, "wx");
|
|
46
|
-
await handle.write(
|
|
49
|
+
await handle.write(payload);
|
|
47
50
|
await handle.close();
|
|
48
51
|
return {
|
|
49
52
|
acquired: true,
|
|
@@ -53,9 +56,60 @@ export class FsShipAuditStore {
|
|
|
53
56
|
};
|
|
54
57
|
}
|
|
55
58
|
catch {
|
|
59
|
+
// EEXIST: a claim already exists. If its writer is provably gone
|
|
60
|
+
// (dead pid) or it is old enough, reclaim it instead of failing ships
|
|
61
|
+
// forever until manual cleanup (issue #167.5).
|
|
62
|
+
if (await this.#claimIsAbandoned(lock)) {
|
|
63
|
+
await fs.rm(lock, { force: true }).catch(() => undefined);
|
|
64
|
+
return this.claim(proposalId);
|
|
65
|
+
}
|
|
56
66
|
return { acquired: false, release: async () => undefined };
|
|
57
67
|
}
|
|
58
68
|
}
|
|
69
|
+
/** A claim is abandoned when the owning pid no longer exists, or when it
|
|
70
|
+
* is older than the staleness window (crash without cleanup, cross-machine
|
|
71
|
+
* writers where the pid check cannot apply). */
|
|
72
|
+
static #CLAIM_STALE_MS = 30 * 60 * 1000;
|
|
73
|
+
async #claimIsAbandoned(lock) {
|
|
74
|
+
try {
|
|
75
|
+
const raw = await fs.readFile(lock, "utf8");
|
|
76
|
+
let pid;
|
|
77
|
+
let ts;
|
|
78
|
+
try {
|
|
79
|
+
const parsed = JSON.parse(raw);
|
|
80
|
+
if (typeof parsed.pid === "number")
|
|
81
|
+
pid = parsed.pid;
|
|
82
|
+
if (typeof parsed.ts === "number")
|
|
83
|
+
ts = parsed.ts;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Legacy format: a bare pid line.
|
|
87
|
+
const parsedPid = Number.parseInt(raw.trim(), 10);
|
|
88
|
+
if (Number.isInteger(parsedPid))
|
|
89
|
+
pid = parsedPid;
|
|
90
|
+
}
|
|
91
|
+
if (pid !== undefined && Number.isInteger(pid) && pid > 0) {
|
|
92
|
+
try {
|
|
93
|
+
process.kill(pid, 0); // liveness probe — throws if the process is gone
|
|
94
|
+
// Alive: only age can make the claim abandonable.
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// POSIX (review): EPERM means the process EXISTS but is owned by
|
|
98
|
+
// another user — reclaiming on EPERM would break mutual exclusion.
|
|
99
|
+
// Only ESRCH ("no such process") proves the owner is gone.
|
|
100
|
+
if (error.code === "ESRCH")
|
|
101
|
+
return true;
|
|
102
|
+
// Any other probe error: fall through to the age check.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (ts !== undefined && Date.now() - ts > _a.#CLAIM_STALE_MS)
|
|
106
|
+
return true;
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false; // unreadable claim: keep failing closed
|
|
111
|
+
}
|
|
112
|
+
}
|
|
59
113
|
#fileFor(proposalId) {
|
|
60
114
|
const name = String(proposalId);
|
|
61
115
|
if (!SAFE_ID.test(name))
|
|
@@ -66,4 +120,5 @@ export class FsShipAuditStore {
|
|
|
66
120
|
return file;
|
|
67
121
|
}
|
|
68
122
|
}
|
|
123
|
+
_a = FsShipAuditStore;
|
|
69
124
|
//# sourceMappingURL=fs-ship-audit.store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs-ship-audit.store.js","sourceRoot":"","sources":["../../../src/adapters/ship-audit/fs-ship-audit.store.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+CAA+C;AAC/C,+DAA+D;AAC/D,8EAA8E;AAC9E,4EAA4E;AAC5E,mEAAmE
|
|
1
|
+
{"version":3,"file":"fs-ship-audit.store.js","sourceRoot":"","sources":["../../../src/adapters/ship-audit/fs-ship-audit.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ClockPort } from "../../ports/clock.js";
|
|
2
2
|
import type { DaoStateRepositoryPort } from "../../ports/repository.js";
|
|
3
|
-
import type { ControlCheckResult } from "../../types/index.js";
|
|
3
|
+
import type { ControlCheckResult, Proposal } from "../../types/index.js";
|
|
4
4
|
export type ControlProposalResult = {
|
|
5
5
|
ok: true;
|
|
6
6
|
control: ControlCheckResult;
|
|
7
|
+
status: Proposal["status"];
|
|
7
8
|
} | {
|
|
8
9
|
ok: false;
|
|
9
10
|
error: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-proposal.use-case.d.ts","sourceRoot":"","sources":["../../../src/application/proposals/control-proposal.use-case.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAc,kBAAkB,EAAY,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"control-proposal.use-case.d.ts","sourceRoot":"","sources":["../../../src/application/proposals/control-proposal.use-case.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAc,kBAAkB,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE/F,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;CAAE,GACrE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,qBAAa,sBAAsB;IAE/B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAAZ,YAAY,EAAE;QAC7B,UAAU,EAAE,sBAAsB,CAAC;QACnC,KAAK,EAAE,SAAS,CAAC;KAClB;IAGU,OAAO,CAAC,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA8DlH,OAAO,CAAC,KAAK;CAYd"}
|