@llm4ts/shell 2.47.1 → 2.48.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/flows/lib/epic-mvp.js
CHANGED
|
@@ -83,6 +83,7 @@ export const mvpPlanInstructions = (epicId, guidance, orientation) => [
|
|
|
83
83
|
" `acceptance` outcome, and none depends on a parked one.",
|
|
84
84
|
"- Prefer fewer, thinner stories: the thinnest end-to-end path that works.",
|
|
85
85
|
"- Items an approved brief marks deferred or dropped are parked, never planned.",
|
|
86
|
+
"- Leave `testsDeferred` out: every MVP story is written test-first.",
|
|
86
87
|
'Add `"later":[…]` to each story and `"parked":[…]` beside `"stories"` in the JSON.'
|
|
87
88
|
].join("\n");
|
|
88
89
|
/** The MVP planner: writes `mvp-cut.json` beside the plan with the stories parked whole. */
|
|
@@ -750,7 +750,8 @@ export const storyJsonSchema = {
|
|
|
750
750
|
readFirst: { type: "array", items: { type: "string" } },
|
|
751
751
|
acceptance: { type: "array", items: { type: "string" } },
|
|
752
752
|
provides: { type: "array", items: { type: "string" } },
|
|
753
|
-
testsChange: { type: "boolean" }
|
|
753
|
+
testsChange: { type: "boolean" },
|
|
754
|
+
testsDeferred: { type: "string" }
|
|
754
755
|
},
|
|
755
756
|
required: [
|
|
756
757
|
"id",
|
|
@@ -797,6 +798,10 @@ orientation) => [
|
|
|
797
798
|
"- `testsChange`: true only for a story whose job includes deleting, skipping or rewriting",
|
|
798
799
|
" existing tests (a migration of a test suite, a removed feature). Every other story leaves",
|
|
799
800
|
" it out: the gates fail a story that deletes a test file or adds a skip marker.",
|
|
801
|
+
"- `testsDeferred`: only for a story whose code runs nowhere but a live runtime the repository",
|
|
802
|
+
" cannot host in a test (IBM ACE message flows and ESQL, a mainframe transaction): one",
|
|
803
|
+
" sentence naming that runtime. The judge then does not ask it for tests. Never set it because",
|
|
804
|
+
" tests are hard to write; every other story leaves it out and owns its test files.",
|
|
800
805
|
"- `owned` sets are pairwise DISJOINT: no path prefix appears under two stories.",
|
|
801
806
|
"- Shared surfaces (the kit, the theme, house rules) are never edited by a feature story. A new",
|
|
802
807
|
" shared component is its own story, and every story using it depends on it.",
|
|
@@ -998,10 +1003,15 @@ export const storyDimensions = [
|
|
|
998
1003
|
* a partial score elsewhere is a Warning a follow-up round can close.
|
|
999
1004
|
*/
|
|
1000
1005
|
export const judgeSeverity = (name, score) => name === "provides" || score <= 0 ? "Critical" : "Warning";
|
|
1006
|
+
/** The dimensions a story is held to: all of them, less `tests` when the plan deferred them. */
|
|
1007
|
+
export const judgedDimensionsOf = (story) => storyDimensions
|
|
1008
|
+
.map((dimension) => dimension.name)
|
|
1009
|
+
.filter((name) => story.testsDeferred === undefined || name !== "tests");
|
|
1001
1010
|
const subBar = (scored, story) => ReviewResult.make({
|
|
1002
1011
|
issues: scored.scores
|
|
1003
|
-
.filter((score) => score.
|
|
1004
|
-
|
|
1012
|
+
.filter((score) => judgedDimensionsOf(story).includes(score.name) &&
|
|
1013
|
+
score.score <
|
|
1014
|
+
(storyDimensions.find((dimension) => dimension.name === score.name)?.maxScore ?? 2))
|
|
1005
1015
|
.map((score) => ReviewIssue.make({
|
|
1006
1016
|
severity: judgeSeverity(score.name, score.score),
|
|
1007
1017
|
title: `judge[${story.id}]: ${score.name} scored ${score.score}`,
|
|
@@ -1009,6 +1019,13 @@ const subBar = (scored, story) => ReviewResult.make({
|
|
|
1009
1019
|
})),
|
|
1010
1020
|
summary: `judge:${story.id}`
|
|
1011
1021
|
});
|
|
1022
|
+
/** What a judge is told about a story the plan exempted from tests; nothing otherwise. */
|
|
1023
|
+
export const testsDeferredLines = (reason) => reason === undefined
|
|
1024
|
+
? []
|
|
1025
|
+
: [
|
|
1026
|
+
`Tests deferred by the plan: ${reason}`,
|
|
1027
|
+
"No test file is expected; do not mark any dimension down for its absence."
|
|
1028
|
+
];
|
|
1012
1029
|
/**
|
|
1013
1030
|
* What the story judge reads beside the diff: the story, and what every
|
|
1014
1031
|
* story it depends on provides. Without the dependencies the judge cannot
|
|
@@ -1039,6 +1056,7 @@ export const storyJudgeQuery = (story, plan, subject = "diff") => {
|
|
|
1039
1056
|
`Provides: ${story.provides.join(", ") || "(none)"}`,
|
|
1040
1057
|
`Owned paths: ${story.owned.join(", ")}`,
|
|
1041
1058
|
`Shared read-only: ${story.sharedReadOnly.join(", ") || "(none)"}`,
|
|
1059
|
+
...testsDeferredLines(story.testsDeferred),
|
|
1042
1060
|
...(dependencies.length === 0
|
|
1043
1061
|
? []
|
|
1044
1062
|
: [
|
package/flows/lib/story-board.js
CHANGED
|
@@ -17,10 +17,10 @@ import { answerKey, decideRule, decisionKey, publishBlackboardRun, runErrorToFlo
|
|
|
17
17
|
import { cap } from "@llm4ts/flow/Context";
|
|
18
18
|
import { FlowLlmError } from "@llm4ts/flow/FlowError";
|
|
19
19
|
import { JudgmentObserved, publishJudgmentObserved } from "@llm4ts/flow/FlowEvents";
|
|
20
|
-
import { certaintyOf, decide, judgmentOf } from "@llm4ts/flow/Judgment";
|
|
20
|
+
import { certaintyOf, Decision, decide, judgmentOf } from "@llm4ts/flow/Judgment";
|
|
21
21
|
import { ReviewIssue, ReviewResult } from "@llm4ts/flow/Review";
|
|
22
22
|
import { dependenciesOf } from "@llm4ts/flow/StoryPlan";
|
|
23
|
-
import { storyDimensions } from "./epic-stories.js";
|
|
23
|
+
import { storyDimensions, testsDeferredLines } from "./epic-stories.js";
|
|
24
24
|
export class StoryBrief extends Schema.Class("StoryBrief")({
|
|
25
25
|
id: Schema.String,
|
|
26
26
|
title: Schema.String,
|
|
@@ -29,7 +29,8 @@ export class StoryBrief extends Schema.Class("StoryBrief")({
|
|
|
29
29
|
provides: Schema.Array(Schema.String),
|
|
30
30
|
owned: Schema.Array(Schema.String),
|
|
31
31
|
sharedReadOnly: Schema.Array(Schema.String),
|
|
32
|
-
dependencies: Schema.Array(Schema.Struct({ id: Schema.String, provides: Schema.Array(Schema.String) }))
|
|
32
|
+
dependencies: Schema.Array(Schema.Struct({ id: Schema.String, provides: Schema.Array(Schema.String) })),
|
|
33
|
+
testsDeferred: Schema.optionalKey(Schema.String)
|
|
33
34
|
}) {
|
|
34
35
|
}
|
|
35
36
|
export const storyBriefOf = (story, plan) => StoryBrief.make({
|
|
@@ -45,7 +46,8 @@ export const storyBriefOf = (story, plan) => StoryBrief.make({
|
|
|
45
46
|
: dependenciesOf(plan, story.id).flatMap((id) => {
|
|
46
47
|
const dependency = plan.story(id);
|
|
47
48
|
return dependency === undefined ? [] : [{ id, provides: dependency.provides }];
|
|
48
|
-
})
|
|
49
|
+
}),
|
|
50
|
+
...(story.testsDeferred === undefined ? {} : { testsDeferred: story.testsDeferred })
|
|
49
51
|
});
|
|
50
52
|
// ---- Facts ------------------------------------------------------------------
|
|
51
53
|
export const storyBrief = makeKey("story.brief", StoryBrief);
|
|
@@ -53,6 +55,7 @@ export const storyDiff = makeKey("story.diff", Schema.String);
|
|
|
53
55
|
export const houseRules = makeKey("story.houseRules", Schema.String);
|
|
54
56
|
export const mergeable = makeKey("story.mergeable", Schema.Boolean);
|
|
55
57
|
export const issues = makeKey("story.issues", Schema.Array(ReviewIssue));
|
|
58
|
+
const decisions = makeKey("story.decisions", Schema.Array(Decision));
|
|
56
59
|
/** One judged answer and one decision per story dimension, keyed by the dimension's name. */
|
|
57
60
|
const judged = (name) => answerKey(`judge.${name}`);
|
|
58
61
|
const decided = (name) => decisionKey(`decision.${name}`);
|
|
@@ -86,6 +89,7 @@ export const stateOf = (brief, diff, rules) => [
|
|
|
86
89
|
`Provides: ${brief.provides.join(", ") || "(none)"}`,
|
|
87
90
|
`Owned paths: ${brief.owned.join(", ")}`,
|
|
88
91
|
`Shared read-only: ${brief.sharedReadOnly.join(", ") || "(none)"}`,
|
|
92
|
+
...testsDeferredLines(brief.testsDeferred),
|
|
89
93
|
...(brief.dependencies.length === 0
|
|
90
94
|
? []
|
|
91
95
|
: [
|
|
@@ -122,25 +126,34 @@ export const makeStoryBoard = (policy) => {
|
|
|
122
126
|
return answer?.type === "score" ? [d.answer.of(answer)] : [];
|
|
123
127
|
})
|
|
124
128
|
});
|
|
125
|
-
const
|
|
129
|
+
const deciders = dimensions.map((d) => decideRule({
|
|
126
130
|
name: `decide-${d.dimension.name}`,
|
|
127
131
|
answer: d.answer,
|
|
128
132
|
decision: d.decision,
|
|
129
133
|
...(policy === undefined ? {} : { policy })
|
|
130
134
|
}));
|
|
135
|
+
// The four decisions as one fact, so the bar can also read the brief.
|
|
136
|
+
const joined = derive({
|
|
137
|
+
name: "decisions",
|
|
138
|
+
condition: all(provides.decision, scope.decision, houseStyle.decision, tests.decision),
|
|
139
|
+
produces: [decisions],
|
|
140
|
+
derive: (verdicts) => [decisions.of(verdicts)]
|
|
141
|
+
});
|
|
131
142
|
const bar = derive({
|
|
132
143
|
name: "bar",
|
|
133
|
-
condition: all(provides.answer, scope.answer, houseStyle.answer, tests.answer,
|
|
144
|
+
condition: all(storyBrief, provides.answer, scope.answer, houseStyle.answer, tests.answer, decisions),
|
|
134
145
|
produces: [mergeable, issues],
|
|
135
|
-
derive: ([a1, a2, a3, a4,
|
|
146
|
+
derive: ([brief, a1, a2, a3, a4, verdicts]) => {
|
|
136
147
|
const found = [];
|
|
137
148
|
const answers = [a1, a2, a3, a4];
|
|
138
|
-
const verdicts = [v1, v2, v3, v4];
|
|
139
149
|
dimensions.forEach((d, index) => {
|
|
140
150
|
const answer = answers[index];
|
|
141
151
|
const verdict = verdicts[index];
|
|
142
152
|
if (answer === undefined || verdict === undefined || answer.type !== "score")
|
|
143
153
|
return;
|
|
154
|
+
// The plan deferred this story's tests (it runs only in a live runtime).
|
|
155
|
+
if (d === tests && brief.testsDeferred !== undefined)
|
|
156
|
+
return;
|
|
144
157
|
if (answer.score < passingScore) {
|
|
145
158
|
found.push(ReviewIssue.make({
|
|
146
159
|
severity: "Critical",
|
|
@@ -163,7 +176,7 @@ export const makeStoryBoard = (policy) => {
|
|
|
163
176
|
name: "story-board",
|
|
164
177
|
imports: [storyBrief, storyDiff, houseRules],
|
|
165
178
|
exports: [mergeable, issues],
|
|
166
|
-
rules: [storyJudge, ...
|
|
179
|
+
rules: [storyJudge, ...deciders, joined, bar]
|
|
167
180
|
});
|
|
168
181
|
};
|
|
169
182
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
source: jsp
|
|
4
4
|
scaffold: ../../scaffolds/nextjs-spa
|
|
5
|
-
sources: .*\.(jsp|java|xml)
|
|
5
|
+
sources: .*\.(jsp|jspf|js|java|xml)$
|
|
6
6
|
spec-schema: pagespec
|
|
7
7
|
programs: .*\.jsp
|
|
8
8
|
specs-dir: docs/modernization/specs
|
|
@@ -27,54 +27,131 @@ setup: pnpm install --offline
|
|
|
27
27
|
|
|
28
28
|
## Coverage: servlet-url
|
|
29
29
|
|
|
30
|
-
files: .*web\.xml
|
|
30
|
+
files: .*web\.xml$
|
|
31
31
|
unit: <url-pattern>([^<]+)</url-pattern>
|
|
32
32
|
|
|
33
33
|
## Coverage: jsp-form
|
|
34
34
|
|
|
35
|
-
files: .*\.jsp
|
|
35
|
+
files: .*\.jsp$
|
|
36
36
|
unit: action="([^"]+)"
|
|
37
37
|
|
|
38
38
|
## Coverage: jsp-ajax
|
|
39
39
|
|
|
40
|
-
files: .*\.jsp
|
|
40
|
+
files: .*\.jsp$
|
|
41
41
|
unit: url:\s*['"]([^'"]+)['"]
|
|
42
42
|
|
|
43
43
|
## Survey: jsp-include
|
|
44
44
|
|
|
45
|
-
files: .*\.jsp
|
|
45
|
+
files: .*\.jsp$
|
|
46
46
|
unit: <jsp:include page="([^"]+)"
|
|
47
47
|
|
|
48
|
+
## Survey: jsp-static-include
|
|
49
|
+
|
|
50
|
+
files: .*\.jspf?$
|
|
51
|
+
unit: <%@\s*include\s+file="([^"]+)"
|
|
52
|
+
|
|
48
53
|
## Survey: servlet-class
|
|
49
54
|
|
|
50
|
-
files: .*web\.xml
|
|
55
|
+
files: .*web\.xml$
|
|
51
56
|
unit: <servlet-class>[a-z.]*\.([A-Za-z0-9]+)</servlet-class>
|
|
52
57
|
|
|
53
58
|
## Node: ajax-call
|
|
54
59
|
|
|
55
|
-
files: .*\.(jsp|js)
|
|
60
|
+
files: .*\.(jsp|js)$
|
|
56
61
|
pattern: (?:["']?url["']?\s*:\s*|\$\.(?:get|post|getJSON)\(\s*)['"](?<name>[^'"]+)['"]
|
|
57
62
|
attrs: url=name
|
|
58
63
|
|
|
59
64
|
## Node: form
|
|
60
65
|
|
|
61
|
-
files: .*\.jsp
|
|
66
|
+
files: .*\.jsp$
|
|
62
67
|
pattern: <(?:[a-z]+:)?[Ff][Oo][Rr][Mm]\b[^>]*\b[Aa][Cc][Tt][Ii][Oo][Nn]=["'](?<name>[^"']+)["']
|
|
63
68
|
attrs: url=name
|
|
64
69
|
|
|
65
70
|
## Node: servlet-mapping
|
|
66
71
|
|
|
67
|
-
files: .*web\.xml
|
|
72
|
+
files: .*web\.xml$
|
|
68
73
|
pattern: <servlet-mapping>\s*<servlet-name>(?<name>[^<]+)</servlet-name>\s*<url-pattern>(?<url>[^<]+)</url-pattern>
|
|
69
74
|
descriptor: yes
|
|
70
75
|
|
|
71
76
|
## Node: servlet-decl
|
|
72
77
|
|
|
73
|
-
files: .*web\.xml
|
|
78
|
+
files: .*web\.xml$
|
|
74
79
|
pattern: <servlet>(?:(?!</servlet>)[\s\S])*?<servlet-name>(?<name>[^<]+)</servlet-name>(?:(?!</servlet>)[\s\S])*?<servlet-class>(?:[a-z0-9_]+\.)*(?<class>[A-Za-z0-9_]+)</servlet-class>
|
|
75
80
|
descriptor: yes
|
|
76
81
|
anchor: class
|
|
77
82
|
|
|
83
|
+
## Node: rest-resource
|
|
84
|
+
|
|
85
|
+
files: .*\.java$
|
|
86
|
+
pattern: @(?:Path|RequestMapping)\(\s*(?:value\s*=\s*)?"(?<url>[^"]+)"\s*\)(?:\s*@\w+(?:\([^)]*\))?)*\s*public\s+(?:abstract\s+)?class\s+(?<name>\w+)
|
|
87
|
+
|
|
88
|
+
## Node: java-bean
|
|
89
|
+
|
|
90
|
+
files: .*\.java$
|
|
91
|
+
pattern: @(?:Named|ManagedBean|Stateless|Stateful|Singleton|Component|Service|Controller)\b(?:\([^)]*\))?(?:\s*@\w+(?:\([^)]*\))?)*\s*public\s+class\s+(?<name>\w+)
|
|
92
|
+
|
|
93
|
+
## Node: el-bean-ref
|
|
94
|
+
|
|
95
|
+
files: .*\.jspf?$
|
|
96
|
+
pattern: \$\{(?<name>[a-z]\w*)\.
|
|
97
|
+
|
|
98
|
+
## Edge: jsp-usebean
|
|
99
|
+
|
|
100
|
+
files: .*\.jspf?$
|
|
101
|
+
pattern: <jsp:useBean\b[^>]*\bclass="(?:[\w]+\.)*(?<to>\w+)"
|
|
102
|
+
unknown: drop
|
|
103
|
+
|
|
104
|
+
## Edge: jsp-static-call
|
|
105
|
+
|
|
106
|
+
files: .*\.jspf?$
|
|
107
|
+
pattern: \b(?<to>[A-Z][A-Za-z0-9]+)\.[a-z]\w*\(
|
|
108
|
+
unknown: drop
|
|
109
|
+
|
|
110
|
+
## Edge: jsp-new
|
|
111
|
+
|
|
112
|
+
files: .*\.jspf?$
|
|
113
|
+
pattern: \bnew\s+(?<to>[A-Z]\w*)\s*\(
|
|
114
|
+
unknown: drop
|
|
115
|
+
|
|
116
|
+
## Node: servlet-jsp
|
|
117
|
+
|
|
118
|
+
files: .*web\.xml$
|
|
119
|
+
pattern: <servlet>(?:(?!</servlet>)[\s\S])*?<servlet-name>(?<name>[^<]+)</servlet-name>(?:(?!</servlet>)[\s\S])*?<jsp-file>(?<page>[^<]+)</jsp-file>
|
|
120
|
+
descriptor: yes
|
|
121
|
+
anchor: page
|
|
122
|
+
|
|
123
|
+
## Edge: jsp-form-to-page
|
|
124
|
+
|
|
125
|
+
files: .*\.jsp$
|
|
126
|
+
pattern: <(?:[a-z]+:)?[Ff][Oo][Rr][Mm]\b[^>]*\b[Aa][Cc][Tt][Ii][Oo][Nn]=["'](?<to>[^"'?]+\.jsp)
|
|
127
|
+
from: form
|
|
128
|
+
unknown: drop
|
|
129
|
+
|
|
130
|
+
## Edge: jsp-ajax-to-page
|
|
131
|
+
|
|
132
|
+
files: .*\.(jsp|js)$
|
|
133
|
+
pattern: (?:["']?url["']?\s*:\s*|\$\.(?:get|post|getJSON)\(\s*)['"](?<to>[^'"?]+\.jsp)
|
|
134
|
+
from: ajax-call
|
|
135
|
+
unknown: drop
|
|
136
|
+
|
|
137
|
+
## Edge: java-import
|
|
138
|
+
|
|
139
|
+
files: .*\.java$
|
|
140
|
+
pattern: ^import\s+(?:static\s+)?[\w.]*\.(?<to>[A-Z]\w*)\s*;
|
|
141
|
+
unknown: drop
|
|
142
|
+
|
|
143
|
+
## Edge: java-new
|
|
144
|
+
|
|
145
|
+
files: .*\.java$
|
|
146
|
+
pattern: \bnew\s+(?<to>[A-Z]\w*)\s*[<(]
|
|
147
|
+
unknown: drop
|
|
148
|
+
|
|
149
|
+
## Edge: java-static-call
|
|
150
|
+
|
|
151
|
+
files: .*\.java$
|
|
152
|
+
pattern: \b(?<to>[A-Z]\w+)\.[a-z]\w*\(
|
|
153
|
+
unknown: drop
|
|
154
|
+
|
|
78
155
|
## Join: jsp-ajax-target
|
|
79
156
|
|
|
80
157
|
from: ajax-call.url
|
|
@@ -87,13 +164,31 @@ from: form.url
|
|
|
87
164
|
to: servlet-mapping.url
|
|
88
165
|
match: url
|
|
89
166
|
|
|
167
|
+
## Join: jsp-ajax-rest
|
|
168
|
+
|
|
169
|
+
from: ajax-call.url
|
|
170
|
+
to: rest-resource.url
|
|
171
|
+
match: url-path
|
|
172
|
+
|
|
173
|
+
## Join: jsp-el-bean
|
|
174
|
+
|
|
175
|
+
from: el-bean-ref.name
|
|
176
|
+
to: java-bean.name
|
|
177
|
+
match: exact-ci
|
|
178
|
+
|
|
90
179
|
## Join: servlet-wiring
|
|
91
180
|
|
|
92
181
|
from: servlet-mapping.name
|
|
93
182
|
to: servlet-decl.name
|
|
94
183
|
scope: file
|
|
95
184
|
|
|
185
|
+
## Join: servlet-wiring
|
|
186
|
+
|
|
187
|
+
from: servlet-mapping.name
|
|
188
|
+
to: servlet-jsp.name
|
|
189
|
+
scope: file
|
|
190
|
+
|
|
96
191
|
## Consolidate
|
|
97
192
|
|
|
98
|
-
- cluster: jsp-form-action, jsp-ajax-target
|
|
99
|
-
- context: jsp-include
|
|
193
|
+
- cluster: jsp-form-action, jsp-ajax-target, jsp-form-to-page, jsp-ajax-to-page, jsp-ajax-rest
|
|
194
|
+
- context: jsp-include, jsp-static-include
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm4ts/shell",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.48.0",
|
|
4
4
|
"description": "Interactive shell and CLI for llm4ts: flow discovery, run-a-flow, and view",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@effect/platform-node": "4.0.0",
|
|
63
63
|
"@effect/platform-node-shared": "4.0.0",
|
|
64
|
-
"@llm4ts/
|
|
65
|
-
"@llm4ts/
|
|
66
|
-
"@llm4ts/runner": "2.
|
|
64
|
+
"@llm4ts/flow": "2.48.0",
|
|
65
|
+
"@llm4ts/core": "2.48.0",
|
|
66
|
+
"@llm4ts/runner": "2.48.0"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"effect": "4.0.0"
|