@llm4ts/flow 0.17.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Approval.d.ts +27 -0
- package/dist/Approval.d.ts.map +1 -0
- package/dist/Approval.js +36 -0
- package/dist/Approval.js.map +1 -0
- package/dist/Artifacts.d.ts +57 -0
- package/dist/Artifacts.d.ts.map +1 -0
- package/dist/Artifacts.js +103 -0
- package/dist/Artifacts.js.map +1 -0
- package/dist/Pack.d.ts +6 -0
- package/dist/Pack.d.ts.map +1 -1
- package/dist/Pack.js +7 -0
- package/dist/Pack.js.map +1 -1
- package/dist/PageSpec.d.ts +16 -0
- package/dist/PageSpec.d.ts.map +1 -1
- package/dist/PageSpec.js +78 -7
- package/dist/PageSpec.js.map +1 -1
- package/dist/SpecChecks.d.ts +42 -2
- package/dist/SpecChecks.d.ts.map +1 -1
- package/dist/SpecChecks.js +81 -22
- package/dist/SpecChecks.js.map +1 -1
- package/package.json +4 -2
- package/src/Approval.ts +51 -0
- package/src/Artifacts.ts +161 -0
- package/src/Pack.ts +13 -0
- package/src/PageSpec.ts +92 -8
- package/src/SpecChecks.ts +128 -28
package/dist/SpecChecks.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
|
-
import { ReviewResult } from "./Review.ts";
|
|
3
|
+
import { ReviewIssue, ReviewResult } from "./Review.ts";
|
|
4
4
|
import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts";
|
|
5
5
|
declare const CoverageRule_base: Schema.Class<CoverageRule, Schema.Struct<{
|
|
6
6
|
readonly name: Schema.String;
|
|
@@ -15,8 +15,48 @@ export declare class CoverageRule extends CoverageRule_base {
|
|
|
15
15
|
* candidate units, not every file that shares the tree with them.
|
|
16
16
|
*/
|
|
17
17
|
export declare const matchingFiles: (workspace: WorkspaceShape, regex: string, exclude?: string) => Effect.Effect<ReadonlyArray<string>, WorkspaceError>;
|
|
18
|
+
/** A unit a coverage rule captured, with every file it was captured from. */
|
|
19
|
+
export interface CapturedUnit {
|
|
20
|
+
readonly rule: string;
|
|
21
|
+
readonly unit: string;
|
|
22
|
+
readonly paths: ReadonlyArray<string>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Every unit each rule captures across the workspace, in first-seen order
|
|
26
|
+
* per rule, each with the files it was found in — the provenance a
|
|
27
|
+
* wave-scoped gate needs to tell a unit of this wave from one of another.
|
|
28
|
+
*/
|
|
29
|
+
export declare const capturedUnits: (workspace: WorkspaceShape, rules: readonly CoverageRule[]) => Effect.Effect<readonly CapturedUnit[], WorkspaceError, never>;
|
|
18
30
|
export declare const coverageUnits: (workspace: WorkspaceShape, rules: readonly CoverageRule[]) => Effect.Effect<Readonly<Record<string, readonly string[]>>, WorkspaceError, never>;
|
|
19
|
-
export
|
|
31
|
+
export interface CoverageOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Restricts the gate to units captured from at least one file this
|
|
34
|
+
* predicate accepts. A wave-scoped extraction passes the wave's program
|
|
35
|
+
* files here: a unit that lives only in another wave's sources (or in an
|
|
36
|
+
* estate-wide descriptor such as web.xml) is reported in `outOfScope`
|
|
37
|
+
* rather than failing this wave's gate. Absent: every unit gates.
|
|
38
|
+
*/
|
|
39
|
+
readonly inScope?: (path: string) => boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface CoverageReport {
|
|
42
|
+
readonly result: ReviewResult;
|
|
43
|
+
/** Uncovered units the scope excluded from the gate, as `rule: unit`. */
|
|
44
|
+
readonly outOfScope: ReadonlyArray<string>;
|
|
45
|
+
}
|
|
46
|
+
/** Coverage with the scope split: what gates, and what was left to a later wave. */
|
|
47
|
+
export declare const coverageReport: (workspace: WorkspaceShape, rules: readonly CoverageRule[], traceability: string, options?: CoverageOptions | undefined) => Effect.Effect<CoverageReport, WorkspaceError, never>;
|
|
48
|
+
export declare const coverage: (workspace: WorkspaceShape, rules: readonly CoverageRule[], traceability: string, options?: CoverageOptions | undefined) => Effect.Effect<ReviewResult, WorkspaceError, never>;
|
|
20
49
|
export declare const features: (workspace: WorkspaceShape, directory: string) => Effect.Effect<ReviewResult, WorkspaceError, never>;
|
|
50
|
+
/**
|
|
51
|
+
* Deterministic schema check of the specs a pack declares `spec-schema:`
|
|
52
|
+
* for: each program's spec must embed a block the schema decodes. A failure
|
|
53
|
+
* is a per-program Critical finding (titled like a judge finding, so the
|
|
54
|
+
* extraction gate's fix turn repairs that program), never a guess in a
|
|
55
|
+
* later phase — convert-page would otherwise be the first to notice.
|
|
56
|
+
*/
|
|
57
|
+
export declare const specSchemaIssues: (schema: string | undefined, specs: readonly {
|
|
58
|
+
readonly name: string;
|
|
59
|
+
readonly markdown: string | undefined;
|
|
60
|
+
}[]) => Effect.Effect<readonly ReviewIssue[], never, never>;
|
|
21
61
|
export {};
|
|
22
62
|
//# sourceMappingURL=SpecChecks.d.ts.map
|
package/dist/SpecChecks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecChecks.d.ts","sourceRoot":"","sources":["../src/SpecChecks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"SpecChecks.d.ts","sourceRoot":"","sources":["../src/SpecChecks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;;;;;;AAEpE,qBAAa,YAAa,SAAQ,iBAIhC;CAAG;AAcL;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACxB,WAAW,cAAc,EACzB,OAAO,MAAM,EACb,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,cAAc,CAMH,CAAA;AAEnD,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACtC;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,8HAwBxB,CAAA;AAEF,eAAO,MAAM,aAAa,kJAYxB,CAAA;AAEF,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAC3C;AAUD,oFAAoF;AACpF,eAAO,MAAM,cAAc,kLA0BzB,CAAA;AAEF,eAAO,MAAM,QAAQ,gLAOnB,CAAA;AA2BF,eAAO,MAAM,QAAQ,sGA+BnB,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;mBAEW,MAAM;uBAAqB,MAAM,GAAG,SAAS;2DAyBnF,CAAA"}
|
package/dist/SpecChecks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
|
+
import { pageSpecShapeHint, parsePageSpec } from "./PageSpec.js";
|
|
3
4
|
import { ReviewIssue, ReviewResult } from "./Review.js";
|
|
4
5
|
export class CoverageRule extends Schema.Class("CoverageRule")({
|
|
5
6
|
name: Schema.String,
|
|
@@ -26,43 +27,75 @@ export const matchingFiles = (workspace, regex, exclude) => workspace
|
|
|
26
27
|
...(exclude === undefined ? {} : { excluding: new RegExp(exclude) })
|
|
27
28
|
})
|
|
28
29
|
.pipe(Effect.map((paths) => [...paths].sort()));
|
|
29
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Every unit each rule captures across the workspace, in first-seen order
|
|
32
|
+
* per rule, each with the files it was found in — the provenance a
|
|
33
|
+
* wave-scoped gate needs to tell a unit of this wave from one of another.
|
|
34
|
+
*/
|
|
35
|
+
export const capturedUnits = Effect.fn("@llm4ts/flow/SpecChecks.capturedUnits")(function* (workspace, rules) {
|
|
30
36
|
const paths = yield* workspace.discover();
|
|
31
|
-
const result =
|
|
37
|
+
const result = [];
|
|
32
38
|
for (const rule of rules) {
|
|
33
39
|
const filePattern = new RegExp(rule.files);
|
|
34
40
|
const unitPattern = new RegExp(rule.unit);
|
|
35
|
-
const units = [];
|
|
36
41
|
for (const path of paths.filter((path) => filePattern.test(path))) {
|
|
37
42
|
const contents = yield* workspace.read(path);
|
|
38
43
|
for (const line of contents.split(/\r?\n/)) {
|
|
39
44
|
for (const unit of capture(unitPattern, line)) {
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const existing = result.find((entry) => entry.rule === rule.name && entry.unit === unit);
|
|
46
|
+
if (existing === undefined) {
|
|
47
|
+
result.push({ rule: rule.name, unit, paths: [path] });
|
|
48
|
+
}
|
|
49
|
+
else if (!existing.paths.includes(path)) {
|
|
50
|
+
existing.paths.push(path);
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
}
|
|
46
|
-
result[rule.name] = units;
|
|
47
55
|
}
|
|
48
56
|
return result;
|
|
49
57
|
});
|
|
50
|
-
export const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
export const coverageUnits = Effect.fn("@llm4ts/flow/SpecChecks.coverageUnits")(function* (workspace, rules) {
|
|
59
|
+
const captured = yield* capturedUnits(workspace, rules);
|
|
60
|
+
const result = {};
|
|
61
|
+
for (const rule of rules) {
|
|
62
|
+
result[rule.name] = captured
|
|
63
|
+
.filter((entry) => entry.rule === rule.name)
|
|
64
|
+
.map((entry) => entry.unit);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
});
|
|
68
|
+
const uncoveredIssue = (rule, unit) => ReviewIssue.make({
|
|
69
|
+
severity: "Critical",
|
|
70
|
+
title: `uncovered ${rule}: ${unit}`,
|
|
71
|
+
description: `'${unit}' exists in the legacy source but does not ` + "appear in the traceability matrix."
|
|
72
|
+
});
|
|
73
|
+
/** Coverage with the scope split: what gates, and what was left to a later wave. */
|
|
74
|
+
export const coverageReport = Effect.fn("@llm4ts/flow/SpecChecks.coverageReport")(function* (workspace, rules, traceability, options = {}) {
|
|
75
|
+
const captured = yield* capturedUnits(workspace, rules);
|
|
76
|
+
const issues = [];
|
|
77
|
+
const outOfScope = [];
|
|
78
|
+
for (const entry of captured) {
|
|
79
|
+
if (traceability.includes(entry.unit)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (options.inScope === undefined || entry.paths.some(options.inScope)) {
|
|
83
|
+
issues.push(uncoveredIssue(entry.rule, entry.unit));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
outOfScope.push(`${entry.rule}: ${entry.unit}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
result: ReviewResult.make({
|
|
91
|
+
issues,
|
|
92
|
+
summary: issues.length === 0 ? "coverage complete" : `${issues.length} unit(s) uncovered`
|
|
93
|
+
}),
|
|
94
|
+
outOfScope
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
export const coverage = Effect.fn("@llm4ts/flow/SpecChecks.coverage")(function* (workspace, rules, traceability, options = {}) {
|
|
98
|
+
return (yield* coverageReport(workspace, rules, traceability, options)).result;
|
|
66
99
|
});
|
|
67
100
|
const featureIssue = (path, contents) => {
|
|
68
101
|
const lines = contents.split(/\r?\n/).map((line) => line.trim());
|
|
@@ -111,4 +144,30 @@ export const features = Effect.fn("@llm4ts/flow/SpecChecks.features")(function*
|
|
|
111
144
|
summary: issues.length === 0 ? "features well-formed" : `${issues.length} malformed`
|
|
112
145
|
});
|
|
113
146
|
});
|
|
147
|
+
/**
|
|
148
|
+
* Deterministic schema check of the specs a pack declares `spec-schema:`
|
|
149
|
+
* for: each program's spec must embed a block the schema decodes. A failure
|
|
150
|
+
* is a per-program Critical finding (titled like a judge finding, so the
|
|
151
|
+
* extraction gate's fix turn repairs that program), never a guess in a
|
|
152
|
+
* later phase — convert-page would otherwise be the first to notice.
|
|
153
|
+
*/
|
|
154
|
+
export const specSchemaIssues = Effect.fn("@llm4ts/flow/SpecChecks.specSchemaIssues")(function* (schema, specs) {
|
|
155
|
+
if (schema !== "pagespec") {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
const issues = [];
|
|
159
|
+
for (const spec of specs) {
|
|
160
|
+
const problem = spec.markdown === undefined
|
|
161
|
+
? "spec file is missing"
|
|
162
|
+
: yield* parsePageSpec(spec.markdown).pipe(Effect.map(() => undefined), Effect.catch((error) => Effect.succeed(error.message)));
|
|
163
|
+
if (problem !== undefined) {
|
|
164
|
+
issues.push(ReviewIssue.make({
|
|
165
|
+
severity: "Critical",
|
|
166
|
+
title: `judge[${spec.name}]: invalid pagespec block`,
|
|
167
|
+
description: `${problem}. ${pageSpecShapeHint}`
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return issues;
|
|
172
|
+
});
|
|
114
173
|
//# sourceMappingURL=SpecChecks.js.map
|
package/dist/SpecChecks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecChecks.js","sourceRoot":"","sources":["../src/SpecChecks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAGvD,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,KAAK,CAAe,cAAc,CAAC,CAAC;IAC3E,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,IAAI,EAAE,MAAM,CAAC,MAAM;CACpB,CAAC;CAAG;AAEL,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,IAAY,EAAyB,EAAE;IACrE,MAAM,UAAU,GAAG,IAAI,MAAM,CAC3B,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAC5D,CAAA;IACD,MAAM,MAAM,GAAkB,EAAE,CAAA;IAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,SAAyB,EACzB,KAAa,EACb,OAAgB,EACsC,EAAE,CACxD,SAAS;KACN,QAAQ,CAAC,MAAM,EAAE;IAChB,QAAQ,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC;IAC3B,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;CACrE,CAAC;KACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"SpecChecks.js","sourceRoot":"","sources":["../src/SpecChecks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm4ts/flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Effect-native LLM workflow, persistence, review, and repository automation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"src"
|
|
14
14
|
],
|
|
15
15
|
"exports": {
|
|
16
|
+
"./Approval": "./dist/Approval.js",
|
|
17
|
+
"./Artifacts": "./dist/Artifacts.js",
|
|
16
18
|
"./package.json": "./package.json",
|
|
17
19
|
"./Activity": "./dist/Activity.js",
|
|
18
20
|
"./AzureDevOpsTool": "./dist/AzureDevOpsTool.js",
|
|
@@ -89,7 +91,7 @@
|
|
|
89
91
|
"typescript"
|
|
90
92
|
],
|
|
91
93
|
"dependencies": {
|
|
92
|
-
"@llm4ts/core": "0.
|
|
94
|
+
"@llm4ts/core": "0.18.1"
|
|
93
95
|
},
|
|
94
96
|
"peerDependencies": {
|
|
95
97
|
"effect": "4.0.0-beta.102"
|
package/src/Approval.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
import type { FlowError } from "./FlowError.ts"
|
|
4
|
+
import type { PlainFileStoreShape } from "./Persistence.ts"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The human gates of the modernization phases: a markdown document ends
|
|
8
|
+
* with a draft marker the flow writes, and the next phase refuses to start
|
|
9
|
+
* until a person has ticked it. (Moved from `@llm4ts/modernize` in 0.18.0.)
|
|
10
|
+
*/
|
|
11
|
+
export class ApprovalRequired extends Schema.TaggedErrorClass<ApprovalRequired>()(
|
|
12
|
+
"ApprovalRequired",
|
|
13
|
+
{
|
|
14
|
+
path: Schema.String,
|
|
15
|
+
marker: Schema.String
|
|
16
|
+
}
|
|
17
|
+
) {
|
|
18
|
+
get message(): string {
|
|
19
|
+
return `approval required in ${this.path}: set '${this.marker}'`
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class MissingModernizeArtifact extends Schema.TaggedErrorClass<MissingModernizeArtifact>()(
|
|
24
|
+
"MissingModernizeArtifact",
|
|
25
|
+
{
|
|
26
|
+
path: Schema.String,
|
|
27
|
+
message: Schema.String
|
|
28
|
+
}
|
|
29
|
+
) {}
|
|
30
|
+
|
|
31
|
+
export const ApprovedMarker = "- [x] Approved"
|
|
32
|
+
export const DraftApprovalMarker = "- [ ] Approved"
|
|
33
|
+
|
|
34
|
+
export const withDraftApproval = (markdown: string): string =>
|
|
35
|
+
`${markdown.trimEnd()}\n\n${DraftApprovalMarker}\n`
|
|
36
|
+
|
|
37
|
+
export const requireApproval = Effect.fn("@llm4ts/flow/Approval.requireApproval")(function* (
|
|
38
|
+
files: PlainFileStoreShape,
|
|
39
|
+
path: string
|
|
40
|
+
): Effect.fn.Return<void, FlowError | ApprovalRequired | MissingModernizeArtifact> {
|
|
41
|
+
const contents = yield* files.read(path)
|
|
42
|
+
if (contents === undefined) {
|
|
43
|
+
return yield* MissingModernizeArtifact.make({
|
|
44
|
+
path,
|
|
45
|
+
message: `approval document does not exist: ${path}`
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
if (!contents.includes(ApprovedMarker)) {
|
|
49
|
+
return yield* ApprovalRequired.make({ path, marker: ApprovedMarker })
|
|
50
|
+
}
|
|
51
|
+
})
|
package/src/Artifacts.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Ref from "effect/Ref"
|
|
3
|
+
import * as Result from "effect/Result"
|
|
4
|
+
import * as Schema from "effect/Schema"
|
|
5
|
+
import type { FlowError } from "./FlowError.ts"
|
|
6
|
+
import type { PlainFileStoreShape } from "./Persistence.ts"
|
|
7
|
+
|
|
8
|
+
export class ProgramUnit extends Schema.Class<ProgramUnit>("ProgramUnit")({
|
|
9
|
+
name: Schema.String,
|
|
10
|
+
sourcePath: Schema.String
|
|
11
|
+
}) {}
|
|
12
|
+
|
|
13
|
+
export class ProgramArtifacts extends Schema.Class<ProgramArtifacts>("ProgramArtifacts")({
|
|
14
|
+
spec: Schema.String,
|
|
15
|
+
feature: Schema.String,
|
|
16
|
+
traceability: Schema.String,
|
|
17
|
+
mapping: Schema.String
|
|
18
|
+
}) {}
|
|
19
|
+
|
|
20
|
+
export class ResumeSummary extends Schema.Class<ResumeSummary>("ResumeSummary")({
|
|
21
|
+
created: Schema.Array(Schema.String),
|
|
22
|
+
skipped: Schema.Array(Schema.String)
|
|
23
|
+
}) {}
|
|
24
|
+
|
|
25
|
+
const programName = (name: string): string => name.toLowerCase()
|
|
26
|
+
|
|
27
|
+
/** The four per-program artifact paths under `directory`, relative to the repository. */
|
|
28
|
+
export const programArtifactPaths = (
|
|
29
|
+
unit: ProgramUnit,
|
|
30
|
+
directory = "docs/modernization"
|
|
31
|
+
): ReadonlyArray<string> => [
|
|
32
|
+
`${directory}/specs/${unit.name}.md`,
|
|
33
|
+
`${directory}/features/${programName(unit.name)}.feature`,
|
|
34
|
+
`${directory}/traceability/${unit.name}.md`,
|
|
35
|
+
`${directory}/mapping/${unit.name}.md`
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
export interface ExtractProgramsOptions<E2, R2> {
|
|
39
|
+
/**
|
|
40
|
+
* How many programs extract at once (default 1: the sequential order the
|
|
41
|
+
* units were given in). The programs of a wave are independent — each
|
|
42
|
+
* analyst reads only its source and resolved closure and writes only its
|
|
43
|
+
* own four files — so the bound is about the model seat's quota, not about
|
|
44
|
+
* correctness.
|
|
45
|
+
*/
|
|
46
|
+
readonly concurrency?: number
|
|
47
|
+
/**
|
|
48
|
+
* Runs after a program's four artifacts are on disk and before it counts as
|
|
49
|
+
* created — the seam for per-program post-processing (pattern tagging, the
|
|
50
|
+
* program's own commit). Runs under the same concurrency as extraction:
|
|
51
|
+
* anything in it that must not interleave (git) serialises itself.
|
|
52
|
+
*/
|
|
53
|
+
readonly onCreated?: (unit: ProgramUnit) => Effect.Effect<void, E2, R2>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type Outcome = "created" | "skipped" | "not-started"
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Extracts every unit whose spec is missing, writes its four artifacts, and
|
|
60
|
+
* reports what was created and what was skipped. Resumable by construction:
|
|
61
|
+
* a rerun skips every program whose spec exists.
|
|
62
|
+
*
|
|
63
|
+
* Failure keeps finished work: a program that fails stops NEW programs from
|
|
64
|
+
* starting, but programs already in flight run to completion and land on
|
|
65
|
+
* disk (and through `onCreated`) before the first failure, in unit order, is
|
|
66
|
+
* re-raised. With `concurrency` 1 that is exactly fail-fast; above 1 a quota
|
|
67
|
+
* death costs only the programs it interrupted, and a rerun resumes the rest.
|
|
68
|
+
*/
|
|
69
|
+
export const extractProgramsResumably = Effect.fn(
|
|
70
|
+
"@llm4ts/flow/Artifacts.extractProgramsResumably"
|
|
71
|
+
)(function* <E, R, E2 = never, R2 = never>(
|
|
72
|
+
files: PlainFileStoreShape,
|
|
73
|
+
units: ReadonlyArray<ProgramUnit>,
|
|
74
|
+
extract: (unit: ProgramUnit) => Effect.Effect<ProgramArtifacts, E, R>,
|
|
75
|
+
directory = "docs/modernization",
|
|
76
|
+
options: ExtractProgramsOptions<E2, R2> = {}
|
|
77
|
+
): Effect.fn.Return<ResumeSummary, E | E2 | FlowError, R | R2> {
|
|
78
|
+
const concurrency = Math.max(1, Math.floor(options.concurrency ?? 1))
|
|
79
|
+
const halted = yield* Ref.make(false)
|
|
80
|
+
|
|
81
|
+
const extractOne = (unit: ProgramUnit): Effect.Effect<Outcome, E | E2 | FlowError, R | R2> =>
|
|
82
|
+
Effect.gen(function* () {
|
|
83
|
+
if (yield* Ref.get(halted)) {
|
|
84
|
+
return "not-started"
|
|
85
|
+
}
|
|
86
|
+
const [specPath, featurePath, traceabilityPath, mappingPath] = programArtifactPaths(
|
|
87
|
+
unit,
|
|
88
|
+
directory
|
|
89
|
+
)
|
|
90
|
+
if (
|
|
91
|
+
specPath === undefined ||
|
|
92
|
+
featurePath === undefined ||
|
|
93
|
+
traceabilityPath === undefined ||
|
|
94
|
+
mappingPath === undefined
|
|
95
|
+
) {
|
|
96
|
+
return "not-started"
|
|
97
|
+
}
|
|
98
|
+
if ((yield* files.read(specPath)) !== undefined) {
|
|
99
|
+
return "skipped"
|
|
100
|
+
}
|
|
101
|
+
const artifacts = yield* extract(unit)
|
|
102
|
+
yield* files.writeAtomic(specPath, artifacts.spec)
|
|
103
|
+
yield* files.writeAtomic(featurePath, artifacts.feature)
|
|
104
|
+
yield* files.writeAtomic(traceabilityPath, artifacts.traceability)
|
|
105
|
+
yield* files.writeAtomic(mappingPath, artifacts.mapping)
|
|
106
|
+
if (options.onCreated !== undefined) {
|
|
107
|
+
yield* options.onCreated(unit)
|
|
108
|
+
}
|
|
109
|
+
return "created"
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const outcomes = yield* Effect.forEach(
|
|
113
|
+
units,
|
|
114
|
+
(unit) =>
|
|
115
|
+
Effect.result(extractOne(unit)).pipe(
|
|
116
|
+
Effect.tap((outcome) => (Result.isFailure(outcome) ? Ref.set(halted, true) : Effect.void))
|
|
117
|
+
),
|
|
118
|
+
{ concurrency }
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
const firstFailure = outcomes.find(Result.isFailure)
|
|
122
|
+
if (firstFailure !== undefined) {
|
|
123
|
+
return yield* Effect.fail(firstFailure.failure)
|
|
124
|
+
}
|
|
125
|
+
const created: Array<string> = []
|
|
126
|
+
const skipped: Array<string> = []
|
|
127
|
+
for (const [index, outcome] of outcomes.entries()) {
|
|
128
|
+
const unit = units[index]
|
|
129
|
+
if (unit === undefined || Result.isFailure(outcome)) {
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
if (outcome.success === "created") {
|
|
133
|
+
created.push(unit.name)
|
|
134
|
+
} else if (outcome.success === "skipped") {
|
|
135
|
+
skipped.push(unit.name)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return ResumeSummary.make({ created, skipped })
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
export const generateVectorsResumably = Effect.fn(
|
|
142
|
+
"@llm4ts/flow/Artifacts.generateVectorsResumably"
|
|
143
|
+
)(function* <E, R>(
|
|
144
|
+
files: PlainFileStoreShape,
|
|
145
|
+
programs: ReadonlyArray<string>,
|
|
146
|
+
generate: (program: string) => Effect.Effect<string, E, R>,
|
|
147
|
+
directory = "docs/modernization/vectors"
|
|
148
|
+
): Effect.fn.Return<ResumeSummary, E | FlowError, R> {
|
|
149
|
+
const created: Array<string> = []
|
|
150
|
+
const skipped: Array<string> = []
|
|
151
|
+
for (const program of programs) {
|
|
152
|
+
const path = `${directory}/${program}.jsonl`
|
|
153
|
+
if ((yield* files.read(path)) !== undefined) {
|
|
154
|
+
skipped.push(program)
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
yield* files.writeAtomic(path, yield* generate(program))
|
|
158
|
+
created.push(program)
|
|
159
|
+
}
|
|
160
|
+
return ResumeSummary.make({ created, skipped })
|
|
161
|
+
})
|
package/src/Pack.ts
CHANGED
|
@@ -41,6 +41,12 @@ export interface Pack {
|
|
|
41
41
|
// paths), `<NAME>` substituted with the program name. The seam that makes
|
|
42
42
|
// per-program judging possible.
|
|
43
43
|
readonly programFiles: string | undefined
|
|
44
|
+
/**
|
|
45
|
+
* The schema every program spec must embed, validated deterministically
|
|
46
|
+
* by the extraction gate: `pagespec` (a ```json pagespec block decodable
|
|
47
|
+
* as `PageSpec`) is the only one today; absent means prose-only specs.
|
|
48
|
+
*/
|
|
49
|
+
readonly specSchema: string | undefined
|
|
44
50
|
readonly dir: string
|
|
45
51
|
readonly gate: (name: string) => ReadonlyArray<string> | undefined
|
|
46
52
|
readonly prompt: (name: string) => string | undefined
|
|
@@ -206,6 +212,12 @@ export const loadPack = Effect.fn("@llm4ts/flow/Pack.load")(function* (
|
|
|
206
212
|
message: `pack manifest 'programFiles:' is not a valid regex template: ${programFiles}`
|
|
207
213
|
})
|
|
208
214
|
}
|
|
215
|
+
const specSchema = fields["spec-schema"]
|
|
216
|
+
if (specSchema !== undefined && specSchema !== "pagespec") {
|
|
217
|
+
return yield* PlanParseError.make({
|
|
218
|
+
message: `pack manifest 'spec-schema:' must be 'pagespec' when set, got: ${specSchema}`
|
|
219
|
+
})
|
|
220
|
+
}
|
|
209
221
|
const exclude = fields.exclude
|
|
210
222
|
if (exclude !== undefined && !isValidRegExp(exclude)) {
|
|
211
223
|
return yield* PlanParseError.make({
|
|
@@ -239,6 +251,7 @@ export const loadPack = Effect.fn("@llm4ts/flow/Pack.load")(function* (
|
|
|
239
251
|
lenses,
|
|
240
252
|
lessons: lessons === undefined || lessons.length === 0 ? undefined : lessons,
|
|
241
253
|
programFiles,
|
|
254
|
+
specSchema,
|
|
242
255
|
dir: directory,
|
|
243
256
|
gate: (name) => gates[name],
|
|
244
257
|
prompt: (name) => prompts[name],
|