@lmzhen/dsh-evolution-core 0.20.0 → 0.22.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/lib/index.js +356 -147
- package/lib/types/index.d.ts +1 -0
- package/lib/types/live.d.ts +78 -0
- package/lib/types/numeric.d.ts +0 -20
- package/lib/types/params.d.ts +18 -1
- package/lib/types/write-inventory.d.ts +16 -0
- package/package.json +1 -1
- package/persisted-write-inventory.json +175 -136
package/lib/types/index.d.ts
CHANGED
|
@@ -77,6 +77,7 @@ export * from './usage.ts';
|
|
|
77
77
|
export * from './constants.ts';
|
|
78
78
|
export * from './params.ts';
|
|
79
79
|
export * from './namespace-migration.ts';
|
|
80
|
+
export * from './live.ts';
|
|
80
81
|
export * from './numeric.ts';
|
|
81
82
|
export * from './opt-in.ts';
|
|
82
83
|
export * from './payload.ts';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical type of a config field that may arrive as a live reference or a plain value.
|
|
3
|
+
*
|
|
4
|
+
* Spelled STRUCTURALLY on purpose: the platform's `Volatile<T>` (cordis) satisfies it for every payload
|
|
5
|
+
* shape, so a field declared `Volatile<number | undefined>` and one declared `Volatile<number>` both pass
|
|
6
|
+
* without the readers importing cordis or narrowing the payload. Where a plugin wants the
|
|
7
|
+
* canonical spelling it may still write `Volatile<T>` in its own Config.
|
|
8
|
+
*/
|
|
9
|
+
export type LiveValue<T> = T | {
|
|
10
|
+
get(): T | undefined;
|
|
11
|
+
} | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Unwrap a live reference; return a plain value unchanged. The ONLY place that decides the shape.
|
|
14
|
+
*
|
|
15
|
+
* The discriminant is `typeof get === 'function'` rather than a bare `typeof value === 'object'`: a
|
|
16
|
+
* config field may legitimately carry an object (a policy record), and calling `.get()` on that
|
|
17
|
+
* used to throw where a plain read was meant.
|
|
18
|
+
*
|
|
19
|
+
* @param value - the field as supplied: live reference, plain value, or absent.
|
|
20
|
+
* @returns the plain value, or `undefined` when the reference/field is absent.
|
|
21
|
+
*/
|
|
22
|
+
export declare function unwrapLive<T>(value: LiveValue<T>): T | undefined;
|
|
23
|
+
/** Options for {@link liveNumber}; the warning triple is all-or-nothing. */
|
|
24
|
+
export interface LiveNumberOptions {
|
|
25
|
+
/** Inclusive lower bound; omit for no lower bound (0 is then a legitimate value). */
|
|
26
|
+
readonly min?: number;
|
|
27
|
+
/** Inclusive upper bound. */
|
|
28
|
+
readonly max?: number;
|
|
29
|
+
/** The keys this caller already warned about (caller-owned, so the warning stays once-per-key). */
|
|
30
|
+
readonly warned?: Set<string>;
|
|
31
|
+
/** The warning sink (each package prefixes its own name). */
|
|
32
|
+
readonly warn?: (message: string) => void;
|
|
33
|
+
/** The config key, for the warning text. Required together with `warned` and `warn`. */
|
|
34
|
+
readonly name?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Read a numeric knob (live or plain) and clamp it, warning ONCE per key.
|
|
38
|
+
*
|
|
39
|
+
* @param value - the field as supplied: live reference, plain value, or absent.
|
|
40
|
+
* @param fallback - the package default an invalid value falls back to.
|
|
41
|
+
* @param opts - bounds plus the optional warning triple.
|
|
42
|
+
* @returns the value the engine may use.
|
|
43
|
+
*/
|
|
44
|
+
export declare function liveNumber(value: LiveValue<number>, fallback: number, opts?: LiveNumberOptions): number;
|
|
45
|
+
/**
|
|
46
|
+
* Clamp one numeric knob and warn ONCE per key — the pre-0.22 signature, kept as a thin adapter so
|
|
47
|
+
* existing callers migrate one at a time instead of in one commit.
|
|
48
|
+
*
|
|
49
|
+
* @param warned - the keys this caller already warned about.
|
|
50
|
+
* @param warn - the warning sink (each package prefixes its own name).
|
|
51
|
+
* @param name - the config key, for the warning.
|
|
52
|
+
* @param value - the value as supplied, or its live reference.
|
|
53
|
+
* @param fallback - the package default a corrected value falls back to.
|
|
54
|
+
* @param min - the smallest value the engine can act on.
|
|
55
|
+
* @param max - the largest value the carrier can hold, when bounded.
|
|
56
|
+
* @returns the value the engine may use.
|
|
57
|
+
*/
|
|
58
|
+
export declare function clampOnce(warned: Set<string>, warn: (message: string) => void, name: string, value: LiveValue<number>, fallback: number, min: number, max?: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* Read a boolean flag (live or plain). Absent ⇒ `fallback` (default `false`).
|
|
61
|
+
*
|
|
62
|
+
* The boolean counterpart of {@link liveNumber}: `value === true` is never true for a live
|
|
63
|
+
* reference, which is the silent failure this reader exists to prevent.
|
|
64
|
+
*/
|
|
65
|
+
export declare function liveBool(value: LiveValue<boolean>, fallback?: boolean): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Read a string (live or plain). Blank/whitespace counts as unset — the same rule `evolutionRoot` uses
|
|
68
|
+
* for `DSH_HOME`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function liveString(value: LiveValue<string>, fallback: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Read an enumerated string (live or plain); a value outside `allowed` falls back.
|
|
73
|
+
*
|
|
74
|
+
* Used for the mode-like knobs (review mode, trigger, citation policy, write-confirm mode) where a
|
|
75
|
+
* bare `?? default` would accept a typo the schema never saw.
|
|
76
|
+
*/
|
|
77
|
+
export declare function liveEnum<T extends string>(value: LiveValue<T>, allowed: readonly T[], fallback: T): T;
|
|
78
|
+
//# sourceMappingURL=live.d.ts.map
|
package/lib/types/numeric.d.ts
CHANGED
|
@@ -35,24 +35,4 @@ export declare function clampedNumber(value: number | undefined, fallback: numbe
|
|
|
35
35
|
min?: number;
|
|
36
36
|
max?: number;
|
|
37
37
|
}): number;
|
|
38
|
-
/**
|
|
39
|
-
* Clamp one numeric knob and warn ONCE per key.
|
|
40
|
-
*
|
|
41
|
-
* S2.9 (T3-10): `evolution-review` and `evolution-curator` each carried their own copy of this
|
|
42
|
-
* helper — their own once-per-key set and their own message — so the two settings readers of ONE
|
|
43
|
-
* policy row could drift apart. The logic lives here; each caller passes its own sink (and its own
|
|
44
|
-
* package prefix). A live field is read at every use, so the warning cannot be per-read.
|
|
45
|
-
*
|
|
46
|
-
* @param warned - the keys this caller already warned about.
|
|
47
|
-
* @param warn - the warning sink (each package prefixes its own name).
|
|
48
|
-
* @param name - the config key, for the warning.
|
|
49
|
-
* @param value - the value as supplied, or its volatile reference.
|
|
50
|
-
* @param fallback - the package default a corrected value falls back to.
|
|
51
|
-
* @param min - the smallest value the engine can act on.
|
|
52
|
-
* @param max - the largest value the carrier can hold, when bounded.
|
|
53
|
-
* @returns the value the engine may use.
|
|
54
|
-
*/
|
|
55
|
-
export declare function clampOnce(warned: Set<string>, warn: (message: string) => void, name: string, value: number | {
|
|
56
|
-
get(): number | undefined;
|
|
57
|
-
} | undefined, fallback: number, min: number, max?: number): number;
|
|
58
38
|
//# sourceMappingURL=numeric.d.ts.map
|
package/lib/types/params.d.ts
CHANGED
|
@@ -42,7 +42,12 @@ export declare function canonicalWriteId(id: string): string;
|
|
|
42
42
|
* @returns the canonical value when present, else the alias value, else undefined.
|
|
43
43
|
*/
|
|
44
44
|
export declare function readParam(carrier: object | undefined, id: string): unknown;
|
|
45
|
-
/** Exposure group (design §7.2): the unit a developer changes together.
|
|
45
|
+
/** Exposure group (design §7.2): the unit a developer changes together.
|
|
46
|
+
*
|
|
47
|
+
* A DOC-SECTION axis, not the exposure decision: the group says which family of knobs a value
|
|
48
|
+
* belongs to, the tier says who may change it. `internal` here is a group id with no rows today;
|
|
49
|
+
* the `why: 'internal'` a non-E3 row carries is a different value on the other axis (the reason it
|
|
50
|
+
* has no settings card) — the shared spelling is a coincidence, not a routing rule. */
|
|
46
51
|
export type ParamGroup = 'library' | 'write-caps' | 'review' | 'memory' | 'curator' | 'deployment' | 'internal';
|
|
47
52
|
/**
|
|
48
53
|
* Chinese display name per group, one entry per line (the scripts read this block
|
|
@@ -80,6 +85,12 @@ export type ParamAuthority = 'code' | 'cordis' | 'install';
|
|
|
80
85
|
* those values for display, one label per value, in the same order. The stored
|
|
81
86
|
* value is never translated: only what the control shows.
|
|
82
87
|
*/
|
|
88
|
+
/** Why a parameter has NO settings card (the `why` half of the exposure key, 0.21.0 / G2).
|
|
89
|
+
* `internal` = timeouts, retries, cooldowns, weights, caps and windows; `deployment` = mount
|
|
90
|
+
* switches and library-scope policy; `security` = safety surfaces (never user-writable);
|
|
91
|
+
* `identity` = providers/models/registered names; `path` = per-row directories; `install` = what
|
|
92
|
+
* the installer freezes. Exactly one of `card`/`why` rides every row, and the tier decides which. */
|
|
93
|
+
export type ParamWhy = 'deployment' | 'security' | 'identity' | 'path' | 'install' | 'internal';
|
|
83
94
|
export interface ParamExposure {
|
|
84
95
|
id: string;
|
|
85
96
|
group: ParamGroup;
|
|
@@ -95,6 +106,12 @@ export interface ParamExposure {
|
|
|
95
106
|
hint?: string;
|
|
96
107
|
/** Which control the card renders for this field. Required on E3 rows. */
|
|
97
108
|
control?: 'number' | 'switch' | 'select' | 'text';
|
|
109
|
+
/** The settings card this row appears on — its OWN row id (the platform keys a card by the Loader
|
|
110
|
+
* entry id, and a card may only render its own row's Config). Required on E3 rows. */
|
|
111
|
+
card?: string;
|
|
112
|
+
/** The registered reason this row has NO card. Required on every non-E3 row; mutually exclusive
|
|
113
|
+
* with `card`. This is what makes "an E3 row that no card renders" unspellable. */
|
|
114
|
+
why?: ParamWhy;
|
|
98
115
|
/** Unit suffix shown next to a number (empty when it carries none). */
|
|
99
116
|
unit?: string;
|
|
100
117
|
/** Pipe-joined allowed values; non-empty exactly when control is 'select'. */
|
|
@@ -36,6 +36,18 @@ export interface PersistedWriteSite {
|
|
|
36
36
|
/** One line on what the file holds; the inventory reads as a whole. */
|
|
37
37
|
readonly note: string;
|
|
38
38
|
}
|
|
39
|
+
/** One root symbol of the inventory's own legend. `segments` is the path tail the symbol appends to
|
|
40
|
+
* its base (empty for the home itself), and `basedOn` names the symbol it extends. The gate
|
|
41
|
+
* `verify-write-inventory.mjs` checks those segments against the resolver's SOURCE, so the legend
|
|
42
|
+
* cannot drift from the code that resolves it. */
|
|
43
|
+
export interface WritePathSymbol {
|
|
44
|
+
/** Name of the core resolver that produces this root (or 'caller' for a caller-supplied path). */
|
|
45
|
+
readonly resolver: string;
|
|
46
|
+
/** Path segments appended to the base root, in order. */
|
|
47
|
+
readonly segments: readonly string[];
|
|
48
|
+
/** The symbol this one extends, when it is not rooted at the DSH home itself. */
|
|
49
|
+
readonly basedOn?: string;
|
|
50
|
+
}
|
|
39
51
|
/** Instance keys held by writer services — the single source shared by the
|
|
40
52
|
* claim site and the inventory row that declares it (rule N20 checks the row's
|
|
41
53
|
* `instance` names one of these). */
|
|
@@ -58,6 +70,10 @@ export declare const INSTANCE_KEYS: {
|
|
|
58
70
|
* @returns the sites, in file order.
|
|
59
71
|
*/
|
|
60
72
|
export declare function persistedWriteSites(): readonly PersistedWriteSite[];
|
|
73
|
+
/** The inventory's symbol legend — the roots every declared path is written against, with the
|
|
74
|
+
* segments the gate checks against the resolver's source.
|
|
75
|
+
* @internal no production caller — the gates read the JSON asset directly. */
|
|
76
|
+
export declare function writePathSymbols(): Readonly<Record<string, WritePathSymbol>>;
|
|
61
77
|
/** Sites serialized by the per-home instance claim, with their instance keys.
|
|
62
78
|
* @internal S2.4 (PLAN 2026-09-16): no production caller — the arch guard
|
|
63
79
|
* reads the JSON asset directly; the tests are the only in-tree consumers. */
|
package/package.json
CHANGED
|
@@ -1,137 +1,176 @@
|
|
|
1
|
-
|
|
2
|
-
{
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
{
|
|
2
|
+
"symbols": {
|
|
3
|
+
"<dshHome>": {
|
|
4
|
+
"resolver": "evolutionRoot",
|
|
5
|
+
"segments": []
|
|
6
|
+
},
|
|
7
|
+
"<evolutionHome>": {
|
|
8
|
+
"resolver": "evolutionHome",
|
|
9
|
+
"segments": [
|
|
10
|
+
"evolution"
|
|
11
|
+
],
|
|
12
|
+
"basedOn": "<dshHome>"
|
|
13
|
+
},
|
|
14
|
+
"<skillsRoot>": {
|
|
15
|
+
"resolver": "skillsRoot",
|
|
16
|
+
"segments": [
|
|
17
|
+
"skills"
|
|
18
|
+
],
|
|
19
|
+
"basedOn": "<dshHome>"
|
|
20
|
+
},
|
|
21
|
+
"<memoryRoot>": {
|
|
22
|
+
"resolver": "memoryRoot",
|
|
23
|
+
"segments": [
|
|
24
|
+
"memories"
|
|
25
|
+
],
|
|
26
|
+
"basedOn": "<dshHome>"
|
|
27
|
+
},
|
|
28
|
+
"<historyRoot>": {
|
|
29
|
+
"resolver": "historyRoot",
|
|
30
|
+
"segments": [
|
|
31
|
+
".history"
|
|
32
|
+
],
|
|
33
|
+
"basedOn": "<skillsRoot>"
|
|
34
|
+
},
|
|
35
|
+
"<callerSupplied>": {
|
|
36
|
+
"resolver": "caller"
|
|
37
|
+
}
|
|
10
38
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
39
|
+
"sites": [
|
|
40
|
+
{
|
|
41
|
+
"id": "usage",
|
|
42
|
+
"path": "<skillsRoot>/.usage.json",
|
|
43
|
+
"writer": "evolution-core/src/usage.ts",
|
|
44
|
+
"serializedBy": "transact",
|
|
45
|
+
"marker": "transactIo(io, usageFile(root)",
|
|
46
|
+
"state": [],
|
|
47
|
+
"note": "Skill usage counters. RMW under the IO backend's cross-process lock; the reader side rebuilds from the events log."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "suppressed",
|
|
51
|
+
"path": "<skillsRoot>/.curator-suppressed.json",
|
|
52
|
+
"writer": "evolution-core/src/usage.ts",
|
|
53
|
+
"serializedBy": "transact",
|
|
54
|
+
"marker": "transactIo(io, suppressedFile(root)",
|
|
55
|
+
"state": [],
|
|
56
|
+
"note": "Curator suppression list (archived bundled skills that must not re-seed)."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "mutations",
|
|
60
|
+
"path": "<skillsRoot>/.mutations.json",
|
|
61
|
+
"writer": "evolution-core/src/mutations.ts",
|
|
62
|
+
"serializedBy": "transact",
|
|
63
|
+
"marker": "transactIo(io, mutationsFile(root)",
|
|
64
|
+
"state": [],
|
|
65
|
+
"note": "Skill mutation audit records (bounded ring)."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "events",
|
|
69
|
+
"path": "<evolutionHome>/events.json",
|
|
70
|
+
"writer": "evolution-core/src/evolution-events.ts",
|
|
71
|
+
"serializedBy": "transact",
|
|
72
|
+
"marker": "transactIo(io, path",
|
|
73
|
+
"state": [],
|
|
74
|
+
"note": "The rc.68 evolution event log; feedback/skill-usage stats fold it. Shares ONE file with the \"feedback-events\" site (two writers, one log under the IO lock)."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "memory",
|
|
78
|
+
"path": "<memoryRoot>/<target>.md",
|
|
79
|
+
"writer": "evolution-core/src/memory-store.ts",
|
|
80
|
+
"serializedBy": "transact",
|
|
81
|
+
"marker": "transactIo(this.io, fileFor(this.root, target)",
|
|
82
|
+
"state": [],
|
|
83
|
+
"note": "Durable memory documents written by the memory tool."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "state",
|
|
87
|
+
"path": "<evolutionHome>/<review|curator|pending>-state.json",
|
|
88
|
+
"writer": "evolution-state-json/src/index.ts",
|
|
89
|
+
"serializedBy": "transact",
|
|
90
|
+
"marker": "transactIo(io(), join(root, file)",
|
|
91
|
+
"state": [],
|
|
92
|
+
"note": "The durable state stack (review/curator/pending). S4.4/O-6 moved its three warn/rewrite sets into the provider instance (QuarantineMemory), so no module-scope N12 store remains here."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "curator-reports",
|
|
96
|
+
"path": "<evolutionHome>/reports/curator-*.json",
|
|
97
|
+
"writer": "evolution-curator/src/index.ts",
|
|
98
|
+
"serializedBy": "transact",
|
|
99
|
+
"marker": "transactIo(this.io, reportsSweepLockTarget()",
|
|
100
|
+
"state": [],
|
|
101
|
+
"note": "Curator run reports + their retention sweep. The sweep is a multi-step list + delete-beyond-the-window over a DIRECTORY, so it has no per-file lock: v43 FLOW2-1 put it inside the IO backend's cross-process write lock (transactIo) on a per-home lock target, <evolutionHome>/reports/.retention (lock file <target>.lock, pid:token body). The former declaration here was instance-claim, which is in-process ONLY (instance-scope.ts is a module-scope Map). Two curator ROWS in one process still cannot sweep at once (the loser yields on that claim), and report writes are name-unique per runId. Trigger for the remaining gap: a second PROCESS sharing DSH_HOME now WAITS on the lock instead of sweeping concurrently, and on a backend without transact there is no lock at all, so the sweep degrades to best-effort idempotent deletes."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "runs-index",
|
|
105
|
+
"path": "<evolutionHome>/runs.json",
|
|
106
|
+
"writer": "evolution-core/src/run-registry.ts",
|
|
107
|
+
"serializedBy": "transact",
|
|
108
|
+
"marker": "transactIo(options.io, path",
|
|
109
|
+
"state": [],
|
|
110
|
+
"note": "The run registry's index (0.19.0/S2): what is in flight, how each run ended, where its report lives. Read-modify-write under the IO backend's cross-process lock, and it MERGES instead of overwriting — the desktop and the web plane share one DSH_HOME, so two hosts each know their own runs and this file is their shared view. resultRef only POINTS at the report; results never live here."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "maintain-reports",
|
|
114
|
+
"path": "<evolutionHome>/reports/maintain-*.json",
|
|
115
|
+
"writer": "evolution-commands/src/index.ts",
|
|
116
|
+
"serializedBy": "transact",
|
|
117
|
+
"marker": "transactIo(io, reportsSweepLockTarget()",
|
|
118
|
+
"state": [],
|
|
119
|
+
"note": "One report per maintenance run (0.19.0/S2), written once at settle by that run's owner under a name unique to the run, so the write itself needs no per-file lock. The retention sweep is multi-step (list the directory, then delete beyond the window) and takes the SAME per-home <evolutionHome>/reports/.retention lock the curator's sweep holds — one lock for one directory. On a backend without transact it degrades to idempotent best-effort deletes."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "skill-tree",
|
|
123
|
+
"path": "<skillsRoot>/**",
|
|
124
|
+
"writer": "evolution-core/src/skill-store.ts",
|
|
125
|
+
"serializedBy": "write-lock",
|
|
126
|
+
"marker": "LOCK_SUFFIX",
|
|
127
|
+
"state": [],
|
|
128
|
+
"note": "The skill tree itself. Per-target `<path>.lock` claims (pid:token body) guard every byte writer; destructive movers refuse a live lock."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"id": "activity-log",
|
|
132
|
+
"path": "<evolutionHome>/activity.json",
|
|
133
|
+
"writer": "evolution-activity/src/index.ts",
|
|
134
|
+
"serializedBy": "transact",
|
|
135
|
+
"marker": "transactIo(io, activityFile(root)",
|
|
136
|
+
"state": [],
|
|
137
|
+
"note": "The plan-applied activity sidecar. Declared by v46 S1.12: the file carried a transactional write that no inventory row vouched for, and the N20 reverse scan is what named it."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "skill-history-index",
|
|
141
|
+
"path": "<historyRoot>/skills/<name>/index.json",
|
|
142
|
+
"writer": "evolution-core/src/skill-history.ts",
|
|
143
|
+
"serializedBy": "transact",
|
|
144
|
+
"marker": "transactIo(io, file,",
|
|
145
|
+
"state": [],
|
|
146
|
+
"note": "One skill's recorded-version index (content chain + blob references). Declared by v46 S1.12: the replace path was already transactional but undeclared, so a regression to a plain write would have been invisible."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "skill-store-transact-primitive",
|
|
150
|
+
"path": "<callerSupplied>",
|
|
151
|
+
"writer": "evolution-core/src/skill-store.ts",
|
|
152
|
+
"serializedBy": "transact",
|
|
153
|
+
"marker": "transactIo(ioLike, path, task)",
|
|
154
|
+
"state": [],
|
|
155
|
+
"note": "The store's transaction helper: it prefers the backend's own transact and falls back to the seam's transactIo. The target is the caller's path — each durable target has its own row above; this row vouches for the primitive itself."
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"id": "feedback-events",
|
|
159
|
+
"path": "<evolutionHome>/events.json",
|
|
160
|
+
"writer": "evolution-feedback/src/index.ts",
|
|
161
|
+
"serializedBy": "transact",
|
|
162
|
+
"marker": "transactIo(io, eventsPath,",
|
|
163
|
+
"state": [],
|
|
164
|
+
"note": "Feedback appends to the family event log under the cross-process lock. Declared by v46 S1.12 (N20 reverse). Shares ONE file with the \"events\" site (two writers, one log under the IO lock)."
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "pending-archive",
|
|
168
|
+
"path": "<evolutionHome>/pending-state-archive.json",
|
|
169
|
+
"writer": "evolution-state-json/src/index.ts",
|
|
170
|
+
"serializedBy": "transact",
|
|
171
|
+
"marker": "transactIo(io(), pathOf(PENDING_ARCHIVE_FILE)",
|
|
172
|
+
"state": [],
|
|
173
|
+
"note": "The resolved-pending archive (the legacy merge target). The store already declared its record map; v46 S1.12 declared the archive append as well — the second site in one file is exactly what the reverse scan exists to find."
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|