@techspokes/typescript-wsdl-client 0.35.0 → 0.36.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.
@@ -0,0 +1,930 @@
1
+ # xs:anyAttribute Wildcard Bag Implementation Plan
2
+
3
+ ## Agentic Worker Requirement
4
+
5
+ REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
6
+
7
+ ## Goal
8
+
9
+ Emit `xs:anyAttribute` wildcard attributes as a typed attribute-bag property so the capability can move from partial to supported before `1.0.0`.
10
+
11
+ ## Architecture
12
+
13
+ Keep known XML attributes flattened as peer DTO properties. Emit wildcard attributes as one optional bag property named by `compiled.options.attributesKey`, defaulting to `$attributes`, because unknown attribute names can collide with child elements or known attributes. Reuse the generated client's existing input attribute-bag serialization path, and add TypeScript, OpenAPI, mock-data, conformance, and documentation evidence around that shape.
14
+
15
+ ## Tech Stack
16
+
17
+ TypeScript strict mode, ESM, Node.js 24+, Vitest, OpenAPI 3.1 JSON Schema, Fastify generated gateway tests, existing conformance registry under `test/conformance/`.
18
+
19
+ ## Current Evidence
20
+
21
+ Known attributes are emitted as peer TypeScript properties in `src/client/generateTypes.ts` and peer OpenAPI object properties in `src/openapi/generateSchemas.ts`. The generated client already serializes an input attribute bag from `attributesKeyIn`, default `$attributes`, into node-soap attributes in `src/client/generateClient.ts`.
22
+
23
+ The compiler already retains `attributeWildcards` on compiled types in `src/compiler/schemaCompiler.ts`. The conformance row `xs-anyattribute` currently proves only metadata retention and asserts that generated TypeScript, OpenAPI, and gateway schemas do not expose a wildcard bag.
24
+
25
+ ## Target Contract
26
+
27
+ For a compiled type with `attributeWildcards`, generated TypeScript must include this property, using the configured attribute key:
28
+
29
+ ```text
30
+ /** @xsd {"kind":"attributeWildcard","type":"xs:anyAttribute"} */
31
+ "$attributes"?: Record<string, string>;
32
+ ```
33
+
34
+ OpenAPI must include the same property as an object of string values:
35
+
36
+ ```json
37
+ {
38
+ "type": "object",
39
+ "additionalProperties": {
40
+ "type": "string"
41
+ }
42
+ }
43
+ ```
44
+
45
+ Generated mock data should include a minimal deterministic sample bag so generated tests exercise the path:
46
+
47
+ ```text
48
+ {
49
+ "$attributes": {
50
+ "extra:trace": "sample"
51
+ }
52
+ }
53
+ ```
54
+
55
+ Do not flatten wildcard attributes as peer properties. Do not loosen the entire schema with `additionalProperties: true`. Do not add a new CLI flag.
56
+
57
+ ## Files
58
+
59
+ Modify `src/compiler/schemaCompiler.ts` to add `localAttributeWildcards` for complex-content extensions.
60
+
61
+ Modify `src/compiler/shapeResolver.ts` to preserve `localAttributeWildcards` during canonicalization.
62
+
63
+ Create `src/util/attributeWildcards.ts` to centralize the default bag name and schema helper.
64
+
65
+ Modify `src/client/generateTypes.ts` to emit the wildcard bag in generated interfaces.
66
+
67
+ Modify `src/openapi/generateSchemas.ts` to emit the wildcard bag in component schemas.
68
+
69
+ Modify `src/test/mockData.ts` to generate deterministic wildcard-bag sample data.
70
+
71
+ Modify `test/unit/mock-data.test.ts` to cover generated mock data for `attributeWildcards`.
72
+
73
+ Modify `test/unit/compiler-wildcards.test.ts` to cover `localAttributeWildcards` on extension types.
74
+
75
+ Modify `test/conformance/registry.ts` to promote `xs-anyattribute` to supported and assert the new downstream contract.
76
+
77
+ Modify generated support docs through `npm run docs:support-matrix`, then manually update `docs/supported-patterns.md`, `docs/roadmap/v1.0-wsdl-coverage-matrix.md`, `docs/roadmap/v1.0-capability-conformance-framework.md`, and `CHANGELOG.md`.
78
+
79
+ ## Task 1: Add Failing Unit Tests For Compiler And Mock Data
80
+
81
+ ### Files
82
+
83
+ - Modify: `test/unit/compiler-wildcards.test.ts`
84
+ - Modify: `test/unit/mock-data.test.ts`
85
+
86
+ - [ ] Step 1: Add a compiler test for local extension wildcard metadata
87
+
88
+ Append this test inside the existing `describe("compiler: wildcard retention", () => { ... })` block in `test/unit/compiler-wildcards.test.ts`:
89
+
90
+ ```text
91
+ it("retains local xs:anyAttribute metadata separately on complexContent extensions", async () => {
92
+ const schema = `
93
+ <xs:complexType name="BaseWithWildcard">
94
+ <xs:sequence>
95
+ <xs:element name="baseValue" type="xs:string"/>
96
+ </xs:sequence>
97
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
98
+ </xs:complexType>
99
+ <xs:complexType name="ExtendedWithWildcard">
100
+ <xs:complexContent>
101
+ <xs:extension base="tns:BaseWithWildcard">
102
+ <xs:sequence>
103
+ <xs:element name="childValue" type="xs:string"/>
104
+ </xs:sequence>
105
+ <xs:anyAttribute namespace="##any" processContents="skip"/>
106
+ </xs:extension>
107
+ </xs:complexContent>
108
+ </xs:complexType>
109
+ <xs:element name="StreamRequest" type="tns:ExtendedWithWildcard"/>
110
+ <xs:element name="StreamResponse" type="xs:string"/>`;
111
+ const compiled = await compileFromFixture(buildWsdl(schema), "extension-anyattribute");
112
+ const base = compiled.types.find((t) => t.name === "BaseWithWildcard");
113
+ const child = compiled.types.find((t) => t.name === "ExtendedWithWildcard");
114
+
115
+ expect(base?.attributeWildcards).toEqual([
116
+ {namespace: "##other", processContents: "lax"},
117
+ ]);
118
+ expect((base as any).localAttributeWildcards).toBeUndefined();
119
+ expect(child?.attributeWildcards).toEqual([
120
+ {namespace: "##other", processContents: "lax"},
121
+ {namespace: "##any", processContents: "skip"},
122
+ ]);
123
+ expect((child as any).localAttributeWildcards).toEqual([
124
+ {namespace: "##any", processContents: "skip"},
125
+ ]);
126
+ });
127
+ ```
128
+
129
+ - [ ] Step 2: Add mock-data tests for wildcard bags
130
+
131
+ Add these tests after `it("generates attribute-only types (no elements)", ... )` in `test/unit/mock-data.test.ts`:
132
+
133
+ ```text
134
+ it("includes the default wildcard attribute bag for types with xs:anyAttribute", () => {
135
+ const catalog: CatalogForMocks = {
136
+ options: {
137
+ attributesKey: "$attributes",
138
+ },
139
+ meta: {
140
+ childType: {
141
+ AnyAttributeRequest: {value: "string"},
142
+ },
143
+ propMeta: {},
144
+ },
145
+ types: [
146
+ {
147
+ name: "AnyAttributeRequest",
148
+ attrs: [],
149
+ elems: [{name: "value", max: 1}],
150
+ attributeWildcards: [{namespace: "##other", processContents: "lax"}],
151
+ },
152
+ ],
153
+ };
154
+ const result = generateMockData("AnyAttributeRequest", catalog);
155
+ expect(result).toMatchObject({
156
+ value: "sample",
157
+ $attributes: {
158
+ "extra:trace": "sample",
159
+ },
160
+ });
161
+ });
162
+
163
+ it("uses the configured attributesKey for wildcard attribute bags", () => {
164
+ const catalog: CatalogForMocks = {
165
+ options: {
166
+ attributesKey: "@@attrs",
167
+ },
168
+ meta: {
169
+ childType: {
170
+ AnyAttributeRequest: {value: "string"},
171
+ },
172
+ propMeta: {},
173
+ },
174
+ types: [
175
+ {
176
+ name: "AnyAttributeRequest",
177
+ attrs: [],
178
+ elems: [{name: "value", max: 1}],
179
+ attributeWildcards: [{namespace: "##any"}],
180
+ },
181
+ ],
182
+ };
183
+ const result = generateMockData("AnyAttributeRequest", catalog);
184
+ expect(result).toMatchObject({
185
+ value: "sample",
186
+ "@@attrs": {
187
+ "extra:trace": "sample",
188
+ },
189
+ });
190
+ });
191
+ ```
192
+
193
+ - [ ] Step 3: Run the focused tests and verify they fail
194
+
195
+ Run:
196
+
197
+ ```bash
198
+ npx vitest run test/unit/compiler-wildcards.test.ts test/unit/mock-data.test.ts
199
+ ```
200
+
201
+ Expected result: the new compiler test fails because `localAttributeWildcards` is missing, and the new mock-data tests fail at TypeScript compile time or runtime because `CatalogForMocks` does not expose `options.attributesKey` or `types[].attributeWildcards`.
202
+
203
+ ## Task 2: Add Shared Wildcard Attribute Helpers
204
+
205
+ ### Files
206
+
207
+ - Create: `src/util/attributeWildcards.ts`
208
+
209
+ - [ ] Step 1: Create the helper module
210
+
211
+ Create `src/util/attributeWildcards.ts`:
212
+
213
+ ```text
214
+ export const DEFAULT_ATTRIBUTE_BAG_KEY = "$attributes";
215
+
216
+ export interface AttributeWildcardCarrier {
217
+ attributeWildcards?: readonly unknown[];
218
+ }
219
+
220
+ export interface AttributeWildcardOptions {
221
+ attributesKey?: string;
222
+ }
223
+
224
+ export function hasAttributeWildcards(value: AttributeWildcardCarrier | undefined): boolean {
225
+ return Array.isArray(value?.attributeWildcards) && value.attributeWildcards.length > 0;
226
+ }
227
+
228
+ export function wildcardAttributeBagName(options: AttributeWildcardOptions | undefined): string {
229
+ const configured = options?.attributesKey?.trim();
230
+ return configured && configured.length > 0 ? configured : DEFAULT_ATTRIBUTE_BAG_KEY;
231
+ }
232
+
233
+ export function wildcardAttributeBagSchema(): {type: "object"; additionalProperties: {type: "string"}} {
234
+ return {
235
+ type: "object",
236
+ additionalProperties: {type: "string"},
237
+ };
238
+ }
239
+ ```
240
+
241
+ - [ ] Step 2: Run typecheck and verify the helper compiles
242
+
243
+ Run:
244
+
245
+ ```bash
246
+ npm run typecheck
247
+ ```
248
+
249
+ Expected result: the command still fails because Task 1 intentionally introduced failing tests, or passes if tests are not part of the production TypeScript project. There must be no syntax error reported for `src/util/attributeWildcards.ts`.
250
+
251
+ ## Task 3: Preserve Local Attribute Wildcards In Compiler Metadata
252
+
253
+ ### Files
254
+
255
+ - Modify: `src/compiler/schemaCompiler.ts`
256
+ - Modify: `src/compiler/shapeResolver.ts`
257
+
258
+ - [ ] Step 1: Extend `CompiledType` with local wildcard metadata
259
+
260
+ In `src/compiler/schemaCompiler.ts`, add this documentation line after the `localElems` JSDoc line:
261
+
262
+ ```text
263
+ * @property {Array<Object>} [localAttributeWildcards] - Attribute wildcards added in extension
264
+ ```
265
+
266
+ Add this property to the `CompiledType` type after `attributeWildcards?: CompiledAttributeWildcard[];`:
267
+
268
+ ```text
269
+ localAttributeWildcards?: CompiledAttributeWildcard[];
270
+ ```
271
+
272
+ - [ ] Step 2: Store local wildcard metadata on complex-content extensions
273
+
274
+ In the complex-content extension result object in `getOrCompileComplex`, replace:
275
+
276
+ ```text
277
+ ...(attributeWildcards.length > 0 ? {attributeWildcards} : {}),
278
+ ```
279
+
280
+ with:
281
+
282
+ ```text
283
+ ...(attributeWildcards.length > 0 ? {attributeWildcards} : {}),
284
+ ...(localAttributeWildcards.length > 0 ? {localAttributeWildcards} : {}),
285
+ ```
286
+
287
+ - [ ] Step 3: Preserve local wildcard metadata in shape canonicalization
288
+
289
+ In `src/compiler/shapeResolver.ts`, find the canonicalized type object that already maps `attributeWildcards`. Add this property immediately after it:
290
+
291
+ ```text
292
+ localAttributeWildcards: (t.localAttributeWildcards ?? []).map((w) => ({
293
+ ...(w.namespace ? {namespace: w.namespace} : {}),
294
+ ...(w.processContents ? {processContents: w.processContents} : {}),
295
+ })),
296
+ ```
297
+
298
+ If the surrounding function omits empty optional arrays for `wildcards` and `attributeWildcards`, use the same conditional style already present in that function. The final emitted catalog must not add `localAttributeWildcards` to types that do not have local wildcard declarations.
299
+
300
+ - [ ] Step 4: Run the compiler wildcard test
301
+
302
+ Run:
303
+
304
+ ```bash
305
+ npx vitest run test/unit/compiler-wildcards.test.ts
306
+ ```
307
+
308
+ Expected result: the new `localAttributeWildcards` test passes. Existing wildcard tests still pass.
309
+
310
+ ## Task 4: Emit Wildcard Attribute Bags In Generated TypeScript
311
+
312
+ ### Files
313
+
314
+ - Modify: `src/client/generateTypes.ts`
315
+
316
+ - [ ] Step 1: Import the shared helpers
317
+
318
+ Add this import near the existing imports in `src/client/generateTypes.ts`:
319
+
320
+ ```text
321
+ import {hasAttributeWildcards, wildcardAttributeBagName} from "../util/attributeWildcards.js";
322
+ ```
323
+
324
+ - [ ] Step 2: Add a bag-name constant inside `generateTypes`
325
+
326
+ After `const isChoiceUnionMode = compiled.options.choice === "union";`, add:
327
+
328
+ ```text
329
+ const wildcardBagName = wildcardAttributeBagName(compiled.options);
330
+ ```
331
+
332
+ - [ ] Step 3: Add an emitter for wildcard bags
333
+
334
+ After `emitAttributeProperty`, add:
335
+
336
+ ```text
337
+ const emitWildcardAttributeBagProperty = (indent: string) => {
338
+ const annObj = {
339
+ kind: "attributeWildcard" as const,
340
+ type: "xs:anyAttribute",
341
+ };
342
+ lines.push("");
343
+ lines.push(`${indent}/** @xsd ${JSON.stringify(annObj)} */`);
344
+ lines.push(`${indent}${emitPropName(wildcardBagName)}?: Record<string, string>;`);
345
+ };
346
+ ```
347
+
348
+ - [ ] Step 4: Compute local wildcard bags for normal and choice-mode interfaces
349
+
350
+ After the `attrsToEmit` line, add:
351
+
352
+ ```text
353
+ const wildcardCarrier = complexBase
354
+ ? {attributeWildcards: t.localAttributeWildcards}
355
+ : {attributeWildcards: t.attributeWildcards};
356
+ ```
357
+
358
+ - [ ] Step 5: Emit the bag in choice union base interfaces
359
+
360
+ In the choice-union branch, after the loop that emits attributes:
361
+
362
+ ```text
363
+ for (const a of attrsToEmit) {
364
+ emitAttributeProperty(" ", a);
365
+ }
366
+ ```
367
+
368
+ add:
369
+
370
+ ```text
371
+ if (hasAttributeWildcards(wildcardCarrier)) {
372
+ emitWildcardAttributeBagProperty(" ");
373
+ }
374
+ ```
375
+
376
+ - [ ] Step 6: Emit the bag in normal interfaces
377
+
378
+ In the normal interface branch, after the loop that emits attributes:
379
+
380
+ ```text
381
+ for (const a of attrsToEmit) {
382
+ emitAttributeProperty(" ", a);
383
+ }
384
+ ```
385
+
386
+ add:
387
+
388
+ ```text
389
+ if (hasAttributeWildcards(wildcardCarrier)) {
390
+ emitWildcardAttributeBagProperty(" ");
391
+ }
392
+ ```
393
+
394
+ - [ ] Step 7: Run snapshot tests to see the intentional diff
395
+
396
+ Run:
397
+
398
+ ```bash
399
+ npx vitest run test/snapshot
400
+ ```
401
+
402
+ Expected result: snapshots may fail only if the weather fixture contains `xs:anyAttribute`. If snapshots fail, inspect the diff and update only intentional generated type output later in Task 9.
403
+
404
+ ## Task 5: Emit Wildcard Attribute Bags In OpenAPI Schemas
405
+
406
+ ### Files
407
+
408
+ - Modify: `src/openapi/generateSchemas.ts`
409
+
410
+ - [ ] Step 1: Import the shared helpers
411
+
412
+ Add this import near the existing imports:
413
+
414
+ ```text
415
+ import {hasAttributeWildcards, wildcardAttributeBagName, wildcardAttributeBagSchema} from "../util/attributeWildcards.js";
416
+ ```
417
+
418
+ - [ ] Step 2: Add an `attributesKey` parameter to `buildComplexSchema`
419
+
420
+ Change the `buildComplexSchema` signature from:
421
+
422
+ ```text
423
+ function buildComplexSchema(
424
+ t: CompiledType,
425
+ closed: boolean,
426
+ knownTypeNames: Set<string>,
427
+ aliasNames: Set<string>,
428
+ flattenWrappers: boolean,
429
+ choiceUnionMode: boolean,
430
+ ): any {
431
+ ```
432
+
433
+ to:
434
+
435
+ ```text
436
+ function buildComplexSchema(
437
+ t: CompiledType,
438
+ closed: boolean,
439
+ knownTypeNames: Set<string>,
440
+ aliasNames: Set<string>,
441
+ flattenWrappers: boolean,
442
+ choiceUnionMode: boolean,
443
+ attributesKey: string,
444
+ ): any {
445
+ ```
446
+
447
+ - [ ] Step 3: Add the bag property after known attributes
448
+
449
+ After the `for (const a of t.attrs) { ... }` loop, add:
450
+
451
+ ```text
452
+ if (hasAttributeWildcards(t)) {
453
+ properties[attributesKey] = wildcardAttributeBagSchema();
454
+ }
455
+ ```
456
+
457
+ - [ ] Step 4: Pass the configured key from `generateSchemas`
458
+
459
+ In `generateSchemas`, add this constant after `const choiceUnionMode = ...` or near the other derived options:
460
+
461
+ ```text
462
+ const attributesKey = wildcardAttributeBagName(compiled.options);
463
+ ```
464
+
465
+ Update the call to `buildComplexSchema` so the final argument list includes `attributesKey`:
466
+
467
+ ```text
468
+ compiled.options.choice === "union",
469
+ attributesKey,
470
+ ```
471
+
472
+ - [ ] Step 5: Run OpenAPI unit and conformance tests enough to observe failure movement
473
+
474
+ Run:
475
+
476
+ ```bash
477
+ npx vitest run test/unit/openapi-choice-union.test.ts test/conformance/conformance.test.ts --runInBand
478
+ ```
479
+
480
+ Expected result: conformance still fails until Task 7 updates the registry assertions. OpenAPI generation must not throw for supported rows.
481
+
482
+ ## Task 6: Generate Wildcard Attribute Bags In Mock Data
483
+
484
+ ### Files
485
+
486
+ - Modify: `src/test/mockData.ts`
487
+ - Modify: `test/unit/mock-data.test.ts`
488
+
489
+ - [ ] Step 1: Import helpers
490
+
491
+ Add this import to `src/test/mockData.ts`:
492
+
493
+ ```text
494
+ import {hasAttributeWildcards, wildcardAttributeBagName} from "../util/attributeWildcards.js";
495
+ ```
496
+
497
+ - [ ] Step 2: Extend mock catalog types
498
+
499
+ Change the `CatalogForMocks` `options` type from:
500
+
501
+ ```text
502
+ options?: {
503
+ choice?: "all-optional" | "union";
504
+ };
505
+ ```
506
+
507
+ to:
508
+
509
+ ```text
510
+ options?: {
511
+ attributesKey?: string;
512
+ choice?: "all-optional" | "union";
513
+ };
514
+ ```
515
+
516
+ Change the `types` entry type from:
517
+
518
+ ```text
519
+ elems: Array<{ name: string; max: number | "unbounded" }>;
520
+ ```
521
+
522
+ to:
523
+
524
+ ```text
525
+ elems: Array<{ name: string; max: number | "unbounded" }>;
526
+ attributeWildcards?: Array<{namespace?: string; processContents?: "lax" | "strict" | "skip"}>;
527
+ ```
528
+
529
+ - [ ] Step 3: Keep wildcard-only types from returning early
530
+
531
+ After `const typeMeta = catalog.types?.find((t) => t.name === typeName);`, add:
532
+
533
+ ```text
534
+ const hasWildcardAttributes = hasAttributeWildcards(typeMeta);
535
+ ```
536
+
537
+ Move the existing early return below that line and change it to:
538
+
539
+ ```text
540
+ if ((!childTypes || Object.keys(childTypes).length === 0) && !attrTypes && !hasWildcardAttributes) {
541
+ return {};
542
+ }
543
+ ```
544
+
545
+ - [ ] Step 4: Add deterministic wildcard bag data
546
+
547
+ Before `return result;`, add:
548
+
549
+ ```text
550
+ if (hasWildcardAttributes) {
551
+ const attributesKey = wildcardAttributeBagName(catalog.options);
552
+ if (!(attributesKey in result)) {
553
+ result[attributesKey] = {
554
+ "extra:trace": "sample",
555
+ };
556
+ }
557
+ }
558
+ ```
559
+
560
+ - [ ] Step 5: Run mock-data tests
561
+
562
+ Run:
563
+
564
+ ```bash
565
+ npx vitest run test/unit/mock-data.test.ts
566
+ ```
567
+
568
+ Expected result: all mock-data tests pass, including the new wildcard attribute bag tests.
569
+
570
+ ## Task 7: Promote The Conformance Row To Supported
571
+
572
+ ### Files
573
+
574
+ - Modify: `test/conformance/registry.ts`
575
+
576
+ - [ ] Step 1: Change row status and contract
577
+
578
+ In the `xs-anyattribute` row, replace:
579
+
580
+ ```text
581
+ status: "partial",
582
+ ```
583
+
584
+ with:
585
+
586
+ ```text
587
+ status: "supported",
588
+ ```
589
+
590
+ Replace the current `publicContract` and `decisionReason` with:
591
+
592
+ ```text
593
+ publicContract: "`xs:anyAttribute` is retained as catalog metadata and emitted as an optional wildcard attribute bag named by the configured attributes key.",
594
+ decision: "support",
595
+ decisionReason: "The compiler retains attribute wildcard metadata, and generated TypeScript, OpenAPI, gateway, generated-test, and app artifacts can represent wildcard attributes through the existing attribute-bag runtime path.",
596
+ ```
597
+
598
+ - [ ] Step 2: Strengthen client expectations
599
+
600
+ Replace the `client` block for `xs-anyattribute` with:
601
+
602
+ ```text
603
+ client: {
604
+ outcome: "success",
605
+ sourceIncludes: [
606
+ {file: "types", text: "\"$attributes\"?: Record<string, string>;"},
607
+ ],
608
+ },
609
+ ```
610
+
611
+ - [ ] Step 3: Strengthen OpenAPI expectations
612
+
613
+ Replace the existing `openapi.assert` body with:
614
+
615
+ ```text
616
+ assert: ({doc}) => {
617
+ const schema = requireSchema(doc, "AnyAttributeRequest");
618
+ assertJsonEqual(
619
+ schema.properties?.$attributes,
620
+ {type: "object", additionalProperties: {type: "string"}},
621
+ "xs:anyAttribute should emit the configured wildcard attribute bag schema.",
622
+ );
623
+ if (schema.additionalProperties === true) {
624
+ throw new Error("xs:anyAttribute should not loosen the whole object schema.");
625
+ }
626
+ },
627
+ ```
628
+
629
+ - [ ] Step 4: Exercise the bag through the generated gateway
630
+
631
+ Change the gateway request payload from:
632
+
633
+ ```text
634
+ payload: {value: "known-only"},
635
+ ```
636
+
637
+ to:
638
+
639
+ ```text
640
+ payload: {value: "known-only", $attributes: {"extra:trace": "sample"}},
641
+ ```
642
+
643
+ Change the `assertClientArgs` expectation to:
644
+
645
+ ```text
646
+ assertClientArgs: args => assertJsonEqual(
647
+ args,
648
+ {value: "known-only", $attributes: {"extra:trace": "sample"}},
649
+ "Gateway should pass the modeled xs:anyAttribute bag to the client.",
650
+ ),
651
+ ```
652
+
653
+ Change the gateway schema assertion from rejecting `$attributes` to requiring it:
654
+
655
+ ```text
656
+ assert: ({readGatewayFile}) => {
657
+ const schemaSource = readGatewayFile("schemas/models/anyattributerequest.json");
658
+ if (!schemaSource.includes("\"$attributes\"")) {
659
+ throw new Error("xs:anyAttribute gateway schema should emit a wildcard attribute bag.");
660
+ }
661
+ },
662
+ ```
663
+
664
+ - [ ] Step 5: Strengthen generated-test expectations
665
+
666
+ Replace the `generatedTests` block with:
667
+
668
+ ```text
669
+ generatedTests: {
670
+ outcome: "success",
671
+ sourceIncludes: [
672
+ {file: "helpers/mock-client.ts", text: "\"$attributes\""},
673
+ {file: "helpers/mock-client.ts", text: "\"extra:trace\""},
674
+ ],
675
+ },
676
+ ```
677
+
678
+ - [ ] Step 6: Run conformance and verify failure points
679
+
680
+ Run:
681
+
682
+ ```bash
683
+ npm run test:conformance
684
+ ```
685
+
686
+ Expected result before Tasks 3 through 6 are complete: this fails on missing generated type, schema, gateway, or generated-test evidence. Expected result after Tasks 3 through 6 are complete: all conformance tests pass.
687
+
688
+ ## Task 8: Update Human-Authored Documentation
689
+
690
+ ### Files
691
+
692
+ - Modify: `docs/supported-patterns.md`
693
+ - Modify: `docs/roadmap/v1.0-wsdl-coverage-matrix.md`
694
+ - Modify: `docs/roadmap/v1.0-capability-conformance-framework.md`
695
+ - Modify: `CHANGELOG.md`
696
+
697
+ - [ ] Step 1: Regenerate the support matrix
698
+
699
+ Run:
700
+
701
+ ```bash
702
+ npm run docs:support-matrix
703
+ ```
704
+
705
+ Expected result: the generated matrix in `docs/supported-patterns.md` changes the `xs-anyattribute` status from `partial` to `supported` and uses the updated public contract.
706
+
707
+ - [ ] Step 2: Update the explanatory `xs:anyAttribute` section
708
+
709
+ In `docs/supported-patterns.md`, replace the `### xs:anyAttribute` paragraph with:
710
+
711
+ ```markdown
712
+ ### xs:anyAttribute
713
+
714
+ Attribute wildcards are retained as catalog metadata and emitted as an optional wildcard attribute bag on the enclosing generated type. The bag property uses the configured client attributes key, defaulting to `$attributes`, and maps arbitrary wildcard attribute names to string values.
715
+
716
+ Known XML attributes remain flattened as peer properties. Wildcard attributes stay inside the bag so unknown names do not collide with child elements or known attributes.
717
+ ```
718
+
719
+ - [ ] Step 3: Move `xs:anyAttribute` from partial to supported in the full support list
720
+
721
+ In `docs/supported-patterns.md`, add this bullet under `## Fully Supported`:
722
+
723
+ ```markdown
724
+ - `xs:anyAttribute` wildcard attributes emitted as an optional configured attribute bag, defaulting to `$attributes`
725
+ ```
726
+
727
+ Remove this bullet from `## Not Yet Supported` if present:
728
+
729
+ ```markdown
730
+ - Full `xs:any` serialization: arbitrary wildcard content is not emitted as a typed contract
731
+ ```
732
+
733
+ Do not remove `xs:any` particle limitations unless a separate `xs:any` content implementation is done. If the existing bullet is about element wildcards, rewrite it to:
734
+
735
+ ```markdown
736
+ - Full `xs:any` element serialization: arbitrary wildcard element content is not emitted as a typed contract
737
+ ```
738
+
739
+ - [ ] Step 4: Update roadmap wording
740
+
741
+ In `docs/roadmap/v1.0-wsdl-coverage-matrix.md`, change the priority row for `xs:anyAttribute` from:
742
+
743
+ ```markdown
744
+ | `xs:anyAttribute` | partial | Metadata retained; generated wildcard attributes deferred |
745
+ ```
746
+
747
+ to:
748
+
749
+ ```markdown
750
+ | `xs:anyAttribute` | supported | Metadata retained; wildcard attribute bag emitted |
751
+ ```
752
+
753
+ In the same file, remove `xs:anyAttribute` from any list of partial-row caveats.
754
+
755
+ In `docs/roadmap/v1.0-capability-conformance-framework.md`, replace this sentence:
756
+
757
+ ```markdown
758
+ For partial rows, prove the documented subset and avoid implying full support. For `xs-anyattribute`, gateway evidence should prove generation and runtime do not emit or require wildcard attribute bags. For external `PolicyReference`, gateway evidence should prove no generated inbound security requirement appears unless configured elsewhere.
759
+ ```
760
+
761
+ with:
762
+
763
+ ```markdown
764
+ For partial rows, prove the documented subset and avoid implying full support. External `PolicyReference` should not imply fetched or enforced external policy. The `xs-anyattribute` row is supported once generated TypeScript, OpenAPI, gateway, generated-test, and app evidence prove the configured wildcard attribute bag.
765
+ ```
766
+
767
+ - [ ] Step 5: Update changelog
768
+
769
+ If the existing Unreleased roadmap bullet is still present, add this bullet under `## [Unreleased]` in `CHANGELOG.md`:
770
+
771
+ ```markdown
772
+ - feat(xsd): emit `xs:anyAttribute` wildcard bags in generated contracts
773
+ ```
774
+
775
+ Do not remove the existing `docs(roadmap): align 1.0 readiness docs with the 0.35.0 baseline` bullet unless the user explicitly asks to split changes into separate commits.
776
+
777
+ ## Task 9: Run Focused Verification And Update Snapshots If Needed
778
+
779
+ ### Files
780
+
781
+ - Test: `test/unit/compiler-wildcards.test.ts`
782
+ - Test: `test/unit/mock-data.test.ts`
783
+ - Test: `test/conformance/`
784
+ - Test: `test/snapshot/`
785
+
786
+ - [ ] Step 1: Run unit tests for changed internals
787
+
788
+ Run:
789
+
790
+ ```bash
791
+ npx vitest run test/unit/compiler-wildcards.test.ts test/unit/mock-data.test.ts
792
+ ```
793
+
794
+ Expected result: all tests pass.
795
+
796
+ - [ ] Step 2: Run conformance
797
+
798
+ Run:
799
+
800
+ ```bash
801
+ npm run test:conformance
802
+ ```
803
+
804
+ Expected result: all conformance tests pass, and `xs-anyattribute` is now a supported row.
805
+
806
+ - [ ] Step 3: Run snapshot tests
807
+
808
+ Run:
809
+
810
+ ```bash
811
+ npm run test:snap
812
+ ```
813
+
814
+ Expected result: snapshots pass if the weather fixture has no `xs:anyAttribute`. If snapshots fail because generated output intentionally changed, update snapshots with:
815
+
816
+ ```bash
817
+ npx vitest run test/snapshot -u
818
+ ```
819
+
820
+ After updating snapshots, run:
821
+
822
+ ```bash
823
+ npm run test:snap
824
+ ```
825
+
826
+ Expected result: snapshot tests pass after reviewing the diff.
827
+
828
+ - [ ] Step 4: Run docs validation
829
+
830
+ Run:
831
+
832
+ ```bash
833
+ npm run docs:validate
834
+ ```
835
+
836
+ Expected result: Markdown links, TypeScript fenced snippets, and support matrix checks pass.
837
+
838
+ ## Task 10: Run End-To-End Verification
839
+
840
+ ### Files
841
+
842
+ - No source edits expected.
843
+
844
+ - [ ] Step 1: Run typecheck
845
+
846
+ Run:
847
+
848
+ ```bash
849
+ npm run typecheck
850
+ ```
851
+
852
+ Expected result: TypeScript reports no errors.
853
+
854
+ - [ ] Step 2: Run the required smoke pipeline
855
+
856
+ Run:
857
+
858
+ ```bash
859
+ npm run smoke:pipeline
860
+ ```
861
+
862
+ Expected result: the command regenerates disposable output under `tmp/smoke/`, generates client, OpenAPI, gateway, and app artifacts, and `tsc -p tsconfig.smoke.json` passes.
863
+
864
+ - [ ] Step 3: Run the full test suite
865
+
866
+ Run:
867
+
868
+ ```bash
869
+ npm test
870
+ ```
871
+
872
+ Expected result: all Vitest tests pass.
873
+
874
+ - [ ] Step 4: Run full CI before release-candidate work
875
+
876
+ Run:
877
+
878
+ ```bash
879
+ npm run ci
880
+ ```
881
+
882
+ Expected result: build, typecheck, skill validation, package validation, docs validation, Vitest, and smoke pipeline pass.
883
+
884
+ ## Task 11: Review Diff And Commit
885
+
886
+ ### Files
887
+
888
+ - Review: all modified files from Tasks 1 through 10.
889
+
890
+ - [ ] Step 1: Inspect changed files
891
+
892
+ Run:
893
+
894
+ ```bash
895
+ git status --short
896
+ git diff --stat
897
+ ```
898
+
899
+ Expected result: only implementation, test, docs, changelog, and possible snapshot files are modified.
900
+
901
+ - [ ] Step 2: Review semantic diff
902
+
903
+ Run:
904
+
905
+ ```bash
906
+ git diff
907
+ ```
908
+
909
+ Expected result: the diff shows `xs:anyAttribute` moving from metadata-only partial support to supported wildcard-bag output. It must not include unrelated formatting churn or generated output under `tmp/`.
910
+
911
+ - [ ] Step 3: Commit the implementation if requested by the maintainer
912
+
913
+ Run:
914
+
915
+ ```bash
916
+ git add src/compiler/schemaCompiler.ts src/compiler/shapeResolver.ts src/util/attributeWildcards.ts src/client/generateTypes.ts src/openapi/generateSchemas.ts src/test/mockData.ts test/unit/compiler-wildcards.test.ts test/unit/mock-data.test.ts test/conformance/registry.ts docs/supported-patterns.md docs/roadmap/v1.0-wsdl-coverage-matrix.md docs/roadmap/v1.0-capability-conformance-framework.md CHANGELOG.md
917
+ git commit -m "Version: 0.35.1 feat(xsd): emit anyAttribute wildcard bags"
918
+ ```
919
+
920
+ Expected result: git creates a focused commit. If snapshot files changed intentionally, include those snapshot files in the `git add` command before committing.
921
+
922
+ ## Self-Review
923
+
924
+ Spec coverage: The plan covers naming, TypeScript shape, OpenAPI shape, runtime reuse, mock generation, conformance promotion, docs, and verification.
925
+
926
+ Placeholder scan: The plan contains no deferred-work markers or unspecified implementation steps.
927
+
928
+ Type consistency: The plan uses `attributeWildcards`, `localAttributeWildcards`, `attributesKey`, `$attributes`, `Record<string, string>`, and `extra:trace` consistently across compiler, emitters, mocks, and tests.
929
+
930
+ Risk check: The plan intentionally does not implement explicit binding selection, external `PolicyReference` resolution, or full `xs:any` element wildcard serialization.