@milehimikey/em 1.8.0 → 1.9.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/.claude/skills/event-modeling/SKILL.md +58 -404
- package/.claude/skills/event-modeling-conform/SKILL.md +77 -0
- package/.claude/skills/{event-modeling → event-modeling-conform}/reference/conform.md +39 -13
- package/.claude/skills/event-modeling-design/SKILL.md +139 -0
- package/.claude/skills/event-modeling-discover/SKILL.md +89 -0
- package/.claude/skills/{event-modeling → event-modeling-discover}/reference/extract.md +3 -2
- package/.claude/skills/event-modeling-implement/SKILL.md +40 -0
- package/.claude/skills/{event-modeling → event-modeling-implement}/reference/implement.md +3 -2
- package/.claude/skills/event-modeling-review/SKILL.md +58 -0
- package/.claude/skills/{event-modeling → event-modeling-shared}/reference/em-dsl.md +128 -84
- package/.claude/skills/{event-modeling → event-modeling-shared}/reference/methodology.md +6 -5
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +147 -0
- package/.claude/skills/{event-modeling → event-modeling-shared}/templates/model-readme.md +2 -2
- package/.claude/skills/{event-modeling → event-modeling-shared}/templates/slice.md +51 -8
- package/README.md +24 -4
- package/dist/catalog/build.js +9 -0
- package/dist/catalog/build.js.map +1 -1
- package/dist/catalog/docJoin.d.ts +15 -0
- package/dist/catalog/docJoin.js +8 -0
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/modelCollisionValidate.d.ts +22 -0
- package/dist/catalog/modelCollisionValidate.js +67 -0
- package/dist/catalog/modelCollisionValidate.js.map +1 -0
- package/dist/catalog/orphanedSliceDocValidate.d.ts +11 -0
- package/dist/catalog/orphanedSliceDocValidate.js +98 -0
- package/dist/catalog/orphanedSliceDocValidate.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +15 -0
- package/dist/catalog/sliceDoc.js +18 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/cli/changelogBuild.d.ts +12 -0
- package/dist/cli/changelogBuild.js +89 -0
- package/dist/cli/changelogBuild.js.map +1 -0
- package/dist/cli/ciInit.d.ts +42 -0
- package/dist/cli/ciInit.js +298 -0
- package/dist/cli/ciInit.js.map +1 -0
- package/dist/cli/conformSupersede.d.ts +32 -0
- package/dist/cli/conformSupersede.js +102 -0
- package/dist/cli/conformSupersede.js.map +1 -0
- package/dist/cli/coverage.d.ts +17 -10
- package/dist/cli/coverage.js +61 -16
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/frontmatterSurgery.d.ts +28 -0
- package/dist/cli/frontmatterSurgery.js +66 -0
- package/dist/cli/frontmatterSurgery.js.map +1 -0
- package/dist/cli/markImplemented.js +6 -45
- package/dist/cli/markImplemented.js.map +1 -1
- package/dist/cli/ratify.d.ts +41 -0
- package/dist/cli/ratify.js +151 -0
- package/dist/cli/ratify.js.map +1 -0
- package/dist/cli/reratify.d.ts +33 -0
- package/dist/cli/reratify.js +137 -0
- package/dist/cli/reratify.js.map +1 -0
- package/dist/cli/skillCheck.d.ts +6 -0
- package/dist/cli/skillCheck.js +46 -0
- package/dist/cli/skillCheck.js.map +1 -1
- package/dist/cli/skillDirs.d.ts +15 -0
- package/dist/cli/skillDirs.js +34 -0
- package/dist/cli/skillDirs.js.map +1 -0
- package/dist/cli/skillSync.d.ts +10 -0
- package/dist/cli/skillSync.js +15 -0
- package/dist/cli/skillSync.js.map +1 -1
- package/dist/cli/sliceIndex.d.ts +3 -0
- package/dist/cli/sliceIndex.js +6 -2
- package/dist/cli/sliceIndex.js.map +1 -1
- package/dist/cli/sliceLink.d.ts +52 -0
- package/dist/cli/sliceLink.js +152 -0
- package/dist/cli/sliceLink.js.map +1 -0
- package/dist/cli/status.d.ts +261 -0
- package/dist/cli/status.js +536 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/usageLog.d.ts +75 -0
- package/dist/cli/usageLog.js +193 -0
- package/dist/cli/usageLog.js.map +1 -0
- package/dist/cli.js +501 -130
- package/dist/cli.js.map +1 -1
- package/dist/emit/diffJson.d.ts +9 -1
- package/dist/emit/diffJson.js +44 -1
- package/dist/emit/diffJson.js.map +1 -1
- package/dist/emit/freshnessJson.d.ts +6 -0
- package/dist/emit/freshnessJson.js +24 -0
- package/dist/emit/freshnessJson.js.map +1 -0
- package/dist/emit/json.d.ts +67 -8
- package/dist/emit/json.js +19 -3
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/statusJson.d.ts +7 -0
- package/dist/emit/statusJson.js +39 -0
- package/dist/emit/statusJson.js.map +1 -0
- package/dist/emit/typespec.d.ts +26 -0
- package/dist/emit/typespec.js +356 -0
- package/dist/emit/typespec.js.map +1 -0
- package/dist/emit/usageReportJson.d.ts +3 -0
- package/dist/emit/usageReportJson.js +23 -0
- package/dist/emit/usageReportJson.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +301 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/rules.d.ts +13 -0
- package/dist/model/rules.js +20 -0
- package/dist/model/rules.js.map +1 -1
- package/dist/model/validate.js +9 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +6 -0
- package/dist/parser/parser.js +20 -6
- package/dist/parser/parser.js.map +1 -1
- package/dist/render/drawNotes.d.ts +11 -1
- package/dist/render/drawNotes.js +20 -2
- package/dist/render/drawNotes.js.map +1 -1
- package/dist/render/render.js +31 -6
- package/dist/render/render.js.map +1 -1
- package/dist/render/serve.js +4 -0
- package/dist/render/serve.js.map +1 -1
- package/dist/render/sliceOverlay.d.ts +7 -2
- package/dist/render/sliceOverlay.js +19 -7
- package/dist/render/sliceOverlay.js.map +1 -1
- package/dist/render/sliceStatus.d.ts +7 -3
- package/dist/render/sliceStatus.js +21 -14
- package/dist/render/sliceStatus.js.map +1 -1
- package/dist/render/viewerHtml.js +1 -1
- package/dist/render/viewerHtml.js.map +1 -1
- package/dist/templates.d.ts +2 -2
- package/dist/templates.js +5 -5
- package/dist/util/markers.d.ts +16 -4
- package/dist/util/markers.js +23 -10
- package/dist/util/markers.js.map +1 -1
- package/package.json +2 -1
- /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/conformance-report.md +0 -0
- /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/state.md +0 -0
package/dist/cli/coverage.js
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
// line "every INV-n has a test that cites its ID" — until now a pure honor system, since no `em`
|
|
4
4
|
// command related a slice doc's invariant IDs to a test suite. Both sides are stable strings
|
|
5
5
|
// (the doc's `INV-*` tokens, a test file's source text), so the citation check is deterministic
|
|
6
|
-
// and grep-shaped: extract every invariant ID a slice doc's
|
|
7
|
-
//
|
|
6
|
+
// and grep-shaped: extract every invariant ID a slice doc's own Invariants/Delta sections define
|
|
7
|
+
// (MIL-149: not every ID its prose merely mentions — a doc narratively citing another slice's ID
|
|
8
|
+
// doesn't own it; MIL-155: including a citation embedded inside those very sections, e.g. an
|
|
9
|
+
// Invariants bullet's explanation pointing at a sibling slice's ID), then scan a test tree for
|
|
10
|
+
// lines that cite it.
|
|
8
11
|
//
|
|
9
12
|
// Deliberately a *second*, independent reader of doc bodies, alongside `em export`'s doc join
|
|
10
13
|
// (docJoin.ts, MIL-91) — not a change to it. `em export`'s frontmatter-only contract is
|
|
@@ -28,31 +31,73 @@ import { join } from "node:path";
|
|
|
28
31
|
import { resolveSliceDocJoin } from "../catalog/docJoin.js";
|
|
29
32
|
import { readSliceDoc } from "../catalog/readSliceDoc.js";
|
|
30
33
|
import { walkDir } from "../util/walkDir.js";
|
|
31
|
-
/** The stable INV token format
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* and `INV-CHK-4`/`INV-CHK-3a` (the per-slice mnemonic style reference/conform.md's own example
|
|
36
|
-
* uses). Exported so tests can assert against the exact grammar. */
|
|
34
|
+
/** The stable INV token format. `INV-` then one or more alphanumeric/hyphen segments — matches
|
|
35
|
+
* both `INV-1`/`INV-2` (the template's bare numbering) and `INV-CHK-4`/`INV-CHK-3a` (the
|
|
36
|
+
* per-slice mnemonic style reference/conform.md's own example uses). Exported so tests can
|
|
37
|
+
* assert against the exact grammar. */
|
|
37
38
|
export const INV_TOKEN_RE = /\bINV-[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*\b/g;
|
|
39
|
+
/** Headings whose content *defines* (or, for `## Delta`, legitimately re-declares) an invariant
|
|
40
|
+
* ID for the slice doc it appears in — `## Invariants / Business Rules` itself, and `## Delta`'s
|
|
41
|
+
* Added/Modified/Renamed requirement subsections, which introduce/rename IDs "from Invariants
|
|
42
|
+
* below" on re-ratification (see templates/slice.md). Every other section — Intent, Scenarios,
|
|
43
|
+
* Trigger & Actor, Dependencies & Read Models Affected, Open Questions, ... — is prose that may
|
|
44
|
+
* narratively *cite* another slice's ID without this doc owning it (MIL-149: such a mention was
|
|
45
|
+
* being cross-credited to the citing slice's own coverage ledger). */
|
|
46
|
+
const OWNERSHIP_HEADING_RE = /^##\s+(?:Invariants(?:\s*\/\s*Business Rules)?|Delta)\s*$/i;
|
|
47
|
+
/** A line contributes INV tokens only when it's *structural* — a top-level bullet/list item
|
|
48
|
+
* (`- ...` / `* ...`, no leading indentation) or a subheading (`### ...` and deeper, since only
|
|
49
|
+
* `#`/`##` toggles `inOwnershipSection` above) — never an indented continuation line wrapping a
|
|
50
|
+
* multi-line bullet, nor a standalone prose paragraph with no bullet marker (MIL-155). Every
|
|
51
|
+
* cross-credit MIL-149 missed turned out to have exactly this shape: a slice's own Invariants
|
|
52
|
+
* bullet legitimately explains its rule across several wrapped lines, and a later wrapped line
|
|
53
|
+
* names a sibling slice's ID for context — e.g. `request-payment`'s `INV-RP-1` bullet wraps onto
|
|
54
|
+
* a line citing `payments-to-request`'s `INV-PTR-2` by way of explaining the exactly-once
|
|
55
|
+
* mechanism. The bullet's own *opening* line is structural and trusted for every ID it mentions
|
|
56
|
+
* (this also covers `## Delta`'s Renamed bullets, which legitimately declare two IDs — old and
|
|
57
|
+
* new — on one line, and its `**MODIFIED (...):** INV-X — ...` bullets, whose bold label isn't
|
|
58
|
+
* itself an ID); each wrapped continuation line is prose and contributes nothing. The indentation
|
|
59
|
+
* test also covers MIL-156's blessed nested-elaboration-bullet shape: `templates/slice.md`'s
|
|
60
|
+
* Invariants section now suggests a rule statement's optional rationale/edge-case detail go on
|
|
61
|
+
* its own indented sub-bullet (` - {{...}}`) rather than run on into the rule sentence, purely
|
|
62
|
+
* for HTML-render readability — that sub-bullet is indented, so it's already excluded here the
|
|
63
|
+
* same as any other continuation line, and never contributes (or steals) an ID. */
|
|
64
|
+
const STRUCTURAL_LINE_RE = /^(?:[-*]\s|#{3,6}\s)/;
|
|
38
65
|
/** Statuses `reference/implement.md`'s coverage gate applies to — a slice hasn't reached the
|
|
39
66
|
* point of needing test citations before `ready-to-implement`, and stays in scope forever after
|
|
40
67
|
* once `implemented` (tests shouldn't regress away from citing an invariant just because the
|
|
41
68
|
* slice shipped). Anything else (`draft`, `reviewed`, or no doc at all) is out of scope. */
|
|
42
69
|
const IN_SCOPE_STATUSES = new Set(["ready-to-implement", "implemented"]);
|
|
43
70
|
/**
|
|
44
|
-
* Extract every distinct `INV-*` token
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
71
|
+
* Extract every distinct `INV-*` token this slice doc *defines*, in first-occurrence order (a
|
|
72
|
+
* rule stated once in `## Invariants` and re-declared in a later `## Delta` section dedupes to
|
|
73
|
+
* its first appearance — order is for stable, readable output, not a claim about which mention is
|
|
74
|
+
* canonical). Scoped to `OWNERSHIP_HEADING_RE`'s sections only, so a doc's prose narratively
|
|
75
|
+
* citing another slice's ID elsewhere (Dependencies, Open Questions, ...) doesn't cross-credit
|
|
76
|
+
* that ID as this slice's own (MIL-149) — the section ends at the next `#`/`##` heading or EOF,
|
|
77
|
+
* same boundary rule sliceDoc.ts's countOpenQuestions() uses, so a `### Added`/`#### Requirement`
|
|
78
|
+
* subheading under `## Delta` stays in scope while a sibling `## Scenarios` does not. Within that
|
|
79
|
+
* scope, only `STRUCTURAL_LINE_RE`'s bullet/subheading lines contribute — MIL-155 — so a wrapped
|
|
80
|
+
* continuation line that legitimately *cites* a sibling slice's ID while explaining this doc's own
|
|
81
|
+
* (e.g. an Invariants bullet's exactly-once rule wrapping onto a line naming another slice's
|
|
82
|
+
* queue-draining ID) doesn't cross-credit that citation just because it sits inside the ownership
|
|
83
|
+
* section too.
|
|
48
84
|
*/
|
|
49
85
|
export function extractInvariantIds(body) {
|
|
50
86
|
const seen = new Set();
|
|
51
87
|
const ids = [];
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
88
|
+
let inOwnershipSection = false;
|
|
89
|
+
for (const line of body.split(/\r?\n/)) {
|
|
90
|
+
if (/^#{1,2}\s/.test(line)) {
|
|
91
|
+
inOwnershipSection = OWNERSHIP_HEADING_RE.test(line);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (!inOwnershipSection || !STRUCTURAL_LINE_RE.test(line))
|
|
95
|
+
continue;
|
|
96
|
+
for (const m of line.matchAll(INV_TOKEN_RE)) {
|
|
97
|
+
if (!seen.has(m[0])) {
|
|
98
|
+
seen.add(m[0]);
|
|
99
|
+
ids.push(m[0]);
|
|
100
|
+
}
|
|
56
101
|
}
|
|
57
102
|
}
|
|
58
103
|
return ids;
|
package/dist/cli/coverage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/cli/coverage.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,8FAA8F;AAC9F,iGAAiG;AACjG,6FAA6F;AAC7F,gGAAgG;AAChG,iGAAiG;AACjG,
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/cli/coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** A field line's matched span within a frontmatter `inner` string: `key[:]<ws>` prefix
|
|
2
|
+
* (preserved verbatim on write) plus the value text that follows, lowercase-key-matched the
|
|
3
|
+
* same way sliceDoc.ts's own parser folds keys for lookup. Column-0 only — same grammar as
|
|
4
|
+
* sliceDoc.ts's `^([A-Za-z][\w-]*):\s*(.*)$`, so this never matches an indented/list-style line
|
|
5
|
+
* the parser wouldn't treat as a field either. */
|
|
6
|
+
export declare function fieldLineRegex(key: string): RegExp;
|
|
7
|
+
/** Strip a value the same way sliceDoc.ts's splitFrontmatter does when reading (surrounding
|
|
8
|
+
* quotes, then blank-after-trim collapses to "absent") — used here only to classify a *current*
|
|
9
|
+
* field value for an idempotent/refusal guard, matching what the parser would have reported for
|
|
10
|
+
* the same text. */
|
|
11
|
+
export declare function normalizeFieldValue(raw: string | undefined): string | null;
|
|
12
|
+
/** Same anchor as `fieldLineRegex`, but captures through the line's own trailing EOL (or
|
|
13
|
+
* end-of-string, for a field that happens to be the frontmatter's last line) so a match can be
|
|
14
|
+
* spliced out ENTIRELY — key, value, and terminator — rather than just replaced. Used by
|
|
15
|
+
* `em slice reratify` (reratify.ts) to drop a now-stale `ratifiedBy:`/`ratifiedOn:` pair: those
|
|
16
|
+
* fields describe who signed off the PRIOR version, and silently leaving them in place would
|
|
17
|
+
* make a fresh version look already ratified. */
|
|
18
|
+
export declare function fieldLineWithEolRegex(key: string): RegExp;
|
|
19
|
+
/** Byte offsets of the frontmatter block's inner lines (between the two `---` fences), located
|
|
20
|
+
* by walking lines with `indexOf` rather than `split(/\r?\n/)` — a rewrite can't afford the
|
|
21
|
+
* line-ending normalization a split/join would silently perform across the WHOLE file the
|
|
22
|
+
* moment it's written back (fine for a read-only parse, not for a surgical rewrite that must
|
|
23
|
+
* preserve the body byte-for-byte). `null` when there's no well-formed leading `---`/`---`
|
|
24
|
+
* fence, mirroring sliceDoc.ts's own "unterminated fence = no frontmatter" treatment. */
|
|
25
|
+
export declare function locateFrontmatterInner(markdown: string): {
|
|
26
|
+
innerStart: number;
|
|
27
|
+
innerEnd: number;
|
|
28
|
+
} | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Shared primitives for surgical in-place slice-doc frontmatter edits — extracted from
|
|
3
|
+
// `markImplemented.ts` (MIL-103) when `ratify.ts` (MIL-165) needed the exact same "find one
|
|
4
|
+
// field line's byte span, replace only that span, copy everything else through verbatim"
|
|
5
|
+
// discipline. Both commands share the same non-negotiable contract: a parse+re-serialize
|
|
6
|
+
// through sliceDoc.ts's own (deliberately read-only) parser would silently drop comments, key
|
|
7
|
+
// order, spacing, and every field the command isn't allowed to touch — see markImplemented.ts's
|
|
8
|
+
// module header for the full rationale, which applies unchanged here.
|
|
9
|
+
//
|
|
10
|
+
// Pure, fs-free — callers own reading/writing the file, same split as everywhere else in `em`.
|
|
11
|
+
/** A field line's matched span within a frontmatter `inner` string: `key[:]<ws>` prefix
|
|
12
|
+
* (preserved verbatim on write) plus the value text that follows, lowercase-key-matched the
|
|
13
|
+
* same way sliceDoc.ts's own parser folds keys for lookup. Column-0 only — same grammar as
|
|
14
|
+
* sliceDoc.ts's `^([A-Za-z][\w-]*):\s*(.*)$`, so this never matches an indented/list-style line
|
|
15
|
+
* the parser wouldn't treat as a field either. */
|
|
16
|
+
export function fieldLineRegex(key) {
|
|
17
|
+
// `[^\r\n]*` (not a lazy `.*?` + `\r?$`) deliberately -- JS regex `$`/multiline line-terminator
|
|
18
|
+
// semantics treat a lone `\r` as its own line boundary, which makes a trailing-`\r?$` anchor
|
|
19
|
+
// ambiguous on CRLF input. Excluding \r/\n from the value class sidesteps that entirely: the
|
|
20
|
+
// match always stops at the first \r or \n, full stop, no boundary-matching subtlety involved.
|
|
21
|
+
return new RegExp(`^(${key}[ \\t]*:[ \\t]*)([^\\r\\n]*)`, "im");
|
|
22
|
+
}
|
|
23
|
+
/** Strip a value the same way sliceDoc.ts's splitFrontmatter does when reading (surrounding
|
|
24
|
+
* quotes, then blank-after-trim collapses to "absent") — used here only to classify a *current*
|
|
25
|
+
* field value for an idempotent/refusal guard, matching what the parser would have reported for
|
|
26
|
+
* the same text. */
|
|
27
|
+
export function normalizeFieldValue(raw) {
|
|
28
|
+
if (raw === undefined)
|
|
29
|
+
return null;
|
|
30
|
+
const v = raw.trim().replace(/^["']|["']$/g, "");
|
|
31
|
+
return v === "" ? null : v;
|
|
32
|
+
}
|
|
33
|
+
/** Same anchor as `fieldLineRegex`, but captures through the line's own trailing EOL (or
|
|
34
|
+
* end-of-string, for a field that happens to be the frontmatter's last line) so a match can be
|
|
35
|
+
* spliced out ENTIRELY — key, value, and terminator — rather than just replaced. Used by
|
|
36
|
+
* `em slice reratify` (reratify.ts) to drop a now-stale `ratifiedBy:`/`ratifiedOn:` pair: those
|
|
37
|
+
* fields describe who signed off the PRIOR version, and silently leaving them in place would
|
|
38
|
+
* make a fresh version look already ratified. */
|
|
39
|
+
export function fieldLineWithEolRegex(key) {
|
|
40
|
+
return new RegExp(`^${key}[ \\t]*:[ \\t]*[^\\r\\n]*\\r?\\n?`, "im");
|
|
41
|
+
}
|
|
42
|
+
/** Byte offsets of the frontmatter block's inner lines (between the two `---` fences), located
|
|
43
|
+
* by walking lines with `indexOf` rather than `split(/\r?\n/)` — a rewrite can't afford the
|
|
44
|
+
* line-ending normalization a split/join would silently perform across the WHOLE file the
|
|
45
|
+
* moment it's written back (fine for a read-only parse, not for a surgical rewrite that must
|
|
46
|
+
* preserve the body byte-for-byte). `null` when there's no well-formed leading `---`/`---`
|
|
47
|
+
* fence, mirroring sliceDoc.ts's own "unterminated fence = no frontmatter" treatment. */
|
|
48
|
+
export function locateFrontmatterInner(markdown) {
|
|
49
|
+
if (!/^---[ \t]*\r?\n/.test(markdown))
|
|
50
|
+
return null;
|
|
51
|
+
const innerStart = markdown.indexOf("\n") + 1;
|
|
52
|
+
let pos = innerStart;
|
|
53
|
+
while (pos <= markdown.length) {
|
|
54
|
+
const nl = markdown.indexOf("\n", pos);
|
|
55
|
+
const lineEnd = nl === -1 ? markdown.length : nl;
|
|
56
|
+
const line = markdown.slice(pos, lineEnd);
|
|
57
|
+
if (/^---[ \t]*\r?$/.test(line)) {
|
|
58
|
+
return { innerStart, innerEnd: pos };
|
|
59
|
+
}
|
|
60
|
+
if (nl === -1)
|
|
61
|
+
break;
|
|
62
|
+
pos = nl + 1;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=frontmatterSurgery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatterSurgery.js","sourceRoot":"","sources":["../../src/cli/frontmatterSurgery.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,uFAAuF;AACvF,4FAA4F;AAC5F,yFAAyF;AACzF,yFAAyF;AACzF,8FAA8F;AAC9F,gGAAgG;AAChG,sEAAsE;AACtE,EAAE;AACF,+FAA+F;AAE/F;;;;mDAImD;AACnD,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,gGAAgG;IAChG,6FAA6F;IAC7F,6FAA6F;IAC7F,+FAA+F;IAC/F,OAAO,IAAI,MAAM,CAAC,KAAK,GAAG,8BAA8B,EAAE,IAAI,CAAC,CAAC;AAClE,CAAC;AAED;;;qBAGqB;AACrB,MAAM,UAAU,mBAAmB,CAAC,GAAuB;IACzD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;kDAKkD;AAClD,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,OAAO,IAAI,MAAM,CAAC,IAAI,GAAG,mCAAmC,EAAE,IAAI,CAAC,CAAC;AACtE,CAAC;AAED;;;;;0FAK0F;AAC1F,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,GAAG,GAAG,UAAU,CAAC;IACrB,OAAO,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QACvC,CAAC;QACD,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,MAAM;QACrB,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -23,54 +23,15 @@
|
|
|
23
23
|
// `- **Status:** ...` / `- **Implemented in:** ...` bullet dialect; this module targets the
|
|
24
24
|
// canonical frontmatter dialect (MIL-86) instead, and folds in the note-binding resolution the
|
|
25
25
|
// bridge's caller (`mark-implemented.ts`) did via a separate `em export` shell-out.
|
|
26
|
+
//
|
|
27
|
+
// The three primitives above (`fieldLineRegex`/`normalizeFieldValue`/`locateFrontmatterInner`)
|
|
28
|
+
// now live in `./frontmatterSurgery.js` (MIL-165) — `ratify.ts` needs the exact same span-finding
|
|
29
|
+
// discipline for its own `status`/`ratifiedBy`/`ratifiedOn` edit, so they moved out to a shared
|
|
30
|
+
// module rather than being copy-pasted a second time.
|
|
26
31
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
27
32
|
import { join } from "node:path";
|
|
28
33
|
import { resolveSliceDocJoin } from "../catalog/docJoin.js";
|
|
29
|
-
|
|
30
|
-
* (preserved verbatim on write) plus the value text that follows, lowercase-key-matched the
|
|
31
|
-
* same way sliceDoc.ts's own parser folds keys for lookup. Column-0 only — same grammar as
|
|
32
|
-
* sliceDoc.ts's `^([A-Za-z][\w-]*):\s*(.*)$`, so this never matches an indented/list-style line
|
|
33
|
-
* the parser wouldn't treat as a field either. */
|
|
34
|
-
function fieldLineRegex(key) {
|
|
35
|
-
// `[^\r\n]*` (not a lazy `.*?` + `\r?$`) deliberately -- JS regex `$`/multiline line-terminator
|
|
36
|
-
// semantics treat a lone `\r` as its own line boundary, which makes a trailing-`\r?$` anchor
|
|
37
|
-
// ambiguous on CRLF input. Excluding \r/\n from the value class sidesteps that entirely: the
|
|
38
|
-
// match always stops at the first \r or \n, full stop, no boundary-matching subtlety involved.
|
|
39
|
-
return new RegExp(`^(${key}[ \\t]*:[ \\t]*)([^\\r\\n]*)`, "im");
|
|
40
|
-
}
|
|
41
|
-
/** Strip a value the same way sliceDoc.ts's splitFrontmatter does when reading (surrounding
|
|
42
|
-
* quotes, then blank-after-trim collapses to "absent") — used here only to classify the
|
|
43
|
-
* *current* implementedIn value for the idempotent/refusal guard, matching what the parser
|
|
44
|
-
* would have reported for the same text. */
|
|
45
|
-
function normalizeFieldValue(raw) {
|
|
46
|
-
if (raw === undefined)
|
|
47
|
-
return null;
|
|
48
|
-
const v = raw.trim().replace(/^["']|["']$/g, "");
|
|
49
|
-
return v === "" ? null : v;
|
|
50
|
-
}
|
|
51
|
-
/** Byte offsets of the frontmatter block's inner lines (between the two `---` fences), located
|
|
52
|
-
* by walking lines with `indexOf` rather than `split(/\r?\n/)` — see the module header for why
|
|
53
|
-
* a rewrite can't afford the line-ending normalization a split/join would silently perform.
|
|
54
|
-
* `null` when there's no well-formed leading `---`/`---` fence, mirroring sliceDoc.ts's own
|
|
55
|
-
* "unterminated fence = no frontmatter" treatment. */
|
|
56
|
-
function locateFrontmatterInner(markdown) {
|
|
57
|
-
if (!/^---[ \t]*\r?\n/.test(markdown))
|
|
58
|
-
return null;
|
|
59
|
-
const innerStart = markdown.indexOf("\n") + 1;
|
|
60
|
-
let pos = innerStart;
|
|
61
|
-
while (pos <= markdown.length) {
|
|
62
|
-
const nl = markdown.indexOf("\n", pos);
|
|
63
|
-
const lineEnd = nl === -1 ? markdown.length : nl;
|
|
64
|
-
const line = markdown.slice(pos, lineEnd);
|
|
65
|
-
if (/^---[ \t]*\r?$/.test(line)) {
|
|
66
|
-
return { innerStart, innerEnd: pos };
|
|
67
|
-
}
|
|
68
|
-
if (nl === -1)
|
|
69
|
-
break;
|
|
70
|
-
pos = nl + 1;
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
34
|
+
import { fieldLineRegex, locateFrontmatterInner, normalizeFieldValue } from "./frontmatterSurgery.js";
|
|
74
35
|
/**
|
|
75
36
|
* Pure text transform: flips `status:` to `implemented` and sets `implementedIn:` to `prUrl` in
|
|
76
37
|
* `raw`'s frontmatter block. Idempotent (re-applying the same URL is a no-op, `changed: false`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markImplemented.js","sourceRoot":"","sources":["../../src/cli/markImplemented.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,+FAA+F;AAC/F,iGAAiG;AACjG,+FAA+F;AAC/F,gGAAgG;AAChG,+EAA+E;AAC/E,8FAA8F;AAC9F,oFAAoF;AACpF,EAAE;AACF,8FAA8F;AAC9F,iGAAiG;AACjG,6FAA6F;AAC7F,8FAA8F;AAC9F,gGAAgG;AAChG,+FAA+F;AAC/F,4FAA4F;AAC5F,4FAA4F;AAC5F,iGAAiG;AACjG,6FAA6F;AAC7F,8EAA8E;AAC9E,EAAE;AACF,uFAAuF;AACvF,4FAA4F;AAC5F,+FAA+F;AAC/F,oFAAoF;
|
|
1
|
+
{"version":3,"file":"markImplemented.js","sourceRoot":"","sources":["../../src/cli/markImplemented.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NormalizedModel } from "../model/model.js";
|
|
2
|
+
import { RefsResult } from "../model/refs.js";
|
|
3
|
+
/** The status this command flips a slice doc to — the handoff gate
|
|
4
|
+
* (docs/process.md#what-ratified-means): contracts/invariants agreed, open questions resolved
|
|
5
|
+
* or deferred, ready for an implementing agent or engineer to pick up. */
|
|
6
|
+
export declare const RATIFIED_STATUS = "ready-to-implement";
|
|
7
|
+
export type ApplyRatifyResult = {
|
|
8
|
+
ok: true;
|
|
9
|
+
content: string;
|
|
10
|
+
changed: boolean;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Pure text transform: flips `status:` to `ready-to-implement` and sets `ratifiedBy:`/
|
|
17
|
+
* `ratifiedOn:` in `raw`'s frontmatter block. Idempotent (re-applying the same by/on pair once
|
|
18
|
+
* already ratified is a no-op, `changed: false`, `content` returned byte-identical to `raw`);
|
|
19
|
+
* refuses (`ok: false`) rather than silently overwrite provenance when the doc is already
|
|
20
|
+
* `status: ready-to-implement` with a *different* recorded ratifier/date — mirroring
|
|
21
|
+
* `applyImplementedFrontmatter`'s refusal to overwrite a different `implementedIn`. There is no
|
|
22
|
+
* such refusal when `status` isn't already the target: re-ratifying a slice that has since moved
|
|
23
|
+
* on (e.g. back from `implemented` after a version bump — docs/slice-doc-schema.md#status-under-
|
|
24
|
+
* re-ratification) is the ordinary, expected use of this command, so it always applies cleanly.
|
|
25
|
+
* No fs access — the caller reads/writes; see `runRatify` below.
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyRatifyFrontmatter(raw: string, ratifiedBy: string, ratifiedOn: string): ApplyRatifyResult;
|
|
28
|
+
export type RunRatifyResult = {
|
|
29
|
+
ok: true;
|
|
30
|
+
path: string;
|
|
31
|
+
changed: boolean;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Resolves `sliceKey` to its bound doc via the same note-binding join `mark-implemented`/
|
|
38
|
+
* `--slice-ready`/`em export` use (MIL-121 cross-binding included), then reads/applies/writes
|
|
39
|
+
* it. `baseDir` is the `.em` file's directory, same convention every doc/note path in `em` uses.
|
|
40
|
+
*/
|
|
41
|
+
export declare function runRatify(model: NormalizedModel, refs: RefsResult, baseDir: string, sliceKey: string, ratifiedBy: string, ratifiedOn: string): RunRatifyResult;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em slice ratify` (MIL-165): makes "who ratified, and when" a first-class recorded fact
|
|
3
|
+
// instead of an unnamed edit anyone with commit access could make. Flips `status:
|
|
4
|
+
// ready-to-implement` and sets `ratifiedBy:`/`ratifiedOn:` on the slice doc resolved from the
|
|
5
|
+
// key via the SAME note-binding resolution `mark-implemented`/`--slice-ready`/`em export` use
|
|
6
|
+
// (catalog/docJoin.ts's resolveSliceDocJoin) — same shape and idempotency discipline as
|
|
7
|
+
// `em slice mark-implemented` (MIL-103, markImplemented.ts), mirrored deliberately so the two
|
|
8
|
+
// lifecycle-flip commands (ratify at handoff, mark-implemented at merge) read as one family.
|
|
9
|
+
//
|
|
10
|
+
// Write strategy: the same surgical index-math splicing markImplemented.ts uses, via the shared
|
|
11
|
+
// primitives in ./frontmatterSurgery.js — never a parse+re-serialize (sliceDoc.ts's own parser
|
|
12
|
+
// is deliberately read-only and drops everything not in its `SliceDoc` shape). Everything
|
|
13
|
+
// outside the edited value spans — the body, `version:`, `implementedIn:`, the lineage/`covers`
|
|
14
|
+
// keys, and (best-effort) the file's own line-ending style — is copied through verbatim.
|
|
15
|
+
//
|
|
16
|
+
// `ratifiedBy`/`ratifiedOn` are additive, optional frontmatter keys (docs/slice-doc-schema.md):
|
|
17
|
+
// omitting them is not an error to any existing `em` command (the "unknown keys are captured,
|
|
18
|
+
// never read, never a warning" contract already covers a doc that predates this feature), and
|
|
19
|
+
// this command is the only thing that ever writes them — same "one write path" discipline
|
|
20
|
+
// `mark-implemented` holds for `implementedIn`.
|
|
21
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { resolveSliceDocJoin } from "../catalog/docJoin.js";
|
|
24
|
+
import { fieldLineRegex, locateFrontmatterInner, normalizeFieldValue } from "./frontmatterSurgery.js";
|
|
25
|
+
import { isValidDateString } from "./stateFile.js";
|
|
26
|
+
/** The status this command flips a slice doc to — the handoff gate
|
|
27
|
+
* (docs/process.md#what-ratified-means): contracts/invariants agreed, open questions resolved
|
|
28
|
+
* or deferred, ready for an implementing agent or engineer to pick up. */
|
|
29
|
+
export const RATIFIED_STATUS = "ready-to-implement";
|
|
30
|
+
/**
|
|
31
|
+
* Pure text transform: flips `status:` to `ready-to-implement` and sets `ratifiedBy:`/
|
|
32
|
+
* `ratifiedOn:` in `raw`'s frontmatter block. Idempotent (re-applying the same by/on pair once
|
|
33
|
+
* already ratified is a no-op, `changed: false`, `content` returned byte-identical to `raw`);
|
|
34
|
+
* refuses (`ok: false`) rather than silently overwrite provenance when the doc is already
|
|
35
|
+
* `status: ready-to-implement` with a *different* recorded ratifier/date — mirroring
|
|
36
|
+
* `applyImplementedFrontmatter`'s refusal to overwrite a different `implementedIn`. There is no
|
|
37
|
+
* such refusal when `status` isn't already the target: re-ratifying a slice that has since moved
|
|
38
|
+
* on (e.g. back from `implemented` after a version bump — docs/slice-doc-schema.md#status-under-
|
|
39
|
+
* re-ratification) is the ordinary, expected use of this command, so it always applies cleanly.
|
|
40
|
+
* No fs access — the caller reads/writes; see `runRatify` below.
|
|
41
|
+
*/
|
|
42
|
+
export function applyRatifyFrontmatter(raw, ratifiedBy, ratifiedOn) {
|
|
43
|
+
const trimmedBy = ratifiedBy.trim();
|
|
44
|
+
if (!trimmedBy)
|
|
45
|
+
return { ok: false, message: "a ratifier name is required (--by)" };
|
|
46
|
+
// Refuse control characters (including an embedded \r/\n, which could splice a multi-line
|
|
47
|
+
// value into the frontmatter and corrupt the fence) — unlike mark-implemented's PR-URL guard,
|
|
48
|
+
// internal whitespace is fine: a person's name legitimately contains spaces ("Alice Smith").
|
|
49
|
+
if (/[\x00-\x1f\x7f]/.test(trimmedBy)) {
|
|
50
|
+
return { ok: false, message: "ratifier name must not contain control characters" };
|
|
51
|
+
}
|
|
52
|
+
if (!isValidDateString(ratifiedOn)) {
|
|
53
|
+
return { ok: false, message: `invalid date "${ratifiedOn}" — expected YYYY-MM-DD` };
|
|
54
|
+
}
|
|
55
|
+
const range = locateFrontmatterInner(raw);
|
|
56
|
+
if (!range)
|
|
57
|
+
return { ok: false, message: "no frontmatter block found" };
|
|
58
|
+
const inner = raw.slice(range.innerStart, range.innerEnd);
|
|
59
|
+
const statusMatch = fieldLineRegex("status").exec(inner);
|
|
60
|
+
if (!statusMatch)
|
|
61
|
+
return { ok: false, message: "no `status:` field found in frontmatter" };
|
|
62
|
+
const byMatch = fieldLineRegex("ratifiedBy").exec(inner);
|
|
63
|
+
const onMatch = fieldLineRegex("ratifiedOn").exec(inner);
|
|
64
|
+
const currentStatus = normalizeFieldValue(statusMatch[2])?.toLowerCase() ?? null;
|
|
65
|
+
const currentBy = byMatch ? normalizeFieldValue(byMatch[2]) : null;
|
|
66
|
+
const currentOn = onMatch ? normalizeFieldValue(onMatch[2]) : null;
|
|
67
|
+
if (currentStatus === RATIFIED_STATUS && currentBy !== null && currentOn !== null) {
|
|
68
|
+
if (currentBy === trimmedBy && currentOn === ratifiedOn) {
|
|
69
|
+
return { ok: true, content: raw, changed: false }; // idempotent no-op
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
message: `already ratified by ${currentBy} on ${currentOn} — refusing to overwrite with ` +
|
|
74
|
+
`${trimmedBy} on ${ratifiedOn}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Match the file's own line-ending style for any freshly-inserted line, same trick
|
|
78
|
+
// markImplemented.ts uses: on a CRLF doc, the text right after the status line's match starts
|
|
79
|
+
// with the "\r\n" that used to terminate it, so reusing it keeps a new line consistent with
|
|
80
|
+
// its neighbors instead of being the one bare-LF line in an otherwise-CRLF file.
|
|
81
|
+
const afterStatus = inner.slice(statusMatch.index + statusMatch[0].length);
|
|
82
|
+
const eol = afterStatus.startsWith("\r\n") ? "\r\n" : "\n";
|
|
83
|
+
// Whichever of ratifiedBy/ratifiedOn have no existing line get folded directly into the status
|
|
84
|
+
// edit's replacement text (inserted right after the new status value) rather than added as
|
|
85
|
+
// separate zero-length "insert" edits — simpler and unambiguous to splice than reasoning about
|
|
86
|
+
// an insert-edit's index coinciding with an adjacent replace-edit's span.
|
|
87
|
+
const missingLines = [];
|
|
88
|
+
if (!byMatch)
|
|
89
|
+
missingLines.push(`ratifiedBy: ${trimmedBy}`);
|
|
90
|
+
if (!onMatch)
|
|
91
|
+
missingLines.push(`ratifiedOn: ${ratifiedOn}`);
|
|
92
|
+
const newStatusText = `${statusMatch[1]}${RATIFIED_STATUS}`;
|
|
93
|
+
const statusNext = missingLines.length > 0 ? `${newStatusText}${eol}${missingLines.join(eol)}` : newStatusText;
|
|
94
|
+
// Apply from the highest index first so an earlier edit's index stays valid — same convention
|
|
95
|
+
// markImplemented.ts uses. Each present match (status always, ratifiedBy/ratifiedOn only when
|
|
96
|
+
// an existing line was found) is replaced independently of the others' positions, so this is
|
|
97
|
+
// safe even when the frontmatter's keys are out of the usual order.
|
|
98
|
+
const edits = [
|
|
99
|
+
{ index: statusMatch.index, oldLen: statusMatch[0].length, next: statusNext },
|
|
100
|
+
];
|
|
101
|
+
if (byMatch)
|
|
102
|
+
edits.push({ index: byMatch.index, oldLen: byMatch[0].length, next: `${byMatch[1]}${trimmedBy}` });
|
|
103
|
+
if (onMatch)
|
|
104
|
+
edits.push({ index: onMatch.index, oldLen: onMatch[0].length, next: `${onMatch[1]}${ratifiedOn}` });
|
|
105
|
+
edits.sort((a, b) => b.index - a.index);
|
|
106
|
+
let updatedInner = inner;
|
|
107
|
+
for (const edit of edits) {
|
|
108
|
+
updatedInner = updatedInner.slice(0, edit.index) + edit.next + updatedInner.slice(edit.index + edit.oldLen);
|
|
109
|
+
}
|
|
110
|
+
const content = raw.slice(0, range.innerStart) + updatedInner + raw.slice(range.innerEnd);
|
|
111
|
+
return { ok: true, content, changed: true };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolves `sliceKey` to its bound doc via the same note-binding join `mark-implemented`/
|
|
115
|
+
* `--slice-ready`/`em export` use (MIL-121 cross-binding included), then reads/applies/writes
|
|
116
|
+
* it. `baseDir` is the `.em` file's directory, same convention every doc/note path in `em` uses.
|
|
117
|
+
*/
|
|
118
|
+
export function runRatify(model, refs, baseDir, sliceKey, ratifiedBy, ratifiedOn) {
|
|
119
|
+
const sliceIndex = refs.sliceKeys.indexOf(sliceKey);
|
|
120
|
+
if (sliceIndex === -1) {
|
|
121
|
+
return { ok: false, message: `no slice with export key "${sliceKey}" in this model` };
|
|
122
|
+
}
|
|
123
|
+
const slice = model.slices[sliceIndex];
|
|
124
|
+
const { doc } = resolveSliceDocJoin(slice, sliceKey, baseDir, (id) => refs.refById.get(id));
|
|
125
|
+
if (doc.reason === "no-doc-bound") {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
message: `slice "${sliceKey}" has no doc bound via \`note "slices/${sliceKey}.md"\` — bind a slice doc before ratifying it`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (doc.reason === "binding-missing-file") {
|
|
132
|
+
return { ok: false, message: `slice "${sliceKey}" notes "${doc.path}" but no such file exists` };
|
|
133
|
+
}
|
|
134
|
+
if (doc.reason === "frontmatter-invalid") {
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
message: `slice doc "${doc.path}" has missing or invalid frontmatter — run \`em validate\` for details`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const absPath = join(baseDir, doc.path);
|
|
141
|
+
const raw = readFileSync(absPath, "utf8");
|
|
142
|
+
const result = applyRatifyFrontmatter(raw, ratifiedBy, ratifiedOn);
|
|
143
|
+
if (!result.ok) {
|
|
144
|
+
return { ok: false, message: `${doc.path}: ${result.message}` };
|
|
145
|
+
}
|
|
146
|
+
if (result.changed) {
|
|
147
|
+
writeFileSync(absPath, result.content, "utf8");
|
|
148
|
+
}
|
|
149
|
+
return { ok: true, path: doc.path, changed: result.changed };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=ratify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratify.js","sourceRoot":"","sources":["../../src/cli/ratify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NormalizedModel } from "../model/model.js";
|
|
2
|
+
import { RefsResult } from "../model/refs.js";
|
|
3
|
+
export type ApplyReratifyResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
content: string;
|
|
6
|
+
newVersion: number;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Pure text transform: bumps `version:` by 1 and flips `status:` to `ready-to-implement` in
|
|
13
|
+
* `raw`'s frontmatter block, clearing any `ratifiedBy:`/`ratifiedOn:` lines found (see module
|
|
14
|
+
* header). Refuses (`ok: false`) unless the doc's CURRENT `status:` is exactly `implemented` —
|
|
15
|
+
* see module header for why this precondition (not idempotent-no-op) is the right refusal
|
|
16
|
+
* shape here. No fs access — the caller reads/writes; see `runReratify` below.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyReratifyFrontmatter(raw: string): ApplyReratifyResult;
|
|
19
|
+
export type RunReratifyResult = {
|
|
20
|
+
ok: true;
|
|
21
|
+
path: string;
|
|
22
|
+
newVersion: number;
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Resolves `sliceKey` to its bound doc via the same note-binding join `mark-implemented`/
|
|
29
|
+
* `ratify`/`--slice-ready`/`em export` use (MIL-121 cross-binding included), then reads/applies/
|
|
30
|
+
* writes it. `baseDir` is the `.em` file's directory, same convention every doc/note path in
|
|
31
|
+
* `em` uses.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runReratify(model: NormalizedModel, refs: RefsResult, baseDir: string, sliceKey: string): RunReratifyResult;
|