@latysh-development/monolith 0.1.0-alpha.3 → 0.1.0-alpha.4
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/README.md +1 -1
- package/dist/brief/cli.d.ts.map +1 -1
- package/dist/brief/cli.js +8 -2
- package/dist/brief/collect.d.ts.map +1 -1
- package/dist/brief/collect.js +11 -3
- package/dist/brief/freshness.d.ts +17 -2
- package/dist/brief/freshness.d.ts.map +1 -1
- package/dist/brief/freshness.js +22 -4
- package/dist/brief/git-signals.d.ts +32 -5
- package/dist/brief/git-signals.d.ts.map +1 -1
- package/dist/brief/git-signals.js +169 -39
- package/dist/brief/knowledge.d.ts +62 -0
- package/dist/brief/knowledge.d.ts.map +1 -0
- package/dist/brief/knowledge.js +123 -0
- package/dist/brief/reconcile.d.ts +7 -1
- package/dist/brief/reconcile.d.ts.map +1 -1
- package/dist/brief/reconcile.js +42 -2
- package/dist/cli/index.js +1 -1
- package/dist/cli/style.d.ts +26 -0
- package/dist/cli/style.d.ts.map +1 -0
- package/dist/cli/style.js +76 -0
- package/dist/cli/ui.d.ts.map +1 -1
- package/dist/cli/ui.js +33 -6
- package/dist/coordinatorium/cli.d.ts.map +1 -1
- package/dist/coordinatorium/cli.js +13 -0
- package/dist/finalize/cli.d.ts.map +1 -1
- package/dist/finalize/cli.js +30 -13
- package/dist/finalize/consistency.d.ts +51 -0
- package/dist/finalize/consistency.d.ts.map +1 -0
- package/dist/finalize/consistency.js +57 -0
- package/dist/finalize/core.d.ts +26 -0
- package/dist/finalize/core.d.ts.map +1 -1
- package/dist/finalize/core.js +140 -1
- package/dist/ideas/cli.d.ts +1 -0
- package/dist/ideas/cli.d.ts.map +1 -1
- package/dist/ideas/cli.js +14 -3
- package/dist/ideas/format.d.ts +31 -1
- package/dist/ideas/format.d.ts.map +1 -1
- package/dist/ideas/format.js +109 -1
- package/dist/ideas/store.d.ts +2 -0
- package/dist/ideas/store.d.ts.map +1 -1
- package/dist/ideas/store.js +4 -1
- package/dist/ideas/types.d.ts +10 -0
- package/dist/ideas/types.d.ts.map +1 -1
- package/dist/ideas/types.js +3 -0
- package/dist/readmodel/dashboard.d.ts.map +1 -1
- package/dist/readmodel/dashboard.js +19 -1
- package/dist/readmodel/index.d.ts +2 -1
- package/dist/readmodel/index.d.ts.map +1 -1
- package/dist/readmodel/index.js +1 -0
- package/dist/readmodel/roadmap.d.ts +34 -0
- package/dist/readmodel/roadmap.d.ts.map +1 -0
- package/dist/readmodel/roadmap.js +254 -0
- package/dist/readmodel/types.d.ts +88 -0
- package/dist/readmodel/types.d.ts.map +1 -1
- package/dist/root/safe-write.d.ts +15 -4
- package/dist/root/safe-write.d.ts.map +1 -1
- package/dist/root/safe-write.js +264 -50
- package/dist/ui/assets/index-BJKxUDPD.css +1 -0
- package/dist/ui/assets/index-ioC9DPHQ.js +28 -0
- package/dist/ui/index.html +2 -2
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +4 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
- package/dist/ui/assets/index-5zXAgig8.css +0 -1
- package/dist/ui/assets/index-CQgoMYLM.js +0 -28
package/dist/ideas/types.d.ts
CHANGED
|
@@ -19,18 +19,28 @@ export interface Idea {
|
|
|
19
19
|
status: IdeaStatus;
|
|
20
20
|
context?: string;
|
|
21
21
|
tags?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* KA-001: project-relative files (or directories, ending in `/`) of the
|
|
24
|
+
* independent knowledge artifact this idea registers. Optional; an idea
|
|
25
|
+
* without `paths` attributes nothing in `monolith brief`.
|
|
26
|
+
*/
|
|
27
|
+
paths?: string[];
|
|
22
28
|
}
|
|
23
29
|
/** What a caller supplies; `id`, `createdAt` and `status` are assigned by the store. */
|
|
24
30
|
export interface NewIdea {
|
|
25
31
|
title: string;
|
|
26
32
|
description: string;
|
|
27
33
|
context?: string;
|
|
34
|
+
paths?: string[];
|
|
28
35
|
}
|
|
29
36
|
/** Limits are protective ceilings (a CLI argument, not a document store). */
|
|
30
37
|
export declare const IDEA_LIMITS: {
|
|
31
38
|
readonly title: 300;
|
|
32
39
|
readonly description: 100000;
|
|
33
40
|
readonly context: 20000;
|
|
41
|
+
/** KA-001: at most this many `paths` per idea, each at most `pathLength` characters. */
|
|
42
|
+
readonly paths: 20;
|
|
43
|
+
readonly pathLength: 300;
|
|
34
44
|
/** Upper bound of one idea file, in bytes (frontmatter + body). */
|
|
35
45
|
readonly fileBytes: number;
|
|
36
46
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ideas/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,2CAA4C,CAAC;AACvE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,WAAW,IAAI;IACnB,8FAA8F;IAC9F,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ideas/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,2CAA4C,CAAC;AACvE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,WAAW,IAAI;IACnB,8FAA8F;IAC9F,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wFAAwF;AACxF,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,6EAA6E;AAC7E,eAAO,MAAM,WAAW;;;;IAItB,wFAAwF;;;IAGxF,mEAAmE;;CAE3D,CAAC;AAEX,MAAM,MAAM,aAAa,GACrB,oBAAoB,GACpB,iBAAiB,GACjB,gBAAgB,GAChB,cAAc,GACd,kBAAkB,GAClB,sBAAsB,GACtB,mBAAmB,CAAC;AAExB,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;gBACjB,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhF;AAED,+FAA+F;AAC/F,MAAM,MAAM,SAAS,GACjB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GACxB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,GAAG,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC"}
|
package/dist/ideas/types.js
CHANGED
|
@@ -11,6 +11,9 @@ export const IDEA_LIMITS = {
|
|
|
11
11
|
title: 300,
|
|
12
12
|
description: 100_000,
|
|
13
13
|
context: 20_000,
|
|
14
|
+
/** KA-001: at most this many `paths` per idea, each at most `pathLength` characters. */
|
|
15
|
+
paths: 20,
|
|
16
|
+
pathLength: 300,
|
|
14
17
|
/** Upper bound of one idea file, in bytes (frontmatter + body). */
|
|
15
18
|
fileBytes: 256 * 1024,
|
|
16
19
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/readmodel/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,EAQL,KAAK,oBAAoB,EAE1B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/readmodel/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,EAQL,KAAK,oBAAoB,EAE1B,MAAM,kBAAkB,CAAC;AAW1B,OAAO,KAAK,EAGV,kBAAkB,EAOlB,eAAe,EAGf,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,WAAW,kCAAkC,CAAC;AAC3D,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,QAAa,CAAC;AAS5C;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE,SAAS,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC,CAa1G,CAAC;AA8MF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAQ1E"}
|
|
@@ -28,6 +28,8 @@ import { CONFIG_FILE_NAME, MAX_PROJECT_FILE_BYTES, isReadSecurityViolation, Root
|
|
|
28
28
|
import { MAX_ACTIVE_CONTEXT_BYTES } from '../brief/collect.js';
|
|
29
29
|
import { ACTIVE_MD_REL } from '../brief/constants.js';
|
|
30
30
|
import { extractCurrentStory } from '../brief/story.js';
|
|
31
|
+
import { parseActiveStoryClaim } from '../brief/freshness.js';
|
|
32
|
+
import { parseRoadmapPhases } from './roadmap.js';
|
|
31
33
|
import { hasCheckpointOrderMismatch, selectActiveMissions, selectLatestCheckpoint, } from '../brief/selectors.js';
|
|
32
34
|
export const ROADMAP_REL = 'context/maps/roadmap-index.md';
|
|
33
35
|
/** Protective ceiling, like the other SEC-001B.1 limits — not a content policy. */
|
|
@@ -178,10 +180,14 @@ function buildModel(projectRoot) {
|
|
|
178
180
|
if (active.length > 1)
|
|
179
181
|
notes.push({ code: 'MULTIPLE_ACTIVE_MISSIONS', severity: 'info' });
|
|
180
182
|
let currentStory = null;
|
|
183
|
+
let storyClaim = null;
|
|
181
184
|
if (storyLoaded.content !== null) {
|
|
182
185
|
const extraction = extractCurrentStory(storyLoaded.content);
|
|
183
186
|
if (extraction.hasCurrentStorySection && extraction.excerpt !== null && extraction.lineRange !== null) {
|
|
184
187
|
currentStory = { excerpt: extraction.excerpt, lineRange: extraction.lineRange };
|
|
188
|
+
// Core's existing parser; no second Story parser in the read model.
|
|
189
|
+
const claim = parseActiveStoryClaim(extraction.excerpt);
|
|
190
|
+
storyClaim = claim.kind === 'ids' ? { kind: 'ids', ids: [...claim.ids] } : { kind: claim.kind };
|
|
185
191
|
}
|
|
186
192
|
else {
|
|
187
193
|
notes.push({ code: 'ACTIVE_MD_NO_CURRENT_STORY', severity: 'info' });
|
|
@@ -196,6 +202,7 @@ function buildModel(projectRoot) {
|
|
|
196
202
|
activeMissionCount: stateUsable ? active.length : null,
|
|
197
203
|
finishedMissionCount: stateUsable ? missions.length - active.length : null,
|
|
198
204
|
currentStory,
|
|
205
|
+
storyClaim,
|
|
199
206
|
};
|
|
200
207
|
const currentMissions = stateUsable ? { active } : null;
|
|
201
208
|
let checkpoints = null;
|
|
@@ -211,7 +218,18 @@ function buildModel(projectRoot) {
|
|
|
211
218
|
}),
|
|
212
219
|
};
|
|
213
220
|
}
|
|
214
|
-
|
|
221
|
+
let roadmap = null;
|
|
222
|
+
if (roadmapLoaded.content !== null) {
|
|
223
|
+
let structured;
|
|
224
|
+
try {
|
|
225
|
+
structured = parseRoadmapPhases(roadmapLoaded.content);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// A parser fault is reported as such, never as an empty roadmap.
|
|
229
|
+
structured = { state: 'error', phases: [], activePhase: { kind: 'not_declared' }, diagnostics: [{ code: 'INTERNAL', severity: 'warn', phaseIndex: null }] };
|
|
230
|
+
}
|
|
231
|
+
roadmap = { text: roadmapLoaded.content, structured };
|
|
232
|
+
}
|
|
215
233
|
return {
|
|
216
234
|
projectOverview: section(overview, [config.report, stateResult.report, storyLoaded.report]),
|
|
217
235
|
currentMissions: section(currentMissions, [stateResult.report]),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { readDashboardModel, ROADMAP_REL, MAX_ROADMAP_BYTES } from './dashboard.js';
|
|
2
|
-
export
|
|
2
|
+
export { parseRoadmapPhases } from './roadmap.js';
|
|
3
|
+
export type { ActivePhaseView, CheckpointsData, CurrentMissions, DashboardReadModel, MissionCheckpoints, NoteCode, ProjectOverview, ReadDiagnostics, ReadNote, RoadmapData, RoadmapDiagnosticCode, RoadmapDiagnosticView, RoadmapParseState, RoadmapPhaseView, StructuredRoadmapView, Section, SourceErrorCode, SourceId, SourceReport, SourceStatus, } from './types.js';
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/readmodel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpF,YAAY,EACV,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,eAAe,EACf,QAAQ,EACR,WAAW,EACX,OAAO,EACP,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/readmodel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,eAAe,EACf,QAAQ,EACR,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,OAAO,EACP,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC"}
|
package/dist/readmodel/index.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `parseRoadmapPhases(text)` — structured extraction of the phases declared in
|
|
3
|
+
* `roadmap-index.md` (UI-003C).
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - Pure, deterministic, synchronous. No I/O, no Git, no network, no LLM, no
|
|
7
|
+
* side effects. The input is the already bounded text read by the read model.
|
|
8
|
+
* - DOES NOT reuse `parseRoadmapStatuses()`: that parser reads STORY table rows
|
|
9
|
+
* (`| id | title | STATUS | date |`). Phase semantics come only from the
|
|
10
|
+
* `## Phases` section; tables anywhere (Completion Summary included) are never
|
|
11
|
+
* phases and never statuses.
|
|
12
|
+
* - Everything a phase reports is REPORTED: the document's own claim, never
|
|
13
|
+
* confirmed. Nothing is derived from position, story counts or absence.
|
|
14
|
+
* - The active phase is taken only from an explicit `Active Phase:` line. A
|
|
15
|
+
* missing, contradictory or unmatched pointer is reported as such, never
|
|
16
|
+
* resolved by guessing.
|
|
17
|
+
* - Diagnostics are fixed codes (no document text, no paths), safe to serialize.
|
|
18
|
+
*
|
|
19
|
+
* Recognised shape (both the MONOLITH roadmap and the project template):
|
|
20
|
+
*
|
|
21
|
+
* ## Phases
|
|
22
|
+
* ### Phase <id> — <title> (the title may be absent: `### Phase 2+`)
|
|
23
|
+
* **Status:** <value>
|
|
24
|
+
* **Goal:** <one line> (optional)
|
|
25
|
+
* ...free body (tables, lists, #### sub-headings) → `details`
|
|
26
|
+
* ## Current Focus
|
|
27
|
+
* Active Phase: <id> — <title> | — (read ONLY inside `## Current Focus`)
|
|
28
|
+
*/
|
|
29
|
+
import type { StructuredRoadmapView } from './types.js';
|
|
30
|
+
export declare const MAX_ROADMAP_PHASES = 200;
|
|
31
|
+
export declare const MAX_PHASE_DETAILS_CHARS = 8000;
|
|
32
|
+
export declare const MAX_PHASE_GOAL_CHARS = 600;
|
|
33
|
+
export declare function parseRoadmapPhases(text: string): StructuredRoadmapView;
|
|
34
|
+
//# sourceMappingURL=roadmap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roadmap.d.ts","sourceRoot":"","sources":["../../src/readmodel/roadmap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAKV,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAKpB,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,oBAAoB,MAAM,CAAC;AA4BxC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CA0LtE"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `parseRoadmapPhases(text)` — structured extraction of the phases declared in
|
|
3
|
+
* `roadmap-index.md` (UI-003C).
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - Pure, deterministic, synchronous. No I/O, no Git, no network, no LLM, no
|
|
7
|
+
* side effects. The input is the already bounded text read by the read model.
|
|
8
|
+
* - DOES NOT reuse `parseRoadmapStatuses()`: that parser reads STORY table rows
|
|
9
|
+
* (`| id | title | STATUS | date |`). Phase semantics come only from the
|
|
10
|
+
* `## Phases` section; tables anywhere (Completion Summary included) are never
|
|
11
|
+
* phases and never statuses.
|
|
12
|
+
* - Everything a phase reports is REPORTED: the document's own claim, never
|
|
13
|
+
* confirmed. Nothing is derived from position, story counts or absence.
|
|
14
|
+
* - The active phase is taken only from an explicit `Active Phase:` line. A
|
|
15
|
+
* missing, contradictory or unmatched pointer is reported as such, never
|
|
16
|
+
* resolved by guessing.
|
|
17
|
+
* - Diagnostics are fixed codes (no document text, no paths), safe to serialize.
|
|
18
|
+
*
|
|
19
|
+
* Recognised shape (both the MONOLITH roadmap and the project template):
|
|
20
|
+
*
|
|
21
|
+
* ## Phases
|
|
22
|
+
* ### Phase <id> — <title> (the title may be absent: `### Phase 2+`)
|
|
23
|
+
* **Status:** <value>
|
|
24
|
+
* **Goal:** <one line> (optional)
|
|
25
|
+
* ...free body (tables, lists, #### sub-headings) → `details`
|
|
26
|
+
* ## Current Focus
|
|
27
|
+
* Active Phase: <id> — <title> | — (read ONLY inside `## Current Focus`)
|
|
28
|
+
*/
|
|
29
|
+
import { maskNonContent } from '../brief/markdown-context.js';
|
|
30
|
+
/** Statuses the documents themselves declare (template comment + the MONOLITH roadmap). */
|
|
31
|
+
const RECOGNIZED_STATUSES = new Set(['PLANNED', 'IN PROGRESS', 'COMPLETE', 'COMPLETED', 'NOT DEFINED']);
|
|
32
|
+
export const MAX_ROADMAP_PHASES = 200;
|
|
33
|
+
export const MAX_PHASE_DETAILS_CHARS = 8000;
|
|
34
|
+
export const MAX_PHASE_GOAL_CHARS = 600;
|
|
35
|
+
const MAX_STATUS_CHARS = 80;
|
|
36
|
+
const MAX_TITLE_CHARS = 200;
|
|
37
|
+
const PHASE_HEADING_RE = /^###\s+Phase\s+(\S+?)(?:\s+[—–-]\s+(.*?))?\s*$/;
|
|
38
|
+
const LOOSE_PHASE_HEADING_RE = /^#{1,6}\s+Phase\b/i;
|
|
39
|
+
const STATUS_RE = /^\*\*Status:\*\*[ \t]*(.*?)\s*$/;
|
|
40
|
+
const GOAL_RE = /^\*\*Goal:\*\*[ \t]*(.*?)\s*$/;
|
|
41
|
+
const ACTIVE_RE = /^Active Phase:[ \t]*(.*?)\s*$/;
|
|
42
|
+
const NO_ACTIVE_RE = /^(?:[—–-]|none|no active phase)$/i;
|
|
43
|
+
const PHASE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$/;
|
|
44
|
+
function normalizeStatus(raw) {
|
|
45
|
+
return raw.replace(/\s+/g, ' ').toUpperCase();
|
|
46
|
+
}
|
|
47
|
+
function cap(s, max) {
|
|
48
|
+
return s.length <= max ? s : s.slice(0, max);
|
|
49
|
+
}
|
|
50
|
+
export function parseRoadmapPhases(text) {
|
|
51
|
+
const diagnostics = [];
|
|
52
|
+
const diag = (code, severity, phaseIndex = null) => {
|
|
53
|
+
diagnostics.push({ code, severity, phaseIndex });
|
|
54
|
+
};
|
|
55
|
+
if (text.trim() === '') {
|
|
56
|
+
return { state: 'empty', phases: [], activePhase: { kind: 'not_declared' }, diagnostics: [{ code: 'DOCUMENT_EMPTY', severity: 'warn', phaseIndex: null }] };
|
|
57
|
+
}
|
|
58
|
+
const original = text.replace(/\r\n?/g, '\n').split('\n');
|
|
59
|
+
const masked = maskNonContent(original.join('\n')).split('\n');
|
|
60
|
+
// --- locate the `## Phases` section (first one wins; extra ones are reported) ---
|
|
61
|
+
let phasesStart = -1;
|
|
62
|
+
let phasesEnd = original.length;
|
|
63
|
+
let phasesSections = 0;
|
|
64
|
+
for (let i = 0; i < masked.length; i++) {
|
|
65
|
+
if (/^##\s+Phases\s*$/.test(masked[i])) {
|
|
66
|
+
phasesSections++;
|
|
67
|
+
if (phasesSections === 1) {
|
|
68
|
+
phasesStart = i + 1;
|
|
69
|
+
// The section runs to the next level-2 heading (a second `## Phases` included).
|
|
70
|
+
const next = masked.findIndex((l, j) => j > i && /^##\s/.test(l));
|
|
71
|
+
phasesEnd = next === -1 ? original.length : next;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (phasesSections > 1)
|
|
76
|
+
diag('PHASES_SECTION_DUPLICATED', 'warn');
|
|
77
|
+
const drafts = [];
|
|
78
|
+
let malformedHeadings = 0;
|
|
79
|
+
if (phasesStart !== -1) {
|
|
80
|
+
let current = null;
|
|
81
|
+
for (let i = phasesStart; i < phasesEnd; i++) {
|
|
82
|
+
const line = masked[i];
|
|
83
|
+
if (/^#{1,3}\s/.test(line)) {
|
|
84
|
+
if (current !== null) {
|
|
85
|
+
current.end = i;
|
|
86
|
+
current = null;
|
|
87
|
+
}
|
|
88
|
+
const m = PHASE_HEADING_RE.exec(line);
|
|
89
|
+
if (m !== null && PHASE_ID_RE.test(m[1])) {
|
|
90
|
+
current = { id: m[1], title: m[2] !== undefined && m[2] !== '' ? cap(m[2], MAX_TITLE_CHARS) : null, start: i + 1, end: phasesEnd };
|
|
91
|
+
drafts.push(current);
|
|
92
|
+
}
|
|
93
|
+
else if (LOOSE_PHASE_HEADING_RE.test(line)) {
|
|
94
|
+
malformedHeadings++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// A phase-looking heading outside the `## Phases` section is not a phase (it is not read).
|
|
100
|
+
if (phasesStart === -1) {
|
|
101
|
+
for (const line of masked)
|
|
102
|
+
if (LOOSE_PHASE_HEADING_RE.test(line))
|
|
103
|
+
malformedHeadings++;
|
|
104
|
+
}
|
|
105
|
+
if (malformedHeadings > 0)
|
|
106
|
+
diag('PHASE_HEADING_MALFORMED', 'warn');
|
|
107
|
+
const truncatedPhases = drafts.length > MAX_ROADMAP_PHASES;
|
|
108
|
+
if (truncatedPhases) {
|
|
109
|
+
drafts.length = MAX_ROADMAP_PHASES;
|
|
110
|
+
diag('PHASES_TRUNCATED', 'warn');
|
|
111
|
+
}
|
|
112
|
+
// --- phases ---
|
|
113
|
+
const phases = drafts.map((d, index) => {
|
|
114
|
+
let statusRaw = null;
|
|
115
|
+
let statusConflict = false;
|
|
116
|
+
let statusEmpty = false;
|
|
117
|
+
let goal = null;
|
|
118
|
+
let goalTruncated = false;
|
|
119
|
+
const detailLines = [];
|
|
120
|
+
for (let i = d.start; i < d.end; i++) {
|
|
121
|
+
const m = STATUS_RE.exec(masked[i]);
|
|
122
|
+
if (m !== null) {
|
|
123
|
+
if (m[1] === '')
|
|
124
|
+
statusEmpty = true;
|
|
125
|
+
else if (statusRaw === null)
|
|
126
|
+
statusRaw = m[1];
|
|
127
|
+
else if (normalizeStatus(statusRaw) !== normalizeStatus(m[1]))
|
|
128
|
+
statusConflict = true;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const g = GOAL_RE.exec(masked[i]);
|
|
132
|
+
if (g !== null && goal === null && g[1] !== '') {
|
|
133
|
+
goal = cap(g[1], MAX_PHASE_GOAL_CHARS);
|
|
134
|
+
goalTruncated = g[1].length > MAX_PHASE_GOAL_CHARS;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
detailLines.push(original[i]);
|
|
138
|
+
}
|
|
139
|
+
let value = null;
|
|
140
|
+
let recognized = false;
|
|
141
|
+
if (statusConflict) {
|
|
142
|
+
diag('PHASE_STATUS_CONFLICT', 'warn', index);
|
|
143
|
+
}
|
|
144
|
+
else if (statusRaw !== null) {
|
|
145
|
+
value = cap(statusRaw.replace(/\s+/g, ' '), MAX_STATUS_CHARS);
|
|
146
|
+
recognized = RECOGNIZED_STATUSES.has(normalizeStatus(statusRaw));
|
|
147
|
+
if (!recognized)
|
|
148
|
+
diag('PHASE_STATUS_UNRECOGNIZED', 'warn', index);
|
|
149
|
+
}
|
|
150
|
+
else if (statusEmpty) {
|
|
151
|
+
diag('PHASE_STATUS_EMPTY', 'warn', index);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
diag('PHASE_STATUS_MISSING', 'info', index);
|
|
155
|
+
}
|
|
156
|
+
// Trailing horizontal rules are section separators, not phase content.
|
|
157
|
+
while (detailLines.length > 0 && /^\s*(?:-{3,})?\s*$/.test(detailLines[detailLines.length - 1]))
|
|
158
|
+
detailLines.pop();
|
|
159
|
+
let details = detailLines.join('\n').replace(/^\n+/, '');
|
|
160
|
+
if (details.length > MAX_PHASE_DETAILS_CHARS) {
|
|
161
|
+
details = details.slice(0, MAX_PHASE_DETAILS_CHARS);
|
|
162
|
+
diag('PHASE_DETAILS_TRUNCATED', 'info', index);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
id: d.id,
|
|
166
|
+
title: d.title,
|
|
167
|
+
status: { value, recognized, provenance: 'REPORTED' },
|
|
168
|
+
goal,
|
|
169
|
+
goalTruncated,
|
|
170
|
+
details: details === '' ? null : details,
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
// --- duplicate ids ---
|
|
174
|
+
const seen = new Map();
|
|
175
|
+
const duplicated = new Set();
|
|
176
|
+
phases.forEach((p, index) => {
|
|
177
|
+
if (p.id === null)
|
|
178
|
+
return;
|
|
179
|
+
if (seen.has(p.id)) {
|
|
180
|
+
duplicated.add(p.id);
|
|
181
|
+
diag('PHASE_ID_DUPLICATE', 'warn', index);
|
|
182
|
+
}
|
|
183
|
+
else
|
|
184
|
+
seen.set(p.id, index);
|
|
185
|
+
});
|
|
186
|
+
// --- active phase: explicit `Active Phase:` pointers only ---
|
|
187
|
+
// Only the `## Current Focus` section(s) are authoritative: a pointer in a phase body,
|
|
188
|
+
// a note, a table or any other section is history/prose, never the global pointer.
|
|
189
|
+
const pointers = [];
|
|
190
|
+
let inFocus = false;
|
|
191
|
+
for (const line of masked) {
|
|
192
|
+
if (/^#{1,2}\s/.test(line))
|
|
193
|
+
inFocus = /^##\s+Current Focus\s*$/.test(line);
|
|
194
|
+
else if (inFocus) {
|
|
195
|
+
const m = ACTIVE_RE.exec(line);
|
|
196
|
+
if (m !== null)
|
|
197
|
+
pointers.push(m[1]);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let activePhase;
|
|
201
|
+
if (pointers.length === 0) {
|
|
202
|
+
activePhase = { kind: 'not_declared' };
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const resolved = pointers.map((p) => {
|
|
206
|
+
if (p === '' || NO_ACTIVE_RE.test(p))
|
|
207
|
+
return { kind: 'none' };
|
|
208
|
+
const id = /^(\S+?)(?:\s+[—–-](?:\s.*)?)?$/.exec(p)?.[1];
|
|
209
|
+
if (id === undefined || !PHASE_ID_RE.test(id))
|
|
210
|
+
return { kind: 'unmatched' };
|
|
211
|
+
if (duplicated.has(id))
|
|
212
|
+
return { kind: 'ambiguous' };
|
|
213
|
+
return seen.has(id) ? { kind: 'phase', id } : { kind: 'unmatched' };
|
|
214
|
+
});
|
|
215
|
+
const first = resolved[0];
|
|
216
|
+
const allSame = resolved.every((r) => r.kind === first.kind && (r.kind !== 'phase' || (first.kind === 'phase' && r.id === first.id)));
|
|
217
|
+
if (!allSame) {
|
|
218
|
+
activePhase = { kind: 'ambiguous' };
|
|
219
|
+
diag('ACTIVE_PHASE_CONFLICT', 'warn');
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
activePhase = first;
|
|
223
|
+
if (first.kind === 'unmatched')
|
|
224
|
+
diag('ACTIVE_PHASE_UNMATCHED', 'warn');
|
|
225
|
+
else if (first.kind === 'ambiguous')
|
|
226
|
+
diag('ACTIVE_PHASE_AMBIGUOUS_ID', 'warn');
|
|
227
|
+
}
|
|
228
|
+
if (activePhase.kind === 'phase') {
|
|
229
|
+
const phase = phases[seen.get(activePhase.id)];
|
|
230
|
+
const v = phase.status.value === null ? null : normalizeStatus(phase.status.value);
|
|
231
|
+
if (v === 'COMPLETE' || v === 'COMPLETED')
|
|
232
|
+
diag('ACTIVE_PHASE_REPORTED_COMPLETE', 'warn', seen.get(activePhase.id));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// --- overall state: parse failure is never an empty success ---
|
|
236
|
+
let state;
|
|
237
|
+
if (phases.length === 0) {
|
|
238
|
+
if (phasesStart === -1) {
|
|
239
|
+
state = 'unrecognized';
|
|
240
|
+
diag('PHASES_SECTION_MISSING', 'warn');
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
state = 'no_phases';
|
|
244
|
+
diag('NO_PHASES_FOUND', 'warn');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else if (malformedHeadings > 0 || truncatedPhases || phasesSections > 1) {
|
|
248
|
+
state = 'partial';
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
state = 'ok';
|
|
252
|
+
}
|
|
253
|
+
return { state, phases, activePhase, diagnostics };
|
|
254
|
+
}
|
|
@@ -38,6 +38,20 @@ export interface Section<T> {
|
|
|
38
38
|
/** The sources this section depends on, in fixed order. */
|
|
39
39
|
sources: SourceReport[];
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Identity of the current story as classified by Core's `parseActiveStoryClaim`
|
|
43
|
+
* (UI-003B-1A). `none` = the section declares no active story; `ids` = a
|
|
44
|
+
* structural identity; `unknown` = the section cannot be classified (including
|
|
45
|
+
* a self-contradicting one). Free text (e.g. contradiction values) is not carried.
|
|
46
|
+
*/
|
|
47
|
+
export type StoryClaimView = {
|
|
48
|
+
kind: 'none';
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'ids';
|
|
51
|
+
ids: string[];
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'unknown';
|
|
54
|
+
};
|
|
41
55
|
export interface ProjectOverview {
|
|
42
56
|
/** `project.name` from `monolith.config.json`; null when absent or not a non-empty string. */
|
|
43
57
|
name: string | null;
|
|
@@ -55,6 +69,8 @@ export interface ProjectOverview {
|
|
|
55
69
|
excerpt: string;
|
|
56
70
|
lineRange: string;
|
|
57
71
|
} | null;
|
|
72
|
+
/** Classification of `currentStory`; null when there is no readable Current Story section. */
|
|
73
|
+
storyClaim: StoryClaimView | null;
|
|
58
74
|
}
|
|
59
75
|
export interface CurrentMissions {
|
|
60
76
|
/** Every `in_progress` mission, ordered by (startedAt, id). More than one is reported, never collapsed. */
|
|
@@ -75,9 +91,81 @@ export interface CheckpointsData {
|
|
|
75
91
|
/** One entry per active mission, same order as `CurrentMissions.active`. */
|
|
76
92
|
byActiveMission: MissionCheckpoints[];
|
|
77
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Structured roadmap (UI-003C), interpreted by Core's `parseRoadmapPhases`.
|
|
96
|
+
* Every status is REPORTED (the document's own claim), never CONFIRMED, and is
|
|
97
|
+
* unrelated to Reconciliation. Diagnostics are fixed codes; no document text.
|
|
98
|
+
*/
|
|
99
|
+
export interface RoadmapPhaseView {
|
|
100
|
+
/** Id from an explicit `### Phase <id>` heading. */
|
|
101
|
+
id: string | null;
|
|
102
|
+
/** Title after the dash; null when the heading has none (e.g. `### Phase 2+`). Untrusted text. */
|
|
103
|
+
title: string | null;
|
|
104
|
+
status: {
|
|
105
|
+
/** Verbatim (whitespace-normalized) value; null when missing, empty or self-contradicting. */
|
|
106
|
+
value: string | null;
|
|
107
|
+
/** True only for a value the documents themselves declare (PLANNED, IN PROGRESS, COMPLETE(D), NOT DEFINED). */
|
|
108
|
+
recognized: boolean;
|
|
109
|
+
provenance: 'REPORTED';
|
|
110
|
+
};
|
|
111
|
+
goal: string | null;
|
|
112
|
+
/**
|
|
113
|
+
* True when Core shortened `goal` to its length limit. Computed in Core only.
|
|
114
|
+
* Always present from this Core; absent in DTOs from older servers, where it means "unknown", not "complete".
|
|
115
|
+
*/
|
|
116
|
+
goalTruncated?: boolean;
|
|
117
|
+
/** Remaining body of the phase section, verbatim and untrusted; null when empty. */
|
|
118
|
+
details: string | null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* - none: the document says explicitly there is no active phase
|
|
122
|
+
* - not_declared: no `Active Phase:` pointer at all
|
|
123
|
+
* - phase: one explicit pointer matching exactly one recognised phase
|
|
124
|
+
* - unmatched: the pointer names no recognised phase
|
|
125
|
+
* - ambiguous: contradictory pointers, or the pointed id is duplicated
|
|
126
|
+
*/
|
|
127
|
+
export type ActivePhaseView = {
|
|
128
|
+
kind: 'none';
|
|
129
|
+
} | {
|
|
130
|
+
kind: 'not_declared';
|
|
131
|
+
} | {
|
|
132
|
+
kind: 'phase';
|
|
133
|
+
id: string;
|
|
134
|
+
} | {
|
|
135
|
+
kind: 'unmatched';
|
|
136
|
+
} | {
|
|
137
|
+
kind: 'ambiguous';
|
|
138
|
+
};
|
|
139
|
+
export type RoadmapDiagnosticCode = 'DOCUMENT_EMPTY' | 'PHASES_SECTION_MISSING' | 'PHASES_SECTION_DUPLICATED' | 'NO_PHASES_FOUND' | 'PHASE_HEADING_MALFORMED' | 'PHASES_TRUNCATED' | 'PHASE_STATUS_MISSING' | 'PHASE_STATUS_EMPTY' | 'PHASE_STATUS_CONFLICT' | 'PHASE_STATUS_UNRECOGNIZED' | 'PHASE_DETAILS_TRUNCATED' | 'PHASE_ID_DUPLICATE' | 'ACTIVE_PHASE_CONFLICT' | 'ACTIVE_PHASE_UNMATCHED' | 'ACTIVE_PHASE_AMBIGUOUS_ID' | 'ACTIVE_PHASE_REPORTED_COMPLETE' | 'INTERNAL';
|
|
140
|
+
export interface RoadmapDiagnosticView {
|
|
141
|
+
code: RoadmapDiagnosticCode;
|
|
142
|
+
severity: 'info' | 'warn';
|
|
143
|
+
/** Index into `phases` when the diagnostic concerns one phase. */
|
|
144
|
+
phaseIndex: number | null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* - ok: phases recognised, nothing structural lost
|
|
148
|
+
* - partial: phases recognised, but some content could not be (malformed heading, truncation)
|
|
149
|
+
* - empty: the document is available but blank
|
|
150
|
+
* - no_phases: a `## Phases` section exists without any phase
|
|
151
|
+
* - unrecognized: non-empty document without a `## Phases` section
|
|
152
|
+
* - error: processing failed; the structure must not be trusted
|
|
153
|
+
*/
|
|
154
|
+
export type RoadmapParseState = 'ok' | 'partial' | 'empty' | 'no_phases' | 'unrecognized' | 'error';
|
|
155
|
+
export interface StructuredRoadmapView {
|
|
156
|
+
state: RoadmapParseState;
|
|
157
|
+
phases: RoadmapPhaseView[];
|
|
158
|
+
activePhase: ActivePhaseView;
|
|
159
|
+
diagnostics: RoadmapDiagnosticView[];
|
|
160
|
+
}
|
|
78
161
|
export interface RoadmapData {
|
|
79
162
|
/** Raw, unparsed text of `roadmap-index.md`. Untrusted: must be shown escaped, never rendered or executed. */
|
|
80
163
|
text: string;
|
|
164
|
+
/**
|
|
165
|
+
* Core's interpretation of `text`. Additive: DTOs from older servers lack it,
|
|
166
|
+
* and consumers must then fall back to `text` without inventing phases.
|
|
167
|
+
*/
|
|
168
|
+
structured?: StructuredRoadmapView;
|
|
81
169
|
}
|
|
82
170
|
export type NoteCode = 'STATE_LEGACY_SCHEMA_MIGRATED' | 'MULTIPLE_ACTIVE_MISSIONS' | 'CHECKPOINT_ORDER_MISMATCH' | 'ACTIVE_MD_NO_CURRENT_STORY';
|
|
83
171
|
export interface ReadNote {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/readmodel/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAEtE,uDAAuD;AACvD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAC;AAE3F,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,wBAAwB,GACxB,qBAAqB,GACrB,oBAAoB,GACpB,cAAc,GACd,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,gBAAgB,GAChB,mBAAmB,GACnB,yBAAyB,GACzB,6BAA6B,GAC7B,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,UAAU,CAAC;AAEf,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,yFAAyF;IACzF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,uCAAuC;IACvC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;CAC9B;AAED,0EAA0E;AAC1E,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,2DAA2D;IAC3D,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,QAAQ,GAAG,SAAS,CAAC;IACzC,kGAAkG;IAClG,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,8FAA8F;IAC9F,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,iGAAiG;IACjG,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/readmodel/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
* temp name, never from an error code. UNKNOWN is never retried and never
|
|
19
19
|
* silently downgraded;
|
|
20
20
|
* - identity-checked cleanup: only a temp file THIS call created, and only if
|
|
21
|
-
* the name still shows the
|
|
21
|
+
* the name still shows the inode of a separate read-only IDENTITY descriptor,
|
|
22
|
+
* which stays open until publication and cleanup are finished (an open
|
|
23
|
+
* descriptor pins the inode, so it cannot be recycled for a foreign file: no
|
|
24
|
+
* ABA). The WRITING descriptor is closed before publication (ADR-012). The
|
|
25
|
+
* `lstat` -> `unlink` step itself remains non-atomic (residual race, ADR-012).
|
|
22
26
|
*
|
|
23
27
|
* Not routed through here: lock release and stale-lock recovery (`lock.ts`)
|
|
24
28
|
* unlink the lock path directly, and `trusted-directory` scope creates its
|
|
@@ -40,7 +44,12 @@
|
|
|
40
44
|
import * as fs from 'fs';
|
|
41
45
|
export type PublicationState = 'NOT_PUBLISHED' | 'PUBLISHED' | 'UNKNOWN';
|
|
42
46
|
export type WriteErrorCode = 'WRITE_SCOPE_REQUIRED' | 'WRITE_BOUNDARY_INVALID' | 'WRITE_OUTSIDE_BOUNDARY' | 'WRITE_PARENT_SYMLINK' | 'WRITE_PARENT_NOT_DIRECTORY' | 'WRITE_PARENT_MISSING' | 'WRITE_PARENT_CHANGED' | 'WRITE_TARGET_SYMLINK' | 'WRITE_TARGET_NOT_REGULAR' | 'WRITE_TARGET_EXISTS' | 'WRITE_TARGET_HARDLINKED' | 'WRITE_TMP_COLLISION' | 'WRITE_TOO_LARGE' | 'WRITE_READONLY' | 'WRITE_PERMISSION' | 'WRITE_NO_SPACE' | 'WRITE_FSYNC_FAILED' | 'WRITE_VERIFY_FAILED' | 'WRITE_LINK_UNSUPPORTED' | 'WRITE_PUBLICATION_UNKNOWN' | 'WRITE_FAILED';
|
|
43
|
-
export type WriteWarningCode = 'DIR_FSYNC_UNSUPPORTED' | 'DIR_FSYNC_FAILED' | 'STAGING_RESIDUE'
|
|
47
|
+
export type WriteWarningCode = 'DIR_FSYNC_UNSUPPORTED' | 'DIR_FSYNC_FAILED' | 'STAGING_RESIDUE'
|
|
48
|
+
/**
|
|
49
|
+
* After a confirmed publication the identity descriptor of the temporary file
|
|
50
|
+
* failed to close. Not data damage, not a leftover file, not a rollback.
|
|
51
|
+
*/
|
|
52
|
+
| 'STAGING_DESCRIPTOR_CLOSE_FAILED' | 'PUBLICATION_VERIFIED_AFTER_ERROR' | 'PLATFORM_BEST_EFFORT';
|
|
44
53
|
export interface WriteWarning {
|
|
45
54
|
code: WriteWarningCode;
|
|
46
55
|
message: string;
|
|
@@ -213,8 +222,10 @@ export type DisposeResult = 'removed' | 'absent' | 'not-owned' | 'failed';
|
|
|
213
222
|
export declare function createExclusive(parent: ParentRef, name: string, mode: number, opts?: InternalOptions): OwnedFile;
|
|
214
223
|
/**
|
|
215
224
|
* Remove `token`'s file iff the name still shows the recorded identity. Never
|
|
216
|
-
* removes anything else.
|
|
217
|
-
*
|
|
225
|
+
* removes anything else. This token-only form has no live descriptor and so
|
|
226
|
+
* cannot exclude inode reuse after the caller closed its own; the writes in
|
|
227
|
+
* this module use `disposeHeld`. The unlink itself is not conditional (no
|
|
228
|
+
* `unlinkat`): a name swapped between the check and the unlink is a residual race.
|
|
218
229
|
*/
|
|
219
230
|
export declare function disposeOwned(parent: ParentRef, token: OwnedToken, opts?: InternalOptions): DisposeResult;
|
|
220
231
|
export interface WriteOptions extends InternalOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-write.d.ts","sourceRoot":"","sources":["../../src/root/safe-write.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"safe-write.d.ts","sourceRoot":"","sources":["../../src/root/safe-write.ts"],"names":[],"mappings":"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"}
|