@llm4ts/flow 0.2.2 → 0.3.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/Package.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export declare const packageName = "@llm4ts/flow";
2
+ /** The published `@llm4ts/flow` version, recorded in provenance manifests. */
3
+ export declare const packageVersion: string;
2
4
  //# sourceMappingURL=Package.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Package.d.ts","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,iBAAiB,CAAA"}
1
+ {"version":3,"file":"Package.d.ts","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,WAAW,iBAAiB,CAAA;AAEzC,8EAA8E;AAC9E,eAAO,MAAM,cAAc,EAAE,MAAyB,CAAA"}
package/dist/Package.js CHANGED
@@ -1,2 +1,8 @@
1
+ import { createRequire } from "node:module";
2
+ import * as Schema from "effect/Schema";
3
+ const Manifest = Schema.Struct({ version: Schema.String });
4
+ const manifest = Schema.decodeUnknownSync(Manifest)(createRequire(import.meta.url)("../package.json"));
1
5
  export const packageName = "@llm4ts/flow";
6
+ /** The published `@llm4ts/flow` version, recorded in provenance manifests. */
7
+ export const packageVersion = manifest.version;
2
8
  //# sourceMappingURL=Package.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Package.js","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA"}
1
+ {"version":3,"file":"Package.js","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;AAE1D,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CACjD,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAClD,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAW,QAAQ,CAAC,OAAO,CAAA"}
@@ -0,0 +1,38 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts";
4
+ declare const PatternCard_base: Schema.Class<PatternCard, Schema.Struct<{
5
+ readonly id: Schema.String;
6
+ readonly matches: Schema.String;
7
+ readonly body: Schema.String;
8
+ }>, {}>;
9
+ /**
10
+ * One reusable modernization pattern: a legacy idiom, its target translation,
11
+ * and the known traps — knowledge as data, like packs and lessons, but
12
+ * universal and curated rather than estate-local and run-accumulated.
13
+ * `matches` is a regex over legacy source: extraction tags each program's
14
+ * traceability fragment with the ids of the cards that hit, and implementation
15
+ * injects only the cited cards into the coder's brief — deterministic
16
+ * selection, bounded context.
17
+ */
18
+ export declare class PatternCard extends PatternCard_base {
19
+ }
20
+ export declare const parsePatternCard: (id: string, text: string) => PatternCard;
21
+ /**
22
+ * All `*.md` cards under `directory`, sorted by id (the filename stem). An
23
+ * absent directory is an empty set — patterns are optional everywhere.
24
+ */
25
+ export declare const loadPatternCards: (workspace: WorkspaceShape, directory: string) => Effect.Effect<readonly PatternCard[], WorkspaceError, never>;
26
+ /**
27
+ * The cards whose `matches` regex hits anywhere in `source` — how extraction
28
+ * decides which patterns a program embodies. A card with no regex never
29
+ * matches; an invalid regex is skipped rather than failing the run.
30
+ */
31
+ export declare const matchingPatternCards: (source: string, cards: ReadonlyArray<PatternCard>) => ReadonlyArray<PatternCard>;
32
+ /**
33
+ * Every `PAT-…` id cited in a spec text, in order of first appearance — how
34
+ * implementation finds the cards its current task's specs were tagged with.
35
+ */
36
+ export declare const taggedPatternIds: (spec: string) => ReadonlyArray<string>;
37
+ export {};
38
+ //# sourceMappingURL=Patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Patterns.d.ts","sourceRoot":"","sources":["../src/Patterns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;;;;;;AAEpE;;;;;;;;GAQG;AACH,qBAAa,WAAY,SAAQ,gBAI/B;CAAG;AAYL,eAAO,MAAM,gBAAgB,GAAI,IAAI,MAAM,EAAE,MAAM,MAAM,KAAG,WAe3D,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,gHAY3B,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,EACd,OAAO,aAAa,CAAC,WAAW,CAAC,KAChC,aAAa,CAAC,WAAW,CAUxB,CAAA;AAEJ;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,aAAa,CAAC,MAAM,CAEnE,CAAA"}
@@ -0,0 +1,80 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ /**
4
+ * One reusable modernization pattern: a legacy idiom, its target translation,
5
+ * and the known traps — knowledge as data, like packs and lessons, but
6
+ * universal and curated rather than estate-local and run-accumulated.
7
+ * `matches` is a regex over legacy source: extraction tags each program's
8
+ * traceability fragment with the ids of the cards that hit, and implementation
9
+ * injects only the cited cards into the coder's brief — deterministic
10
+ * selection, bounded context.
11
+ */
12
+ export class PatternCard extends Schema.Class("PatternCard")({
13
+ id: Schema.String,
14
+ matches: Schema.String,
15
+ body: Schema.String
16
+ }) {
17
+ }
18
+ const frontmatterField = (front, key) => {
19
+ for (const line of front.split(/\r?\n/)) {
20
+ const index = line.indexOf(":");
21
+ if (index > 0 && line.slice(0, index).trim() === key) {
22
+ return line.slice(index + 1).trim();
23
+ }
24
+ }
25
+ return undefined;
26
+ };
27
+ export const parsePatternCard = (id, text) => {
28
+ const stripped = text.trim();
29
+ if (!stripped.startsWith("---")) {
30
+ return PatternCard.make({ id, matches: "", body: stripped });
31
+ }
32
+ const rest = stripped.slice(3);
33
+ const end = /^---[ \t]*$/m.exec(rest);
34
+ if (end === null) {
35
+ return PatternCard.make({ id, matches: "", body: stripped });
36
+ }
37
+ return PatternCard.make({
38
+ id,
39
+ matches: frontmatterField(rest.slice(0, end.index), "match") ?? "",
40
+ body: rest.slice(end.index + end[0].length).trim()
41
+ });
42
+ };
43
+ /**
44
+ * All `*.md` cards under `directory`, sorted by id (the filename stem). An
45
+ * absent directory is an empty set — patterns are optional everywhere.
46
+ */
47
+ export const loadPatternCards = Effect.fn("@llm4ts/flow/Patterns.load")(function* (workspace, directory) {
48
+ const paths = yield* workspace.discover(`${directory}/*.md`).pipe(Effect.orElseSucceed(() => []));
49
+ const cards = [];
50
+ for (const path of [...paths].sort()) {
51
+ const file = path.split("/").at(-1) ?? path;
52
+ const text = yield* workspace.read(path).pipe(Effect.orElseSucceed(() => ""));
53
+ cards.push(parsePatternCard(file.replace(/\.md$/, ""), text));
54
+ }
55
+ return cards.sort((left, right) => left.id.localeCompare(right.id));
56
+ });
57
+ /**
58
+ * The cards whose `matches` regex hits anywhere in `source` — how extraction
59
+ * decides which patterns a program embodies. A card with no regex never
60
+ * matches; an invalid regex is skipped rather than failing the run.
61
+ */
62
+ export const matchingPatternCards = (source, cards) => cards.filter((card) => {
63
+ if (card.matches.length === 0) {
64
+ return false;
65
+ }
66
+ try {
67
+ return new RegExp(card.matches, "m").test(source);
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ });
73
+ /**
74
+ * Every `PAT-…` id cited in a spec text, in order of first appearance — how
75
+ * implementation finds the cards its current task's specs were tagged with.
76
+ */
77
+ export const taggedPatternIds = (spec) => [
78
+ ...new Set(spec.match(/PAT-[A-Za-z0-9-]+/g) ?? [])
79
+ ];
80
+ //# sourceMappingURL=Patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Patterns.js","sourceRoot":"","sources":["../src/Patterns.ts"],"names":[],"mappings":"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"}
package/dist/Wall.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts";
4
+ declare const WallClean_base: Schema.Class<WallClean, Schema.TaggedStruct<"Clean", {}>, {}>;
5
+ /**
6
+ * The enforced clean-room wall: target-phase flows (implement, verify, review)
7
+ * refuse to run when the target workspace contains anything matching the pack's
8
+ * legacy `sources:` regex, so the coder provably never has legacy source in
9
+ * reach. A breach is a recoverable, typed outcome — the caller decides to
10
+ * abort; only I/O failure fails the effect.
11
+ */
12
+ export declare class WallClean extends WallClean_base {
13
+ }
14
+ declare const WallBreached_base: Schema.Class<WallBreached, Schema.TaggedStruct<"Breached", {
15
+ readonly paths: Schema.$Array<Schema.String>;
16
+ }>, {}>;
17
+ export declare class WallBreached extends WallBreached_base {
18
+ }
19
+ export declare const WallResult: Schema.Union<readonly [typeof WallClean, typeof WallBreached]>;
20
+ export type WallResult = typeof WallResult.Type;
21
+ /**
22
+ * Scans the working tree for files matching the pack's legacy `sourcesRegex`.
23
+ * `.git` is exempt — the wall is about the working tree, not repository
24
+ * internals.
25
+ */
26
+ export declare const checkWall: (workspace: WorkspaceShape, sourcesRegex: string) => Effect.Effect<WallClean | WallBreached, WorkspaceError, never>;
27
+ /** The one-line breach message every target-phase flow reports, capped at ten paths. */
28
+ export declare const wallBreachMessage: (breach: WallBreached, consequence: string) => string;
29
+ export {};
30
+ //# sourceMappingURL=Wall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Wall.d.ts","sourceRoot":"","sources":["../src/Wall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;;AAEpE;;;;;;GAMG;AACH,qBAAa,SAAU,SAAQ,cAA4C;CAAG;;;;AAE9E,qBAAa,YAAa,SAAQ,iBAEhC;CAAG;AAEL,eAAO,MAAM,UAAU,gEAA0C,CAAA;AACjE,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,SAAS,qHAQpB,CAAA;AAEF,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,YAAY,EAAE,aAAa,MAAM,KAAG,MAI7E,CAAA"}
package/dist/Wall.js ADDED
@@ -0,0 +1,33 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import { matchingFiles } from "./SpecChecks.js";
4
+ /**
5
+ * The enforced clean-room wall: target-phase flows (implement, verify, review)
6
+ * refuse to run when the target workspace contains anything matching the pack's
7
+ * legacy `sources:` regex, so the coder provably never has legacy source in
8
+ * reach. A breach is a recoverable, typed outcome — the caller decides to
9
+ * abort; only I/O failure fails the effect.
10
+ */
11
+ export class WallClean extends Schema.TaggedClass()("Clean", {}) {
12
+ }
13
+ export class WallBreached extends Schema.TaggedClass()("Breached", {
14
+ paths: Schema.Array(Schema.String)
15
+ }) {
16
+ }
17
+ export const WallResult = Schema.Union([WallClean, WallBreached]);
18
+ /**
19
+ * Scans the working tree for files matching the pack's legacy `sourcesRegex`.
20
+ * `.git` is exempt — the wall is about the working tree, not repository
21
+ * internals.
22
+ */
23
+ export const checkWall = Effect.fn("@llm4ts/flow/Wall.check")(function* (workspace, sourcesRegex) {
24
+ const matches = (yield* matchingFiles(workspace, sourcesRegex)).filter((path) => !path.startsWith(".git/"));
25
+ return matches.length === 0 ? new WallClean() : new WallBreached({ paths: matches });
26
+ });
27
+ /** The one-line breach message every target-phase flow reports, capped at ten paths. */
28
+ export const wallBreachMessage = (breach, consequence) => {
29
+ const shown = breach.paths.slice(0, 10).join(", ");
30
+ const more = breach.paths.length > 10 ? ` (+${breach.paths.length - 10} more)` : "";
31
+ return `clean-room wall breached — legacy source inside the target workspace: ${shown}${more}. ${consequence}`;
32
+ };
33
+ //# sourceMappingURL=Wall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Wall.js","sourceRoot":"","sources":["../src/Wall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAG/C;;;;;;GAMG;AACH,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,WAAW,EAAa,CAAC,OAAO,EAAE,EAAE,CAAC;CAAG;AAE9E,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,EAAgB,CAAC,UAAU,EAAE;IAC/E,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;CACnC,CAAC;CAAG;AAEL,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAA;AAGjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EACrE,SAAyB,EACzB,YAAoB;IAEpB,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CACpE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CACpC,CAAA;IACD,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;AACtF,CAAC,CAAC,CAAA;AAEF,wFAAwF;AACxF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAoB,EAAE,WAAmB,EAAU,EAAE;IACrF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;IACnF,OAAO,yEAAyE,KAAK,GAAG,IAAI,KAAK,WAAW,EAAE,CAAA;AAChH,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llm4ts/flow",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Effect-native LLM workflow, persistence, review, and repository automation",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -36,6 +36,7 @@
36
36
  "./Mermaid": "./dist/Mermaid.js",
37
37
  "./Pack": "./dist/Pack.js",
38
38
  "./Package": "./dist/Package.js",
39
+ "./Patterns": "./dist/Patterns.js",
39
40
  "./Persistence": "./dist/Persistence.js",
40
41
  "./Plan": "./dist/Plan.js",
41
42
  "./PlanExecution": "./dist/PlanExecution.js",
@@ -50,6 +51,7 @@
50
51
  "./SpecChecks": "./dist/SpecChecks.js",
51
52
  "./Survey": "./dist/Survey.js",
52
53
  "./TransientRetry": "./dist/TransientRetry.js",
54
+ "./Wall": "./dist/Wall.js",
53
55
  "./Workspace": "./dist/Workspace.js",
54
56
  "./WorkspaceLayout": "./dist/WorkspaceLayout.js",
55
57
  "./WorkspaceTools": "./dist/WorkspaceTools.js"
@@ -76,7 +78,7 @@
76
78
  "typescript"
77
79
  ],
78
80
  "dependencies": {
79
- "@llm4ts/core": "0.2.2"
81
+ "@llm4ts/core": "0.3.0"
80
82
  },
81
83
  "peerDependencies": {
82
84
  "effect": "^4.0.0-beta.102"
package/src/Package.ts CHANGED
@@ -1 +1,13 @@
1
+ import { createRequire } from "node:module"
2
+ import * as Schema from "effect/Schema"
3
+
4
+ const Manifest = Schema.Struct({ version: Schema.String })
5
+
6
+ const manifest = Schema.decodeUnknownSync(Manifest)(
7
+ createRequire(import.meta.url)("../package.json")
8
+ )
9
+
1
10
  export const packageName = "@llm4ts/flow"
11
+
12
+ /** The published `@llm4ts/flow` version, recorded in provenance manifests. */
13
+ export const packageVersion: string = manifest.version
@@ -0,0 +1,91 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Schema from "effect/Schema"
3
+ import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts"
4
+
5
+ /**
6
+ * One reusable modernization pattern: a legacy idiom, its target translation,
7
+ * and the known traps — knowledge as data, like packs and lessons, but
8
+ * universal and curated rather than estate-local and run-accumulated.
9
+ * `matches` is a regex over legacy source: extraction tags each program's
10
+ * traceability fragment with the ids of the cards that hit, and implementation
11
+ * injects only the cited cards into the coder's brief — deterministic
12
+ * selection, bounded context.
13
+ */
14
+ export class PatternCard extends Schema.Class<PatternCard>("PatternCard")({
15
+ id: Schema.String,
16
+ matches: Schema.String,
17
+ body: Schema.String
18
+ }) {}
19
+
20
+ const frontmatterField = (front: string, key: string): string | undefined => {
21
+ for (const line of front.split(/\r?\n/)) {
22
+ const index = line.indexOf(":")
23
+ if (index > 0 && line.slice(0, index).trim() === key) {
24
+ return line.slice(index + 1).trim()
25
+ }
26
+ }
27
+ return undefined
28
+ }
29
+
30
+ export const parsePatternCard = (id: string, text: string): PatternCard => {
31
+ const stripped = text.trim()
32
+ if (!stripped.startsWith("---")) {
33
+ return PatternCard.make({ id, matches: "", body: stripped })
34
+ }
35
+ const rest = stripped.slice(3)
36
+ const end = /^---[ \t]*$/m.exec(rest)
37
+ if (end === null) {
38
+ return PatternCard.make({ id, matches: "", body: stripped })
39
+ }
40
+ return PatternCard.make({
41
+ id,
42
+ matches: frontmatterField(rest.slice(0, end.index), "match") ?? "",
43
+ body: rest.slice(end.index + end[0].length).trim()
44
+ })
45
+ }
46
+
47
+ /**
48
+ * All `*.md` cards under `directory`, sorted by id (the filename stem). An
49
+ * absent directory is an empty set — patterns are optional everywhere.
50
+ */
51
+ export const loadPatternCards = Effect.fn("@llm4ts/flow/Patterns.load")(function* (
52
+ workspace: WorkspaceShape,
53
+ directory: string
54
+ ): Effect.fn.Return<ReadonlyArray<PatternCard>, WorkspaceError> {
55
+ const paths = yield* workspace.discover(`${directory}/*.md`).pipe(Effect.orElseSucceed(() => []))
56
+ const cards: Array<PatternCard> = []
57
+ for (const path of [...paths].sort()) {
58
+ const file = path.split("/").at(-1) ?? path
59
+ const text = yield* workspace.read(path).pipe(Effect.orElseSucceed(() => ""))
60
+ cards.push(parsePatternCard(file.replace(/\.md$/, ""), text))
61
+ }
62
+ return cards.sort((left, right) => left.id.localeCompare(right.id))
63
+ })
64
+
65
+ /**
66
+ * The cards whose `matches` regex hits anywhere in `source` — how extraction
67
+ * decides which patterns a program embodies. A card with no regex never
68
+ * matches; an invalid regex is skipped rather than failing the run.
69
+ */
70
+ export const matchingPatternCards = (
71
+ source: string,
72
+ cards: ReadonlyArray<PatternCard>
73
+ ): ReadonlyArray<PatternCard> =>
74
+ cards.filter((card) => {
75
+ if (card.matches.length === 0) {
76
+ return false
77
+ }
78
+ try {
79
+ return new RegExp(card.matches, "m").test(source)
80
+ } catch {
81
+ return false
82
+ }
83
+ })
84
+
85
+ /**
86
+ * Every `PAT-…` id cited in a spec text, in order of first appearance — how
87
+ * implementation finds the cards its current task's specs were tagged with.
88
+ */
89
+ export const taggedPatternIds = (spec: string): ReadonlyArray<string> => [
90
+ ...new Set(spec.match(/PAT-[A-Za-z0-9-]+/g) ?? [])
91
+ ]
package/src/Wall.ts ADDED
@@ -0,0 +1,42 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Schema from "effect/Schema"
3
+ import { matchingFiles } from "./SpecChecks.ts"
4
+ import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts"
5
+
6
+ /**
7
+ * The enforced clean-room wall: target-phase flows (implement, verify, review)
8
+ * refuse to run when the target workspace contains anything matching the pack's
9
+ * legacy `sources:` regex, so the coder provably never has legacy source in
10
+ * reach. A breach is a recoverable, typed outcome — the caller decides to
11
+ * abort; only I/O failure fails the effect.
12
+ */
13
+ export class WallClean extends Schema.TaggedClass<WallClean>()("Clean", {}) {}
14
+
15
+ export class WallBreached extends Schema.TaggedClass<WallBreached>()("Breached", {
16
+ paths: Schema.Array(Schema.String)
17
+ }) {}
18
+
19
+ export const WallResult = Schema.Union([WallClean, WallBreached])
20
+ export type WallResult = typeof WallResult.Type
21
+
22
+ /**
23
+ * Scans the working tree for files matching the pack's legacy `sourcesRegex`.
24
+ * `.git` is exempt — the wall is about the working tree, not repository
25
+ * internals.
26
+ */
27
+ export const checkWall = Effect.fn("@llm4ts/flow/Wall.check")(function* (
28
+ workspace: WorkspaceShape,
29
+ sourcesRegex: string
30
+ ): Effect.fn.Return<WallResult, WorkspaceError> {
31
+ const matches = (yield* matchingFiles(workspace, sourcesRegex)).filter(
32
+ (path) => !path.startsWith(".git/")
33
+ )
34
+ return matches.length === 0 ? new WallClean() : new WallBreached({ paths: matches })
35
+ })
36
+
37
+ /** The one-line breach message every target-phase flow reports, capped at ten paths. */
38
+ export const wallBreachMessage = (breach: WallBreached, consequence: string): string => {
39
+ const shown = breach.paths.slice(0, 10).join(", ")
40
+ const more = breach.paths.length > 10 ? ` (+${breach.paths.length - 10} more)` : ""
41
+ return `clean-room wall breached — legacy source inside the target workspace: ${shown}${more}. ${consequence}`
42
+ }