@llm4ts/shell 2.11.0 → 2.13.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/epic-stories.js +25 -5
- package/flows/lib/epic-stories.js +76 -8
- package/kits/soap-ace/README.md +313 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/RUNBOOK.md +102 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/design/api-design.md +620 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/responses/cercaConti.xml +3 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/responses/cercaMovimenti.xml +3 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/responses/confermaBonifico.xml +3 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/responses/dettaglioConto.xml +3 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/responses/inserisciBonifico.xml +3 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/responses/revocaBonifico.xml +3 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/soapui/DemoBank-soapui-project.xml +39 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/DemoBank.wsdl +186 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/LegacyRpc.wsdl +29 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/xsd/banking.xsd +10 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/xsd/bonifici.xsd +107 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/xsd/common.xsd +93 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/xsd/conti.xsd +71 -0
- package/kits/soap-ace/fixtures/demo-bank-soap/wsdl/xsd/movimenti.xsd +55 -0
- package/kits/soap-ace/flows/lib/soap/Analysis.js +508 -0
- package/kits/soap-ace/flows/lib/soap/Auth.js +179 -0
- package/kits/soap-ace/flows/lib/soap/Call.js +186 -0
- package/kits/soap-ace/flows/lib/soap/CallPolicy.js +50 -0
- package/kits/soap-ace/flows/lib/soap/Catalog.js +157 -0
- package/kits/soap-ace/flows/lib/soap/Classification.js +254 -0
- package/kits/soap-ace/flows/lib/soap/Design.js +497 -0
- package/kits/soap-ace/flows/lib/soap/DesignProposal.js +204 -0
- package/kits/soap-ace/flows/lib/soap/Discover.js +139 -0
- package/kits/soap-ace/flows/lib/soap/Envelope.js +132 -0
- package/kits/soap-ace/flows/lib/soap/Epic.js +239 -0
- package/kits/soap-ace/flows/lib/soap/Instance.js +675 -0
- package/kits/soap-ace/flows/lib/soap/Mapping.js +130 -0
- package/kits/soap-ace/flows/lib/soap/Masking.js +508 -0
- package/kits/soap-ace/flows/lib/soap/OpenApi.js +288 -0
- package/kits/soap-ace/flows/lib/soap/Samples.js +287 -0
- package/kits/soap-ace/flows/lib/soap/Scenarios.js +132 -0
- package/kits/soap-ace/flows/lib/soap/SoapUiImport.js +200 -0
- package/kits/soap-ace/flows/lib/soap/Stub.js +34 -0
- package/kits/soap-ace/flows/lib/soap/Transport.js +167 -0
- package/kits/soap-ace/flows/lib/soap/Wsdl.js +533 -0
- package/kits/soap-ace/flows/lib/soap/Xml.js +453 -0
- package/kits/soap-ace/flows/lib/soap/Xsd.js +471 -0
- package/kits/soap-ace/flows/lib/soap/Yaml.js +309 -0
- package/kits/soap-ace/flows/soap-design.js +167 -0
- package/kits/soap-ace/flows/soap-discover.js +85 -0
- package/kits/soap-ace/flows/soap-epic.js +204 -0
- package/kits/soap-ace/flows/soap-sample.js +265 -0
- package/kits/soap-ace/packs/ace12-rest/api-style.md +63 -0
- package/kits/soap-ace/packs/ace12-rest/pack.md +18 -0
- package/kits/soap-ace/packs/ace12-rest/prompts/implement.md +8 -0
- package/kits/soap-ace/packs/ace12-rest/reviewers/contract.md +5 -0
- package/kits/soap-ace/patterns/PAT-ACE-001-esito-to-problem.md +8 -0
- package/kits/soap-ace/patterns/PAT-ACE-002-fault-to-problem.md +8 -0
- package/kits/soap-ace/patterns/PAT-ACE-003-amounts-and-enums.md +8 -0
- package/kits/soap-ace/patterns/PAT-ACE-004-pagination.md +7 -0
- package/kits/soap-ace/patterns/PAT-ACE-005-nil-and-absence.md +7 -0
- package/kits/soap-ace/scaffolds/ace12-rest-api/CONTRIBUTING.md +56 -0
- package/kits/soap-ace/scaffolds/ace12-rest-api/README.md +16 -0
- package/kits/soap-ace/scaffolds/ace12-rest-api/scripts/ace-gates-container.sh +8 -0
- package/kits/soap-ace/scaffolds/ace12-rest-api/scripts/ace-gates.sh +41 -0
- package/package.json +4 -4
package/flows/epic-stories.js
CHANGED
|
@@ -29,7 +29,7 @@ import { landEpic } from "@llm4ts/flow/Landing";
|
|
|
29
29
|
import { implementStoriesFlow } from "@llm4ts/flow/Stories";
|
|
30
30
|
import { makeStoryPlanStore, validateStoryPlan } from "@llm4ts/flow/StoryPlan";
|
|
31
31
|
import * as Console from "effect/Console";
|
|
32
|
-
import { awaitServer, chooseEpic, epicsDir, listEpics, renderEpicList, combineTotals, epicIdFor, flagsFromEnvironment, gateCommands, gatesIn, generateStoryPlan, httpProbe, judgeStory, localCoderServer, parseEpicArgs, reasonerFromEnvironment, serverHealthUrl, setupIn, storyCoderFromEnvironment, verifyBlockedOn, worktreeRootFor, worktreeSetupCommand } from "./lib/epic-stories.js";
|
|
32
|
+
import { awaitServer, chooseEpic, epicsDir, listEpics, renderEpicList, combineTotals, epicIdFor, flagsFromEnvironment, appDirFor, appScripts, gateCommands, gatesIn, inAppDir, generateStoryPlan, httpProbe, judgeStory, localCoderServer, parseEpicArgs, reasonerFromEnvironment, serverHealthUrl, setupAgentEnabled, setupIn, storyCoderFromEnvironment, verifyBlockedOn, worktreeRootFor, worktreeSetupCommand } from "./lib/epic-stories.js";
|
|
33
33
|
/** `LLM4TS_CODER_MODEL` / `LLM4TS_REASONING_MODEL`: pi takes `provider/model` (e.g. `openai-codex/gpt-5.5`). */
|
|
34
34
|
const withOptionalModel = (config, model) => {
|
|
35
35
|
const trimmed = model?.trim();
|
|
@@ -95,6 +95,17 @@ const program = Effect.gen(function* () {
|
|
|
95
95
|
if (flags.planOnly) {
|
|
96
96
|
return;
|
|
97
97
|
}
|
|
98
|
+
// Setup and gates run where the application is, in every checkout.
|
|
99
|
+
const appDir = yield* appDirFor(input.workDir, process.env);
|
|
100
|
+
if (appDir !== ".") {
|
|
101
|
+
yield* events.publish(Info.make({ message: `app dir: ${appDir} (setup and gates run there; LLM4TS_APP_DIR)` }));
|
|
102
|
+
}
|
|
103
|
+
const commands = gateCommands(process.env, yield* appScripts(files, join(input.workDir, appDir)));
|
|
104
|
+
yield* events.publish(Info.make({
|
|
105
|
+
message: commands.length === 0
|
|
106
|
+
? "gates: none (the app defines none of typecheck, lint, test, build; set LLM4TS_GATES)"
|
|
107
|
+
: `gates: ${commands.map((command) => command.join(" ")).join(" · ")}`
|
|
108
|
+
}));
|
|
98
109
|
if (flags.land !== undefined) {
|
|
99
110
|
const landed = yield* landEpic(context, {
|
|
100
111
|
plan,
|
|
@@ -102,7 +113,7 @@ const program = Effect.gen(function* () {
|
|
|
102
113
|
stateDir,
|
|
103
114
|
target: flags.land,
|
|
104
115
|
keepWorktrees: flags.keepWorktrees,
|
|
105
|
-
gates: gatesIn(nodeProcessExecutor, events,
|
|
116
|
+
gates: inAppDir(appDir, gatesIn(nodeProcessExecutor, events, commands)),
|
|
106
117
|
system: ["House rules of the target repository (CONTRIBUTING.md):", guidance].join("\n")
|
|
107
118
|
});
|
|
108
119
|
yield* events.publish(Info.make({
|
|
@@ -126,7 +137,12 @@ const program = Effect.gen(function* () {
|
|
|
126
137
|
message: "this runner cannot rebind seats to a worktree (no contextFor)"
|
|
127
138
|
});
|
|
128
139
|
}
|
|
129
|
-
const
|
|
140
|
+
const appDirNote = appDir === "."
|
|
141
|
+
? []
|
|
142
|
+
: [
|
|
143
|
+
`The application lives in ${appDir}/ — its package.json, sources and tests; the gates run there.`
|
|
144
|
+
];
|
|
145
|
+
const gates = inAppDir(appDir, gatesIn(nodeProcessExecutor, events, commands));
|
|
130
146
|
const setupCommand = worktreeSetupCommand(process.env);
|
|
131
147
|
const healthUrl = serverHealthUrl(localServer, process.env);
|
|
132
148
|
const report = yield* implementStoriesFlow({ ...context, reasoning: reasoningMeter.service }, {
|
|
@@ -151,7 +167,10 @@ const program = Effect.gen(function* () {
|
|
|
151
167
|
}),
|
|
152
168
|
...(setupCommand === undefined
|
|
153
169
|
? {}
|
|
154
|
-
: {
|
|
170
|
+
: {
|
|
171
|
+
setup: inAppDir(appDir, setupIn(nodeProcessExecutor, events, setupCommand)),
|
|
172
|
+
setupAgent: setupAgentEnabled(process.env)
|
|
173
|
+
}),
|
|
155
174
|
gates,
|
|
156
175
|
// With a roster, the judge and the verifier are leased per story,
|
|
157
176
|
// away from the executor coding it (ADR 0019).
|
|
@@ -166,7 +185,8 @@ const program = Effect.gen(function* () {
|
|
|
166
185
|
"House rules of the target repository (CONTRIBUTING.md):",
|
|
167
186
|
guidance,
|
|
168
187
|
"",
|
|
169
|
-
`Imitate the exemplar feature before inventing anything. Story id: ${story.id}
|
|
188
|
+
`Imitate the exemplar feature before inventing anything. Story id: ${story.id}.`,
|
|
189
|
+
...appDirNote
|
|
170
190
|
].join("\n")),
|
|
171
191
|
concurrency,
|
|
172
192
|
failFast: flags.failFast
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// story-plan generator prompt and schema, the story judge, the gate runner,
|
|
3
3
|
// and seat selection. The executor itself is `@llm4ts/flow/Stories`.
|
|
4
4
|
import { readdir } from "node:fs/promises";
|
|
5
|
-
import { join } from "node:path";
|
|
5
|
+
import { isAbsolute, join, normalize } from "node:path";
|
|
6
6
|
import * as Duration from "effect/Duration";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
8
9
|
import { Dimension, Sample } from "@llm4ts/core/eval/Eval";
|
|
9
10
|
import { judge } from "@llm4ts/core/eval/Judge";
|
|
10
11
|
import { TokenUsage } from "@llm4ts/core/Models";
|
|
@@ -34,9 +35,13 @@ export const epicUsage = [
|
|
|
34
35
|
"Seats: LLM4TS_REASONER (claude|gemini|…, default claude) splits, reviews, judges;",
|
|
35
36
|
" LLM4TS_CODER (default pi) implements; LLM4TS_REASONING_MODEL / LLM4TS_CODER_MODEL",
|
|
36
37
|
" pick their models (pi: provider/model); LLM4TS_CODER_FLAGS / LLM4TS_REASONING_FLAGS",
|
|
37
|
-
" add CLI flags (key=value;key=value). LLM4TS_GATES overrides the gate commands
|
|
38
|
+
" add CLI flags (key=value;key=value). LLM4TS_GATES overrides the gate commands",
|
|
39
|
+
" (default: those of pnpm typecheck|lint|test|build the app's package.json defines);",
|
|
38
40
|
" LLM4TS_WORKTREE_SETUP (default: pnpm install --offline) prepares each worktree;",
|
|
39
41
|
" LLM4TS_WORKTREE_ROOT (default: <repo>.worktrees beside the repository) holds them;",
|
|
42
|
+
" LLM4TS_APP_DIR (e.g. frontend) runs setup and gates in that subfolder; unset, a",
|
|
43
|
+
" repository without a root package.json uses its one subfolder that has one;",
|
|
44
|
+
" LLM4TS_SETUP_AGENT=1 gives the coder one turn to fix a failed setup, then reruns it;",
|
|
40
45
|
" LLM4TS_CODER_HEALTH_URL is polled after the coder's serving engine goes down."
|
|
41
46
|
].join("\n");
|
|
42
47
|
/** The flow's own flags, taken out before the shared `--repo`/prompt parsing sees the rest. */
|
|
@@ -544,6 +549,45 @@ export const judgeStory = (reasoning, story, diff, budget, plan) => judge(reason
|
|
|
544
549
|
response: cap(diff, budget).text
|
|
545
550
|
}))
|
|
546
551
|
.pipe(Effect.mapError(FlowLlmError.from), Effect.map((scored) => subBar(scored, story)));
|
|
552
|
+
// ---- App directory ----------------------------------------------------------------
|
|
553
|
+
/** Folders never taken for the application: dependencies, build output, llm4ts state. */
|
|
554
|
+
const ignoredAppDirs = new Set(["node_modules", "dist", "build", "out", "coverage"]);
|
|
555
|
+
const hasPackageJson = (dir) => Effect.tryPromise(() => readdir(dir)).pipe(Effect.map((names) => names.includes("package.json")), Effect.catch(() => Effect.succeed(false)));
|
|
556
|
+
/**
|
|
557
|
+
* Where setup and gates run, relative to a checkout: `LLM4TS_APP_DIR` when set
|
|
558
|
+
* (`.` for the root); otherwise the root when it has a `package.json`, else
|
|
559
|
+
* its ONE first-level subfolder that has one (a Next.js app in `frontend/`).
|
|
560
|
+
* Several candidates, or none, keep the root — the setup error then names it.
|
|
561
|
+
* The value is relative so it applies to every worktree and the epic checkout.
|
|
562
|
+
*/
|
|
563
|
+
export const appDirFor = (workDir, environment) => Effect.gen(function* () {
|
|
564
|
+
const configured = environment.LLM4TS_APP_DIR?.trim();
|
|
565
|
+
if (configured !== undefined && configured.length > 0) {
|
|
566
|
+
const normalized = normalize(configured);
|
|
567
|
+
const relative = normalized.replace(/[\\/]+$/, "");
|
|
568
|
+
if (isAbsolute(normalized) || relative === ".." || /^\.\.[\\/]/.test(relative)) {
|
|
569
|
+
return yield* ScriptUsage.make({
|
|
570
|
+
message: `LLM4TS_APP_DIR must be a folder inside the repository, got '${configured}'`
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
return relative.length === 0 ? "." : relative;
|
|
574
|
+
}
|
|
575
|
+
if (yield* hasPackageJson(workDir)) {
|
|
576
|
+
return ".";
|
|
577
|
+
}
|
|
578
|
+
const entries = yield* Effect.tryPromise(() => readdir(workDir, { withFileTypes: true })).pipe(Effect.catch(() => Effect.succeed([])));
|
|
579
|
+
const candidates = [];
|
|
580
|
+
for (const entry of entries) {
|
|
581
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && !ignoredAppDirs.has(entry.name)) {
|
|
582
|
+
if (yield* hasPackageJson(join(workDir, entry.name))) {
|
|
583
|
+
candidates.push(entry.name);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return candidates.length === 1 ? (candidates[0] ?? ".") : ".";
|
|
588
|
+
});
|
|
589
|
+
/** Runs a per-checkout step in the app directory of whichever checkout it is given. */
|
|
590
|
+
export const inAppDir = (appDir, step) => (workDir) => step(appDir === "." ? workDir : join(workDir, appDir));
|
|
547
591
|
// ---- Worktree setup --------------------------------------------------------------
|
|
548
592
|
export const defaultWorktreeSetup = ["pnpm", "install", "--offline"];
|
|
549
593
|
/**
|
|
@@ -562,13 +606,19 @@ export const worktreeSetupCommand = (environment) => {
|
|
|
562
606
|
.filter((part) => part.length > 0);
|
|
563
607
|
return parts.length === 0 ? undefined : parts;
|
|
564
608
|
};
|
|
609
|
+
/**
|
|
610
|
+
* `LLM4TS_SETUP_AGENT=1` (or `true`/`on`): a failed setup gets one coder turn
|
|
611
|
+
* to make the worktree ready, then runs again as the check. Off by default.
|
|
612
|
+
*/
|
|
613
|
+
export const setupAgentEnabled = (environment) => /^(1|true|on|yes)$/i.test(environment.LLM4TS_SETUP_AGENT?.trim() ?? "");
|
|
614
|
+
const output = (result) => result.issues.map((issue) => issue.description).join("\n");
|
|
565
615
|
/** Runs the setup command in a worktree; a non-zero exit fails the story with the output. */
|
|
566
616
|
export const setupIn = (process, events, command) => (workDir) => Effect.flatMap(lintCommand(process, events, command, workDir), (result) => result.isClean
|
|
567
617
|
? Effect.void
|
|
568
618
|
: FlowAborted.make({
|
|
569
|
-
message: `worktree setup failed (${command.join(" ")}):\n${result
|
|
570
|
-
|
|
571
|
-
|
|
619
|
+
message: `worktree setup failed (${command.join(" ")}):\n${output(result)}${/ERR_PNPM_NO_PKG_MANIFEST|ENOENT.*package\.json/.test(output(result))
|
|
620
|
+
? "\n(the application is not at this folder: set LLM4TS_APP_DIR to its subfolder)"
|
|
621
|
+
: ""}`
|
|
572
622
|
}));
|
|
573
623
|
// ---- Gates ---------------------------------------------------------------------
|
|
574
624
|
export const defaultGateCommands = [
|
|
@@ -577,11 +627,29 @@ export const defaultGateCommands = [
|
|
|
577
627
|
["pnpm", "test"],
|
|
578
628
|
["pnpm", "build"]
|
|
579
629
|
];
|
|
580
|
-
/**
|
|
581
|
-
|
|
630
|
+
/** The part of an application's package.json the gates care about. */
|
|
631
|
+
const PackageScripts = Schema.fromJsonString(Schema.Struct({
|
|
632
|
+
scripts: Schema.optional(Schema.Record(Schema.String, Schema.String))
|
|
633
|
+
}));
|
|
634
|
+
/**
|
|
635
|
+
* The script names in `<appRoot>/package.json`; undefined when it is missing
|
|
636
|
+
* or unreadable, which keeps every default gate (the failure then says why).
|
|
637
|
+
*/
|
|
638
|
+
export const appScripts = (files, appRoot) => files.read(join(appRoot, "package.json")).pipe(Effect.flatMap((text) => text === undefined
|
|
639
|
+
? Effect.succeed(undefined)
|
|
640
|
+
: Effect.map(Schema.decodeUnknownEffect(PackageScripts)(text), (manifest) => new Set(Object.keys(manifest.scripts ?? {})))), Effect.catch(() => Effect.succeed(undefined)));
|
|
641
|
+
/**
|
|
642
|
+
* `LLM4TS_GATES="pnpm typecheck;pnpm test"` overrides the four defaults.
|
|
643
|
+
* Without it, a default gate runs only when the application defines that
|
|
644
|
+
* script: a Next.js app has `lint` and `build` but seldom `typecheck`, and
|
|
645
|
+
* `pnpm typecheck` there fails with "Command not found", not a red gate.
|
|
646
|
+
*/
|
|
647
|
+
export const gateCommands = (environment, scripts) => {
|
|
582
648
|
const raw = environment.LLM4TS_GATES?.trim();
|
|
583
649
|
if (raw === undefined || raw.length === 0) {
|
|
584
|
-
return
|
|
650
|
+
return scripts === undefined
|
|
651
|
+
? defaultGateCommands
|
|
652
|
+
: defaultGateCommands.filter((command) => scripts.has(command[1] ?? ""));
|
|
585
653
|
}
|
|
586
654
|
return raw
|
|
587
655
|
.split(";")
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Kit: soap-ace
|
|
2
|
+
|
|
3
|
+
SOAP services to REST APIs on IBM ACE 12: discover the WSDL, sample and analyse real responses, design the REST contract, and plan the ACE build as an epic.
|
|
4
|
+
|
|
5
|
+
In bank integration work most new REST APIs are exposed through IBM ACE on
|
|
6
|
+
top of an existing SOAP service. This kit turns a WSDL (a file or URL, with
|
|
7
|
+
auth when needed) into evidence first, then into a design, and only then into
|
|
8
|
+
an ACE implementation plan. The spec of record is
|
|
9
|
+
[`specs/pending/soap-ace-kit.md`](../../specs/pending/soap-ace-kit.md).
|
|
10
|
+
|
|
11
|
+
Direction: SOAP is the source and ACE the **target**. The
|
|
12
|
+
`mainframe-java/ace-integration` pack goes the other way (ACE as legacy
|
|
13
|
+
source) and shares nothing with this kit.
|
|
14
|
+
|
|
15
|
+
## Status
|
|
16
|
+
|
|
17
|
+
| Piece | State |
|
|
18
|
+
| -------------------------------------------------- | -------- |
|
|
19
|
+
| Strict XML reader (`flows/lib/soap/Xml.ts`) | done |
|
|
20
|
+
| WSDL 1.1/2.0 + XSD catalog (`Wsdl.ts`, `Xsd.ts`) | done |
|
|
21
|
+
| `demo-bank-soap` fixture | WSDL/XSD |
|
|
22
|
+
| `soap-discover` flow and operation classification | pending |
|
|
23
|
+
| Masking, auth profile, transport, sample authoring | pending |
|
|
24
|
+
| Analysis, REST design, OpenAPI projection | pending |
|
|
25
|
+
| `ace12-rest` pack, scaffold, `soap-epic` | pending |
|
|
26
|
+
|
|
27
|
+
## soap-discover
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
llm4ts run soap-discover --repo ~/work/conti-api ./wsdl/DemoBank.wsdl
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Writes under `<repo>/.llm4ts/soap/<service>/`:
|
|
34
|
+
|
|
35
|
+
| File | What it is |
|
|
36
|
+
| --------------- | ------------------------------------------------------------------------ |
|
|
37
|
+
| `catalog.json` | the typed catalog every later step reads |
|
|
38
|
+
| `catalog.md` | human summary: endpoints, operations and their fields, open questions |
|
|
39
|
+
| `operations.md` | proposed `read`/`mutating` classes; review, then set `Status: confirmed` |
|
|
40
|
+
|
|
41
|
+
`operations.md` is the approval file: until its status is `confirmed` every
|
|
42
|
+
operation counts as unclassified, so nothing can be called and the design
|
|
43
|
+
cannot pick REST verbs. An existing file is never overwritten; rerunning
|
|
44
|
+
reports operations it does not mention and entries no longer in the WSDL.
|
|
45
|
+
Classes are proposed by a name heuristic (Italian and English verbs) and, when
|
|
46
|
+
`LLM4TS_JUDGMENT_PROVIDER` names a judgment seat, by a typed judgment
|
|
47
|
+
(ADR 0017); without one, discovery makes no model call at all.
|
|
48
|
+
`LLM4TS_SOAP_SERVICE` overrides the service directory name; for a WSDL URL it
|
|
49
|
+
also selects the auth profile whose `fetch` side authenticates every document
|
|
50
|
+
request.
|
|
51
|
+
|
|
52
|
+
## Discovery guarantees
|
|
53
|
+
|
|
54
|
+
- The catalog is produced by code, never by a model. Every operation, type,
|
|
55
|
+
and field it lists is read from the WSDL and the schemas it imports.
|
|
56
|
+
- Constructs the reader does not model (`xs:any`, substitution groups,
|
|
57
|
+
model-group refs, complex restrictions, list/union types, dangling
|
|
58
|
+
references) are listed as **open questions** with their document and line.
|
|
59
|
+
- Only document/literal SOAP bindings (bare and wrapped, SOAP 1.1 and 1.2)
|
|
60
|
+
become operations. A WSDL whose every binding is RPC or encoded is refused
|
|
61
|
+
with an error naming the bindings; a WSDL that also offers a
|
|
62
|
+
document/literal binding keeps it and lists the others as open questions.
|
|
63
|
+
- `<!DOCTYPE` is refused outright: no external entities, no entity expansion.
|
|
64
|
+
Schemas in ISO-8859-1 and windows-1252 are decoded by their declaration.
|
|
65
|
+
|
|
66
|
+
## soap-sample
|
|
67
|
+
|
|
68
|
+
Runs after `soap-discover` in the same repository; the task text is a
|
|
69
|
+
command.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
llm4ts run soap-sample --repo . "import ./DemoBank-soapui-project.xml" # requests + mock responses, masked
|
|
73
|
+
llm4ts run soap-sample --repo . "init cercaMovimenti fine-mese" # commented skeleton to edit
|
|
74
|
+
llm4ts run soap-sample --repo . "propose cercaMovimenti" # scenario set from the reasoning seat
|
|
75
|
+
llm4ts run soap-sample --repo . "call cercaMovimenti" # every request of the operation
|
|
76
|
+
llm4ts run soap-sample --repo . -- --allow-mutating revocaBonifico "call revocaBonifico/gia-eseguito"
|
|
77
|
+
llm4ts run soap-sample --repo . list
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
A request is a YAML projection of the XSD, one file per scenario under
|
|
81
|
+
`samples/<operation>/<name>.request.yaml`:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
# cercaMovimenti: fine mese
|
|
85
|
+
operation: cercaMovimenti
|
|
86
|
+
purpose: "fine mese"
|
|
87
|
+
body:
|
|
88
|
+
iban: IT12L0542811101309953525271 # IbanType, string, pattern …, length 27 [1]
|
|
89
|
+
dataDa: 2026-01-15 # date [1]
|
|
90
|
+
dataA: 2026-01-15 # date [1]
|
|
91
|
+
paginazione: # PaginazioneRichiesta [1]
|
|
92
|
+
numeroPagina: 1 # int [1]
|
|
93
|
+
dimensionePagina: 1 # int, 1..100 [1]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Required fields come filled with values that satisfy the schema: identifiers
|
|
97
|
+
already seen in earlier (masked) samples first, then checksummed example
|
|
98
|
+
IBANs and codici fiscali, enumeration values, and strings built from the XSD
|
|
99
|
+
patterns. Optional fields and choice alternatives are commented out. Every
|
|
100
|
+
value is a string, sent exactly as written; `~` sends `xsi:nil`. A raw
|
|
101
|
+
`<name>.request.xml` (the body element or a whole envelope, with an optional
|
|
102
|
+
`<!-- purpose: … -->`) is accepted as a second form and validated the same way.
|
|
103
|
+
|
|
104
|
+
`call` never sends a request that does not validate; it lists what to fix.
|
|
105
|
+
It records `<name>.exchange.json` beside the request: the masked request
|
|
106
|
+
and response envelopes without security headers, safe HTTP headers, status,
|
|
107
|
+
latency, any SOAP fault, and every place the response breaks its schema
|
|
108
|
+
(values outside an enumeration, undeclared elements), which are findings for
|
|
109
|
+
the design rather than errors. `--keep-raw` also writes the unmasked response
|
|
110
|
+
to the gitignored `raw/`, never shown to a model.
|
|
111
|
+
|
|
112
|
+
`propose` asks the reasoning seat (`LLM4TS_REASONER`, default `claude`) for
|
|
113
|
+
three to six scenarios (happy path, empty result, pagination boundary, a
|
|
114
|
+
business error, a schema edge) and writes each as a request file, listing
|
|
115
|
+
at its top whatever does not validate. `import` reads the requests saved in a
|
|
116
|
+
SoapUI or ReadyAPI project (interface calls and test steps) and its mock
|
|
117
|
+
responses; stored credentials are counted and ignored, and everything is
|
|
118
|
+
masked before it is written. `LLM4TS_SOAP_STUB=<dir>` answers calls from
|
|
119
|
+
`<dir>/<operation>.xml` instead of the network, for rehearsals; the fixture's
|
|
120
|
+
`responses/` directory is one.
|
|
121
|
+
|
|
122
|
+
## soap-design
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
llm4ts run soap-design --repo . "analyse" # evidence per operation (no model)
|
|
126
|
+
llm4ts run soap-design --repo . mapping # the 1:1 XSD ↔ JSON mapping (no model)
|
|
127
|
+
llm4ts run soap-design --repo . design # the reasoning seat drafts design/api-design.md
|
|
128
|
+
llm4ts run soap-design --repo . check # deterministic review; rerun after every edit
|
|
129
|
+
llm4ts run soap-design --repo . revise # redraft against the check's findings
|
|
130
|
+
llm4ts run soap-design --repo . openapi # design/openapi.yaml from an approved design
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### analyse
|
|
134
|
+
|
|
135
|
+
Reads the recorded exchanges (calls and SoapUI mocks) and writes
|
|
136
|
+
`analysis/<operation>.md` with a typed `analysis/<operation>.json` beside it.
|
|
137
|
+
No model is involved; the report is the evidence the REST design must cite:
|
|
138
|
+
|
|
139
|
+
- **observations**: values outside a declared enumeration, undeclared code
|
|
140
|
+
lists, required fields that are always empty, optional ones always present,
|
|
141
|
+
`xsi:nil` usage, business errors carried inside successful responses,
|
|
142
|
+
faults, and how many declared fields were never observed;
|
|
143
|
+
- **business outcome codes** (`esito.codice` and similar) with their
|
|
144
|
+
descriptions and the samples that produced them;
|
|
145
|
+
- **SOAP faults** by code, detail element, and reason;
|
|
146
|
+
- **pagination**: page-number, offset, or cursor style, from the request and
|
|
147
|
+
response field names, plus what the samples showed (last-page flags, most
|
|
148
|
+
items per response);
|
|
149
|
+
- **response fields**: presence, emptiness and nil counts, and the masked
|
|
150
|
+
values seen (most frequent first);
|
|
151
|
+
- **schema findings**: every place a response broke its WSDL;
|
|
152
|
+
- fields **never observed**, optional **request fields no sample used**,
|
|
153
|
+
and **latency** across real calls.
|
|
154
|
+
|
|
155
|
+
### mapping, design, check, openapi
|
|
156
|
+
|
|
157
|
+
`mapping` writes `design/mapping.json` and `mapping.md`: every request and
|
|
158
|
+
response field with the JSON path and type a literal facade would use
|
|
159
|
+
(decimals stay strings). It is the traceability base: the only paths a
|
|
160
|
+
design may cite.
|
|
161
|
+
|
|
162
|
+
`design` asks the reasoning seat (`LLM4TS_REASONER`, default `claude`) for a
|
|
163
|
+
resource-oriented design, giving it the style guide as hard rules, the
|
|
164
|
+
confirmed classes, the mapping, the named XSD types, and the analysis
|
|
165
|
+
evidence. The draft is written to `design/api-design.md` under
|
|
166
|
+
`Status: proposed`, with the check's findings above the design of record, a
|
|
167
|
+
` ```json apidesign ` block:
|
|
168
|
+
|
|
169
|
+
- **endpoints** name their SOAP `sources`, parameters cite request paths,
|
|
170
|
+
responses name a model and the response path it comes from (`list`/`paged`
|
|
171
|
+
for collections), **errors** list the outcome codes and fault elements they
|
|
172
|
+
map, and **evidence** cites the analysis;
|
|
173
|
+
- **models** bind to an XSD element or named type (`sourceType`); each
|
|
174
|
+
property cites a path within it (`saldo.valore`) or a `derivation`, and an
|
|
175
|
+
`enumMap` translates SOAP values to REST values;
|
|
176
|
+
- **excluded** lists operations deliberately not exposed, with reasons.
|
|
177
|
+
|
|
178
|
+
`check` is the deterministic reviewer, run on every draft and after every
|
|
179
|
+
edit. Errors: an operation neither exposed nor excluded, `GET` over a
|
|
180
|
+
mutating operation, a source path that is not in the XSD, a model rendered
|
|
181
|
+
from a type it is not bound to, an `enumMap` missing a declared value or a
|
|
182
|
+
value the samples showed (`SOSPESO`), and style violations (paths, names,
|
|
183
|
+
`esito` in payloads, problem codes). Warnings: REST promising a field the
|
|
184
|
+
XSD leaves optional, business errors or faults seen in samples but not
|
|
185
|
+
mapped, unused models. `revise` hands the findings back to the seat.
|
|
186
|
+
|
|
187
|
+
The file is the approval: edit the JSON block, rerun `check`, and set
|
|
188
|
+
`Status: approved`. `design` never overwrites it. `openapi` refuses a design
|
|
189
|
+
that is not approved or still has errors, then projects `design/openapi.yaml`
|
|
190
|
+
(OpenAPI 3.1): component schemas per model, `{ items, page }` wrappers for
|
|
191
|
+
collections, RFC 9457 problems per error status with their codes
|
|
192
|
+
(`x-problem-codes`), a `Location` header on creations, a default `502`, and
|
|
193
|
+
`x-soap-*` extensions tracing every operation, parameter, and property to the
|
|
194
|
+
SOAP service. Nobody edits it; the ACE pack implements it. The fixture's
|
|
195
|
+
[`design/api-design.md`](fixtures/demo-bank-soap/design/api-design.md) is a
|
|
196
|
+
reviewed example.
|
|
197
|
+
|
|
198
|
+
## soap-epic
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
llm4ts run soap-epic --repo . doctor # ACE 12 usable here?
|
|
202
|
+
llm4ts run soap-epic --repo . -- --target ~/work/demo-bank-ace plan # seed + story plan
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`plan` takes an approved design with no check errors and prepares the ACE
|
|
206
|
+
repository (`--target`, a git repository):
|
|
207
|
+
|
|
208
|
+
- **regenerated every run**: `contracts/` (the design, `openapi.yaml` 3.1,
|
|
209
|
+
`openapi-ace.yaml` 3.0.3 because ACE 12 imports OpenAPI 3.0, the 1:1
|
|
210
|
+
mapping, the WSDL and XSDs re-encoded as UTF-8), `test-data/` (the masked
|
|
211
|
+
exchanges the backend stubs are built from), `docs/analysis/`, and
|
|
212
|
+
`docs/patterns/` (the ESQL pattern cards);
|
|
213
|
+
- **created once, then the team's**: the
|
|
214
|
+
[`ace12-rest-api`](scaffolds/ace12-rest-api) scaffold: README,
|
|
215
|
+
`CONTRIBUTING.md` (house rules every coder reads), and
|
|
216
|
+
`scripts/ace-gates.sh build | test` (`ibmint package`; `ibmint deploy` to a
|
|
217
|
+
scratch work directory and `IntegrationServer --test-project`, after
|
|
218
|
+
sourcing `mqsiprofile`), plus a container variant.
|
|
219
|
+
|
|
220
|
+
It then derives the story plan, not generates it: `api-skeleton` (REST API
|
|
221
|
+
project descriptors and main flow), `policies` (dev/test/uat endpoints and
|
|
222
|
+
security), `shared-lib` (WSDL, backend call, error mapping), `backend-stub`
|
|
223
|
+
(test project and stubs), one `resource-<name>` story per resource owning its
|
|
224
|
+
broker schema folder and tests, and `contract-check`. The plan is saved where
|
|
225
|
+
the engine's `epic-stories` flow looks for it
|
|
226
|
+
(`.llm4ts/epics/<epic-id>/plan.md`, same epic id), so that flow executes it
|
|
227
|
+
unchanged: parallel stories in worktrees (concurrency 2), the ACE gates,
|
|
228
|
+
the judge, the board, branches only. An existing plan is kept; editing it is
|
|
229
|
+
the re-plan. `plan` prints the exact `epic-stories` command.
|
|
230
|
+
|
|
231
|
+
The [`ace12-rest`](packs/ace12-rest/pack.md) pack names the same gates and
|
|
232
|
+
the judge's rubric (contract, mapping, errors); its prompt and reviewer
|
|
233
|
+
sidecars carry the same rules as `CONTRIBUTING.md`.
|
|
234
|
+
|
|
235
|
+
## Auth profile
|
|
236
|
+
|
|
237
|
+
`.llm4ts/soap/<service>/auth.json` is gitignored (discovery writes the
|
|
238
|
+
directory's `.gitignore`) and holds **references only**: `env:NAME` or
|
|
239
|
+
`file:path`. A literal where a secret belongs is refused when the profile is
|
|
240
|
+
read, and errors name the reference, never its value.
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"environment": "uat",
|
|
245
|
+
"endpoint": "https://esb-uat.bank.internal/soap/DemoBank",
|
|
246
|
+
"fetch": { "auth": { "scheme": "basic", "user": "svc-wsdl", "password": "env:WSDL_PASSWORD" } },
|
|
247
|
+
"call": {
|
|
248
|
+
"auth": { "scheme": "bearer", "token": "env:SOAP_TOKEN" },
|
|
249
|
+
"tls": {
|
|
250
|
+
"pfx": "file:/secure/client.p12",
|
|
251
|
+
"passphrase": "env:P12_PASS",
|
|
252
|
+
"ca": "file:/secure/ca.pem"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
"wsSecurity": { "user": "env:WS_USER", "password": "env:WS_PASSWORD", "passwordType": "digest" },
|
|
256
|
+
"mutating": { "uat": "deny" }
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
- `environment` must be `dev`, `test`, or `uat`; nothing else is accepted.
|
|
261
|
+
- `fetch` and `call` are configured separately: `auth` is `none`, `basic`, or
|
|
262
|
+
`bearer`; `tls` is `cert`+`key` (PEM) or `pfx`+`passphrase`, plus an
|
|
263
|
+
optional `ca`.
|
|
264
|
+
- `wsSecurity` adds a WS-Security `UsernameToken` (`text` or `digest`) to
|
|
265
|
+
calls.
|
|
266
|
+
- Operations confirmed as `mutating` in `operations.md` follow the
|
|
267
|
+
environment's policy: `confirm` (default in dev) asks, `flag` (default in
|
|
268
|
+
test and uat) needs `--allow-mutating <operation>`, `deny` refuses.
|
|
269
|
+
Unclassified operations are never called.
|
|
270
|
+
|
|
271
|
+
Persisted exchanges drop `wsse:Security` headers and credential-bearing HTTP
|
|
272
|
+
headers (`authorization`, cookies, anything named like a token or secret).
|
|
273
|
+
|
|
274
|
+
## Masking
|
|
275
|
+
|
|
276
|
+
Every SOAP sample is masked before it is written anywhere or shown to a
|
|
277
|
+
model; there is no switch. Personal data is found two ways:
|
|
278
|
+
|
|
279
|
+
- **by field**: element and attribute names (Italian and English words:
|
|
280
|
+
`iban`, `codiceFiscale`, `partitaIva`, `numeroCarta`, `intestatario`,
|
|
281
|
+
`indirizzo`, `dataNascita`, `codiceOtp`, ...) and the XSD type a field is
|
|
282
|
+
declared with (`IbanType`, `CodiceFiscaleType`);
|
|
283
|
+
- **by value**, anywhere in text: IBANs (mod-97), codici fiscali (check
|
|
284
|
+
letter), PANs (Luhn, issuer range), partite IVA after `IT`, emails, and
|
|
285
|
+
`+39` phone numbers.
|
|
286
|
+
|
|
287
|
+
Replacements are keyed HMAC pseudonyms that keep the format and stay valid:
|
|
288
|
+
an Italian IBAN keeps its ABI/CAB and gets a correct CIN and check digits, a
|
|
289
|
+
codice fiscale keeps the holder's sex and a valid check letter, a PAN keeps
|
|
290
|
+
its BIN and Luhn digit, a partita IVA its office code. One key per service
|
|
291
|
+
maps the same input to the same pseudonym everywhere, so relationships
|
|
292
|
+
between request and response survive. Free-text fields (`causale`,
|
|
293
|
+
`descrizione`) are only scanned for identifiers; a name written inside them
|
|
294
|
+
needs a `mask` override in `masking.json` (`{ "fields": { "causale": "mask" } }`),
|
|
295
|
+
which also accepts `keep` for a field wrongly caught by a name rule. Every
|
|
296
|
+
replacement is reported by path, kind, and reason.
|
|
297
|
+
|
|
298
|
+
## Layout
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
soap-ace/
|
|
302
|
+
README.md
|
|
303
|
+
flows/soap-discover.ts catalog + open questions + proposed operation classes
|
|
304
|
+
flows/soap-sample.ts request files, calls, SoapUI import, scenario proposals
|
|
305
|
+
flows/soap-design.ts analysis, 1:1 mapping, design draft and check, OpenAPI projection
|
|
306
|
+
flows/soap-epic.ts ACE repository seed and the story plan epic-stories runs
|
|
307
|
+
packs/ace12-rest/ gates, judge rubric, prompts, and api-style.md (the REST style guide)
|
|
308
|
+
scaffolds/ace12-rest-api/ README, CONTRIBUTING.md, gate scripts for the ACE repository
|
|
309
|
+
patterns/ ESQL pattern cards (esito, faults, amounts and enums, paging, nil)
|
|
310
|
+
flows/lib/soap/ the SOAP library (XML, WSDL/XSD, auth, transport, masking, samples)
|
|
311
|
+
fixtures/demo-bank-soap/ synthetic service (WSDL/XSD, responses, SoapUI project, reference design) and the RUNBOOK
|
|
312
|
+
test/
|
|
313
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Runbook: from a WSDL to ACE 12 branches
|
|
2
|
+
|
|
3
|
+
A developer laptop with Node 22+, llm4ts, a Claude (or other) CLI for the
|
|
4
|
+
reasoning seat, and IBM ACE 12 installed. Rehearse it first on the
|
|
5
|
+
synthetic fixture (every command below works against it with the stub), then
|
|
6
|
+
on a real service in dev, test, or uat, never production.
|
|
7
|
+
|
|
8
|
+
## 0. Two repositories
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
mkdir -p ~/work/conti-analysis ~/work/conti-ace
|
|
12
|
+
git -C ~/work/conti-analysis init && git -C ~/work/conti-ace init
|
|
13
|
+
cd ~/work/conti-analysis
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The analysis repository holds `.llm4ts/soap/<service>/`: catalog, samples,
|
|
17
|
+
analysis, design. The ACE repository receives the contracts and the code.
|
|
18
|
+
|
|
19
|
+
## 1. Discover
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
llm4ts run soap-discover --repo . ./wsdl/ContiService.wsdl # or an https URL
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Read `catalog.md`, especially the open questions. Review
|
|
26
|
+
`operations.md`: every `class:` is `read` or `mutating`; set
|
|
27
|
+
`Status: confirmed`. For a URL behind auth, write `auth.json` first (below)
|
|
28
|
+
and set `LLM4TS_SOAP_SERVICE`.
|
|
29
|
+
|
|
30
|
+
## 2. Credentials and environment
|
|
31
|
+
|
|
32
|
+
`.llm4ts/soap/<service>/auth.json` (gitignored), references only:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"environment": "test",
|
|
37
|
+
"endpoint": "https://esb-test.bank.internal/soap/Conti",
|
|
38
|
+
"call": {
|
|
39
|
+
"auth": { "scheme": "basic", "user": "svc-conti", "password": "env:CONTI_PASSWORD" },
|
|
40
|
+
"tls": { "pfx": "file:/secure/client.p12", "passphrase": "env:P12_PASS" }
|
|
41
|
+
},
|
|
42
|
+
"wsSecurity": { "user": "env:WS_USER", "password": "env:WS_PASSWORD", "passwordType": "digest" }
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 3. Samples
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
llm4ts run soap-sample --repo . "import ./ContiService-soapui-project.xml" # if the team has one
|
|
50
|
+
llm4ts run soap-sample --repo . "propose cercaMovimenti" # scenario drafts
|
|
51
|
+
llm4ts run soap-sample --repo . "init dettaglioConto" # or write one by hand
|
|
52
|
+
llm4ts run soap-sample --repo . "call all"
|
|
53
|
+
llm4ts run soap-sample --repo . -- --allow-mutating revocaBonifico "call revocaBonifico/gia-eseguito"
|
|
54
|
+
llm4ts run soap-sample --repo . list
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Replace seeded identifiers with ones the test environment knows before
|
|
58
|
+
calling. Rehearsal without a backend: `LLM4TS_SOAP_STUB=<dir of <operation>.xml>`.
|
|
59
|
+
|
|
60
|
+
## 4. Analyse and design
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
llm4ts run soap-design --repo . analyse # read analysis/*.md: this is the evidence
|
|
64
|
+
llm4ts run soap-design --repo . design # draft design/api-design.md
|
|
65
|
+
llm4ts run soap-design --repo . check # edit the JSON block, re-check, repeat
|
|
66
|
+
llm4ts run soap-design --repo . revise # optional: let the seat fix what check found
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Set `Status: approved` when the check reports no errors and the warnings are
|
|
70
|
+
understood; then `llm4ts run soap-design --repo . openapi`.
|
|
71
|
+
|
|
72
|
+
## 5. Plan the ACE build
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
llm4ts run soap-epic --repo . doctor
|
|
76
|
+
llm4ts run soap-epic --repo . -- --target ~/work/conti-ace plan
|
|
77
|
+
git -C ~/work/conti-ace add -A && git -C ~/work/conti-ace commit -m "Seed from soap-ace"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Review `.llm4ts/epics/<epic-id>/plan.md` in the ACE repository (edit it to
|
|
81
|
+
re-plan), then run the command `plan` printed:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
LLM4TS_GATES="bash scripts/ace-gates.sh build;bash scripts/ace-gates.sh test" LLM4TS_WORKTREE_SETUP= \
|
|
85
|
+
llm4ts run epic-stories --repo ~/work/conti-ace -- --concurrency 2 "<the epic sentence>"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Each story lands on its own branch and merges into `epic/<epic-id>` after its
|
|
89
|
+
gates and judge pass; nothing reaches `main` without a human. The board and
|
|
90
|
+
report live under `.llm4ts/epics/<epic-id>/`. Azure DevOps mirroring is the
|
|
91
|
+
optional board of ADR 0011.
|
|
92
|
+
|
|
93
|
+
## When something is off
|
|
94
|
+
|
|
95
|
+
- `ace-gates.sh` cannot find `mqsiprofile`: set `MQSI_PROFILE` or `ACE_HOME`;
|
|
96
|
+
on a machine without ACE use `scripts/ace-gates-container.sh` with
|
|
97
|
+
`ACE_IMAGE`.
|
|
98
|
+
- Your ACE fix pack spells a command differently: override the whole gate
|
|
99
|
+
with `LLM4TS_GATES`.
|
|
100
|
+
- The service changed: rerun discover, sample, analyse, and `check`; the
|
|
101
|
+
design check points at every citation that broke. `soap-epic plan`
|
|
102
|
+
regenerates contracts and keeps the team's files.
|