@llm4ts/flow 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/package.json +4 -2
- package/src/Approval.ts +51 -0
- package/src/Artifacts.ts +161 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import type { PlainFileStoreShape } from "./Persistence.ts";
|
|
4
|
+
declare const ApprovalRequired_base: Schema.Class<ApprovalRequired, Schema.TaggedStruct<"ApprovalRequired", {
|
|
5
|
+
readonly path: Schema.String;
|
|
6
|
+
readonly marker: Schema.String;
|
|
7
|
+
}>, import("effect/Cause").YieldableError>;
|
|
8
|
+
/**
|
|
9
|
+
* The human gates of the modernization phases: a markdown document ends
|
|
10
|
+
* with a draft marker the flow writes, and the next phase refuses to start
|
|
11
|
+
* until a person has ticked it. (Moved from `@llm4ts/modernize` in 0.18.0.)
|
|
12
|
+
*/
|
|
13
|
+
export declare class ApprovalRequired extends ApprovalRequired_base {
|
|
14
|
+
get message(): string;
|
|
15
|
+
}
|
|
16
|
+
declare const MissingModernizeArtifact_base: Schema.Class<MissingModernizeArtifact, Schema.TaggedStruct<"MissingModernizeArtifact", {
|
|
17
|
+
readonly path: Schema.String;
|
|
18
|
+
readonly message: Schema.String;
|
|
19
|
+
}>, import("effect/Cause").YieldableError>;
|
|
20
|
+
export declare class MissingModernizeArtifact extends MissingModernizeArtifact_base {
|
|
21
|
+
}
|
|
22
|
+
export declare const ApprovedMarker = "- [x] Approved";
|
|
23
|
+
export declare const DraftApprovalMarker = "- [ ] Approved";
|
|
24
|
+
export declare const withDraftApproval: (markdown: string) => string;
|
|
25
|
+
export declare const requireApproval: (files: PlainFileStoreShape, path: string) => Effect.Effect<void, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").ColumnNotFound | import("./FlowError.ts").BudgetExceeded | import("./FlowError.ts").StoryPlanInvalid | import("./FlowError.ts").PerimeterViolation | import("./FlowError.ts").MissingDependency | import("./FlowError.ts").MergeConflict | import("./FlowError.ts").StoryFailed | ApprovalRequired | MissingModernizeArtifact, never>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=Approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Approval.d.ts","sourceRoot":"","sources":["../src/Approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;;;;;AAE3D;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,qBAMrC;IACC,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF;;;;;AAED,qBAAa,wBAAyB,SAAQ,6BAM7C;CAAG;AAEJ,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAC9C,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AAEnD,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,MACA,CAAA;AAErD,eAAO,MAAM,eAAe,41BAc1B,CAAA"}
|
package/dist/Approval.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
/**
|
|
4
|
+
* The human gates of the modernization phases: a markdown document ends
|
|
5
|
+
* with a draft marker the flow writes, and the next phase refuses to start
|
|
6
|
+
* until a person has ticked it. (Moved from `@llm4ts/modernize` in 0.18.0.)
|
|
7
|
+
*/
|
|
8
|
+
export class ApprovalRequired extends Schema.TaggedErrorClass()("ApprovalRequired", {
|
|
9
|
+
path: Schema.String,
|
|
10
|
+
marker: Schema.String
|
|
11
|
+
}) {
|
|
12
|
+
get message() {
|
|
13
|
+
return `approval required in ${this.path}: set '${this.marker}'`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class MissingModernizeArtifact extends Schema.TaggedErrorClass()("MissingModernizeArtifact", {
|
|
17
|
+
path: Schema.String,
|
|
18
|
+
message: Schema.String
|
|
19
|
+
}) {
|
|
20
|
+
}
|
|
21
|
+
export const ApprovedMarker = "- [x] Approved";
|
|
22
|
+
export const DraftApprovalMarker = "- [ ] Approved";
|
|
23
|
+
export const withDraftApproval = (markdown) => `${markdown.trimEnd()}\n\n${DraftApprovalMarker}\n`;
|
|
24
|
+
export const requireApproval = Effect.fn("@llm4ts/flow/Approval.requireApproval")(function* (files, path) {
|
|
25
|
+
const contents = yield* files.read(path);
|
|
26
|
+
if (contents === undefined) {
|
|
27
|
+
return yield* MissingModernizeArtifact.make({
|
|
28
|
+
path,
|
|
29
|
+
message: `approval document does not exist: ${path}`
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (!contents.includes(ApprovedMarker)) {
|
|
33
|
+
return yield* ApprovalRequired.make({ path, marker: ApprovedMarker });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=Approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Approval.js","sourceRoot":"","sources":["../src/Approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAIvC;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,gBAAgB,EAAoB,CAC/E,kBAAkB,EAClB;IACE,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM;CACtB,CACF;IACC,IAAI,OAAO;QACT,OAAO,wBAAwB,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,MAAM,GAAG,CAAA;IAClE,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,MAAM,CAAC,gBAAgB,EAA4B,CAC/F,0BAA0B,EAC1B;IACE,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CACF;CAAG;AAEJ,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAA;AAC9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAA;AAEnD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAU,EAAE,CAC5D,GAAG,QAAQ,CAAC,OAAO,EAAE,OAAO,mBAAmB,IAAI,CAAA;AAErD,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,uCAAuC,CAAC,CAAC,QAAQ,CAAC,EACzF,KAA0B,EAC1B,IAAY;IAEZ,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACxC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAAC;YAC1C,IAAI;YACJ,OAAO,EAAE,qCAAqC,IAAI,EAAE;SACrD,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;IACvE,CAAC;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import type { PlainFileStoreShape } from "./Persistence.ts";
|
|
4
|
+
declare const ProgramUnit_base: Schema.Class<ProgramUnit, Schema.Struct<{
|
|
5
|
+
readonly name: Schema.String;
|
|
6
|
+
readonly sourcePath: Schema.String;
|
|
7
|
+
}>, {}>;
|
|
8
|
+
export declare class ProgramUnit extends ProgramUnit_base {
|
|
9
|
+
}
|
|
10
|
+
declare const ProgramArtifacts_base: Schema.Class<ProgramArtifacts, Schema.Struct<{
|
|
11
|
+
readonly spec: Schema.String;
|
|
12
|
+
readonly feature: Schema.String;
|
|
13
|
+
readonly traceability: Schema.String;
|
|
14
|
+
readonly mapping: Schema.String;
|
|
15
|
+
}>, {}>;
|
|
16
|
+
export declare class ProgramArtifacts extends ProgramArtifacts_base {
|
|
17
|
+
}
|
|
18
|
+
declare const ResumeSummary_base: Schema.Class<ResumeSummary, Schema.Struct<{
|
|
19
|
+
readonly created: Schema.$Array<Schema.String>;
|
|
20
|
+
readonly skipped: Schema.$Array<Schema.String>;
|
|
21
|
+
}>, {}>;
|
|
22
|
+
export declare class ResumeSummary extends ResumeSummary_base {
|
|
23
|
+
}
|
|
24
|
+
/** The four per-program artifact paths under `directory`, relative to the repository. */
|
|
25
|
+
export declare const programArtifactPaths: (unit: ProgramUnit, directory?: string) => ReadonlyArray<string>;
|
|
26
|
+
export interface ExtractProgramsOptions<E2, R2> {
|
|
27
|
+
/**
|
|
28
|
+
* How many programs extract at once (default 1: the sequential order the
|
|
29
|
+
* units were given in). The programs of a wave are independent — each
|
|
30
|
+
* analyst reads only its source and resolved closure and writes only its
|
|
31
|
+
* own four files — so the bound is about the model seat's quota, not about
|
|
32
|
+
* correctness.
|
|
33
|
+
*/
|
|
34
|
+
readonly concurrency?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Runs after a program's four artifacts are on disk and before it counts as
|
|
37
|
+
* created — the seam for per-program post-processing (pattern tagging, the
|
|
38
|
+
* program's own commit). Runs under the same concurrency as extraction:
|
|
39
|
+
* anything in it that must not interleave (git) serialises itself.
|
|
40
|
+
*/
|
|
41
|
+
readonly onCreated?: (unit: ProgramUnit) => Effect.Effect<void, E2, R2>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extracts every unit whose spec is missing, writes its four artifacts, and
|
|
45
|
+
* reports what was created and what was skipped. Resumable by construction:
|
|
46
|
+
* a rerun skips every program whose spec exists.
|
|
47
|
+
*
|
|
48
|
+
* Failure keeps finished work: a program that fails stops NEW programs from
|
|
49
|
+
* starting, but programs already in flight run to completion and land on
|
|
50
|
+
* disk (and through `onCreated`) before the first failure, in unit order, is
|
|
51
|
+
* re-raised. With `concurrency` 1 that is exactly fail-fast; above 1 a quota
|
|
52
|
+
* death costs only the programs it interrupted, and a rerun resumes the rest.
|
|
53
|
+
*/
|
|
54
|
+
export declare const extractProgramsResumably: <E, R, E2 = never, R2 = never>(files: PlainFileStoreShape, units: readonly ProgramUnit[], extract: (unit: ProgramUnit) => Effect.Effect<ProgramArtifacts, E, R>, directory?: string | undefined, options?: ExtractProgramsOptions<E2, R2> | undefined) => Effect.Effect<ResumeSummary, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").ColumnNotFound | import("./FlowError.ts").BudgetExceeded | import("./FlowError.ts").StoryPlanInvalid | import("./FlowError.ts").PerimeterViolation | import("./FlowError.ts").MissingDependency | import("./FlowError.ts").MergeConflict | import("./FlowError.ts").StoryFailed | E | E2, R | R2>;
|
|
55
|
+
export declare const generateVectorsResumably: <E, R>(files: PlainFileStoreShape, programs: readonly string[], generate: (program: string) => Effect.Effect<string, E, R>, directory?: string | undefined) => Effect.Effect<ResumeSummary, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").ColumnNotFound | import("./FlowError.ts").BudgetExceeded | import("./FlowError.ts").StoryPlanInvalid | import("./FlowError.ts").PerimeterViolation | import("./FlowError.ts").MissingDependency | import("./FlowError.ts").MergeConflict | import("./FlowError.ts").StoryFailed | E, R>;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=Artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Artifacts.d.ts","sourceRoot":"","sources":["../src/Artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;;;;;AAE3D,qBAAa,WAAY,SAAQ,gBAG/B;CAAG;;;;;;;AAEL,qBAAa,gBAAiB,SAAQ,qBAKpC;CAAG;;;;;AAEL,qBAAa,aAAc,SAAQ,kBAGjC;CAAG;AAIL,yFAAyF;AACzF,eAAO,MAAM,oBAAoB,GAC/B,MAAM,WAAW,EACjB,kBAAgC,KAC/B,aAAa,CAAC,MAAM,CAKtB,CAAA;AAED,MAAM,WAAW,sBAAsB,CAAC,EAAE,EAAE,EAAE;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;CACxE;AAID;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAExB,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,qFAGf,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,EAAE,CAAC,CAAC,42BAiErE,CAAA;AAEF,eAAO,MAAM,wBAAwB,GAExB,CAAC,EAAE,CAAC,+EAGK,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,4yBAe1D,CAAA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
export class ProgramUnit extends Schema.Class("ProgramUnit")({
|
|
6
|
+
name: Schema.String,
|
|
7
|
+
sourcePath: Schema.String
|
|
8
|
+
}) {
|
|
9
|
+
}
|
|
10
|
+
export class ProgramArtifacts extends Schema.Class("ProgramArtifacts")({
|
|
11
|
+
spec: Schema.String,
|
|
12
|
+
feature: Schema.String,
|
|
13
|
+
traceability: Schema.String,
|
|
14
|
+
mapping: Schema.String
|
|
15
|
+
}) {
|
|
16
|
+
}
|
|
17
|
+
export class ResumeSummary extends Schema.Class("ResumeSummary")({
|
|
18
|
+
created: Schema.Array(Schema.String),
|
|
19
|
+
skipped: Schema.Array(Schema.String)
|
|
20
|
+
}) {
|
|
21
|
+
}
|
|
22
|
+
const programName = (name) => name.toLowerCase();
|
|
23
|
+
/** The four per-program artifact paths under `directory`, relative to the repository. */
|
|
24
|
+
export const programArtifactPaths = (unit, directory = "docs/modernization") => [
|
|
25
|
+
`${directory}/specs/${unit.name}.md`,
|
|
26
|
+
`${directory}/features/${programName(unit.name)}.feature`,
|
|
27
|
+
`${directory}/traceability/${unit.name}.md`,
|
|
28
|
+
`${directory}/mapping/${unit.name}.md`
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Extracts every unit whose spec is missing, writes its four artifacts, and
|
|
32
|
+
* reports what was created and what was skipped. Resumable by construction:
|
|
33
|
+
* a rerun skips every program whose spec exists.
|
|
34
|
+
*
|
|
35
|
+
* Failure keeps finished work: a program that fails stops NEW programs from
|
|
36
|
+
* starting, but programs already in flight run to completion and land on
|
|
37
|
+
* disk (and through `onCreated`) before the first failure, in unit order, is
|
|
38
|
+
* re-raised. With `concurrency` 1 that is exactly fail-fast; above 1 a quota
|
|
39
|
+
* death costs only the programs it interrupted, and a rerun resumes the rest.
|
|
40
|
+
*/
|
|
41
|
+
export const extractProgramsResumably = Effect.fn("@llm4ts/flow/Artifacts.extractProgramsResumably")(function* (files, units, extract, directory = "docs/modernization", options = {}) {
|
|
42
|
+
const concurrency = Math.max(1, Math.floor(options.concurrency ?? 1));
|
|
43
|
+
const halted = yield* Ref.make(false);
|
|
44
|
+
const extractOne = (unit) => Effect.gen(function* () {
|
|
45
|
+
if (yield* Ref.get(halted)) {
|
|
46
|
+
return "not-started";
|
|
47
|
+
}
|
|
48
|
+
const [specPath, featurePath, traceabilityPath, mappingPath] = programArtifactPaths(unit, directory);
|
|
49
|
+
if (specPath === undefined ||
|
|
50
|
+
featurePath === undefined ||
|
|
51
|
+
traceabilityPath === undefined ||
|
|
52
|
+
mappingPath === undefined) {
|
|
53
|
+
return "not-started";
|
|
54
|
+
}
|
|
55
|
+
if ((yield* files.read(specPath)) !== undefined) {
|
|
56
|
+
return "skipped";
|
|
57
|
+
}
|
|
58
|
+
const artifacts = yield* extract(unit);
|
|
59
|
+
yield* files.writeAtomic(specPath, artifacts.spec);
|
|
60
|
+
yield* files.writeAtomic(featurePath, artifacts.feature);
|
|
61
|
+
yield* files.writeAtomic(traceabilityPath, artifacts.traceability);
|
|
62
|
+
yield* files.writeAtomic(mappingPath, artifacts.mapping);
|
|
63
|
+
if (options.onCreated !== undefined) {
|
|
64
|
+
yield* options.onCreated(unit);
|
|
65
|
+
}
|
|
66
|
+
return "created";
|
|
67
|
+
});
|
|
68
|
+
const outcomes = yield* Effect.forEach(units, (unit) => Effect.result(extractOne(unit)).pipe(Effect.tap((outcome) => (Result.isFailure(outcome) ? Ref.set(halted, true) : Effect.void))), { concurrency });
|
|
69
|
+
const firstFailure = outcomes.find(Result.isFailure);
|
|
70
|
+
if (firstFailure !== undefined) {
|
|
71
|
+
return yield* Effect.fail(firstFailure.failure);
|
|
72
|
+
}
|
|
73
|
+
const created = [];
|
|
74
|
+
const skipped = [];
|
|
75
|
+
for (const [index, outcome] of outcomes.entries()) {
|
|
76
|
+
const unit = units[index];
|
|
77
|
+
if (unit === undefined || Result.isFailure(outcome)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (outcome.success === "created") {
|
|
81
|
+
created.push(unit.name);
|
|
82
|
+
}
|
|
83
|
+
else if (outcome.success === "skipped") {
|
|
84
|
+
skipped.push(unit.name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return ResumeSummary.make({ created, skipped });
|
|
88
|
+
});
|
|
89
|
+
export const generateVectorsResumably = Effect.fn("@llm4ts/flow/Artifacts.generateVectorsResumably")(function* (files, programs, generate, directory = "docs/modernization/vectors") {
|
|
90
|
+
const created = [];
|
|
91
|
+
const skipped = [];
|
|
92
|
+
for (const program of programs) {
|
|
93
|
+
const path = `${directory}/${program}.jsonl`;
|
|
94
|
+
if ((yield* files.read(path)) !== undefined) {
|
|
95
|
+
skipped.push(program);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
yield* files.writeAtomic(path, yield* generate(program));
|
|
99
|
+
created.push(program);
|
|
100
|
+
}
|
|
101
|
+
return ResumeSummary.make({ created, skipped });
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=Artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Artifacts.js","sourceRoot":"","sources":["../src/Artifacts.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.0",
|
|
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.0"
|
|
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
|
+
})
|