@llm4ts/shell 2.12.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 +9 -3
- package/flows/lib/epic-stories.js +24 -4
- 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, appDirFor, gateCommands, gatesIn, inAppDir, generateStoryPlan, httpProbe, judgeStory, localCoderServer, parseEpicArgs, reasonerFromEnvironment, serverHealthUrl, setupAgentEnabled, 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();
|
|
@@ -100,6 +100,12 @@ const program = Effect.gen(function* () {
|
|
|
100
100
|
if (appDir !== ".") {
|
|
101
101
|
yield* events.publish(Info.make({ message: `app dir: ${appDir} (setup and gates run there; LLM4TS_APP_DIR)` }));
|
|
102
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
|
+
}));
|
|
103
109
|
if (flags.land !== undefined) {
|
|
104
110
|
const landed = yield* landEpic(context, {
|
|
105
111
|
plan,
|
|
@@ -107,7 +113,7 @@ const program = Effect.gen(function* () {
|
|
|
107
113
|
stateDir,
|
|
108
114
|
target: flags.land,
|
|
109
115
|
keepWorktrees: flags.keepWorktrees,
|
|
110
|
-
gates: inAppDir(appDir, gatesIn(nodeProcessExecutor, events,
|
|
116
|
+
gates: inAppDir(appDir, gatesIn(nodeProcessExecutor, events, commands)),
|
|
111
117
|
system: ["House rules of the target repository (CONTRIBUTING.md):", guidance].join("\n")
|
|
112
118
|
});
|
|
113
119
|
yield* events.publish(Info.make({
|
|
@@ -136,7 +142,7 @@ const program = Effect.gen(function* () {
|
|
|
136
142
|
: [
|
|
137
143
|
`The application lives in ${appDir}/ — its package.json, sources and tests; the gates run there.`
|
|
138
144
|
];
|
|
139
|
-
const gates = inAppDir(appDir, gatesIn(nodeProcessExecutor, events,
|
|
145
|
+
const gates = inAppDir(appDir, gatesIn(nodeProcessExecutor, events, commands));
|
|
140
146
|
const setupCommand = worktreeSetupCommand(process.env);
|
|
141
147
|
const healthUrl = serverHealthUrl(localServer, process.env);
|
|
142
148
|
const report = yield* implementStoriesFlow({ ...context, reasoning: reasoningMeter.service }, {
|
|
@@ -5,6 +5,7 @@ import { readdir } from "node:fs/promises";
|
|
|
5
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,7 +35,8 @@ 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;",
|
|
40
42
|
" LLM4TS_APP_DIR (e.g. frontend) runs setup and gates in that subfolder; unset, a",
|
|
@@ -625,11 +627,29 @@ export const defaultGateCommands = [
|
|
|
625
627
|
["pnpm", "test"],
|
|
626
628
|
["pnpm", "build"]
|
|
627
629
|
];
|
|
628
|
-
/**
|
|
629
|
-
|
|
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) => {
|
|
630
648
|
const raw = environment.LLM4TS_GATES?.trim();
|
|
631
649
|
if (raw === undefined || raw.length === 0) {
|
|
632
|
-
return
|
|
650
|
+
return scripts === undefined
|
|
651
|
+
? defaultGateCommands
|
|
652
|
+
: defaultGateCommands.filter((command) => scripts.has(command[1] ?? ""));
|
|
633
653
|
}
|
|
634
654
|
return raw
|
|
635
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.
|