Conformance suite

Cross-language and cross-tool contract. The future Go provider vendors this directory and must pass identically.

schema/flowdoc-0.1.schema.json    machine-readable FlowDoc definition
        flow-language/actions.md          Connect action types, shapes, and cited doc URLs
        demo/appointment-line.flowdoc.json  the canonical demo flow
        lint/README.md                    fixture format and the rule for adding one
        lint/<rule-id>/pass-*.json        FlowDoc producing no finding for the rule
        lint/<rule-id>/fail-*.json        FlowDoc plus expected findings [{rule, blockId, messageIncludes}]
        roundtrip/<case>/doc.flowdoc.json codegen->synth must reproduce the doc (modulo meta)
        emit-tf/<case>/case.json          emitter case description, inputs, and validate expectation
        emit-tf/<case>/<name>.flowdoc.json  optional; input documents a case does not borrow from demo/ or roundtrip/
        emit-tf/<case>/address-map.json   optional; reference key -> terraform address expression
        emit-tf/<case>/expected/          golden .tf and .tftpl output tree, byte-compared
        emit-tf/<case>/validate/          optional test-only providers and stub resources
        materialize/<case>/doc.flowdoc.json      input FlowDoc for a materialization case
        materialize/<case>/map.json              token -> resolved value (map backend cases)
        materialize/<case>/binder.json           token -> opaque binder output (binder backend cases)
        materialize/<case>/expected.content.json deployable content golden, byte-compared
        export/<case>/inventory.json             recorded instance inventory (the List* responses)
        export/<case>/flows/<id>.json            DescribeContactFlow Content, verbatim live Flow language
        export/<case>/flows/<id>.saved.json      content of a flow that has never been published
        export/<case>/expected/<name>.flowdoc.json  exported FlowDoc golden
        export/<case>/expected/<name>.flow.ts    codegen golden for that FlowDoc
        export/<case>/expected-error.json        {unknownArns, interpolatedArns} for a case that must fail
        schema/scenario-0.1.schema.json          machine-readable simulate scenario definition
        simulate/<case>/scenario.json            authored simulate scenario
        simulate/<case>/expected.testcase.json   compiled CreateTestCase input, tokens still in place
        simulate/invalid/scenarios.json          scenarios that must be rejected, with finding paths
        simulate/report/run.json                 a simulation run
        simulate/report/expected.junit.xml       JUnit reporter golden, byte-compared
        simulate/report/expected.report.json     JSON reporter golden, byte-compared
        

An export case's flows/<id>.json is what DescribeContactFlow returned, so a literal ARN there is the point: it is the input side of export. Only the expected/*.flowdoc.json goldens are authored FlowDocs, and those stay ARN-free. flows/<id>.saved.json stands in for a flow that has never been published: describing it without the $SAVED alias throws ContactFlowNotPublishedException, as the API does.

A simulate expected.testcase.json keeps ${cdref:...} tokens: compilation and token resolution are separate steps, so the golden never carries an ARN. The compiled Content shape is the one artifact here that no offline test can confirm, because CreateTestCase validates it server-side.

An emit-tf case is a whole emitter run. case.json carries a description, a docs array of FlowDoc paths relative to the case directory (a case may point at ../../demo/ or ../../roundtrip/ rather than copy a document), optional options passed to the emitter, and validate, which is pass, fail, or skip. address-map.json, when present, becomes options.addressMap. The emitted files must equal expected/ byte for byte, so the tree also pins the file set: an added or dropped output file fails the case.

validate/ is not emitter output. It holds the minimum provider configuration and stub resources a terraform validate or tofu validate run needs for the emitted addresses to resolve, and every file in it says so at the top. A case marked pass must validate clean with those files present; a case marked fail must fail, which is how the TODO placeholders for unmapped references are proved to be loud rather than silently deployable. Those runs need a real binary and a provider download, so they are gated behind RUN_TOFU_VALIDATE=1 and run in CI's emit-tf job. The goldens themselves are compared on every run.

A materialize case carries map.json or binder.json, never both. Binder cases apply the file as a token -> output lookup so the opaque values are recorded in the fixture. Literal ARNs are legal in map.json and in expected.content.json: producing them is the point of materialization. They remain forbidden in every authored FlowDoc, doc.flowdoc.json included.

Every rule, builder feature, codegen case, and emitter case lands with fixtures in the same commit.

Roundtrip fixtures are FlowDocs in synth normal form: declaration-ordered Actions, Errors and Conditions arrays present (possibly empty) on every non-terminal action, {} transitions on terminal actions, sorted keys, and a layout covering every action. A provider generates code from doc.flowdoc.json, executes it, synthesizes the result, and must get the same document back (ignoring meta). The seven cases cover the demo flow (appointment-line), a flow of entirely unmodeled action types with tokens inside parameters (unknown-actions), a module invoking another module by alias (after-call-survey), a flow where Compare is the only user of jsonPath (compare-only, so the import is emitted for it), a flow of GetParticipantInput menus with Text, SSML and PromptId bodies (dtmf-menu), a GetParticipantInput whose key branches twice, which the builder refuses and so must stay GenericBlock (repeated-key), and edge cases (SSML, PromptId refs, Compare branches, JSONPath refs, hand-placed layout, an explicit start, and a modeled Type that must fall back to GenericBlock).

The demo flow itself is the synth fixture: packages/core/src/synth.test.ts builds it with the typed builder and compares the result against demo/appointment-line.flowdoc.json.

Fixtures are data, not source: they are excluded from ESLint and Prettier so that goldens stay byte-stable. The assertions that guard this directory live in packages/core/src/conformance.test.ts.